%%%----------------------------------------------------------------------------- %%% @copyright (C) 2011-2019, 2600Hz %%% @doc %%% @author Karl Anderson %%% @author James Aimonetti %%% @end %%%----------------------------------------------------------------------------- -module(cb_groups). -export([init/0 ,allowed_methods/0, allowed_methods/1 ,resource_exists/0, resource_exists/1 ,validate/1, validate/2 ,put/1 ,post/2 ,patch/2 ,delete/2 ]). -include("crossbar.hrl"). -define(CB_LIST, <<"groups/crossbar_listing">>). -define(CB_LIST_BY_USER, <<"groups/crossbar_listing_by_user">>). %%%============================================================================= %%% API %%%============================================================================= %%------------------------------------------------------------------------------ %% @doc Initializes the bindings this module will respond to. %% @end %%------------------------------------------------------------------------------ -spec init() -> 'ok'. init() -> _ = crossbar_bindings:bind(<<"*.allowed_methods.groups">>, ?MODULE, 'allowed_methods'), _ = crossbar_bindings:bind(<<"*.resource_exists.groups">>, ?MODULE, 'resource_exists'), _ = crossbar_bindings:bind(<<"*.validate.groups">>, ?MODULE, 'validate'), _ = crossbar_bindings:bind(<<"*.execute.put.groups">>, ?MODULE, 'put'), _ = crossbar_bindings:bind(<<"*.execute.post.groups">>, ?MODULE, 'post'), _ = crossbar_bindings:bind(<<"*.execute.patch.groups">>, ?MODULE, 'patch'), crossbar_bindings:bind(<<"*.execute.delete.groups">>, ?MODULE, 'delete'). %%------------------------------------------------------------------------------ %% @doc Given the path tokens related to this module, what HTTP methods are %% going to be responded to. %% @end %%------------------------------------------------------------------------------ -spec allowed_methods() -> http_methods(). allowed_methods() -> [?HTTP_GET, ?HTTP_PUT]. -spec allowed_methods(path_token()) -> http_methods(). allowed_methods(_GroupId) -> [?HTTP_GET, ?HTTP_POST, ?HTTP_PATCH, ?HTTP_DELETE]. %%------------------------------------------------------------------------------ %% @doc Does the path point to a valid resource. %% For example: %% %% ``` %% /groups => [] %% /groups/foo => [<<"foo">>] %% /groups/foo/bar => [<<"foo">>, <<"bar">>] %% ''' %% @end %%------------------------------------------------------------------------------ -spec resource_exists() -> 'true'. resource_exists() -> 'true'. -spec resource_exists(path_token()) -> 'true'. resource_exists(_) -> 'true'. %%------------------------------------------------------------------------------ %% @doc Check the request (request body, query string params, path tokens, etc) %% and load necessary information. %% /groups might load a list of group objects %% /groups/123 might load the group object 123 %% Generally, use crossbar_doc to manipulate the cb_context{} record %% @end %%------------------------------------------------------------------------------ -spec validate(cb_context:context()) -> cb_context:context(). validate(Context) -> validate_groups(Context, cb_context:req_verb(Context)). validate_groups(Context, ?HTTP_GET) -> summary(Context); validate_groups(Context, ?HTTP_PUT) -> create(Context). -spec validate(cb_context:context(), path_token()) -> cb_context:context(). validate(Context, Id) -> validate_group(Context, Id, cb_context:req_verb(Context)). validate_group(Context, Id, ?HTTP_GET) -> read(Id, Context); validate_group(Context, Id, ?HTTP_POST) -> update(Id, Context); validate_group(Context, Id, ?HTTP_PATCH) -> validate_patch(Id, Context); validate_group(Context, Id, ?HTTP_DELETE) -> read(Id, Context). %%------------------------------------------------------------------------------ %% @doc If the HTTP verb is PUT, execute the actual action, usually a db save. %% @end %%------------------------------------------------------------------------------ -spec put(cb_context:context()) -> cb_context:context(). put(Context) -> crossbar_doc:save(Context). %%------------------------------------------------------------------------------ %% @doc If the HTTP verb is POST, execute the actual action, usually a db save %% (after a merge perhaps). %% @end %%------------------------------------------------------------------------------ -spec post(cb_context:context(), path_token()) -> cb_context:context(). post(Context, _) -> crossbar_doc:save(Context). %%------------------------------------------------------------------------------ %% @doc If the HTTP verb is PATCH, execute the actual action, usually a db save %% (after a merge perhaps). %% @end %%------------------------------------------------------------------------------ -spec patch(cb_context:context(), path_token()) -> cb_context:context(). patch(Context, _) -> crossbar_doc:save(Context). %%------------------------------------------------------------------------------ %% @doc If the HTTP verb is DELETE, execute the actual action, usually a db delete %% @end %%------------------------------------------------------------------------------ -spec delete(cb_context:context(), path_token()) -> cb_context:context(). delete(Context, _) -> crossbar_doc:delete(Context). %%------------------------------------------------------------------------------ %% @doc Create a new instance with the data provided, if it is valid %% @end %%------------------------------------------------------------------------------ -spec create(cb_context:context()) -> cb_context:context(). create(Context) -> OnSuccess = fun(C) -> on_successful_validation('undefined', C) end, cb_context:validate_request_data(<<"groups">>, Context, OnSuccess). %%------------------------------------------------------------------------------ %% @doc Load an instance from the database %% @end %%------------------------------------------------------------------------------ -spec read(kz_term:ne_binary(), cb_context:context()) -> cb_context:context(). read(Id, Context) -> crossbar_doc:load(Id, Context, ?TYPE_CHECK_OPTION(<<"group">>)). %%------------------------------------------------------------------------------ %% @doc Update an existing menu document with the data provided, if it is %% valid %% @end %%------------------------------------------------------------------------------ -spec update(kz_term:ne_binary(), cb_context:context()) -> cb_context:context(). update(Id, Context) -> OnSuccess = fun(C) -> on_successful_validation(Id, C) end, cb_context:validate_request_data(<<"groups">>, Context, OnSuccess). %%------------------------------------------------------------------------------ %% @doc Update an existing menu document with the data provided, if it is %% valid %% @end %%------------------------------------------------------------------------------ -spec validate_patch(kz_term:ne_binary(), cb_context:context()) -> cb_context:context(). validate_patch(Id, Context) -> crossbar_doc:patch_and_validate(Id, Context, fun update/2). %%------------------------------------------------------------------------------ %% @doc Attempt to load a summarized listing of all instances of this %% resource. %% @end %%------------------------------------------------------------------------------ -spec summary(cb_context:context()) -> cb_context:context(). summary(Context) -> case cb_context:user_id(Context) of 'undefined' -> crossbar_doc:load_view(?CB_LIST, [], Context, fun normalize_view_results/2); UserId -> crossbar_doc:load_view(?CB_LIST_BY_USER, [{'key', UserId}], Context, fun normalize_view_results/2) end. %%------------------------------------------------------------------------------ %% @doc %% @end %%------------------------------------------------------------------------------ -spec on_successful_validation(kz_term:api_binary(), cb_context:context()) -> cb_context:context(). on_successful_validation('undefined', Context) -> cb_context:set_doc(Context, kz_doc:set_type(cb_context:doc(Context), <<"group">>)); on_successful_validation(Id, Context) -> crossbar_doc:load_merge(Id, Context, ?TYPE_CHECK_OPTION(<<"group">>)). %%------------------------------------------------------------------------------ %% @doc Normalizes the results of a view. %% @end %%------------------------------------------------------------------------------ -spec normalize_view_results(kz_json:object(), kz_json:objects()) -> kz_json:objects(). normalize_view_results(JObj, Acc) -> [kz_json:get_value(<<"value">>, JObj)|Acc].