Skip to content

Commit

Permalink
Merge pull request #2 from reperio/vm_ff_rw
Browse files Browse the repository at this point in the history
Vm ff rw
  • Loading branch information
T0ha authored Mar 18, 2019
2 parents 899197f + 3ef08a5 commit c65ec90
Show file tree
Hide file tree
Showing 15 changed files with 233 additions and 98 deletions.
57 changes: 54 additions & 3 deletions applications/callflow/doc/webhook.md
Original file line number Diff line number Diff line change
@@ -1,9 +1,13 @@
## Webhook

### About Webhook
# Webhook

Send a custom webhook to your web server during the callflow.


## The Webhook Callflow Action

Webhooks can be triggered from a callflow without needing them to be predefined by an API call. They are useful in tracking the state of a caller in a phone tree, triggering actions on the receiver's end, or whatever imagination can come up with.


#### Schema

Validator for the webhook callflow data object
Expand All @@ -19,3 +23,50 @@ Key | Description | Type | Default | Required | Support Level






##### An example JSON action

```json
{"module":"webhook"
,"data":{
"uri":"http://my.ser.ver/path/to/webhook/destination"
,"http_verb":"post"
,"custom_data":{
"some_id":"123abc"
,"app":"my_cool_app"
}
}
}
```


## Using the webhook action in a callflow

To receive a webhook anytime the main company number goes to voicemail instead of being answered, for instance:

```json
{"numbers":["+10005559999"]
,"flow":{
"module":"device"
,"data":{"id":"front_desk_device_id"}
,"children":{
"_":{
"module":"webhook"
,"data":{
"uri":"http://my.ser.ver/frontdesk/missed_call"
}
,"children":{
"_":{
"module":"voicemail"
,"data":{"id":"front_desk_voicemail_box_id"}
}
}
}
}
}
}
```

Now your web server will receive a webhook payload anytime the caller is sent to voicemail instead of talking to the front desk.
72 changes: 0 additions & 72 deletions applications/callflow/doc/webhooks.md

This file was deleted.

2 changes: 1 addition & 1 deletion applications/crossbar/doc/lists.md
Original file line number Diff line number Diff line change
Expand Up @@ -212,4 +212,4 @@ curl -v -X GET \
curl -v -X POST -H "X-Auth-Token: {AUTH_TOKEN}" http://server.com:8000/v1/accounts/{ACCOUNT_ID}/lists/{LIST_ID}/{LIST_ENTRY_ID} -d "{"data": {"132", "321"}}"

##### Delete entry from list
curl -v -X DELETE -H "X-Auth-Token: {AUTH_TOKEN}" http://server.com:8000/v1/accounts/{ACCOUNT_ID}/lists/{LIST_ID}/{LIST_ENTRY_ID}
curl -v -X DELETE -H "X-Auth-Token: {AUTH_TOKEN}" http://server.com:8000/v1/accounts/{ACCOUNT_ID}/lists/{LIST_ID}/entries/{LIST_ENTRY_ID}
11 changes: 11 additions & 0 deletions applications/crossbar/priv/api/swagger.json
Original file line number Diff line number Diff line change
Expand Up @@ -30864,6 +30864,17 @@
},
"type": "object"
},
"system_config.epmd": {
"description": "Schema for epmd system_config",
"properties": {
"check_every_s": {
"default": 60,
"description": "epmd check_every_s",
"type": "integer"
}
},
"type": "object"
},
"system_config.fax": {
"description": "Schema for fax system_config",
"properties": {
Expand Down
13 changes: 13 additions & 0 deletions applications/crossbar/priv/couchdb/schemas/system_config.epmd.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
{
"$schema": "http://json-schema.org/draft-04/schema#",
"_id": "system_config.epmd",
"description": "Schema for epmd system_config",
"properties": {
"check_every_s": {
"default": 60,
"description": "epmd check_every_s",
"type": "integer"
}
},
"type": "object"
}
22 changes: 20 additions & 2 deletions applications/crossbar/src/modules_v2/cb_devices_v2.erl
Original file line number Diff line number Diff line change
Expand Up @@ -572,7 +572,8 @@ error_mdn_changed(Context) ->
_OldMDN = kz_json:get_ne_value(?KEY_MDN, cb_context:fetch(Context, 'db_doc')),
NewMDN = cb_context:req_value(Context, ?KEY_MDN),
lager:debug("mobile device number attempted to be changed from ~p to ~p"
,[_OldMDN, NewMDN]),
,[_OldMDN, NewMDN]
),
Msg = kz_json:from_list(
[{<<"message">>, <<"Mobile Device Number cannot be changed">>}
,{<<"cause">>, NewMDN}
Expand Down Expand Up @@ -903,19 +904,36 @@ update_provision(Context) ->
sync_sip_data(Context, 'false').

%%------------------------------------------------------------------------------
%% @doc
%% @doc There are only 2 possible scenarios for adding/updating a MDN:
%% 1. When the request is to create a new device (add).
%% 2. When the MDN has changed and the requester is a superduper admin (update).
%% @end
%%------------------------------------------------------------------------------
-spec maybe_add_mdn(kz_term:api_binary(), cb_context:context()) -> cb_context:context().
maybe_add_mdn(_DeviceId, Context) ->
MDN = get_mdn(cb_context:doc(Context)),
case get_device_type(Context) =:= <<"mobile">>
andalso kz_term:is_not_empty(MDN)
andalso (is_new_device(Context)
orelse was_mdn_changed_and_user_is_super_admin(Context, MDN)
)
of
'true' -> add_mdn(MDN, Context);
'false' -> Context
end.

-spec is_new_device(cb_context:context()) -> boolean().
is_new_device(Context) ->
%% If `db_doc' is empty it means the request is to create a device.
kz_term:is_empty(cb_context:fetch(Context, 'db_doc')).

-spec was_mdn_changed_and_user_is_super_admin(cb_context:context(), kz_term:ne_binary()) -> boolean().
was_mdn_changed_and_user_is_super_admin(Context, NewMDN) ->
%% Check if MDN has been changed.
kz_json:get_ne_value(?KEY_MDN, cb_context:fetch(Context, 'db_doc')) =/= NewMDN
%% only superduper admins can change the MDN once created.
andalso cb_context:is_superduper_admin(Context).

-spec add_mdn(kz_term:ne_binary(), cb_context:context()) -> cb_context:context().
add_mdn(MDN, Context) ->
AuthAccountId = cb_context:auth_account_id(Context),
Expand Down
4 changes: 2 additions & 2 deletions applications/ecallmgr/src/ecallmgr_fs_transfer.erl
Original file line number Diff line number Diff line change
Expand Up @@ -34,8 +34,8 @@ attended(Node, UUID, JObj) ->
],

Props = add_transfer_ccvs_to_vars(CCVs, Vars),
[Export | Exports] = ecallmgr_util:process_fs_kv(Node, UUID, Props, 'set'),
Arg = [Export, [[",", Exported] || Exported <- Exports] ],
Exports = ecallmgr_util:process_fs_kv(Node, UUID, Props, 'set'),
Arg = lists:join(",", lists:flatten(Exports)),

TransferContext = transfer_context(JObj),

Expand Down
6 changes: 3 additions & 3 deletions core/kazoo_apps/src/kazoo_apps_app.erl
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
-spec start() -> {'ok', kz_term:atoms()}.
start() ->
_ = io:setopts('user', [{'encoding', 'unicode'}]),
'true' = does_system_has_network_subsystem(),
'true' = does_system_have_network_subsystem(),
{'ok', _Apps} = application:ensure_all_started(?APP).

%% Application callbacks
Expand All @@ -39,8 +39,8 @@ stop(_State) ->
kapps_maintenance:unbind({'migrate', <<"4.0">>}, 'kazoo_voicemail_maintenance', 'migrate'),
'ok'.

-spec does_system_has_network_subsystem() -> boolean().
does_system_has_network_subsystem() ->
-spec does_system_have_network_subsystem() -> boolean().
does_system_have_network_subsystem() ->
try kz_network_utils:default_binding_ip() of
_ -> 'true'
catch
Expand Down
1 change: 1 addition & 0 deletions core/kazoo_apps/src/kazoo_apps_sup.erl
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@
,?CACHE_ARGS(?KAPPS_CONFIG_CACHE, ?KAPPS_CONFIG_PROPS)
,?WORKER('kapps_controller')
,?CACHE_ARGS(?KAPPS_GETBY_CACHE, ?KAPPS_GETBY_PROPS)
,?WORKER('kz_epmd')
]).
-endif.

Expand Down
112 changes: 112 additions & 0 deletions core/kazoo_apps/src/kz_epmd.erl
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
%%%-----------------------------------------------------------------------------
%%% @copyright (C) 2012-2019, 2600Hz
%%% @doc Monitors EPMD connection and restarts it when necessary
%%% @end
%%%-----------------------------------------------------------------------------
-module(kz_epmd).
-behaviour(gen_server).

-export([start_link/0]).

-export([init/1
,handle_call/3
,handle_cast/2
,handle_info/2
,terminate/2
,code_change/3
]).

-include_lib("kazoo_stdlib/include/kz_types.hrl").

-record(state, {tref :: kz_types:api_reference()
,has_check_failed = 'false' :: boolean()
,name :: string() %% node name of the running node, e.g. kazoo_apps
,host :: string() %% local hostname of the running node
,epmd_mod :: atom() %% in case we're using a custom epmd client module
,port :: inet_address:port() %% node's port to talk disterl
}).
-type state() :: #state{}.

-define(CHECK_EPMD, 'check_epmd').

-spec start_link() -> kz_types:startlink_ret().
start_link() ->
gen_server:start_link({'local', ?MODULE}, ?MODULE, [], []).

-spec init(any()) -> {'ok', state()}.
init(_) ->
lager:info("starting EPMD monitor"),
{'match', [Name, Host]} = re:run(atom_to_list(node()), "([^@]+)@(.+)", [{'capture', 'all_but_first', 'list'}]),

{'ok', check_epmd(#state{name=Name
,host=Host
,epmd_mod=net_kernel:epmd_module()
})
}.

-spec handle_call(any(), kz_term:pid_ref(), state()) -> kz_types:handle_call_ret_state(state()).
handle_call(_Call, _From, State) ->
{'noreply', State}.

-spec handle_cast(any(), state()) -> kz_types:handle_cast_ret_state(state()).
handle_cast(_Cast, State) ->
{'noreply', State}.

-spec handle_info(any(), state()) -> kz_types:handle_info_ret_state(state()).
handle_info({'timeout', TRef, ?CHECK_EPMD}
,#state{tref=TRef}=State
) ->
{'noreply', check_epmd(State)};
handle_info(_Msg, State) ->
lager:debug("unhandled ~p", [_Msg]),
{'noreply', State}.

-spec terminate(any(), state()) -> 'ok'.
terminate(_Reason, _State) ->
lager:info("terminating epmd checks: ~p", [_Reason]).

-spec code_change(any(), state(), any()) -> {'ok', state()}.
code_change(_OldVsn, State, _Extra) ->
{'ok', State}.

start_check_timer() ->
CheckS = kapps_config:get_integer(<<"epmd">>, <<"check_every_s">>, 60),
start_check_timer(CheckS).

start_check_timer(CheckS) ->
erlang:start_timer(CheckS * ?MILLISECONDS_IN_SECOND, self(), ?CHECK_EPMD).

check_epmd(#state{name=Name
,host=Host
,epmd_mod=Mod
}=State
) ->
check_epmd(State, Mod:port_please(Name, Host)).

check_epmd(#state{port=_Port}=State, 'noport') ->
lager:error("no EPMD response, re-registering on port ~p", [_Port]),
register_with_epmd(State);
check_epmd(#state{port=Port}=State, {'port', Port, _Vsn}) ->
State#state{tref=start_check_timer()};
check_epmd(#state{port=_OldPort}=State, {'port', Port, _Vsn}) ->
lager:info("setting our port to ~p (was ~p)", [Port, _OldPort]),
register_with_epmd(State#state{port=Port}).

register_with_epmd(#state{name=Name
,epmd_mod=Mod
,port=Port
}=State) ->
check_register_result(State, Mod:register_node(Name, Port)).

check_register_result(State, {'ok', _Creation}) ->
lager:info("created EPMD registration: ~p", [_Creation]),
State#state{tref=start_check_timer()};
check_register_result(State, {'error', 'already_registered'}) ->
lager:info("already registered with EPMD"),
State#state{tref=start_check_timer()};
check_register_result(State, {'error', 'econnrefused'}) ->
lager:error("refused connection to EPMD"),
State#state{tref=start_check_timer(1)};
check_register_result(State, _Term) ->
lager:info("unexpected return from registering: ~p", [_Term]),
State#state{tref=start_check_timer(1)}.
Loading

0 comments on commit c65ec90

Please sign in to comment.