<commit_before>%% -*- erlang-indent-level: 4;indent-tabs-mode: nil; fill-column: 92 -*-
%% ex: ts=4 sw=4 et
%% @author <NAME> <<EMAIL>>
%% Copyright 2013 Opscode, Inc. All Rights Reserved.

-module(oc_chef_wm_groups).

-include_lib("chef_wm/include/chef_wm.hrl").
-include_lib("oc_chef_wm.hrl").
-include_lib("eunit/include/eunit.hrl").

-mixin([{chef_wm_base, [
                        content_types_accepted/2,
                        content_types_provided/2,
                        finish_request/2,
                        malformed_request/2,
                        ping/2,
                        post_is_create/2,
                        {list_objects_json/2, to_json}
                       ]}]).

-mixin([{oc_chef_wm_base, [forbidden/2,
                           is_authorized/2,
                           service_available/2]}]).

-behaviour(chef_wm).

-export([
         auth_info/2,
         init/1,
         init_resource_state/1,
         malformed_request_message/3,
         request_type/0,
         validate_request/3
        ]).

-export([
         allowed_methods/2,
         conflict_message/1,
         create_path/2,
         from_json/2,
         resource_exists/2
        ]).

-export([
         validate_group_name/1,
         group_name_invalid/2
        ]).

init(Config) ->
    chef_wm_base:init(?MODULE, Config).

init_resource_state(_Config) ->
    {ok, #group_state{}}.

request_type() ->
    "groups".

allowed_methods(Req, State) ->
    {['GET', 'POST'], Req, State}.

validate_request('GET', Req, #base_state{organization_guid = OrgId} = State) ->
    {Req, State#base_state{superuser_bypasses_checks = true,
                           resource_state = #oc_chef_group{org_id = OrgId}}};
validate_request('POST', Req, #base_state{organization_guid = OrgId,
                                          resource_state = ResourceState}
                 = State) ->
    Body = wrq:req_body(Req),
    {ok, Json} = oc_chef_group:parse_binary_json(Body),
    {Req, State#base_state{superuser_bypasses_checks = true,
                           resource_state = ResourceState#group_state{
                                              oc_chef_group =
                                                  #oc_chef_group{
                                                     org_id = OrgId},
                                              group_data = Json}}}.

auth_info(Req, State) ->
    auth_info(wrq:method(Req), Req, State).

auth_info('GET', Req, State ) ->
    {{container, group}, Req, State};
auth_info('POST', Req, State = #base_state{resource_state = #group_state{group_data = Json}}) ->
    case validate_group_name(ej:get({<<"id">>}, Json, ej:get({<<"groupname">>}, Json))) of
        invalid ->
            group_name_invalid(Req, State);
        missing ->
            group_name_invalid(Req, State);
        valid ->
            {{create_in_container, group}, Req, State}
    end.

resource_exists(Req, State) ->
    {true, Req, State}.

create_path(Req, #base_state{resource_state = #group_state{group_data = GroupData}} = State) ->
    Name = ej:get({<<"id">>}, GroupData, ej:get({<<"groupname">>}, GroupData)),
    {binary_to_list(Name), Req, State}.

from_json(Req, #base_state{resource_state = #group_state{group_data = GroupData,
                                                             group_authz_id = AuthzId}} = State) ->
    chef_wm_base:create_from_json(Req, State, oc_chef_group, {authz_id, AuthzId}, GroupData).

malformed_request_message(Any, _Req, _State) ->
    error({unexpected_malformed_request_message, Any}).

-spec conflict_message(binary()) -> ejson_term().
conflict_message(_Name) ->
    {[{<<"error">>, [<<"Group already exists">>]}]}.

-define(VALID_NAME_REGEX, "^[a-z0-9\-_]+$").

validate_group_name(undefined) ->
    missing;
validate_group_name(Name) ->
    {ok, CompiledRegex} = re:compile(?VALID_NAME_REGEX),
    case re:run(Name, CompiledRegex) of
        {match, _} ->
            valid;
        nomatch  ->
            invalid
    end.

group_name_invalid(Req, State) ->
    Msg = <<"Invalid group name.">>,
    JsonError = {[{<<"error">>, [Msg]}]},
    {{halt, 400}, chef_wm_util:set_json_body(Req, JsonError), State}.
<commit_msg>More PR comments<commit_after>%% -*- erlang-indent-level: 4;indent-tabs-mode: nil; fill-column: 92 -*-
%% ex: ts=4 sw=4 et
%% @author <NAME> <<EMAIL>>
%% Copyright 2013 Opscode, Inc. All Rights Reserved.

-module(oc_chef_wm_groups).

-include_lib("chef_wm/include/chef_wm.hrl").
-include_lib("oc_chef_wm.hrl").
-include_lib("eunit/include/eunit.hrl").

-mixin([{chef_wm_base, [
                        content_types_accepted/2,
                        content_types_provided/2,
                        finish_request/2,
                        malformed_request/2,
                        ping/2,
                        post_is_create/2,
                        {list_objects_json/2, to_json}
                       ]}]).

-mixin([{oc_chef_wm_base, [forbidden/2,
                           is_authorized/2,
                           service_available/2]}]).

-behaviour(chef_wm).

-export([
         auth_info/2,
         init/1,
         init_resource_state/1,
         malformed_request_message/3,
         request_type/0,
         validate_request/3
        ]).

-export([
         allowed_methods/2,
         conflict_message/1,
         create_path/2,
         from_json/2,
         resource_exists/2
        ]).

-export([
         validate_group_name/1,
         group_name_invalid/2
        ]).

init(Config) ->
    chef_wm_base:init(?MODULE, Config).

init_resource_state(_Config) ->
    {ok, #group_state{}}.

request_type() ->
    "groups".

allowed_methods(Req, State) ->
    {['GET', 'POST'], Req, State}.

validate_request('GET', Req, #base_state{organization_guid = OrgId} = State) ->
    {Req, State#base_state{superuser_bypasses_checks = true,
                           resource_state = #oc_chef_group{org_id = OrgId}}};
validate_request('POST', Req, #base_state{organization_guid = OrgId,
                                          resource_state = ResourceState}
                 = State) ->
    Body = wrq:req_body(Req),
    {ok, Json} = oc_chef_group:parse_binary_json(Body),
    {Req, State#base_state{superuser_bypasses_checks = true,
                           resource_state = ResourceState#group_state{
                                              oc_chef_group =
                                                  #oc_chef_group{
                                                     org_id = OrgId},
                                              group_data = Json}}}.

auth_info(Req, State) ->
    auth_info(wrq:method(Req), Req, State).

auth_info('GET', Req, State ) ->
    {{container, group}, Req, State};
auth_info('POST', Req, State = #base_state{resource_state = #group_state{group_data = Json}}) ->
    case validate_group_name(fetch_id_name_from_json(Json)) of
        valid ->
            {{create_in_container, group}, Req, State};
        invalid ->
            group_name_invalid(Req, State)
    end.

resource_exists(Req, State) ->
    {true, Req, State}.

create_path(Req, #base_state{resource_state = #group_state{group_data = GroupData}} = State) ->
    Name = fetch_id_name_from_json(GroupData),
    {binary_to_list(Name), Req, State}.

from_json(Req, #base_state{resource_state = #group_state{group_data = GroupData,
                                                             group_authz_id = AuthzId}} = State) ->
    chef_wm_base:create_from_json(Req, State, oc_chef_group, {authz_id, AuthzId}, GroupData).

malformed_request_message(Any, _Req, _State) ->
    error({unexpected_malformed_request_message, Any}).

-spec conflict_message(binary()) -> ejson_term().
conflict_message(_Name) ->
    {[{<<"error">>, [<<"Group already exists">>]}]}.

-define(VALID_NAME_REGEX, "^[a-z0-9\-_]+$").

validate_group_name(undefined) ->
    invalid;
validate_group_name(Name) ->
    {ok, CompiledRegex} = re:compile(?VALID_NAME_REGEX),
    case re:run(Name, CompiledRegex) of
        {match, _} ->
            valid;
        nomatch  ->
            invalid
    end.

group_name_invalid(Req, State) ->
    Msg = <<"Invalid group name.">>,
    JsonError = {[{<<"error">>, [Msg]}]},
    {{halt, 400}, chef_wm_util:set_json_body(Req, JsonError), State}.

%% We allow either id or groupname to be set for a group.  Precedence is
%% given to id over groupname if both set.
fetch_id_name_from_json(GroupJson) ->
    ej:get({<<"id">>}, GroupJson, ej:get({<<"groupname">>}, GroupJson)).
{'fraction_non_alphanumeric': 0.13769645789350224, 'fraction_numerical': 0.008679333802486512, 'mean_word_length': 2.115454877603215, 'pattern_counts': {'":': 0, '<': 37, '<?xml version=': 0, '>': 83, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 24, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '6406201', 'n_tokens_mistral': 2765, 'n_tokens_neox': 2698, 'n_words': 446}