Skip to content

Commit

Permalink
PISTON-1163: Refactors cf_acdc_agent status update logic.
Browse files Browse the repository at this point in the history
We wanted to introduce an additional `end_wrapup` action that requires `cf_acdc_agent` changes. The existing logic seems somewhat harder to follow than necessary; is inconsistent in various ways; and seems to still include obsolete code. These changes only refactor the status update logic to give the code a more consistent structure and simplify future changes. Other than making log messages more consistent, these changes should have no functionally observable effect. The `end_wrapup` changes will not be made at the present time, because of bugs that require more effort to fix.

* Kazoo 4.3 upstream changes: 2600hz#6672
* ACDC community upstream changes: kazoo-community/kazoo-acdc#32
  • Loading branch information
Roger Neate committed Dec 17, 2020
1 parent a39d51c commit c3fa6fc
Showing 1 changed file with 136 additions and 64 deletions.
200 changes: 136 additions & 64 deletions applications/acdc/src/cf_acdc_agent.erl
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,6 @@
,find_agent_status/2
,play_not_an_agent/1
,play_agent_invalid/1
,login_agent/2
,logout_agent/2
]).

-include("acdc_config.hrl").
Expand All @@ -40,88 +38,128 @@ handle(Data, Call) ->
play_not_an_agent(Call);
{'ok', AgentId} ->
Status = find_agent_status(Call, AgentId),
NewStatus = fix_data_status(kz_json:get_value(<<"action">>, Data)),
lager:info("agent ~s maybe changing status from ~s to ~s", [AgentId, Status, NewStatus]),

maybe_update_status(Call, AgentId, Status, NewStatus, Data);
Action = fix_data_action(kz_json:get_value(<<"action">>, Data)),
lager:info("agent ~s maybe action ~s from status ~s", [AgentId, Action, Status]),
maybe_update_status(Call, AgentId, Action, Status, Data);
{'error', 'multiple_owners'} ->
lager:info("too many owners of device ~s, not logging in", [kapps_call:authorizing_id(Call)]),
play_agent_invalid(Call)
end,
lager:info("finished with acdc agent callflow"),
cf_exe:continue(Call).

%%------------------------------------------------------------------------------
%% @doc Get a normalized current agent status value.
%% @end
%%------------------------------------------------------------------------------
-spec find_agent_status(kapps_call:call() | kz_term:ne_binary(), kz_term:ne_binary()) -> kz_term:ne_binary().
find_agent_status(?NE_BINARY = AcctId, AgentId) ->
fix_agent_status(acdc_agent_util:most_recent_status(AcctId, AgentId));
find_agent_status(Call, AgentId) ->
find_agent_status(kapps_call:account_id(Call), AgentId).

%%------------------------------------------------------------------------------
%% @doc Normalizes agent status values.
%% @end
%%------------------------------------------------------------------------------
-spec fix_agent_status({'ok', kz_term:ne_binary()}) -> kz_term:ne_binary().
fix_agent_status({'ok', <<"resume">>}) -> <<"ready">>;
fix_agent_status({'ok', <<"wrapup">>}) -> <<"ready">>;
fix_agent_status({'ok', <<"busy">>}) -> <<"ready">>;
fix_agent_status({'ok', <<"logout">>}) -> <<"logged_out">>;
fix_agent_status({'ok', <<"login">>}) -> <<"ready">>;
fix_agent_status({'ok', <<"outbound">>}) -> <<"ready">>;
fix_agent_status({'ok', Status}) -> Status.

fix_data_status(<<"pause">>) -> <<"paused">>;
fix_data_status(Status) -> Status.

maybe_update_status(Call, AgentId, _Curr, <<"logout">>, Data) ->
lager:info("agent ~s wants to log out (currently: ~s)", [AgentId, _Curr]),
logout_agent(Call, AgentId, Data),
play_agent_logged_out(Call);
maybe_update_status(Call, AgentId, <<"logged_out">>, <<"resume">>, _Data) ->
lager:debug("agent ~s is logged out, resuming doesn't make sense", [AgentId]),
play_agent_invalid(Call);
maybe_update_status(Call, AgentId, <<"logged_out">>, <<"login">>, Data) ->
maybe_login_agent(Call, AgentId, Data);
maybe_update_status(Call, AgentId, <<"unknown">>, <<"login">>, Data) ->
maybe_login_agent(Call, AgentId, Data);
maybe_update_status(Call, AgentId, <<"ready">>, <<"login">>, Data) ->
lager:info("agent ~s is already logged in", [AgentId]),
_ = play_agent_logged_in_already(Call),
send_new_status(Call, AgentId, Data, fun kapi_acdc_agent:publish_login/1, 'undefined');
maybe_update_status(Call, AgentId, FromStatus, <<"paused">>, Data) ->
maybe_pause_agent(Call, AgentId, FromStatus, Data);
maybe_update_status(Call, AgentId, <<"paused">>, <<"ready">>, Data) ->
lager:info("agent ~s is coming back from pause", [AgentId]),
resume_agent(Call, AgentId, Data),
play_agent_resume(Call);
maybe_update_status(Call, AgentId, <<"paused">>, <<"resume">>, Data) ->
lager:info("agent ~s is coming back from pause", [AgentId]),
resume_agent(Call, AgentId, Data),
play_agent_resume(Call);
maybe_update_status(Call, AgentId, <<"outbound">>, <<"resume">>, Data) ->
lager:info("agent ~s is coming back from pause", [AgentId]),
resume_agent(Call, AgentId, Data),
play_agent_resume(Call);
maybe_update_status(Call, AgentId, <<"ready">>, <<"resume">>, Data) ->
lager:info("agent ~s is coming back from pause", [AgentId]),
resume_agent(Call, AgentId, Data),
play_agent_resume(Call);
maybe_update_status(Call, _AgentId, _Status, _NewStatus, _Data) ->
lager:info("agent ~s: invalid status change from ~s to ~s", [_AgentId, _Status, _NewStatus]),
%%------------------------------------------------------------------------------
%% @doc Normalizes action values.
%% @end
%%------------------------------------------------------------------------------
-spec fix_data_action(kz_term:ne_binary()) -> kz_term:ne_binary().
fix_data_action(<<"paused">>) -> <<"pause">>;
fix_data_action(Status) -> Status.

%%------------------------------------------------------------------------------
%% @doc Update an agent's status if the action is valid for the current status.
%% @end
%%------------------------------------------------------------------------------
-spec maybe_update_status(kapps_call:call(), kz_term:ne_binary(), kz_term:ne_binary(), kz_term:ne_binary(), kz_json:object()) ->
kapps_call:kapps_api_std_return().
maybe_update_status(Call, AgentId, <<"logout">>, _Status, Data) ->
logout_agent(Call, AgentId, Data);
maybe_update_status(Call, AgentId, <<"login">>, Status, Data) ->
maybe_login_agent(Call, AgentId, Status, Data);
maybe_update_status(Call, AgentId, <<"pause">>, Status, Data) ->
maybe_pause_agent(Call, AgentId, Status, Data);
maybe_update_status(Call, AgentId, <<"ready">>, <<"paused">>, Data) ->
maybe_update_status(Call, AgentId, <<"resume">>, <<"paused">>, Data);
maybe_update_status(Call, AgentId, <<"resume">>, Status, Data) ->
maybe_resume_agent(Call, AgentId, Status, Data);
maybe_update_status(Call, AgentId, Action, _Status, _Data) ->
lager:info("agent ~s: action ~s is invalid", [AgentId, Action]),
play_agent_invalid(Call).

%%------------------------------------------------------------------------------
%% @doc Login an agent if the action is valid for the current status.
%% @end
%%------------------------------------------------------------------------------
-spec maybe_login_agent(kapps_call:call(), kz_term:ne_binary(), kz_term:ne_binary(), kz_json:object()) ->
kapps_call:kapps_api_std_return().
maybe_login_agent(Call, AgentId, Status, Data) ->
case lists:member(Status, [<<"logged_out">>, <<"unknown">>]) of
'true' ->
maybe_login_agent(Call, AgentId, Data);
'false' ->
lager:info("agent ~s is already logged in when status is ~s", [AgentId, Status]),
send_new_status(Call, AgentId, Data, fun kapi_acdc_agent:publish_login/1, 'undefined'),
play_agent_logged_in_already(Call)
end.

%%------------------------------------------------------------------------------
%% @doc Attempt to login an agent.
%% @end
%%------------------------------------------------------------------------------
-spec maybe_login_agent(kapps_call:call(), kz_term:ne_binary(), kz_json:object()) ->
kapps_call:kapps_api_std_return().
maybe_login_agent(Call, AgentId, Data) ->
lager:debug("agent ~s wants to log in", [AgentId]),
case login_agent(Call, AgentId, Data) of
<<"success">> -> play_agent_logged_in(Call);
<<"failed">> -> play_agent_invalid(Call)
end.

maybe_pause_agent(Call, AgentId, <<"ready">>, Data) ->
pause_agent(Call, AgentId, Data);
maybe_pause_agent(Call, _AgentId, FromStatus, _Data) ->
lager:info("unable to go from ~s to paused", [FromStatus]),
play_agent_invalid(Call).
%%------------------------------------------------------------------------------
%% @doc Pause an agent if the action is valid for the current status.
%% @end
%%------------------------------------------------------------------------------
-spec maybe_pause_agent(kapps_call:call(), kz_term:ne_binary(), kz_term:ne_binary(), kz_json:object()) ->
kapps_call:kapps_api_std_return().
maybe_pause_agent(Call, AgentId, Status, Data) ->
case lists:member(Status, [<<"ready">>, <<"wrapup">>]) of
'true' ->
pause_agent(Call, AgentId, Data);
'false' ->
lager:info("agent ~s cannot pause when status is ~s", [AgentId, Status]),
play_agent_invalid(Call)
end.

-spec login_agent(kapps_call:call(), kz_term:ne_binary()) -> kz_term:api_ne_binary().
login_agent(Call, AgentId) ->
login_agent(Call, AgentId, kz_json:new()).
%%------------------------------------------------------------------------------
%% @doc Resume an agent if the action is valid for the current status.
%% @end
%%------------------------------------------------------------------------------
-spec maybe_resume_agent(kapps_call:call(), kz_term:ne_binary(), kz_term:ne_binary(), kz_json:object()) ->
kapps_call:kapps_api_std_return().
maybe_resume_agent(Call, AgentId, Status, Data) ->
case lists:member(Status, [<<"paused">>, <<"outbound">>, <<"ready">>, <<"wrapup">>]) of
'true' ->
resume_agent(Call, AgentId, Data);
'false' ->
lager:info("agent ~s cannot resume when status is ~s", [AgentId, Status]),
play_agent_invalid(Call)
end.

%%------------------------------------------------------------------------------
%% @doc Publish an AMQP agent `login' message.
%% @end
%%------------------------------------------------------------------------------
-spec login_agent(kapps_call:call(), kz_term:ne_binary(), kz_json:object()) -> kz_term:api_ne_binary().
login_agent(Call, AgentId, Data) ->
Update = props:filter_undefined(
Expand All @@ -137,36 +175,70 @@ login_agent(Call, AgentId, Data) ->
)
of
{'ok', RespJObj} ->
lager:info("agent ~s is logging in", [AgentId]),
kz_json:get_value(<<"Status">>, RespJObj);
{'error', _E} ->
lager:debug("failed to hear back about login: ~p", [_E]),
<<"failed">>
end.

-spec logout_agent(kapps_call:call(), kz_term:ne_binary()) -> 'ok'.
logout_agent(Call, AgentId) ->
logout_agent(Call, AgentId, kz_json:new()).

-spec logout_agent(kapps_call:call(), kz_term:ne_binary(), kz_json:object()) -> 'ok'.
%%------------------------------------------------------------------------------
%% @doc Publish an AMQP agent `logout' message.
%% @end
%%------------------------------------------------------------------------------
-spec logout_agent(kapps_call:call(), kz_term:ne_binary(), kz_json:object()) ->
kapps_call:kapps_api_std_return().
logout_agent(Call, AgentId, Data) ->
update_agent_status(Call, AgentId, Data, fun kapi_acdc_agent:publish_logout/1).
lager:info("agent ~s is logging out", [AgentId]),
update_agent_status(Call, AgentId, Data, fun kapi_acdc_agent:publish_logout/1),
play_agent_logged_out(Call).

%%------------------------------------------------------------------------------
%% @doc Publish an AMQP agent `pause' message.
%% @end
%%------------------------------------------------------------------------------
-spec pause_agent(kapps_call:call(), kz_term:ne_binary(), kz_json:object(), kz_term:api_integer()) ->
kapps_call:kapps_api_std_return().
pause_agent(Call, AgentId, Data, Timeout) ->
_ = play_agent_pause(Call),
update_agent_status(Call, AgentId, Data, fun kapi_acdc_agent:publish_pause/1, Timeout).
lager:info("agent ~s is pausing work for ~p s", [AgentId, Timeout]),
update_agent_status(Call, AgentId, Data, fun kapi_acdc_agent:publish_pause/1, Timeout),
play_agent_pause(Call).

%%------------------------------------------------------------------------------
%% @doc Publish an AMQP agent `pause' message.
%% @end
%%------------------------------------------------------------------------------
-spec pause_agent(kapps_call:call(), kz_term:ne_binary(), kz_json:object()) ->
kapps_call:kapps_api_std_return().
pause_agent(Call, AgentId, Data) ->
Timeout = kz_json:get_value(<<"timeout">>, Data, ?DEFAULT_AGENT_PAUSE_TIMEOUT),
lager:info("agent ~s is pausing work for ~p s", [AgentId, Timeout]),
pause_agent(Call, AgentId, Data, Timeout).

%%------------------------------------------------------------------------------
%% @doc Publish an AMQP agent `resume' message.
%% @end
%%------------------------------------------------------------------------------
-spec resume_agent(kapps_call:call(), kz_term:ne_binary(), kz_json:object()) ->
kapps_call:kapps_api_std_return().
resume_agent(Call, AgentId, Data) ->
update_agent_status(Call, AgentId, Data, fun kapi_acdc_agent:publish_resume/1).
lager:info("agent ~s is coming back from pause", [AgentId]),
update_agent_status(Call, AgentId, Data, fun kapi_acdc_agent:publish_resume/1),
play_agent_resume(Call).

%%------------------------------------------------------------------------------
%% @doc Publish an AMQP agent status-change message.
%% @end
%%------------------------------------------------------------------------------
-spec update_agent_status(kapps_call:call(), kz_term:ne_binary(), kz_json:object(), kz_amqp_worker:publish_fun()) -> 'ok'.
update_agent_status(Call, AgentId, Data, PubFun) ->
update_agent_status(Call, AgentId, Data, PubFun, 'undefined').
update_agent_status(Call, AgentId, Data, PubFun, Timeout) ->
send_new_status(Call, AgentId, Data, PubFun, Timeout).

%%------------------------------------------------------------------------------
%% @doc Publish an AMQP agent status-change message.
%% @end
%%------------------------------------------------------------------------------
-spec send_new_status(kapps_call:call(), kz_term:ne_binary(), kz_json:object(), kz_amqp_worker:publish_fun(), kz_term:api_integer() | kz_term:ne_binary()) -> 'ok'.
send_new_status(Call, AgentId, Data, PubFun, Timeout) ->
Update = props:filter_undefined(
Expand Down

0 comments on commit c3fa6fc

Please sign in to comment.