Second untested approach to session machines

This commit is contained in:
Andrey Fadeev 2018-06-14 21:38:59 +03:00
parent 2503d01cee
commit c79e6e24f4
9 changed files with 194 additions and 108 deletions

View File

@ -26,7 +26,8 @@
-type destination() :: #{
wallet := wallet(),
resource := resource(),
status := status()
status := status(),
masked_pan := binary()
}.
-export_type([destination/0]).

View File

@ -0,0 +1,15 @@
%%% Withdrawal adapter generic
-module(ff_adpt).
%%
%% Types
%%
-type adapter() :: atom().
-type adapter_state() :: any().
-type withdrawal() :: ff_adpt_withdrawal:withdrawal().
-export_type([adapter/0]).
-export_type([withdrawal/0]).
-export_type([adapter_state/0]).

View File

@ -0,0 +1,143 @@
%%% Client for adapter for withdrawal provider
-module(ff_adpt_client).
-include_lib("dmsl/include/dmsl_domain_thrift.hrl").
-include_lib("dmsl/include/dmsl_withdrawals_provider_adapter_thrift.hrl").
%% API
-export([process_withdrawal/5]).
%%
%% Internal types
%%
-type id() :: machinery:id().
-type identity_id() :: id().
-type withdrawal() :: ff_adpt_withdrawal:withdrawal().
-type destination() :: ff_adpt_withdrawal:destination().
-type cash() :: ff_adpt_withdrawal:cash().
-type adapter() :: ff_wthadpt:adapter().
-type intent() :: {finish, status()} | {sleep, timer()}.
-type status() :: {success, trx_info()} | {failure, ff_adpt:failure()}.
-type timer() :: dmsl_base_thrift:'Timer'().
-type trx_info() :: dmsl_domain_thrift:'TransactionInfo'().
-type adapter_state() :: ff_adpt:adapter_state().
-type process_result() :: {ok, intent(), adapter_state()} | {ok, intent()}.
-type domain_withdrawal() :: dmsl_withdrawals_provider_adapter_thrift:'Withdrawal'().
-type domain_cash() :: dmsl_withdrawals_provider_adapter_thrift:'Cash'().
-type domain_currency() :: dmsl_domain_thrift:'Currency'().
-type domain_destination() :: dmsl_withdrawals_provider_adapter_thrift:'Destination'().
-type domain_identity() :: dmsl_withdrawals_provider_adapter_thrift:'Identity'().
-type backend() :: machinery:backend(_).
%%
%% API
%%
-spec process_withdrawal(Adapter, Withdrawal, ASt, AOpt, Be) -> process_result() when
Adapter :: adapter(),
Withdrawal :: withdrawal(),
ASt :: adapter_state(),
AOpt :: map(),
Be :: backend().
process_withdrawal(Adapter, Withdrawal, ASt, AOpt, Be) ->
DomainWithdrawal = build_and_encode_withdrawal(Withdrawal, Be),
{ok, Result} = call(Adapter, 'ProcessWithdrawal', [DomainWithdrawal, ASt, AOpt]),
decode_result(Result).
%%
%% Internals
%%
call(Adapter, Function, Args) ->
Request = {{dmsl_withdrawals_provider_adapter_thrift, 'Adapter'}, Function, Args},
ff_woody_client:call(Adapter, Request).
%% Encoders
-spec build_and_encode_withdrawal(Withdrawal, Be) -> domain_withdrawal() when
Withdrawal :: withdrawal(),
Be :: backend().
build_and_encode_withdrawal(Withdrawal, Be) ->
#{
id := WId,
cash := Cash,
destination := Dest,
sender := Sender,
receiver := Receiver
} = Withdrawal,
#wthadpt_Withdrawal{
id = WId,
body = encode_body(Cash),
destination = encode_destination(Dest),
sender = fetch_and_encode_identity(Sender, Be),
receiver = fetch_and_encode_identity(Receiver, Be)
}.
-spec encode_body(cash()) -> domain_cash().
encode_body({Amount, CurrencyId}) ->
Currency = ff_currency:get(CurrencyId),
DomainCurrency = encode_currency(Currency),
#wthadpt_Cash{amount = Amount, currency = DomainCurrency}.
-spec encode_currency(ff_currency:currency()) -> domain_currency().
encode_currency(#{
name := Name,
symcode := Symcode,
numcode := Numcode,
exponent := Exponent
}) ->
#domain_Currency{
name = Name,
symbolic_code = Symcode,
numeric_code = Numcode,
exponent = Exponent
}.
-spec encode_destination(destination()) -> domain_destination().
encode_destination(Destination) ->
#{resource := Resource} = Destination,
#{
token := Token,
payment_system := PaymentSystem,
bin := Bin,
masked_pan := MaskedPan
} = Resource,
{bank_card, #domain_BankCard{
token = Token,
payment_system = PaymentSystem,
bin = Bin,
masked_pan = MaskedPan
}}.
-spec fetch_and_encode_identity
(identity_id(), backend()) -> domain_identity();
(undefined, backend()) -> undefined.
fetch_and_encode_identity(undefined, _Be) ->
undefined;
fetch_and_encode_identity(IdentityId, _Be) ->
% {ok, Identity} = ff_identity:get(IdentityId, Be),
% TODO: Add documents and contract fields
#wthdm_Identity{
id = IdentityId
}.
-spec decode_result(dmsl_withdrawals_provider_adapter_thrift:'ProcessResult'()) -> process_result().
decode_result(#wthadpt_ProcessResult{intent = Intent, next_state = undefined}) ->
{ok, decode_intent(Intent)};
decode_result(#wthadpt_ProcessResult{intent = Intent, next_state = NextState}) ->
{ok, decode_intent(Intent), NextState}.
%% Decoders
-spec decode_intent(dmsl_withdrawals_provider_adapter_thrift:'Intent'()) -> intent().
decode_intent({finish, #wthadpt_FinishIntent{status = {success, #wthadpt_Success{trx_info = TrxInfo}}}}) ->
{finish, {success, TrxInfo}};
decode_intent({finish, #wthadpt_FinishIntent{status = {failure, Failure}}}) ->
{finish, {failure, Failure}};
decode_intent({sleep, #wthadpt_SleepIntent{timer = Timer}}) ->
{sleep, Timer}.

View File

@ -1,7 +1,7 @@
%%%
%%% Withdrawal session machine
%%%
-module(ff_wth_session_machine).
-module(ff_adpt_session_machine).
-behaviour(machinery).
%% API
@ -20,11 +20,11 @@
-type session() :: #{
id => id(),
status => session_status(),
withdrawal => ff_wthadpt:withdrawal(),
withdrawal => withdrawal(),
adapter => adapter()
}.
-type session_result() :: {success, trx_info()} | {failed, ff_wthadpt:failure()}.
-type session_result() :: {success, trx_info()} | {failed, ff_adpt:failure()}.
-type session_status() :: new
| active
@ -32,10 +32,10 @@
-type ev() :: {created, session()}
| started
| {next_state, ff_wthadpt:adapter_state()}
| {next_state, ff_adpt:adapter_state()}
| {finished, session_result()}.
-type adapter() :: {ff_wthadpt:adapter(), map()}.
-type adapter() :: {ff_adpt:adapter(), map()}.
%%
%% Internal types
@ -47,6 +47,7 @@
-type auxst() :: #{}.
-type withdrawal() :: ff_adpt_withdrawal:withdrawal().
-type machine() :: machinery:machine(ev(), auxst()).
-type result() :: machinery:result(ev(), auxst()).
-type handler_opts() :: machinery:handler_opts().
@ -56,7 +57,7 @@
-type st() :: #{
session => session(),
adapter_state => ff_wthadpt:adapter_state()
adapter_state => ff_adpt:adapter_state()
}.
%% Pipeline
@ -71,7 +72,7 @@
Ns :: namespace(),
Id :: id(),
Adapter :: adapter(),
Withdrawal :: ff_wthadpt:withdrawal(),
Withdrawal :: withdrawal(),
Be :: backend(),
Error :: {error, exists}.
create(Ns, Id, Adapter, Withdrawal, Be) ->
@ -121,7 +122,7 @@ process_session(#{session := #{status := active} = Session} = St) ->
withdrawal := Withdrawal
} = Session,
ASt = maps:get(adapter_state, St, []),
case ff_wthadpt_client:process_withdrawal(Adapter, Withdrawal, ASt, AdapterOpts) of
case ff_adpt_client:process_withdrawal(Adapter, Withdrawal, ASt, AdapterOpts) of
{ok, Intent, NextState} ->
process_intent(Intent, NextState);
{ok, Intent} ->
@ -145,7 +146,7 @@ process_intent({sleep, Timer}) ->
%%
-spec create_session(id(), adapter(), ff_wthadpt:withdrawal()) -> session().
-spec create_session(id(), adapter(), ff_adpt:withdrawal()) -> session().
create_session(Id, Adapter, Withdrawal) ->
#{
id => Id,

View File

@ -0,0 +1,22 @@
-module(ff_adpt_withdrawal).
%%
%% Types
%%
-type destination() :: ff_destination:destination().
-type identity() :: ff_identity:identity().
-type cash() :: ff_transfer:body().
-type withdrawal() :: #{
id => binary(),
destination => destination(),
cash => cash(),
sender => identity() | undefined,
receiver => identity() | undefined
}.
-export_type([destination/0]).
-export_type([identity/0]).
-export_type([cash/0]).
-export_type([withdrawal/0]).

View File

@ -19,6 +19,7 @@
}.
-export_type([id/0]).
-export_type([currency/0]).
-export([get/1]).

View File

@ -32,6 +32,7 @@
}.
-export_type([transfer/0]).
-export_type([posting/0]).
-export_type([status/0]).
-export([trxid/1]).

View File

@ -1,31 +0,0 @@
%%% Withdrawal generic
-module(ff_wthadpt).
%%
%% Types
%%
-type adapter() :: atom().
-type wth_id() :: binary().
-type destination() :: dmsl_withdrawals_provider_adapter_thrift:'Destination'().
-type identity() :: dmsl_withdrawals_provider_adapter_thrift:'Identity'().
-type cash() :: dmsl_withdrawals_provider_adapter_thrift:'Cash'().
-type failure() :: dmsl_domain_thrift:'Failure'().
-type adapter_state() :: any().
-type withdrawal() :: #{
id => wth_id(),
body => cash(),
destination => destination(),
sender => identity() | indefined,
receiver => identity() | indefined
}.
-export_type([adapter/0]).
-export_type([wth_id/0]).
-export_type([destination/0]).
-export_type([identity/0]).
-export_type([cash/0]).
-export_type([failure/0]).
-export_type([withdrawal/0]).
-export_type([adapter_state/0]).

View File

@ -1,67 +0,0 @@
%%% Client for adapter for withdrawal provider
-module(ff_wthadpt_client).
-include_lib("dmsl/include/dmsl_withdrawals_provider_adapter_thrift.hrl").
%% API
-export([process_withdrawal/4]).
%%
%% Internal types
%%
-type withdrawal() :: ff_wthadpt:withdrawal().
-type adapter() :: ff_wthadpt:adapter().
-type intent() :: {finish, status()} | {sleep, timer()}.
-type status() :: {success, trx_info()} | {failure, ff_wthadpt:failure()}.
-type timer() :: dmsl_base_thrift:'Timer'().
-type trx_info() :: dmsl_domain_thrift:'TransactionInfo'().
-type adapter_state() :: ff_wthadpt:adapter_state().
-type process_result() :: {ok, intent(), adapter_state()} | {ok, intent()}.
%%
%% API
%%
-spec process_withdrawal(adapter(), withdrawal(), adapter_state(), map()) -> process_result().
process_withdrawal(Adapter, Withdrawal, State, Options) ->
{ok, Result} = call(Adapter, 'ProcessWithdrawal', [encode_withdrawal(Withdrawal), State, Options]),
decode_result(Result).
%%
%% Internals
%%
call(Adapter, Function, Args) ->
Request = {{dmsl_withdrawals_provider_adapter_thrift, 'Adapter'}, Function, Args},
ff_woody_client:call(Adapter, Request).
-spec encode_withdrawal(withdrawal()) -> dmsl_withdrawals_provider_adapter_thrift:'Withdrawal'().
encode_withdrawal(#{
id := Id,
body := Body,
destination := Destination,
sender := Sender,
receiver := Receiver
}) ->
#wthadpt_Withdrawal{
id = Id,
body = Body,
destination = Destination,
sender = Sender,
receiver = Receiver
}.
-spec decode_result(dmsl_withdrawals_provider_adapter_thrift:'ProcessResult'()) -> process_result().
decode_result(#wthadpt_ProcessResult{intent = Intent, next_state = undefined}) ->
{ok, decode_intent(Intent)};
decode_result(#wthadpt_ProcessResult{intent = Intent, next_state = NextState}) ->
{ok, decode_intent(Intent), NextState}.
-spec decode_intent(dmsl_withdrawals_provider_adapter_thrift:'Intent'()) -> intent().
decode_intent({finish, #wthadpt_FinishIntent{status = {success, #wthadpt_Success{trx_info = TrxInfo}}}}) ->
{finish, {success, TrxInfo}};
decode_intent({finish, #wthadpt_FinishIntent{status = {failure, Failure}}}) ->
{finish, {failure, Failure}};
decode_intent({sleep, #wthadpt_SleepIntent{timer = Timer}}) ->
{sleep, Timer}.