Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP

Comparing changes

Choose two branches to see what's changed or to start a new pull request. If you need to, you can also compare across forks.

Open a pull request

Create a new pull request by comparing changes across two branches. If you need to, you can also compare across forks.
base fork: 2600hz/kazoo
...
head fork: 2600hz/kazoo
Checking mergeability… Don't worry, you can still create the pull request.
  • 14 commits
  • 36 files changed
  • 0 commit comments
  • 2 contributors
Showing with 961 additions and 553 deletions.
  1. +2 −2 ecallmgr/src/ecallmgr.hrl
  2. +35 −1 ecallmgr/src/ecallmgr_call_command.erl
  3. +43 −119 ecallmgr/src/ecallmgr_call_control.erl
  4. +3 −3 ecallmgr/src/ecallmgr_call_control_sup.erl
  5. +1 −1  ecallmgr/src/ecallmgr_call_event_sup.erl
  6. +24 −24 ecallmgr/src/ecallmgr_call_events.erl
  7. +63 −7 ecallmgr/src/ecallmgr_fs_node.erl
  8. +7 −1 ecallmgr/src/ecallmgr_fs_query.erl
  9. +12 −13 ecallmgr/src/ecallmgr_fs_sup.erl
  10. BIN  lib/erlang-localtime-1.0/ebin/localtime.beam
  11. +19 −13 lib/erlang-localtime-1.0/src/localtime.erl
  12. +21 −0 lib/whistle-1.0.0/src/api/wapi_dialplan.erl
  13. +11 −0 lib/whistle-1.0.0/src/api/wapi_dialplan.hrl
  14. +2 −1  lib/whistle-1.0.0/src/wh_util.erl
  15. +68 −67 lib/whistle_couch-1.0.0/src/couch_compactor.erl
  16. +5 −6 whistle_apps/apps/callflow/src/callflow_maintenance.erl
  17. +117 −8 whistle_apps/apps/callflow/src/cf_call_command.erl
  18. +2 −2 whistle_apps/apps/callflow/src/cf_exe.erl
  19. +50 −11 whistle_apps/apps/callflow/src/module/cf_park.erl
  20. +11 −11 whistle_apps/apps/conference/src/conf_discovery.erl
  21. +51 −0 whistle_apps/apps/conference/src/conference_maintenance.erl
  22. +0 −12 whistle_apps/apps/crossbar/priv/couchdb/views/registrations.json
  23. +1 −1  whistle_apps/apps/crossbar/src/crossbar_doc.erl
  24. +5 −176 whistle_apps/apps/crossbar/src/crossbar_maintenance.erl
  25. +1 −6 whistle_apps/apps/crossbar/src/modules/cb_accounts.erl
  26. +0 −3  whistle_apps/apps/crossbar/src/modules/cb_registrations.erl
  27. +1 −1  whistle_apps/apps/registrar/src/reg_util.erl
  28. +0 −12 whistle_apps/apps/stepswitch/priv/couchdb/views/routes.json
  29. +1 −3 whistle_apps/apps/stepswitch/src/stepswitch_listener.erl
  30. +24 −0 whistle_apps/apps/stepswitch/src/stepswitch_maintenance.erl
  31. +8 −9 whistle_apps/apps/stepswitch/src/stepswitch_outbound.erl
  32. 0  whistle_apps/{apps/crossbar → }/priv/couchdb/views/accounts.json
  33. 0  whistle_apps/{apps/crossbar → }/priv/couchdb/views/maintenance.json
  34. +235 −0 whistle_apps/src/whapps_maintenance.erl
  35. +0 −34 whistle_apps/src/whapps_update.erl
  36. +138 −6 whistle_apps/src/whapps_util.erl
View
4 ecallmgr/src/ecallmgr.hrl
@@ -52,8 +52,7 @@
%% Call and Channel Vars that have a special prefix instead of the standard CHANNEL_VAR_PREFIX prefix
%% [{AMQP-Header, FS-var-name}]
%% so FS-var-name of "foo_var" would become "foo_var=foo_val" in the channel/call string
--define(SPECIAL_CHANNEL_VARS, [
- {<<"Auto-Answer">>, <<"sip_auto_answer">>}
+-define(SPECIAL_CHANNEL_VARS, [{<<"Auto-Answer">>, <<"sip_auto_answer">>}
,{<<"Eavesdrop-Group">>, <<"eavesdrop_group">>}
,{<<"Outgoing-Caller-ID-Name">>, <<"origination_caller_id_name">>}
,{<<"Outgoing-Caller-ID-Number">>,<<"origination_caller_id_number">>}
@@ -110,6 +109,7 @@
,{<<"noop">>, <<"noop">>}
,{<<"execute_extension">>, <<"execute_extension">>}
,{<<"playback">>, <<"hold">>}
+ ,{<<"presence">>, <<"presence">>}
]).
-define(FS_EVENTS, [<<"CHANNEL_EXECUTE">>, <<"CHANNEL_EXECUTE_COMPLETE">>, <<"CHANNEL_HANGUP">>
View
36 ecallmgr/src/ecallmgr_call_command.erl
@@ -17,9 +17,9 @@
-spec exec_cmd/4 :: (atom(), ne_binary(), json_object(), pid()) -> 'ok' | 'timeout' | {'error', 'invalid_callid' | 'failed'}.
exec_cmd(Node, UUID, JObj, ControlPID) ->
DestID = wh_json:get_value(<<"Call-ID">>, JObj),
+ App = wh_json:get_value(<<"Application-Name">>, JObj),
case DestID =:= UUID of
true ->
- App = wh_json:get_value(<<"Application-Name">>, JObj),
case get_fs_app(Node, UUID, JObj, App) of
{'error', Msg} ->
_ = ecallmgr_util:fs_log(Node, wh_util:to_list(Msg), []),
@@ -43,6 +43,7 @@ exec_cmd(Node, UUID, JObj, ControlPID) ->
end;
false ->
?LOG("command ~s not meant for us but for ~s", [wh_json:get_value(<<"Application-Name">>, JObj), DestID]),
+ ecallmgr_call_control:event_execute_complete(ControlPID, UUID, App),
{'error', invalid_callid}
end.
@@ -525,6 +526,39 @@ get_fs_app(Node, UUID, JObj, <<"fetch">>) ->
end),
{<<"fetch">>, noop};
+get_fs_app(_Node, _UUID, JObj, <<"presence">>) ->
+ NodeHandlers = ecallmgr_fs_sup:node_handlers(),
+ UUID = case wh_json:get_ne_value(<<"Msg-ID">>, JObj) of
+ undefined -> wh_util:to_list(wh_util:current_tstamp());
+ Else -> wh_util:to_list(Else)
+ end,
+ State = case wh_json:get_value(<<"State">>, JObj) of
+ <<"early">> -> "early";
+ <<"confirmed">> -> "confirmed";
+ _ -> "terminated"
+ end,
+ Event = [{"unique-id", UUID}
+ ,{"channel-state", "CS_ROUTING"}
+ ,{"answer-state", State}
+ ,{"proto", "any"}
+ ,{"login", "src/mod/event_handlers/mod_erlang_event/handle_msg.c"}
+ ,{"from", wh_json:get_string_value(<<"User">>, JObj)}
+ ,{"rpid", "unknown"}
+ ,{"status", "CS_ROUTING"}
+ ,{"event_type", "presence"}
+ ,{"alt_event_type", "dialog"}
+ ,{"presence-call-direction", "outbound"}
+ ,{"event_cound", "0"}
+ ],
+ [begin
+ ?LOG("sending presence in event to ~p~n", [Node]),
+ freeswitch:sendevent(Node, 'PRESENCE_IN', Event)
+ end
+ || NodeHandler <- NodeHandlers
+ ,(Node = ecallmgr_fs_node:fs_node(NodeHandler)) =/= undefined
+ ],
+ {<<"presence">>, noop};
+
get_fs_app(_Node, _UUID, JObj, <<"conference">>) ->
case wapi_dialplan:conference_v(JObj) of
false -> {'error', <<"conference failed to execute as JObj did not validate">>};
View
162 ecallmgr/src/ecallmgr_call_control.erl
@@ -52,6 +52,7 @@
-export([event_execute_complete/3]).
-export([add_leg/1, rm_leg/1]).
-export([other_legs/1]).
+-export([transferer/2, transferee/2]).
%% gen_server callbacks
-export([init/1, handle_call/3, handle_cast/2, handle_info/2, handle_event/2
@@ -117,7 +118,7 @@ start_link(Node, CallId, WhAppQ) ->
-spec callid/1 :: (pid()) -> ne_binary().
callid(Srv) ->
- gen_listener:call(Srv, {callid}).
+ gen_server:call(Srv, {callid}, 100).
-spec queue_name/1 :: (pid()) -> ne_binary().
queue_name(Srv) ->
@@ -125,11 +126,11 @@ queue_name(Srv) ->
-spec other_legs/1 :: (pid()) -> [] | [ne_binary(),...].
other_legs(Srv) ->
- gen_listener:call(Srv, {other_legs}).
+ gen_server:call(Srv, {other_legs}, 100).
-spec event_execute_complete/3 :: (pid(), ne_binary(), ne_binary()) -> 'ok'.
event_execute_complete(Srv, CallId, App) ->
- gen_listener:cast(Srv, {event_execute_complete, CallId, App}).
+ gen_server:cast(Srv, {event_execute_complete, CallId, App}).
-spec add_leg/1 :: (proplist()) -> pid().
add_leg(Props) ->
@@ -142,7 +143,7 @@ add_leg(Props) ->
false -> ok;
{error, _} -> ok;
{ok, Srv} ->
- gen_listener:cast(Srv, {add_leg, wh_json:from_list(Props)})
+ gen_server:cast(Srv, {add_leg, wh_json:from_list(Props)})
end
end).
@@ -157,48 +158,48 @@ rm_leg(Props) ->
false -> ok;
{error, _} -> ok;
{ok, Srv} ->
- gen_listener:cast(Srv, {rm_leg, wh_json:from_list(Props)})
+ gen_server:cast(Srv, {rm_leg, wh_json:from_list(Props)})
end
end).
+-spec transferer/2 :: (pid(), proplist()) -> 'ok'.
+transferer(Srv, Props) ->
+ gen_server:cast(Srv, {transferer, wh_json:from_list(Props)}).
+
+-spec transferee/2 :: (pid(), proplist()) -> 'ok'.
+transferee(Srv, Props) ->
+ gen_server:cast(Srv, {transferee, wh_json:from_list(Props)}).
+
-spec handle_call_command/2 :: (json_object(), proplist()) -> 'ok'.
handle_call_command(JObj, Props) ->
Srv = props:get_value(server, Props),
- gen_listener:cast(Srv, {dialplan, JObj}).
+ gen_server:cast(Srv, {dialplan, JObj}).
-spec handle_conference_command/2 :: (json_object(), proplist()) -> 'ok'.
handle_conference_command(JObj, Props) ->
Srv = props:get_value(server, Props),
- gen_listener:cast(Srv, {dialplan, JObj}).
+ gen_server:cast(Srv, {dialplan, JObj}).
-spec handle_call_events/2 :: (json_object(), proplist()) -> 'ok'.
handle_call_events(JObj, Props) ->
Srv = props:get_value(server, Props),
CallId = wh_json:get_value(<<"Call-ID">>, JObj),
put(callid, CallId),
- case {wh_json:get_value(<<"Event-Name">>, JObj), get_transfer_state(JObj)} of
- {<<"CHANNEL_EXECUTE_COMPLETE">>, _} ->
+ case wh_json:get_value(<<"Event-Name">>, JObj) of
+ <<"CHANNEL_EXECUTE_COMPLETE">> ->
Application = wh_json:get_value(<<"Raw-Application-Name">>, JObj),
?LOG("control queue ~p channel execute completion for '~s'", [Srv, Application]),
- gen_listener:cast(Srv, {event_execute_complete, CallId, Application});
- {<<"CHANNEL_DESTROY">>, _} ->
- gen_listener:cast(Srv, {channel_destroyed, JObj});
- {<<"CHANNEL_HANGUP">>, undefined} ->
- ok;
- {<<"CHANNEL_HANGUP">>, Transfer} ->
- ?LOG("control queue ~p channel hangup due to a transfer and we are the ~s", [Srv, Transfer]),
- gen_listener:cast(Srv, {Transfer, JObj});
- {<<"CHANNEL_UNBRIDGE">>, undefined} ->
- gen_listener:cast(Srv, {rm_leg, JObj});
- {<<"CHANNEL_UNBRIDGE">>, Transfer} ->
- ?LOG("control queue ~p channel unbridged due to a transfer and we are the ~s", [Srv, Transfer]),
- gen_listener:cast(Srv, {Transfer, JObj});
- {<<"CHANNEL_BRIDGE">>, _} ->
- gen_listener:cast(Srv, {add_leg, JObj});
- {<<"controller_queue">>, _} ->
+ gen_server:cast(Srv, {event_execute_complete, CallId, Application});
+ <<"CHANNEL_DESTROY">> ->
+ gen_server:cast(Srv, {channel_destroyed, JObj});
+ <<"CHANNEL_UNBRIDGE">> ->
+ gen_server:cast(Srv, {rm_leg, JObj});
+ <<"CHANNEL_BRIDGE">> ->
+ gen_server:cast(Srv, {add_leg, JObj});
+ <<"controller_queue">> ->
ControllerQ = wh_json:get_value(<<"Controller-Queue">>, JObj),
- gen_listener:cast(Srv, {controller_queue, ControllerQ});
- {_, _} ->
+ gen_server:cast(Srv, {controller_queue, ControllerQ});
+ _ ->
ok
end.
@@ -272,15 +273,21 @@ handle_cast({transferer, _}, #state{callid=CallId, controller_q=ControllerQ}=Sta
spawn(fun() -> publish_control_transfer(ControllerQ, CallId) end),
{stop, normal, State};
handle_cast({transferee, JObj}, #state{other_legs=Legs, node=Node, callid=PrevCallId, self=Self}=State) ->
- %% TODO: once we are satisfied that this is not breaking anything we can reduce the verbosity...
- OtherLegCallId = wh_json:get_value(<<"Other-Leg-Unique-ID">>, JObj),
- case OtherLegCallId =/= undefined andalso freeswitch:api(Node, uuid_dump, wh_util:to_list(OtherLegCallId)) of
- {ok, Result} ->
- ?LOG("this call control process is a transferee, updating call id..."),
- Props = ecallmgr_util:eventstr_to_proplist(Result),
- NewCallId = props:get_value(<<"Channel-Call-UUID">>, Props),
+ ?LOG("this call control process is a transferee, updating call id..."),
+ NewCallId = case {wh_json:get_value(<<"Bridge-With">>, JObj), wh_json:get_value(<<"Transferee-UUID">>, JObj)} of
+ {undefined, CallId} -> CallId;
+ {CallId, _} -> CallId
+ end,
+ case NewCallId of
+ undefined ->
+ ?LOG("could not determin new call id"),
+ {noreply, State};
+ PrevCallId ->
+ ?LOG("new callid is the same as the old callid"),
+ {noreply, State};
+ _Else ->
spawn(fun() -> publish_callid_update(PrevCallId, NewCallId, queue_name(Self)) end),
- ?LOG("updating callid to ~s", [NewCallId]),
+ ?LOG(PrevCallId, "updating callid to ~s", [NewCallId]),
put(callid, NewCallId),
?LOG("removing call event bindings for ~s", [PrevCallId]),
gen_listener:rm_binding(self(), call, [{callid, PrevCallId}]),
@@ -289,9 +296,7 @@ handle_cast({transferee, JObj}, #state{other_legs=Legs, node=Node, callid=PrevCa
?LOG("ensuring event listener exists"),
_ = ecallmgr_call_sup:start_event_process(Node, NewCallId),
?LOG("...call id updated, continuing post-transfer"),
- {noreply, State#state{callid=NewCallId, other_legs=lists:delete(NewCallId, Legs)}};
- _ ->
- {noreply, State}
+ {noreply, State#state{callid=NewCallId, other_legs=lists:delete(NewCallId, Legs)}}
end;
handle_cast({add_leg, JObj}, #state{other_legs=Legs, node=Node, callid=CallId}=State) ->
LegId = case wh_json:get_value(<<"Event-Name">>, JObj) of
@@ -679,84 +684,3 @@ publish_control_transfer(ControllerQ, CallId) ->
| wh_api:default_headers(?APP_NAME, ?APP_VERSION)
],
wapi_call:publish_control_transfer(ControllerQ, Transfer).
-
--spec get_transfer_state/1 :: (json_object()) -> 'undefined' | 'transferer' | 'transferee'.
--spec get_transfer_state/2 :: ({ne_binary(), ne_binary()}, json_object()) -> 'undefined' | 'transferer' | 'transferee'.
--spec do_get_transfer_state/2 :: (ne_binary(), json_object()) -> 'undefined' | 'transferer' | 'transferee'.
-
-get_transfer_state(JObj) ->
- get_transfer_state(wh_util:get_event_type(JObj), JObj).
-
-get_transfer_state({<<"call_event">>, <<"CHANNEL_DESTROY">>}, JObj) ->
- do_get_transfer_state(<<"CHANNEL_DESTROY">>, JObj);
-get_transfer_state({<<"call_event">>, <<"CHANNEL_HANGUP">>}, JObj) ->
- do_get_transfer_state(<<"CHANNEL_HANGUP">>, JObj);
-get_transfer_state({<<"call_event">>, <<"CHANNEL_UNBRIDGE">>}, JObj) ->
- do_get_transfer_state(<<"CHANNEL_UNBRIDGE">>, JObj);
-get_transfer_state(_, _) ->
- undefined.
-
-do_get_transfer_state(<<"CHANNEL_UNBRIDGE">>, JObj) ->
- Timestamp = wh_json:get_value(<<"Timestamp">>, JObj, <<>>),
- Epoch = binary:part(wh_util:pad_binary(Timestamp, 10, <<"0">>), 0, 10),
- Transfer = wh_json:get_value([<<"Transfer-History">>, Epoch], JObj),
- Disposition = wh_json:get_value(<<"Disposition">>, JObj),
- case {Disposition, Transfer} of
- %% caller preforms a blind transfer
- {<<"BLIND_TRANSFER">>, undefined} ->
- ?LOG("channel was unbridged as a result of a blind transfer"),
- transferer;
- %% callee preforms a attended transfer (on C-leg)
- {<<"ATTENDED_TRANSFER">>, undefined} ->
- ?LOG("channel was unbridged as a result of an attended transfer, acquire control"),
- transferee;
- %% caller preforms a attended transfer
- %% caller preforms a partial attended
- {<<"ANSWER">>, undefined} ->
- %% to be sure check if it was during a transfer, may not be necessary...
- case wh_json:get_value(<<"Hangup-Cause">>, JObj) of
- undefined ->
- ?LOG("channel was unbridged as a result of a transfer"),
- transferer;
- _Else ->
- undefined
- end;
- %% just a catch for undefined Transfer History Item
- %% IE: This unbridge was NOT part of the transfer history,
- %% otherwise it WAS and the next clause will handle it.
- {_, undefined} ->
- undefined;
- %% callee preforms a blind transfer
- %% callee preforms a partial attended
- %% callee preforms a attended transfer
- {_, _} ->
- ?LOG("channel was unbridged as a result of a transfer"),
- transferer
- end;
-do_get_transfer_state(_, JObj) ->
- case wh_json:get_value(<<"Disposition">>, JObj) of
- %% caller preforms a blind transfer
- <<"BLIND_TRANSFER">> ->
- ?LOG("channel was hungup as a result of a blind transfer"),
- transferer;
- %% callee preforms partial attended
- %% callee preforms attended transfer
- <<"ATTENDED_TRANSFER">> ->
- ?LOG("channel was hungup as a result of an attended transfer, acquire control"),
- transferee;
- %% caller preforms a attended transfer
- %% caller preforms a partial attended
- <<"ANSWER">> ->
- %% to be sure check if it was during a transfer, may not be necessary...
- case wh_json:get_value(<<"Hangup-Cause">>, JObj) of
- undefined ->
- ?LOG("channel was hungup as a result of a transfer"),
- trasferer;
- _Else ->
- undefined
- end;
- %% missing events:
- %% callee preforms blind transfer
- _Else ->
- undefined
- end.
View
6 ecallmgr/src/ecallmgr_call_control_sup.erl
@@ -51,12 +51,12 @@ workers() ->
find_worker(CallID) ->
do_find_worker(workers(), CallID).
-do_find_worker([], _) ->
+do_find_worker([], _CallId) ->
{error, not_found};
do_find_worker([Srv|T], CallID) ->
- case ecallmgr_call_control:callid(Srv) of
+ case catch(ecallmgr_call_control:callid(Srv)) of
CallID -> {ok, Srv};
- _ -> do_find_worker(T, CallID)
+ _E -> do_find_worker(T, CallID)
end.
-spec find_control_queue/1 :: (ne_binary()) -> {'error', 'not_found'} | {'ok', ne_binary()}.
View
2  ecallmgr/src/ecallmgr_call_event_sup.erl
@@ -60,7 +60,7 @@ find_worker(CallId) ->
do_find_worker([], _) ->
{error, not_found};
do_find_worker([Srv|T], CallId) ->
- case ecallmgr_call_events:callid(Srv) of
+ case catch(ecallmgr_call_events:callid(Srv)) of
CallId -> {ok, Srv};
_ -> do_find_worker(T, CallId)
end.
View
48 ecallmgr/src/ecallmgr_call_events.erl
@@ -57,7 +57,7 @@ start_link(Node, CallId) ->
-spec callid/1 :: (pid()) -> ne_binary().
callid(Srv) ->
- gen_server:call(Srv, {callid}).
+ gen_server:call(Srv, {callid}, 100).
transfer(Srv, TransferType, Props) ->
gen_listener:cast(Srv, {TransferType, Props}).
@@ -257,29 +257,29 @@ create_event(EventName, ApplicationName, Props) ->
{Mega,Sec,Micro} = erlang:now(),
Timestamp = wh_util:to_binary(((Mega * 1000000 + Sec) * 1000000 + Micro)),
Event = [ KV || {_, V}=KV <- [{<<"Msg-ID">>, props:get_value(<<"Event-Date-Timestamp">>, Props, Timestamp)}
- ,{<<"Timestamp">>, props:get_value(<<"Event-Date-Timestamp">>, Props, Timestamp)}
- ,{<<"Call-ID">>, props:get_value(<<"Caller-Unique-ID">>, Props)}
- ,{<<"Call-Direction">>, props:get_value(<<"Call-Direction">>, Props)}
- ,{<<"Channel-Call-State">>, props:get_value(<<"Channel-Call-State">>, Props)}
- ,{<<"Channel-State">>, get_channel_state(Props)}
- ,{<<"Transfer-History">>, get_transfer_history(Props)}
- ,{<<"Hangup-Cause">>, get_hangup_cause(Props)}
- ,{<<"Hangup-Code">>, get_hangup_code(Props)}
- ,{<<"Disposition">>, get_disposition(Props)}
- ,{<<"Other-Leg-Direction">>, props:get_value(<<"Other-Leg-Direction">>, Props)}
- ,{<<"Other-Leg-Caller-ID-Name">>, props:get_value(<<"Other-Leg-Caller-ID-Name">>, Props)}
- ,{<<"Other-Leg-Caller-ID-Number">>, props:get_value(<<"Other-Leg-Caller-ID-Number">>, Props)}
- ,{<<"Other-Leg-Destination-Number">>, props:get_value(<<"Other-Leg-Destination-Number">>, Props)}
- ,{<<"Other-Leg-Unique-ID">>, props:get_value(<<"Other-Leg-Unique-ID">>, Props,
- props:get_value(<<"variable_holding_uuid">>, Props))}
- ,{<<"Custom-Channel-Vars">>, wh_json:from_list(CCVs)}
- %% this sucks, its leaky but I dont see a better way around it since we need the raw application
- %% name in call_control... (see note in call_control on start_link for why we need to use AMQP
- %% to communicate to it)
- ,{<<"Raw-Application-Name">>, props:get_value(<<"Application">>, Props, ApplicationName)}
- | event_specific(EventName, ApplicationName, Props)
- ],
- V =/= undefined],
+ ,{<<"Timestamp">>, props:get_value(<<"Event-Date-Timestamp">>, Props, Timestamp)}
+ ,{<<"Call-ID">>, props:get_value(<<"Caller-Unique-ID">>, Props)}
+ ,{<<"Call-Direction">>, props:get_value(<<"Call-Direction">>, Props)}
+ ,{<<"Channel-Call-State">>, props:get_value(<<"Channel-Call-State">>, Props)}
+ ,{<<"Channel-State">>, get_channel_state(Props)}
+ ,{<<"Transfer-History">>, get_transfer_history(Props)}
+ ,{<<"Hangup-Cause">>, get_hangup_cause(Props)}
+ ,{<<"Hangup-Code">>, get_hangup_code(Props)}
+ ,{<<"Disposition">>, get_disposition(Props)}
+ ,{<<"Other-Leg-Direction">>, props:get_value(<<"Other-Leg-Direction">>, Props)}
+ ,{<<"Other-Leg-Caller-ID-Name">>, props:get_value(<<"Other-Leg-Caller-ID-Name">>, Props)}
+ ,{<<"Other-Leg-Caller-ID-Number">>, props:get_value(<<"Other-Leg-Caller-ID-Number">>, Props)}
+ ,{<<"Other-Leg-Destination-Number">>, props:get_value(<<"Other-Leg-Destination-Number">>, Props)}
+ ,{<<"Other-Leg-Unique-ID">>, props:get_value(<<"Other-Leg-Unique-ID">>, Props,
+ props:get_value(<<"variable_holding_uuid">>, Props))}
+ ,{<<"Custom-Channel-Vars">>, wh_json:from_list(CCVs)}
+ %% this sucks, its leaky but I dont see a better way around it since we need the raw application
+ %% name in call_control... (see note in call_control on start_link for why we need to use AMQP
+ %% to communicate to it)
+ ,{<<"Raw-Application-Name">>, props:get_value(<<"Application">>, Props, ApplicationName)}
+ | event_specific(EventName, ApplicationName, Props)
+ ],
+ V =/= undefined],
wh_api:default_headers(<<>>, ?EVENT_CAT, EventName, ?APP_NAME, ?APP_VERSION) ++ Event.
-spec publish_event/1 :: (proplist()) -> 'ok'.
View
70 ecallmgr/src/ecallmgr_fs_node.erl
@@ -58,23 +58,38 @@ distributed_presence(Srv, Type, Event) ->
-spec show_channels/1 :: (pid()) -> [proplist(),...] | [].
show_channels(Srv) ->
- gen_server:call(Srv, show_channels).
+ case catch(gen_server:call(Srv, show_channels, 100)) of
+ {'EXIT', _} -> [];
+ Else -> Else
+ end.
-spec hostname/1 :: (pid()) -> fs_api_ret().
hostname(Srv) ->
- gen_server:call(Srv, hostname).
+ case catch(gen_server:call(Srv, hostname, 100)) of
+ {'EXIT', _} -> timeout;
+ Else -> Else
+ end.
-spec fs_node/1 :: (pid()) -> atom().
fs_node(Srv) ->
- gen_server:call(Srv, fs_node).
+ case catch(gen_server:call(Srv, fs_node, 100)) of
+ {'EXIT', _} -> undefined;
+ Else -> Else
+ end.
-spec uuid_exists/2 :: (pid(), binary()) -> boolean().
uuid_exists(Srv, UUID) ->
- gen_server:call(Srv, {uuid_exists, UUID}).
+ case catch(gen_server:call(Srv, {uuid_exists, UUID}, 100)) of
+ {'EXIT', _} -> false;
+ Else -> Else
+ end.
-spec uuid_dump/2 :: (pid(), binary()) -> proplist().
uuid_dump(Srv, UUID) ->
- gen_server:call(Srv, {uuid_dump, UUID}).
+ case catch(gen_server:call(Srv, {uuid_dump, UUID}, 100)) of
+ {'EXIT', _} -> [];
+ Else -> Else
+ end.
-spec start_link/1 :: (Node :: atom()) -> {'ok', pid()} | {'error', term()}.
start_link(Node) ->
@@ -151,7 +166,7 @@ handle_info(timeout, #state{stats=Stats, node=Node}=State) ->
ok = freeswitch:event(Node, ['CHANNEL_CREATE', 'CHANNEL_DESTROY', 'HEARTBEAT', 'CHANNEL_HANGUP_COMPLETE'
,'PRESENCE_IN', 'PRESENCE_OUT', 'PRESENCE_PROBE'
- ,'CUSTOM', 'sofia::register'
+ ,'CUSTOM', 'sofia::register', 'sofia::transfer'
]),
{noreply, State#state{stats=(Stats#node_stats{
@@ -227,7 +242,7 @@ handle_info({event, [UUID | Data]}, #state{node=Node, stats=#node_stats{created_
end,
{noreply, State, hibernate};
<<"CUSTOM">> ->
- spawn(fun() -> process_custom_data(Data) end),
+ process_custom_data(Data),
{noreply, State};
_ ->
{noreply, State}
@@ -375,6 +390,9 @@ process_custom_data(Data) ->
<<"sofia::register">> ->
?LOG("received registration event"),
publish_register_event(Data);
+ <<"sofia::transfer">> ->
+ ?LOG("received transfer event"),
+ process_transfer_event(props:get_value(<<"Type">>, Data), Data);
_ ->
ok
end.
@@ -397,6 +415,44 @@ publish_register_event(Data) ->
?LOG("sending successful registration"),
wapi_registration:publish_success(ApiProp).
+-spec process_transfer_event/2 :: (ne_binary(), proplist()) -> ok.
+process_transfer_event(<<"BLIND_TRANSFER">>, Data) ->
+ TransfererCtrlUUId = case props:get_value(<<"Transferor-Direction">>, Data) of
+ <<"inbound">> ->
+ props:get_value(<<"Transferor-UUID">>, Data);
+ _ ->
+ props:get_value(<<"Transferee-UUID">>, Data)
+ end,
+ case ecallmgr_call_control_sup:find_worker(TransfererCtrlUUId) of
+ {ok, Pid1} ->
+ ?LOG(TransfererCtrlUUId, "sending transferer notice to ecallmgr_call_control ~p", [Pid1]),
+ ecallmgr_call_control:transferer(Pid1, Data);
+ {error, not_found} ->
+ ok
+ end;
+process_transfer_event(_, Data) ->
+ TransfererCtrlUUId = case props:get_value(<<"Transferor-Direction">>, Data) of
+ <<"inbound">> ->
+ props:get_value(<<"Transferor-UUID">>, Data);
+ _ ->
+ props:get_value(<<"Transferee-UUID">>, Data)
+ end,
+ case ecallmgr_call_control_sup:find_worker(TransfererCtrlUUId) of
+ {ok, Pid1} ->
+ ?LOG(TransfererCtrlUUId, "sending transferer notice to ecallmgr_call_control ~p", [Pid1]),
+ ecallmgr_call_control:transferer(Pid1, Data);
+ {error, not_found} ->
+ ok
+ end,
+ TransfereeCtrlUUId = props:get_value(<<"Replaces">>, Data),
+ case ecallmgr_call_control_sup:find_worker(TransfereeCtrlUUId) of
+ {ok, Pid2} ->
+ ?LOG(TransfereeCtrlUUId, "sending transferee notice to ecallmgr_call_control ~p", [Pid2]),
+ ecallmgr_call_control:transferee(Pid2, Data);
+ {error, not_found} ->
+ ok
+ end.
+
get_originate_action(<<"transfer">>, JObj) ->
case wh_json:get_value([<<"Application-Data">>, <<"Route">>], JObj) of
undefined -> <<"error">>;
View
8 ecallmgr/src/ecallmgr_fs_query.erl
@@ -58,7 +58,13 @@ handle_channel_status(JObj, _Props) ->
?LOG_START("channel status request received"),
case [ecallmgr_fs_node:hostname(NH) || NH <- ecallmgr_fs_sup:node_handlers(), ecallmgr_fs_node:uuid_exists(NH, CallID)] of
- [] -> ?LOG("no node found with channel ~s", [CallID]);
+ [] ->
+ ?LOG("no node found with channel ~s", [CallID]),
+ Resp = [{<<"Call-ID">>, CallID}
+ ,{<<"Status">>, <<"terminated">>}
+ ,{<<"Error-Msg">>, <<"no node found with channel">>}
+ | wh_api:default_headers(?APP_NAME, ?APP_VERSION)],
+ wapi_call:publish_channel_status_resp(wh_json:get_value(<<"Server-ID">>, JObj), Resp);
[{ok, Hostname}] ->
?LOG("call is on ~s", [Hostname]),
Resp = [{<<"Call-ID">>, CallID}
View
25 ecallmgr/src/ecallmgr_fs_sup.erl
@@ -41,10 +41,10 @@ start_link() ->
start_handlers(Node, Options) when is_atom(Node) ->
NodeB = wh_util:to_binary(Node),
[ begin
- Name = wh_util:to_atom(<<NodeB/binary, H/binary>>, true),
- Mod = wh_util:to_atom(<<"ecallmgr_fs", H/binary>>),
- ?LOG("Starting handler ~s", [Name]),
- supervisor:start_child(?SERVER, ?CHILD(Name, Mod, [Node, Options]))
+ Name = wh_util:to_atom(<<NodeB/binary, H/binary>>, true),
+ Mod = wh_util:to_atom(<<"ecallmgr_fs", H/binary>>),
+ ?LOG("Starting handler ~s", [Name]),
+ supervisor:start_child(?SERVER, ?CHILD(Name, Mod, [Node, Options]))
end
|| H <- [<<"_auth">>, <<"_route">>, <<"_node">>] ].
@@ -52,24 +52,23 @@ start_handlers(Node, Options) when is_atom(Node) ->
stop_handlers(Node) when is_atom(Node) ->
NodeB = wh_util:to_binary(Node),
[ begin
- ok = supervisor:terminate_child(?SERVER, Name),
- supervisor:delete_child(?SERVER, Name)
+ ok = supervisor:terminate_child(?SERVER, Name),
+ supervisor:delete_child(?SERVER, Name)
end || {Name, _, _, [_]} <- supervisor:which_children(?SERVER)
- ,node_matches(NodeB, wh_util:to_binary(Name))
+ ,node_matches(NodeB, wh_util:to_binary(Name))
].
-spec node_handlers/0 :: () -> [pid(),...] | [].
node_handlers() ->
[ Pid || {_, Pid, worker, [HandlerMod]} <- supervisor:which_children(?SERVER),
- HandlerMod =:= ecallmgr_fs_node].
+ HandlerMod =:= ecallmgr_fs_node].
-spec get_handler_pids/1 :: (atom()) -> {pid() | 'error', pid() | 'error', pid() | 'error'}.
get_handler_pids(Node) when is_atom(Node) ->
NodeB = wh_util:to_binary(Node),
NodePids = [ {HandlerMod, Pid} || {Name, Pid, worker, [HandlerMod]} <- supervisor:which_children(?SERVER)
- ,node_matches(NodeB, wh_util:to_binary(Name))],
- {
- props:get_value(ecallmgr_fs_auth, NodePids, error)
+ ,node_matches(NodeB, wh_util:to_binary(Name))],
+ {props:get_value(ecallmgr_fs_auth, NodePids, error)
,props:get_value(ecallmgr_fs_route, NodePids, error)
,props:get_value(ecallmgr_fs_node, NodePids, error)
}.
@@ -108,6 +107,6 @@ init([]) ->
node_matches(NodeB, Name) ->
Size = byte_size(NodeB),
case binary:match(Name, NodeB) of
- {_, End} -> Size =:= End;
- nomatch -> false
+ {_, End} -> Size =:= End;
+ nomatch -> false
end.
View
BIN  lib/erlang-localtime-1.0/ebin/localtime.beam
Binary file not shown
View
32 lib/erlang-localtime-1.0/src/localtime.erl
@@ -71,10 +71,10 @@ process_tz_rule(UtcDateTime, {_, _, _, Shift, DstShift, _, _, _, _}=TzRule) ->
% UtcDateTime = DateTime()
% ErrDescr = atom(), unknown_tz
-spec local_to_utc/2 :: (calendar:t_datetime(), binary() | nonempty_string()) -> calendar:t_datetime() | {'error', 'unknown_tz' | 'time_not_exists'} |
- {'stdname', {string(), string()}}.
+ {'stdname', {string(), string()}}.
local_to_utc(LocalDateTime, "UTC") ->
LocalDateTime;
-local_to_utc(LocalDateTime, Timezone) ->
+local_to_utc(LocalDateTime, Timezone) when is_list(Timezone) ->
case get_timezone(Timezone) of
{shift, Shift} ->
adjust_datetime(LocalDateTime, invert_shift(Shift));
@@ -89,21 +89,27 @@ local_to_utc(LocalDateTime, Timezone) ->
{error, time_not_exists}
end;
E -> E
- end.
+ end;
+local_to_utc(LocalDateTime, Timezone) when is_binary(Timezone) ->
+ local_to_utc(LocalDateTime, binary_to_list(Timezone)).
% local_to_local(LocalDateTime, TimezoneFrom, TimezoneTo) -> LocalDateTime | tim_not_exists | {error, ErrDescr}
% LocalDateTime = DateTime()
% TimezoneFrom = String()
% TimezoneTo = String()
% ErrDescr = atom(), unknown_tz
--spec local_to_local/3 :: (calendar:t_datetime(), string(), string()) -> calendar:t_datetime().
-local_to_local(LocalDateTime, TimezoneFrom, TimezoneTo) ->
- case local_to_utc(LocalDateTime, TimezoneFrom) of
- Date = {{_,_,_},{_,_,_}} ->
- utc_to_local(Date, TimezoneTo);
- Res ->
- Res
- end.
+-spec local_to_local/3 :: (calendar:t_datetime(), binary() | string(), binary() | string()) -> calendar:t_datetime().
+local_to_local(LocalDateTime, TimezoneFrom, TimezoneTo) when is_list(TimezoneFrom), is_list(TimezoneTo) ->
+ case local_to_utc(LocalDateTime, TimezoneFrom) of
+ Date = {{_,_,_},{_,_,_}} ->
+ utc_to_local(Date, TimezoneTo);
+ Res ->
+ Res
+ end;
+local_to_local(LocalDateTime, TimezoneFrom, TimezoneTo) when is_binary(TimezoneFrom) ->
+ local_to_local(LocalDateTime, binary_to_list(TimezoneFrom), TimezoneTo);
+local_to_local(LocalDateTime, TimezoneFrom, TimezoneTo) when is_binary(TimezoneTo) ->
+ local_to_local(LocalDateTime, TimezoneFrom, binary_to_list(TimezoneTo)).
% tz_name(DateTime(), Timezone) -> {Abbr, Name} | {{StdAbbr, StdName}, {DstAbbr, DstName}} | unable_to_detect | {error, ErrDesc}
% Timezone = String()
@@ -166,7 +172,7 @@ tz_shift(LocalDateTime, Timezone) ->
time_not_exists ->
unable_to_detect
end;
- {error, unknown_tz}=E -> E
+ {error, unknown_tz}=E -> E
end.
% the same as tz_shift/2, but calculates time difference between two local timezones
@@ -210,7 +216,7 @@ fmt_shift(0) ->
0.
-spec get_timezone/1 :: (nonempty_string() | binary()) -> {'error', 'unknown_tz'} | {'stdname', {string(), string()}} |
- {'shift', integer()} | {'tzrule', tz_db_row()}.
+ {'shift', integer()} | {'tzrule', tz_db_row()}.
get_timezone(TZ) when is_binary(TZ) ->
get_timezone(binary_to_list(TZ));
get_timezone(TZ) ->
View
21 lib/whistle-1.0.0/src/api/wapi_dialplan.erl
@@ -24,6 +24,7 @@
-export([answer/1, answer_v/1]).
-export([hold/1, hold_v/1]).
-export([park/1, park_v/1]).
+-export([presence/1, presence_v/1]).
-export([play_and_collect_digits/1, play_and_collect_digits_v/1]).
-export([call_pickup/1, call_pickup_v/1]).
-export([hangup/1, hangup_v/1]).
@@ -400,6 +401,26 @@ park_v(JObj) ->
park_v(wh_json:to_proplist(JObj)).
%%--------------------------------------------------------------------
+%% @doc Presence a call - see wiki
+%% Takes proplist, creates JSON string or error
+%% @end
+%%--------------------------------------------------------------------
+-spec presence/1 :: (proplist() | json_object()) -> {'ok', iolist()} | {'error', string()}.
+presence(Prop) when is_list(Prop) ->
+ case presence_v(Prop) of
+ true -> wh_api:build_message(Prop, ?PRESENCE_REQ_HEADERS, ?OPTIONAL_PRESENCE_REQ_HEADERS);
+ false -> {error, "Proplist failed validation for presence_req"}
+ end;
+presence(JObj) ->
+ presence(wh_json:to_proplist(JObj)).
+
+-spec presence_v/1 :: (proplist() | json_object()) -> boolean().
+presence_v(Prop) when is_list(Prop) ->
+ wh_api:validate(Prop, ?PRESENCE_REQ_HEADERS, ?PRESENCE_REQ_VALUES, ?PRESENCE_REQ_TYPES);
+presence_v(JObj) ->
+ presence_v(wh_json:to_proplist(JObj)).
+
+%%--------------------------------------------------------------------
%% @doc Set Custom Channel variables - see wiki
%% Takes proplist, creates JSON string or error
%% @end
View
11 lib/whistle-1.0.0/src/api/wapi_dialplan.hrl
@@ -182,6 +182,17 @@
]).
-define(PARK_REQ_TYPES, []).
+%% Presence
+-define(PRESENCE_REQ_HEADERS, [<<"Application-Name">>, <<"Call-ID">>, <<"User">>]).
+-define(OPTIONAL_PRESENCE_REQ_HEADERS, [<<"Insert-At">>, <<"State">>]).
+-define(PRESENCE_REQ_VALUES, [{<<"Event-Category">>, <<"call">>}
+ ,{<<"Event-Name">>, <<"command">>}
+ ,{<<"Application-Name">>, <<"presence">>}
+ ,{<<"State">>, [<<"early">>, <<"confirmed">>, <<"terminated">>]}
+ ,?INSERT_AT_TUPLE
+ ]).
+-define(PRESENCE_REQ_TYPES, []).
+
%% Set
-define(SET_REQ_HEADERS, [<<"Application-Name">>, <<"Call-ID">>, <<"Custom-Channel-Vars">>, <<"Custom-Call-Vars">>]).
-define(OPTIONAL_SET_REQ_HEADERS, [<<"Insert-At">>]).
View
3  lib/whistle-1.0.0/src/wh_util.erl
@@ -100,7 +100,8 @@ is_account_enabled(AccountId) ->
PvtEnabled;
{error, R} ->
?LOG("unable to find enabled status of account ~s: ~p", [AccountId, R]),
- false
+ wh_cache:store({?MODULE, is_account_enabled, AccountId}, true, 300),
+ true
end
end.
View
135 lib/whistle_couch-1.0.0/src/couch_compactor.erl
@@ -14,7 +14,7 @@
%% Conflict resolution-enabled API
-export([compact_all/1, compact_all/2, compact_node/2, compact_node/3
- ,compact_db/3, compact_db/4]).
+ ,compact_db/3, compact_db/4]).
-include("wh_couch.hrl").
-define(SLEEP_BETWEEN_COMPACTION, 60000). %% sleep 60 seconds between shard compactions
@@ -24,22 +24,23 @@ start_link() ->
proc_lib:start_link(?MODULE, init, [self()], infinity, []).
init(Parent) ->
+ timer:sleep(5000),
case {couch_config:fetch(compact_automatically), couch_config:fetch(conflict_strategy)} of
- {true, undefined} ->
- ?LOG_SYS("just compacting"),
- proc_lib:init_ack(Parent, {ok, self()}),
- compact_all();
- {true, Strategy} ->
- ?LOG_SYS("compacting and removing conflicts"),
- proc_lib:init_ack(Parent, {ok, self()}),
- compact_all(Strategy);
- {false, _Strategy} ->
- ?LOG_SYS("auto-compaction not enabled"),
- proc_lib:init_ack(Parent, ignore);
- {undefined, _Strategy} ->
- ?LOG_SYS("auto-compaction not enabled"),
- proc_lib:init_ack(Parent, ignore),
- couch_config:store(compact_automatically, false)
+ {true, undefined} ->
+ ?LOG_SYS("just compacting"),
+ proc_lib:init_ack(Parent, {ok, self()}),
+ compact_all();
+ {true, Strategy} ->
+ ?LOG_SYS("compacting and removing conflicts"),
+ proc_lib:init_ack(Parent, {ok, self()}),
+ compact_all(Strategy);
+ {false, _Strategy} ->
+ ?LOG_SYS("auto-compaction not enabled"),
+ proc_lib:init_ack(Parent, ignore);
+ {undefined, _Strategy} ->
+ ?LOG_SYS("auto-compaction not enabled"),
+ proc_lib:init_ack(Parent, ignore),
+ couch_config:store(compact_automatically, false)
end.
-spec compact_all/0 :: () -> 'done'.
@@ -149,12 +150,12 @@ compact_node_db(NodeBin, DB, Conn, AdminConn) ->
?LOG("starting DB compaction"),
case get_db_shards(AdminConn, DBEncoded) of
- [] ->
- ?LOG("no shards found matching ~s", [DBEncoded]);
- Shards ->
- DesignDocs = get_db_design_docs(Conn, DBEncoded),
- _ = [ compact_shard(AdminConn, Shard, DesignDocs) || Shard <- Shards ],
- ok
+ [] ->
+ ?LOG("no shards found matching ~s", [DBEncoded]);
+ Shards ->
+ DesignDocs = get_db_design_docs(Conn, DBEncoded),
+ _ = [ compact_shard(AdminConn, Shard, DesignDocs) || Shard <- Shards ],
+ ok
end.
-spec compact_shard/3 :: (#server{}, ne_binary(), [ne_binary(),...] | []) -> 'ok'.
@@ -175,20 +176,20 @@ compact_shard(AdminConn, Shard, DesignDocs) ->
-spec wait_for_compaction/2 :: (#server{}, ne_binary()) -> 'ok'.
wait_for_compaction(AdminConn, Shard) ->
case couch_util:db_info(AdminConn, Shard) of
- {ok, ShardData} ->
- case wh_json:is_true(<<"compact_running">>, ShardData, false) of
- true ->
- ?LOG("compaction running for shard"),
- ok = timer:sleep(couch_config:fetch(<<"sleep_between_poll">>, ?SLEEP_BETWEEN_POLL)),
- wait_for_compaction(AdminConn, Shard);
- false ->
- ?LOG("compaction is not running for shard"),
- ok
- end;
- {error, _E} ->
- ?LOG("failed to query shard for compaction status: ~p", [_E]),
- ok = timer:sleep(couch_config:fetch(<<"sleep_between_poll">>, ?SLEEP_BETWEEN_POLL)),
- wait_for_compaction(AdminConn, Shard)
+ {ok, ShardData} ->
+ case wh_json:is_true(<<"compact_running">>, ShardData, false) of
+ true ->
+ ?LOG("compaction running for shard"),
+ ok = timer:sleep(couch_config:fetch(<<"sleep_between_poll">>, ?SLEEP_BETWEEN_POLL)),
+ wait_for_compaction(AdminConn, Shard);
+ false ->
+ ?LOG("compaction is not running for shard"),
+ ok
+ end;
+ {error, _E} ->
+ ?LOG("failed to query shard for compaction status: ~p", [_E]),
+ ok = timer:sleep(couch_config:fetch(<<"sleep_between_poll">>, ?SLEEP_BETWEEN_POLL)),
+ wait_for_compaction(AdminConn, Shard)
end.
-spec get_db_design_docs/2 :: (#server{}, ne_binary()) -> [ne_binary(),...] | [].
@@ -200,18 +201,18 @@ get_db_design_docs(Conn, DBEncoded) ->
get_db_shards(AdminConn, DBEncoded) ->
{ok, Cache} = whistle_couch_sup:cache_proc(),
case couch_config:fetch({shards, DBEncoded}, Cache) of
- undefined ->
- case couch_util:db_info(AdminConn) of
- {ok, []} -> ?LOG("no shards found on admin conn? That's odd"), [];
- {ok, Shards} ->
- Encoded = [ ShardEncoded || Shard <- Shards, is_a_shard(ShardEncoded=binary:replace(Shard, <<"/">>, <<"%2f">>, [global]), DBEncoded) ],
- couch_config:store({shards, DBEncoded}, Encoded, Cache),
- ?LOG("cached encoded shards for ~s", [DBEncoded]),
- Encoded
- end;
- Encoded ->
- ?LOG("pulled encoded shards from cache for ~s", [DBEncoded]),
- Encoded
+ undefined ->
+ case couch_util:db_info(AdminConn) of
+ {ok, []} -> ?LOG("no shards found on admin conn? That's odd"), [];
+ {ok, Shards} ->
+ Encoded = [ ShardEncoded || Shard <- Shards, is_a_shard(ShardEncoded=binary:replace(Shard, <<"/">>, <<"%2f">>, [global]), DBEncoded) ],
+ couch_config:store({shards, DBEncoded}, Encoded, Cache),
+ ?LOG("cached encoded shards for ~s", [DBEncoded]),
+ Encoded
+ end;
+ Encoded ->
+ ?LOG("pulled encoded shards from cache for ~s", [DBEncoded]),
+ Encoded
end.
-spec is_a_shard/2 :: (ne_binary(), ne_binary()) -> boolean().
@@ -236,32 +237,32 @@ get_ports(Node) ->
Cookie = couch_config:fetch(bigcouch_cookie),
?LOG_SYS("using cookie ~s on node ~s", [Cookie, Node]),
try
- erlang:set_cookie(Node, Cookie),
- get_ports(Node, net_adm:ping(Node))
+ erlang:set_cookie(Node, Cookie),
+ get_ports(Node, net_adm:ping(Node))
catch
- _:_R ->
- ?LOG("failed to get the ports for ~s: ~p", [Node, _R]),
- {?DEFAULT_PORT, ?DEFAULT_ADMIN_PORT}
+ _:_R ->
+ ?LOG("failed to get the ports for ~s: ~p", [Node, _R]),
+ {?DEFAULT_PORT, ?DEFAULT_ADMIN_PORT}
end.
get_ports(Node, pong) ->
?LOG_SYS("trying to find ports from node ~s", [Node]),
Port = case rpc:call(Node, couch_config, get, ["chttpd", "port"]) of
- {badrpc, _} ->
- ?LOG_SYS("failed to get port from RPC"),
- couch_mgr:get_port();
- P ->
- ?LOG_SYS("got port ~s", [P]),
- wh_util:to_integer(P)
- end,
+ {badrpc, _} ->
+ ?LOG_SYS("failed to get port from RPC"),
+ couch_mgr:get_port();
+ P ->
+ ?LOG_SYS("got port ~s", [P]),
+ wh_util:to_integer(P)
+ end,
AdminPort = case rpc:call(Node, couch_config, get, ["httpd", "port"]) of
- {badrpc, _} ->
- ?LOG_SYS("failed to get admin port from RPC"),
- couch_mgr:get_admin_port();
- AP ->
- ?LOG_SYS("got admin port ~s", [AP]),
- wh_util:to_integer(AP)
- end,
+ {badrpc, _} ->
+ ?LOG_SYS("failed to get admin port from RPC"),
+ couch_mgr:get_admin_port();
+ AP ->
+ ?LOG_SYS("got admin port ~s", [AP]),
+ wh_util:to_integer(AP)
+ end,
{Port, AdminPort};
get_ports(_Node, pang) ->
?LOG_SYS("using same ports as couch_mgr"),
View
11 whistle_apps/apps/callflow/src/callflow_maintenance.erl
@@ -24,7 +24,6 @@
-spec blocking_refresh/0 :: () -> 'ok'.
blocking_refresh() ->
lists:foreach(fun(AccountDb) ->
- timer:sleep(2000),
refresh(AccountDb)
end, whapps_util:get_all_accounts()),
ok.
@@ -41,17 +40,17 @@ blocking_refresh() ->
refresh() ->
spawn(fun() ->
lists:foreach(fun(AccountDb) ->
- timer:sleep(2000),
refresh(AccountDb)
end, whapps_util:get_all_accounts())
end),
started.
-refresh(Account) when not is_binary(Account) ->
- refresh(wh_util:to_binary(Account));
-refresh(Account) ->
+refresh(<<Account/binary>>) ->
AccountDb = wh_util:format_account_id(Account, encoded),
- couch_mgr:revise_views_from_folder(AccountDb, callflow).
+ Views = whapps_util:get_views_json(callflow, "views"),
+ whapps_util:update_views(AccountDb, Views);
+refresh(Account) ->
+ refresh(wh_util:to_binary(Account)).
%%--------------------------------------------------------------------
%% @public
View
125 whistle_apps/apps/callflow/src/cf_call_command.erl
@@ -12,12 +12,13 @@
-export([audio_macro/2]).
-export([response/2, response/3, response/4]).
--export([pickup/2]).
+-export([pickup/2, b_pickup/2]).
-export([redirect/3]).
-export([answer/1, hangup/1, set/3, fetch/1, fetch/2]).
-export([call_status/1, call_status/2, channel_status/1, channel_status/2]).
-export([bridge/2, bridge/3, bridge/4, bridge/5, bridge/6]).
-export([hold/1]).
+-export([presence/2, presence/3]).
-export([play/2, play/3]).
-export([record/2, record/3, record/4, record/5, record/6]).
-export([store/3, store/4, store/5]).
@@ -46,7 +47,11 @@
-export([wait_for_message/1, wait_for_message/2, wait_for_message/3, wait_for_message/4]).
-export([wait_for_application/1, wait_for_application/2, wait_for_application/3, wait_for_application/4]).
--export([wait_for_bridge/2, wait_for_unbridge/0]).
+-export([wait_for_headless_application/1, wait_for_headless_application/2
+ ,wait_for_headless_application/3, wait_for_headless_application/4
+ ]).
+-export([wait_for_bridge/2]).
+-export([wait_for_channel_bridge/0, wait_for_channel_unbridge/0]).
-export([wait_for_dtmf/1]).
-export([wait_for_noop/1]).
-export([wait_for_hangup/0]).
@@ -133,6 +138,10 @@ pickup(TargetCallId, Call) ->
,{<<"Target-Call-ID">>, TargetCallId}
],
send_command(Command, Call).
+
+b_pickup(TargetCallId, Call) ->
+ pickup(TargetCallId, Call),
+ wait_for_channel_unbridge().
%%--------------------------------------------------------------------
%% @private
@@ -160,6 +169,26 @@ redirect(Contact, Server, Call) ->
flush_dtmf(Call) ->
play(<<"silence_stream://50">>, Call).
+
+%%--------------------------------------------------------------------
+%% @public
+%% @doc
+%% @end
+%%--------------------------------------------------------------------
+-spec presence/2 :: (ne_binary(), #cf_call{}) -> 'ok'.
+-spec presence/3 :: (ne_binary(), ne_binary(), #cf_call{}) -> 'ok'.
+
+presence(State, #cf_call{from=User}=Call) ->
+ presence(State, User, Call).
+
+presence(State, User, Call) ->
+ Command = [{<<"User">>, User}
+ ,{<<"State">>, State}
+ ,{<<"Insert-At">>, <<"now">>}
+ ,{<<"Application-Name">>, <<"presence">>}
+ ],
+ send_command(Command, Call).
+
%%--------------------------------------------------------------------
%% @public
%% @doc
@@ -535,7 +564,7 @@ b_store(MediaName, Transfer, Method, Call) ->
b_store(MediaName, Transfer, Method, [?EMPTY_JSON_OBJECT], Call).
b_store(MediaName, Transfer, Method, Headers, Call) ->
store(MediaName, Transfer, Method, Headers, Call),
- wait_for_application(<<"store">>).
+ wait_for_headless_application(<<"store">>).
%%--------------------------------------------------------------------
%% @public
@@ -1190,6 +1219,9 @@ wait_for_application(Application, Event, Type, Timeout) ->
DiffMicro = timer:now_diff(erlang:now(), Start),
wait_for_application(Application, Event, Type, Timeout - (DiffMicro div 1000))
end;
+ { <<"call_event">>, <<"CHANNEL_DESTROY">>, _ } ->
+ ?LOG("channel was hungup while waiting for ~s", [Application]),
+ {error, channel_hungup};
{ Type, Event, Application } ->
{ok, JObj};
_ when Timeout =:= infinity ->
@@ -1211,6 +1243,59 @@ wait_for_application(Application, Event, Type, Timeout) ->
%%--------------------------------------------------------------------
%% @public
%% @doc
+%% Wait for an application to complete, ignoring channel state. This
+%% is only interested in events for the application.
+%% @end
+%%--------------------------------------------------------------------
+-type wait_for_headless_application_return() :: {'error', 'timeout'} | {'ok', wh_json:json_object()}.
+-spec wait_for_headless_application/1 :: (ne_binary()) -> wait_for_headless_application_return().
+-spec wait_for_headless_application/2 :: (ne_binary(), ne_binary()) -> wait_for_headless_application_return().
+-spec wait_for_headless_application/3 :: (ne_binary(), ne_binary(), ne_binary()) -> wait_for_headless_application_return().
+-spec wait_for_headless_application/4 :: (ne_binary(), ne_binary(), ne_binary(), 'infinity' | non_neg_integer()) -> wait_for_headless_application_return().
+wait_for_headless_application(Application) ->
+ wait_for_headless_application(Application, <<"CHANNEL_EXECUTE_COMPLETE">>).
+wait_for_headless_application(Application, Event) ->
+ wait_for_headless_application(Application, Event, <<"call_event">>).
+wait_for_headless_application(Application, Event, Type) ->
+ wait_for_headless_application(Application, Event, Type, 500000).
+
+wait_for_headless_application(Application, Event, Type, Timeout) ->
+ Start = erlang:now(),
+ receive
+ {amqp_msg, {struct, _}=JObj} ->
+ case get_event_type(JObj) of
+ { <<"error">>, _, _ } ->
+ case wh_json:get_value(<<"Error-Message">>, JObj) of
+ <<"Could not execute dialplan action: ", Application/binary>> ->
+ ?LOG("channel execution error while waiting for ~s", [Application]),
+ {error, JObj};
+ _ when Timeout =:= infinity ->
+ wait_for_headless_application(Application, Event, Type, Timeout);
+ _ ->
+ DiffMicro = timer:now_diff(erlang:now(), Start),
+ wait_for_headless_application(Application, Event, Type, Timeout - (DiffMicro div 1000))
+ end;
+ { Type, Event, Application } ->
+ {ok, JObj};
+ _ when Timeout =:= infinity ->
+ wait_for_headless_application(Application, Event, Type, Timeout);
+ _ ->
+ DiffMicro = timer:now_diff(erlang:now(), Start),
+ wait_for_headless_application(Application, Event, Type, Timeout - (DiffMicro div 1000))
+ end;
+ _ when Timeout =:= infinity ->
+ wait_for_headless_application(Application, Event, Type, Timeout);
+ _ ->
+ DiffMicro = timer:now_diff(erlang:now(), Start),
+ wait_for_headless_application(Application, Event, Type, Timeout - (DiffMicro div 1000))
+ after
+ Timeout ->
+ {error, timeout}
+ end.
+
+%%--------------------------------------------------------------------
+%% @public
+%% @doc
%% Wait for a DTMF event and extract the digits when it comes
%% @end
%%--------------------------------------------------------------------
@@ -1330,11 +1415,11 @@ wait_for_noop(NoopId) ->
%%--------------------------------------------------------------------
%% @public
%% @doc
-%% Wait forever for the channel to hangup
+%% Wait for a channel to be unbridged from (or destroyed)
%% @end
%%--------------------------------------------------------------------
--spec wait_for_unbridge/0 :: () -> {'ok', json_object()}.
-wait_for_unbridge() ->
+-spec wait_for_channel_unbridge/0 :: () -> {'ok', json_object()}.
+wait_for_channel_unbridge() ->
receive
{amqp_msg, {struct, _}=JObj} ->
case whapps_util:get_event_type(JObj) of
@@ -1343,12 +1428,36 @@ wait_for_unbridge() ->
{ <<"call_event">>, <<"CHANNEL_DESTROY">> } ->
{ok, JObj};
_ ->
- wait_for_unbridge()
+ wait_for_channel_unbridge()
+ end;
+ _ ->
+ %% dont let the mailbox grow unbounded if
+ %% this process hangs around...
+ wait_for_channel_unbridge()
+ end.
+
+%%--------------------------------------------------------------------
+%% @public
+%% @doc
+%% Wait for a channel to be bridged to (or destroyed)
+%% @end
+%%--------------------------------------------------------------------
+-spec wait_for_channel_bridge/0 :: () -> {'ok', json_object()}.
+wait_for_channel_bridge() ->
+ receive
+ {amqp_msg, {struct, _}=JObj} ->
+ case whapps_util:get_event_type(JObj) of
+ { <<"call_event">>, <<"CHANNEL_BRIDGE">> } ->
+ {ok, JObj};
+ { <<"call_event">>, <<"CHANNEL_DESTROY">> } ->
+ {ok, JObj};
+ _ ->
+ wait_for_channel_bridge()
end;
_ ->
%% dont let the mailbox grow unbounded if
%% this process hangs around...
- wait_for_unbridge()
+ wait_for_channel_bridge()
end.
%%--------------------------------------------------------------------
View
4 whistle_apps/apps/callflow/src/cf_exe.erl
@@ -262,8 +262,8 @@ handle_cast({channel_status_received, _}, #state{sanity_timer=RunningTRef}=State
{ok, TRef} = timer:send_after(?CALL_SANITY_CHECK, self(), {call_sanity_check}),
{noreply, State#state{status = <<"sane">>, sanity_timer=TRef}};
handle_cast({callid_update, NewCallId, NewCtrlQ}, #state{call_id=PrevCallId}=State) ->
- ?LOG("updating callid to ~s, catch you on the flip side", [NewCallId]),
put(callid, NewCallId),
+ ?LOG(PrevCallId, "updating callid to ~s, catch you on the flip side", [NewCallId]),
?LOG("removing call event bindings for ~s", [PrevCallId]),
gen_listener:rm_binding(self(), call, [{callid, PrevCallId}]),
?LOG("binding to new call events"),
@@ -319,7 +319,7 @@ handle_info({call_sanity_check}, #state{call_id=CallId, call=Call}=State) ->
handle_info(timeout, #state{call_id=CallId, ctrl_q=CtrlQ, call=#cf_call{cf_pid=Self}}=State) ->
spawn(fun() ->
ControllerQ = queue_name(Self),
- send_controller_queue(ControllerQ, CallId, CtrlQ)
+ send_controller_queue(ControllerQ, CallId, CtrlQ)
end),
{noreply, launch_cf_module(State)};
handle_info(_, State) ->
View
61 whistle_apps/apps/callflow/src/module/cf_park.erl
@@ -12,6 +12,8 @@
-export([handle/2]).
+-define(PARKED_CALLS, <<"parked_calls">>).
+
%%--------------------------------------------------------------------
%% @public
%% @doc
@@ -32,17 +34,21 @@ handle(Data, #cf_call{channel_vars=CCVs}=Call) ->
<<"park">> ->
?LOG("action is to park the call"),
park_call(SlotNumber, ParkedCalls, undefined, Call),
- cf_call_command:hold(Call),
cf_exe:transfer(Call);
<<"retrieve">> ->
?LOG("action is to retrieve a parked call"),
retrieve(SlotNumber, ParkedCalls, CallerHost, Call),
- cf_exe:transfer(Call);
+ cf_exe:continue(Call);
<<"auto">> ->
?LOG("action is to automatically determine if we should retrieve or park"),
- retrieve(SlotNumber, ParkedCalls, CallerHost, Call)
- orelse park_call(SlotNumber, ParkedCalls, undefined, Call),
- cf_exe:transfer(Call)
+ case retrieve(SlotNumber, ParkedCalls, CallerHost, Call) of
+ true ->
+ ?LOG("channel was unbridged from parked caller, continue"),
+ cf_exe:continue(Call);
+ false ->
+ park_call(SlotNumber, ParkedCalls, undefined, Call),
+ cf_exe:transfer(Call)
+ end
end;
nomatch ->
?LOG("call was the result of a blind transfer, assuming intention was to park"),
@@ -52,6 +58,9 @@ handle(Data, #cf_call{channel_vars=CCVs}=Call) ->
?LOG("call was the result of an attended-transfer completion, updating call id"),
update_call_id(Replaces, ParkedCalls, Call),
cf_call_command:hold(Call),
+ cf_call_command:wait_for_channel_bridge(),
+ ?LOG("parked caller has been picked up or hungup"),
+ cleanup_slot(SlotNumber, cf_exe:callid(Call), Call),
cf_exe:transfer(Call)
end.
@@ -94,8 +103,9 @@ retrieve(SlotNumber, ParkedCalls, CallerHost, #cf_call{to_user=ToUser, to_realm=
?LOG("THEY HUNGUP! playback nobody here and clean up", []),
false;
CallerHost ->
+ cleanup_slot(SlotNumber, ParkedCall, Call),
?LOG("pickup call id ~s", [ParkedCall]),
- cf_call_command:pickup(ParkedCall, Call),
+ cf_call_command:b_pickup(ParkedCall, Call),
true;
OtherNode ->
IP = get_node_ip(OtherNode),
@@ -132,10 +142,11 @@ get_node_ip(Node) ->
%% @end
%%--------------------------------------------------------------------
-spec park_call/4 :: (ne_binary(), json_object(), undefined | ne_binary(), #cf_call{}) -> ok.
-park_call(SlotNumber, ParkedCalls, ReferredTo, Call) ->
+park_call(SlotNumber, ParkedCalls, ReferredTo, #cf_call{request_user=Request, from_realm=FromRealm}=Call) ->
?LOG("attempting to park call in slot ~p", [SlotNumber]),
Slot = create_slot(Call),
save_slot(SlotNumber, Slot, ParkedCalls, Call),
+ cf_call_command:presence(<<"early">>, <<Request/binary, "@", FromRealm/binary>>, Call),
case ReferredTo of
undefined ->
?LOG("playback slot number to caller"),
@@ -143,6 +154,8 @@ park_call(SlotNumber, ParkedCalls, ReferredTo, Call) ->
cf_call_command:b_say(wh_util:to_binary(SlotNumber), Call);
_ ->
cf_call_command:hold(Call),
+ cf_call_command:wait_for_channel_bridge(),
+ cleanup_slot(SlotNumber, cf_exe:callid(Call), Call),
ok
end.
@@ -172,9 +185,13 @@ get_slot_number(ParkedCalls, _) ->
Next = case [wh_util:to_integer(Key) || Key <- wh_json:get_keys(Slots)] of
[] -> 100;
Keys ->
- hd(lists:dropwhile(fun(E) ->
- lists:member(E, Keys)
- end, lists:seq(100, lists:max(Keys) + 1)))
+ case lists:max(Keys) of
+ Max when Max < 100 -> 100;
+ Start ->
+ hd(lists:dropwhile(fun(E) ->
+ lists:member(E, Keys)
+ end, lists:seq(100, Start + 1)))
+ end
end,
wh_util:to_binary(Next).
@@ -233,7 +250,7 @@ update_call_id(Replaces, ParkedCalls, #cf_call{account_db=Db}=Call) ->
%%--------------------------------------------------------------------
-spec get_parked_calls/1 :: (#cf_call{}) -> json_object().
get_parked_calls(#cf_call{account_db=Db, account_id=Id}) ->
- case couch_mgr:open_doc(Db, <<"parked_calls">>) of
+ case couch_mgr:open_doc(Db, ?PARKED_CALLS) of
{error, not_found} ->
Timestamp = calendar:datetime_to_gregorian_seconds(calendar:universal_time()),
Generators = [fun(J) -> wh_json:set_value(<<"_id">>, <<"parked_calls">>, J) end
@@ -248,3 +265,25 @@ get_parked_calls(#cf_call{account_db=Db, account_id=Id}) ->
{ok, JObj} ->
JObj
end.
+
+%%--------------------------------------------------------------------
+%% @private
+%% @doc
+%%
+%% @end
+%%--------------------------------------------------------------------
+-spec cleanup_slot/3 :: (ne_binary(), ne_binary(), #cf_call{}) -> ok.
+cleanup_slot(SlotNumber, ParkedCall, #cf_call{account_db=Db, request_user=Request, from_realm=FromRealm}=Call) ->
+ case couch_mgr:open_doc(Db, ?PARKED_CALLS) of
+ {ok, JObj} ->
+ case wh_json:get_value([<<"slots">>, SlotNumber, <<"Call-ID">>], JObj) of
+ ParkedCall ->
+ cf_call_command:presence(<<"terminated">>, <<Request/binary, "@", FromRealm/binary>>, Call),
+ couch_mgr:save_doc(Db, wh_json:delete_key([<<"slots">>, SlotNumber], JObj)),
+ ok;
+ _Else ->
+ ok
+ end;
+ {error, _} ->
+ ok
+ end.
View
22 whistle_apps/apps/conference/src/conf_discovery.erl
@@ -69,7 +69,7 @@ start_link() ->
%------------------------------------------------------------------------------
init([]) ->
?LOG_SYS("starting new conference discovery process"),
- spawn(fun() -> whapps_util:revise_whapp_views_in_accounts(conference) end),
+ conference_maintenance:refresh(),
{ok, #state{}, 0}.
%------------------------------------------------------------------------------
@@ -101,21 +101,21 @@ handle_cast(_Msg, State) ->
%------------------------------------------------------------------------------
handle_info(timeout, #state{amqp_q = <<>>}=State) ->
try
- {ok, Q} = start_amqp(),
- {noreply, State#state{amqp_q=Q}, hibernate}
+ {ok, Q} = start_amqp(),
+ {noreply, State#state{amqp_q=Q}, hibernate}
catch
- _:_ ->
+ _:_ ->
?LOG_SYS("attempting to connect AMQP again in ~b ms", [?AMQP_RECONNECT_INIT_TIMEOUT]),
{ok, _} = timer:send_after(?AMQP_RECONNECT_INIT_TIMEOUT, {amqp_reconnect, ?AMQP_RECONNECT_INIT_TIMEOUT}),
- {noreply, State}
+ {noreply, State}
end;
handle_info({amqp_reconnect, T}, State) ->
try
- {ok, NewQ} = start_amqp(),
- {noreply, State#state{amqp_q=NewQ}, hibernate}
+ {ok, NewQ} = start_amqp(),
+ {noreply, State#state{amqp_q=NewQ}, hibernate}
catch
- _:_ ->
+ _:_ ->
case T * 2 of
Timeout when Timeout > ?AMQP_RECONNECT_MAX_TIMEOUT ->
?LOG_SYS("attempting to reconnect AMQP again in ~b ms", [?AMQP_RECONNECT_MAX_TIMEOUT]),
@@ -181,11 +181,11 @@ code_change(_OldVsn, State, _Extra) ->
-spec start_amqp/0 :: () -> tuple(ok, binary()).
start_amqp() ->
try
- {'basic.qos_ok'} = amqp_util:basic_qos(1),
+ {'basic.qos_ok'} = amqp_util:basic_qos(1),
_ = amqp_util:conference_exchange(),
Q = amqp_util:new_conference_queue(discovery),
amqp_util:bind_q_to_conference(Q, discovery),
- amqp_util:basic_consume(Q, [{exclusive, false}]),
+ amqp_util:basic_consume(Q, [{exclusive, false}]),
?LOG_SYS("connected to AMQP"),
{ok, Q}
catch
@@ -547,7 +547,7 @@ wait_for_command(Command) ->
{error, execution_failure};
{ <<"call_event">>, <<"CHANNEL_EXECUTE_COMPLETE">>, Command } ->
{ok, wh_json:get_value(<<"Application-Response">>, JObj, <<>>)};
- _ ->
+ _ ->
wait_for_command(Command)
end;
_ ->
View
51 whistle_apps/apps/conference/src/conference_maintenance.erl
@@ -0,0 +1,51 @@
+%%%-------------------------------------------------------------------
+%%% @author Karl Anderson <karl@2600hz.org>
+%%% @copyright (C) 2011, VoIP INC
+%%% @doc
+%%%
+%%% @end
+%%% Created : 15 Aug 2011 by Karl Anderson <karl@2600hz.org>
+%%%-------------------------------------------------------------------
+-module(conference_maintenance).
+
+-export([blocking_refresh/0]).
+-export([refresh/0, refresh/1]).
+
+-include("conference.hrl").
+
+%%--------------------------------------------------------------------
+%% @private
+%% @doc
+%%
+%% @end
+%%--------------------------------------------------------------------
+-spec blocking_refresh/0 :: () -> 'ok'.
+blocking_refresh() ->
+ lists:foreach(fun(AccountDb) ->
+ refresh(AccountDb)
+ end, whapps_util:get_all_accounts()),
+ ok.
+
+%%--------------------------------------------------------------------
+%% @private
+%% @doc
+%%
+%% @end
+%%--------------------------------------------------------------------
+-spec refresh/0 :: () -> 'started'.
+-spec refresh/1 :: (binary() | string()) -> 'ok'.
+
+refresh() ->
+ spawn(fun() ->
+ lists:foreach(fun(AccountDb) ->
+ refresh(AccountDb)
+ end, whapps_util:get_all_accounts())
+ end),
+ started.
+
+refresh(<<Account/binary>>) ->
+ AccountDb = wh_util:format_account_id(Account, encoded),
+ Views = whapps_util:get_views_json(conference, "views"),
+ whapps_util:update_views(AccountDb, Views);
+refresh(Account) ->
+ refresh(wh_util:to_binary(Account)).
View
12 whistle_apps/apps/crossbar/priv/couchdb/views/registrations.json
@@ -1,12 +0,0 @@
-{
- "_id":"_design/reg_doc"
- ,"language":"javascript"
- ,"views": {
- "lookup_realm_user": {
- "map": "function(doc) { var expires = parseInt(doc['Reg-Server-Timestamp'] || doc['Event-Timestamp']) + parseInt(doc['Expires']); emit([doc['Realm'], doc['To-User'], expires], null); }"
- },
- "realm_and_username": {
- "map": "function(doc) {var expires = parseInt(doc['Reg-Server-Timestamp'] || doc['Event-Timestamp']) + parseInt(doc['Expires']); emit([doc.Realm, doc.Username], {'id':doc._id, 'realm':doc.Realm, 'username':doc.Username })}"
- }
- }
-}
View
2  whistle_apps/apps/crossbar/src/crossbar_doc.erl
@@ -135,7 +135,7 @@ load_view(View, Options, #cb_context{db_name=DB, query_json=RJ}=Context) ->
true ->
[{<<"include_docs">>, true} | props:delete(<<"include_docs">>, Options)]
end,
- case couch_mgr:get_results(DB, View, [{<<"stale">>, <<"update_after">>}|ViewOptions]) of
+ case couch_mgr:get_results(DB, View, ViewOptions) of
{error, invalid_view_name} ->
?LOG("loading view ~s from ~s failed: invalid view", [View, DB]),
crossbar_util:response_missing_view(Context);
View
181 whistle_apps/apps/crossbar/src/crossbar_maintenance.erl
@@ -8,22 +8,6 @@
-module(crossbar_maintenance).
-export([refresh/0, refresh/1]).
--export([blocking_refresh/0]).
--export([purge_doc_type/2]).
-
--include("../include/crossbar.hrl").
-
--define(DEVICES_CB_LIST, <<"devices/crossbar_listing">>).
-
-%%--------------------------------------------------------------------
-%% @private
-%% @doc
-%%
-%% @end
-%%--------------------------------------------------------------------
--spec blocking_refresh/0 :: () -> 'ok'.
-blocking_refresh() ->
- do_refresh().
%%--------------------------------------------------------------------
%% @private
@@ -31,166 +15,11 @@ blocking_refresh() ->
%%
%% @end
%%--------------------------------------------------------------------
--spec refresh/0 :: () -> 'started'.
--spec refresh/1 :: (ne_binary() | nonempty_string()) -> 'ok'.
+-spec refresh/0 :: () -> string().
+-spec refresh/1 :: (term()) -> string().
refresh() ->
- spawn(fun do_refresh/0),
- started.
+ "please use whapps_maintenance:refresh().".