Skip to content

Commit

Permalink
Props, scripts, and specs (2600hz#2611)
Browse files Browse the repository at this point in the history
* props_usage: simple script to dialyze module usage

./scripts/dialyze-usage.bash props

Will find all calls to the props module and dialyze those beam
files (plus the props.beam). Replace 'props' with module of your
choice (like kz_json, gen_listener, etc)

* props_usage: fix spec

* props_usage: remove unreachable clause

* props_usage: fix return type

* props_usage: fill in spec

* props_usage: update specs

* props_usage: fix return type

* props_usage: descriptive type

* props_usage: fix specs up

* props_usage: filename type updates

* props_usage: fix type, use cast getter

* props_usage: spec refactoring

* props_usage: short-circuit function sooner

* remove unused code

* props_usage: create README of all scripts

* props_usage: cleanup some dialyzer complaints

* props_usage: PR comments
  • Loading branch information
jamesaimonetti authored and fenollp committed Sep 29, 2016
1 parent 714554d commit 81565b8
Show file tree
Hide file tree
Showing 23 changed files with 1,277 additions and 93 deletions.
2 changes: 1 addition & 1 deletion applications/blackhole/src/bh_context.erl
Original file line number Diff line number Diff line change
Expand Up @@ -157,7 +157,7 @@ auth_account_id(#bh_context{auth_account_id=AuthBy}) ->
set_auth_account_id(#bh_context{}=Context, AuthBy) ->
Context#bh_context{auth_account_id=AuthBy}.

-spec is_superduper_admin(context()) -> api_binary().
-spec is_superduper_admin(context()) -> boolean().
is_superduper_admin(#bh_context{auth_account_id=AccountId}) ->
kz_util:is_system_admin(AccountId).

Expand Down
2 changes: 1 addition & 1 deletion applications/callflow/src/cf_route_win.erl
Original file line number Diff line number Diff line change
Expand Up @@ -202,7 +202,7 @@ get_callee_extension_info(Call) ->
%%
%% @end
%%-----------------------------------------------------------------------------
-spec bootstrap_callflow_executer(kz_json:object(), kapps_call:call()) -> {'ok', pid()}.
-spec bootstrap_callflow_executer(kz_json:object(), kapps_call:call()) -> kapps_call:call().
bootstrap_callflow_executer(_JObj, Call) ->
Routines = [fun store_owner_id/1
,fun set_language/1
Expand Down
18 changes: 14 additions & 4 deletions applications/camper/src/camper_onnet_handler.erl
Original file line number Diff line number Diff line change
Expand Up @@ -262,7 +262,6 @@ get_endpoints(Call, EndpointId, <<"device">>) ->
]),
case kz_endpoint:build(EndpointId, Properties, Call) of
{'error', _} -> [];
{'ok', []} -> [];
{'ok', Endpoints} -> Endpoints
end;
get_endpoints(Call, UserId, <<"user">>) ->
Expand All @@ -274,7 +273,10 @@ get_endpoints(Call, UserId, <<"user">>) ->
{'ok', Endpoint} -> Endpoint ++ Acc;
{'error', _E} -> Acc
end
end, [], kz_attributes:owned_by(UserId, <<"device">>, Call));
end
,[]
,kz_attributes:owned_by(UserId, <<"device">>, Call)
);
get_endpoints(_, _, _) ->
[].

Expand All @@ -288,13 +290,21 @@ clear_request(Requestor, Exten, Local) ->
Qs1 = dict:store(SIPName, Queue, Qs),
Reqs = dict:erase({SIPName, Requestor}, get_requests(Acc)),
set_requests(set_requestor_queues(Acc, Qs1), Reqs)
end, Local, SIPNames).
end
,Local
,SIPNames
).

-spec make_requests(ne_binaries(), {ne_binary(), ne_binary()}, ne_binary(), non_neg_integer()) -> dict:dict().
make_requests(SIPNames, Requestor, Exten, Timeout) ->
R = [{SIPName, Requestor} || SIPName <- SIPNames],
{_, Seconds, _} = os:timestamp(),
lists:foldl(fun(Req, Acc) -> dict:store(Req, {Exten, Seconds + Timeout}, Acc) end, dict:new(), R).
lists:foldl(fun(Req, Acc) ->
dict:store(Req, {Exten, Seconds + Timeout}, Acc)
end
,dict:new()
,R
).

-spec maybe_update_queues(ne_binaries(), {ne_binary(), ne_binary()}, dict:dict()) -> dict:dict().
maybe_update_queues(SIPNames, Requestor, Queues) ->
Expand Down
9 changes: 5 additions & 4 deletions applications/cccp/src/cccp_callback_listener.erl
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ start_link(JObj) ->
,{'consume_options', ?CONSUME_OPTIONS}
], [JObj]).

-spec init(kz_json:object()) -> {'ok', state()}.
-spec init([kz_json:object()]) -> {'ok', state()}.
init([JObj]) ->
ALegName = kz_json:get_value(<<"a_leg_name">>, JObj),
ALegNumber = kz_json:get_value(<<"a_leg_number">>, JObj),
Expand Down Expand Up @@ -242,7 +242,7 @@ handle_resource_response(JObj, Props) ->
_ -> 'ok'
end.

-spec handle_originate_response(kz_json:object(), server_ref()) -> 'ok'.
-spec handle_originate_response(kz_json:object(), gen_listener:callback_data()) -> 'ok'.
handle_originate_response(JObj, Props) ->
Srv = props:get_value('server', Props),
case {kz_json:get_value(<<"Application-Name">>, JObj)
Expand Down Expand Up @@ -285,14 +285,15 @@ b_leg_number(Props) ->
BLegNumber
end.

-spec maybe_make_announcement_to_a_leg(kz_proplist()) -> ne_binary().
-spec maybe_make_announcement_to_a_leg(kz_proplist()) -> 'ok'.
maybe_make_announcement_to_a_leg(Props) ->
case props:get_value('media_id', Props) of
<<MediaId:32/binary>> ->
Call = call(Props),
MediaPath = kz_media_util:media_path(MediaId, Call),
_ = timer:sleep(?PROMPT_DELAY),
kapps_call_command:b_play(MediaPath, Call);
_ = kapps_call_command:b_play(MediaPath, Call),
'ok';
_ -> 'ok'
end.

Expand Down
23 changes: 13 additions & 10 deletions applications/cccp/src/cccp_util.erl
Original file line number Diff line number Diff line change
Expand Up @@ -189,7 +189,7 @@ cccp_allowed_callee(Number) ->
'true'
end.

-spec build_request(_,ne_binary(),_,_,_,ne_binary(),_,ne_binary(),ne_binary(),ne_binary()) -> any().
-spec build_request(api_binary(), api_binary(), api_binary(), api_binary(), api_binary(), api_binary(), api_binary(), ne_binary(),binary(),binary()) -> kz_proplist().
build_request(CallId, ToDID, AuthorizingId, Q, CtrlQ, AccountId, Action, RetainCID, RetainName, RetainNumber) ->
Realm = kz_util:get_account_realm(AccountId),
CCVs = props:filter_undefined([{<<"Account-ID">>, AccountId}
Expand Down Expand Up @@ -232,12 +232,13 @@ build_request(CallId, ToDID, AuthorizingId, Q, CtrlQ, AccountId, Action, RetainC
| kz_api:default_headers(Q, <<"resource">>, <<"originate_req">>, ?APP_NAME, ?APP_VERSION)
]).

-spec bridge(ne_binary(), ne_binary(), ne_binary(), ne_binary(), ne_binary(), ne_binary(), ne_binary(), ne_binary()) -> 'ok'.
-spec bridge(ne_binary(), ne_binary(), ne_binary(), ne_binary(), ne_binary(), ne_binary(), binary(), binary()) -> 'ok'.
bridge(CallId, ToDID, AuthorizingId, CtrlQ, AccountId, RetainCID, RetainName, RetainNumber) ->
Req = build_request(CallId, ToDID, AuthorizingId, 'undefined', CtrlQ, AccountId, <<"bridge">>, RetainCID, RetainName, RetainNumber),
kapi_resource:publish_originate_req(Req).

-spec compose_cid(ne_binary(), ne_binary(), ne_binary(), ne_binary(), ne_binary()) -> {ne_binary(), ne_binary()}.
-spec compose_cid(ne_binary(), ne_binary(), binary(), binary(), ne_binary()) ->
{api_binary(), api_binary()}.
compose_cid(ToDID, RetainCID, RetainNumber, RetainName, AccountId) ->
case RetainCID of
<<"true">> ->
Expand All @@ -246,33 +247,35 @@ compose_cid(ToDID, RetainCID, RetainNumber, RetainName, AccountId) ->
{'undefined','undefined'}
end.

-spec maybe_outbound_call(ne_binary(), ne_binary(), ne_binary(), ne_binary()) -> {ne_binary(), ne_binary()}.
-spec maybe_outbound_call(ne_binary(), binary(), binary(), ne_binary()) ->
{binary(), binary()}.
maybe_outbound_call(ToDID, RetainNumber, RetainName, AccountId) ->
case knm_converters:is_reconcilable(ToDID) of
'false' -> {RetainNumber, RetainName};
'true' ->
case knm_converters:is_reconcilable(RetainNumber) of
'true' ->
{knm_converters:normalize(RetainNumber), RetainName};
'false' ->
AccountDb = kz_util:format_account_id(AccountId, 'encoded'),
kz_attributes:maybe_get_assigned_number('undefined', RetainName, AccountDb)
end;
_ ->
{RetainNumber, RetainName}
end
end.

-spec maybe_cid_name(ne_binary(), ne_binary()) -> ne_binary().
maybe_cid_name(<<Name/binary>>, _) -> Name;
maybe_cid_name(_, Number) -> Number.

-spec build_presence(ne_binary()|'undefined', ne_binary()) -> ne_binary()|'undefined'.
-spec build_presence(api_ne_binary(), ne_binary()) -> api_ne_binary().
build_presence(<<Number/binary>>, Realm) -> <<Number/binary, "@", Realm/binary>>;
build_presence(_, _) -> 'undefined'.

-spec current_account_outbound_directions(ne_binary()) -> ne_binaries().
current_account_outbound_directions(AccountId) ->
[kz_json:get_value(<<"destination">>, Channel) || Channel <- current_account_channels(AccountId)
,kz_json:get_value(<<"direction">>, Channel) == <<"outbound">>].
[kz_json:get_value(<<"destination">>, Channel)
|| Channel <- current_account_channels(AccountId),
<<"outbound">> =:= kz_json:get_value(<<"direction">>, Channel)
].

-spec count_user_legs(ne_binary(), ne_binary()) -> integer().
count_user_legs(UserId, AccountId) ->
Expand Down
10 changes: 5 additions & 5 deletions applications/conference/src/conf_config_req.erl
Original file line number Diff line number Diff line change
Expand Up @@ -83,12 +83,12 @@ default_profile() ->
,kz_json:from_list(?DEFAULT_PROFILE_CONFIG)
).

-spec page_profile() -> wh_json:object().
-spec page_profile() -> kz_json:object().
page_profile() ->
kapps_config:get(?CONFIG_CAT
,[<<"profiles">>, ?PAGE_PROFILE_NAME]
,kz_json:from_list(?PAGE_PROFILE_CONFIG)
).
kapps_config:get_json(?CONFIG_CAT
,[<<"profiles">>, ?PAGE_PROFILE_NAME]
,kz_json:from_list(?PAGE_PROFILE_CONFIG)
).

-spec profiles(ne_binary(), kz_json:object()) -> kz_json:object().
profiles(ConfigName, Profile) ->
Expand Down
3 changes: 2 additions & 1 deletion applications/crossbar/src/cb_context.erl
Original file line number Diff line number Diff line change
Expand Up @@ -307,7 +307,8 @@ setters(#cb_context{}=Context, []) -> Context;
setters(#cb_context{}=Context, [_|_]=Setters) ->
lists:foldl(fun setters_fold/2, Context, Setters).

-spec setters_fold(setter_kv(), context() | kz_json:object()) -> context().
-spec setters_fold(setter_kv(), context() | kz_json:object()) ->
context() | kz_json:object().
setters_fold({F, V}, C) -> F(C, V);
setters_fold({F, K, V}, C) -> F(C, K, V);
setters_fold(F, C) when is_function(F, 1) -> F(C).
Expand Down
7 changes: 4 additions & 3 deletions applications/crossbar/src/crossbar_init.erl
Original file line number Diff line number Diff line change
Expand Up @@ -116,8 +116,9 @@ start_mod_version(Version, Mod) ->
%% @public
%% @doc Load a crossbar module's bindings into the bindings server
%%--------------------------------------------------------------------
-spec stop_mod(atom() | string() | binary()) -> any().
stop_mod(CBMod) when not is_atom(CBMod) -> stop_mod(kz_util:to_atom(CBMod, 'true'));
-spec stop_mod(atom() | string() | binary()) -> 'ok'.
stop_mod(CBMod) when not is_atom(CBMod) ->
stop_mod(kz_util:to_atom(CBMod, 'true'));
stop_mod(CBMod) ->
crossbar_bindings:flush_mod(CBMod),
case erlang:function_exported(CBMod, 'stop', 0) of
Expand All @@ -127,7 +128,7 @@ stop_mod(CBMod) ->
maybe_stop_mod_versions(?VERSION_SUPPORTED, CBMod)
end.

-spec do_stop_mod(atom()) -> any().
-spec do_stop_mod(atom()) -> 'ok'.
do_stop_mod(CBMod) ->
try CBMod:stop() of
_ -> 'ok'
Expand Down
39 changes: 19 additions & 20 deletions applications/crossbar/src/crossbar_maintenance.erl
Original file line number Diff line number Diff line change
Expand Up @@ -164,13 +164,10 @@ persist_module(Module, Mods) ->
%%--------------------------------------------------------------------
-spec stop_module(text()) -> 'ok'.
stop_module(Module) ->
case crossbar_init:stop_mod(Module) of
'ok' ->
Mods = crossbar_config:autoload_modules(),
crossbar_config:set_default_autoload_modules(lists:delete(kz_util:to_binary(Module), Mods)),
io:format("stopped and removed ~s from autoloaded modules~n", [Module]);
{'error', Error} -> io:format("failed to stop ~s: ~p~n", [Module, Error])
end.
'ok' = crossbar_init:stop_mod(Module),
Mods = crossbar_config:autoload_modules(),
crossbar_config:set_default_autoload_modules(lists:delete(kz_util:to_binary(Module), Mods)),
io:format("stopped and removed ~s from autoloaded modules~n", [Module]).

%%--------------------------------------------------------------------
%% @public
Expand Down Expand Up @@ -779,11 +776,11 @@ find_apps(AppsPath) ->
end,
filelib:fold_files(AppsPath, "app\\.json", 'true', AccFun, []).

-spec init_app(file:filename()) -> 'ok'.
-spec init_app(file:filename_all()) -> 'ok'.
init_app(AppPath) ->
init_app(AppPath, 'undefined').

-spec init_app(file:filename(), api_binary()) -> 'ok'.
-spec init_app(file:filename_all(), api_binary()) -> 'ok'.
init_app(AppPath, AppUrl) ->
io:format("trying to init app from ~s~n", [AppPath]),
try find_metadata(AppPath) of
Expand All @@ -805,8 +802,8 @@ maybe_set_api_url('undefined', MetaData) ->
maybe_set_api_url(AppUrl, MetaData) ->
kz_json:set_value(<<"api_url">>, AppUrl, MetaData).

-spec maybe_create_app(file:filename(), kz_json:object()) -> 'ok'.
-spec maybe_create_app(file:filename(), kz_json:object(), ne_binary()) -> 'ok'.
-spec maybe_create_app(file:filename_all(), kz_json:object()) -> 'ok'.
-spec maybe_create_app(file:filename_all(), kz_json:object(), ne_binary()) -> 'ok'.
maybe_create_app(AppPath, MetaData) ->
{'ok', MasterAccountDb} = kapps_util:get_master_account_db(),
maybe_create_app(AppPath, MetaData, MasterAccountDb).
Expand All @@ -821,7 +818,7 @@ maybe_create_app(AppPath, MetaData, MasterAccountDb) ->
{'error', _E} -> io:format(" failed to find app ~s: ~p", [AppName, _E])
end.

-spec maybe_update_app(file:filename(), kz_json:object(), ne_binary(), kz_json:object()) -> 'ok'.
-spec maybe_update_app(file:filename_all(), kz_json:object(), ne_binary(), kz_json:object()) -> 'ok'.
maybe_update_app(AppPath, MetaData, MasterAccountDb, AppJObj) ->
ApiUrlKey = <<"api_url">>,
CurrentDocId = kzd_app:id(AppJObj),
Expand All @@ -845,7 +842,7 @@ find_app(Db, Name) ->
ViewOptions = [{'key', Name}],
kz_datamgr:get_single_result(Db, ?CB_APPS_STORE_LIST, ViewOptions).

-spec create_app(file:filename(), kz_json:object(), ne_binary()) -> 'ok'.
-spec create_app(file:filename_all(), kz_json:object(), ne_binary()) -> 'ok'.
create_app(AppPath, MetaData, MasterAccountDb) ->
Doc0 = kz_doc:update_pvt_parameters(MetaData, MasterAccountDb, [{'type', <<"app">>}]),
Doc = kz_json:delete_keys([<<"source_url">>], Doc0),
Expand Down Expand Up @@ -874,7 +871,7 @@ safe_delete_image(AccountDb, AppId, Image) ->
kz_datamgr:delete_attachment(AccountDb, AppId, Image)
end.

-spec maybe_add_images(file:filename(), ne_binary(), kz_json:object(), ne_binary()) -> 'ok'.
-spec maybe_add_images(file:filename_all(), ne_binary(), kz_json:object(), ne_binary()) -> 'ok'.
maybe_add_images(AppPath, ?NE_BINARY=AppId, MetaData, MasterAccountDb) ->
Icon = kzd_app:icon(MetaData),
Screenshots = [kzd_app:screenshots(MetaData)],
Expand All @@ -887,7 +884,7 @@ maybe_add_images(AppPath, ?NE_BINARY=AppId, MetaData, MasterAccountDb) ->
_ = update_images(AppId, MasterAccountDb, [IconPath], <<"icon">>),
_ = update_images(AppId, MasterAccountDb, SShotPaths, <<"screenshots">>).

-type image_path() :: {kz_json:object(), file:filename()}.
-type image_path() :: {kz_json:object(), file:filename_all()}.
-type image_paths() :: [image_path()].

-spec update_images(ne_binary(), ne_binary(), image_paths(), ne_binary()) -> 'ok'.
Expand All @@ -908,26 +905,28 @@ add_images(AppId, MasterAccountDb, Images) ->
],
'ok'.

-spec add_image(ne_binary(), ne_binary(), file:filename(), binary()) -> 'ok'.
-spec add_image(ne_binary(), ne_binary(), file:filename_all(), binary()) -> 'ok'.
add_image(AppId, MasterAccountDb, ImageId, ImageData) ->
case kz_datamgr:put_attachment(MasterAccountDb, AppId, ImageId, ImageData) of
{'ok', _} -> io:format(" saved ~s to ~s~n", [ImageId, AppId]);
{'error', _E} -> io:format(" failed to save ~s to ~s: ~p~n", [ImageId, AppId, _E])
end.

-spec read_images([{ne_binary(), file:filename()}]) -> {'ok', [{file:filename(), binary()}]}.
-spec read_images([{ne_binary(), file:filename_all()}]) ->
{'ok', [{file:filename_all(), binary()}]}.
read_images(Images) ->
{'ok', [{Image, read_image(ImagePath)}
|| {Image, ImagePath} <- Images
]}.

-spec read_image(file:filename()) -> binary().
-spec read_image(file:filename_all()) -> binary().
read_image(File) ->
{'ok', ImageData} = file:read_file(File),
ImageData.

-spec find_metadata(file:filename()) -> {'ok', kz_json:object()} |
{'invalid_data', kz_proplist()}.
-spec find_metadata(file:filename_all()) ->
{'ok', kz_json:object()} |
{'invalid_data', kz_proplist()}.
find_metadata(AppPath) ->
{'ok', JSON} = file:read_file(filename:join([AppPath, <<"metadata">>, <<"app.json">>])),
{'ok', Schema} = kz_json_schema:load(<<"app">>),
Expand Down
3 changes: 2 additions & 1 deletion applications/ecallmgr/src/ecallmgr_fs_channel.erl
Original file line number Diff line number Diff line change
Expand Up @@ -796,7 +796,8 @@ update_callee(UUID, Props) ->
maybe_update_callee_field(Value, 'undefined') -> Value;
maybe_update_callee_field(_Value, Existing) -> Existing.

-spec get_other_leg(ne_binary(), kz_proplist()) -> api_binary().
-spec get_other_leg(api_binary(), kz_proplist()) -> api_binary().
get_other_leg('undefined', _Props) -> 'undefined';
get_other_leg(UUID, Props) ->
get_other_leg_name(UUID, Props, props:get_value(<<"Other-Leg-Channel-Name">>, Props)).

Expand Down
2 changes: 1 addition & 1 deletion applications/notify/src/notify_first_occurrence.erl
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ init() ->
handle_req(JObj, _Props) ->
'true' = kapi_notifications:first_occurrence_v(JObj),
{'ok', Account} = kz_account:fetch(kz_json:get_value(<<"Account-ID">>, JObj)),
send(kz_json:get_integer_value(<<"Occurrence">>, JObj)
send(kz_json:get_binary_value(<<"Occurrence">>, JObj)
,Account
).

Expand Down
2 changes: 1 addition & 1 deletion applications/notify/src/notify_port_request.erl
Original file line number Diff line number Diff line change
Expand Up @@ -248,7 +248,7 @@ get_number_attachments(JObj) ->
_ -> []
end.

-spec get_port_attachments(kz_json:object()) -> attachments().
-spec get_port_attachments(ne_binary()) -> attachments().
get_port_attachments(PortRequestId) ->
case kz_datamgr:open_cache_doc(?KZ_PORT_REQUESTS_DB, PortRequestId) of
{'ok', PortJObj} ->
Expand Down
1 change: 0 additions & 1 deletion applications/omnipresence/src/omnip_dialog_amqp.erl
Original file line number Diff line number Diff line change
Expand Up @@ -371,7 +371,6 @@ maybe_send_update(User, Props) ->
end.

-spec send_update(binaries(), kz_proplist()) -> 'ok'.
send_update([], _Props) -> 'ok';
send_update(Stalkers, Props) ->
lager:debug("sending amqp dialog update state ~p for ~s/~s to ~p",
[props:get_value(<<"State">>, Props)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ build_macro_data(DataJObj) ->
[{<<"system">>, teletype_util:system_params()}
,{<<"account">>, teletype_util:account_params(DataJObj)}
,{<<"user">>, teletype_util:find_account_admin(AccountId)}
,{<<"event">>, kz_json:get_value(<<"occurrence">>, DataJObj)}
,{<<"event">>, kz_json:get_binary_value(<<"occurrence">>, DataJObj)}
].

-spec handle_req(kz_json:object()) -> 'ok'.
Expand Down
6 changes: 3 additions & 3 deletions applications/webhooks/src/webhooks_util.erl
Original file line number Diff line number Diff line change
Expand Up @@ -157,7 +157,7 @@ fire_hook(JObj, #webhook{custom_data = CustomData
do_fire(Hook, EventId, kz_json:merge_jobjs(CustomData, JObj)).

-spec do_fire(webhook(), ne_binary(), kz_json:object()) -> 'ok'.
do_fire(#webhook{uri = URI
do_fire(#webhook{uri = ?NE_BINARY = URI
,http_verb = 'get'
,retries = Retries
} = Hook, EventId, JObj) ->
Expand All @@ -170,7 +170,7 @@ do_fire(#webhook{uri = URI
Debug = debug_req(Hook, EventId, URI, Headers, <<>>),
Fired = kz_http:get(Url, Headers, ?HTTP_OPTS),
handle_resp(Hook, EventId, JObj, Debug, Fired);
do_fire(#webhook{uri = URI
do_fire(#webhook{uri = ?NE_BINARY = URI
,http_verb = 'post'
,retries = Retries
} = Hook, EventId, JObj) ->
Expand Down Expand Up @@ -246,7 +246,7 @@ save_attempt(AccountId, Attempt) ->
_ = kz_datamgr:save_doc(ModDb, Doc, [{'publish_change_notice', 'false'}]),
'ok'.

-spec debug_req(webhook(), ne_binary(), string() | ne_binary(), kz_proplist(), binary()) ->
-spec debug_req(webhook(), ne_binary(), ne_binary(), kz_proplist(), iodata()) ->
kz_proplist().
debug_req(#webhook{hook_id=HookId
,http_verb = Method
Expand Down
Loading

0 comments on commit 81565b8

Please sign in to comment.