erlfrm code formatter (#347)

This commit is contained in:
dinama 2020-12-02 17:43:45 +03:00 committed by GitHub
parent dcc4b32a35
commit d8713b86c9
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
274 changed files with 17547 additions and 17030 deletions

View File

@ -24,7 +24,7 @@ BUILD_IMAGE_TAG := 442c2c274c1d8e484e5213089906a4271641d95e
REGISTRY := dr2.rbkmoney.com
CALL_ANYWHERE := all submodules compile xref lint dialyze release clean distclean
CALL_ANYWHERE := all submodules compile xref lint format check_format dialyze release clean distclean
CALL_ANYWHERE += generate regenerate
CALL_W_CONTAINER := $(CALL_ANYWHERE) test
@ -51,6 +51,12 @@ xref: submodules
lint:
elvis rock
check_format:
$(REBAR) fmt -c
format:
$(REBAR) fmt -w
dialyze: submodules generate
$(REBAR) dialyzer

View File

@ -7,14 +7,14 @@
-type reason() :: binary().
-type failure() :: #{
code := code(),
code := code(),
reason => reason(),
sub => sub_failure()
sub => sub_failure()
}.
-type sub_failure() :: #{
code := code(),
sub => sub_failure()
sub => sub_failure()
}.
-export_type([code/0]).

View File

@ -14,11 +14,12 @@
-export([to_range/1]).
-type ord(T) :: T. % totally ordered
% totally ordered
-type ord(T) :: T.
-type indef(T) :: #{
expected_min := ord(T),
current := ord(T),
current := ord(T),
expected_max := ord(T)
}.
@ -26,40 +27,32 @@
%%
-spec new(T) ->
indef(T).
-spec new(T) -> indef(T).
-spec new(T, T, T) ->
indef(T).
-spec new(T, T, T) -> indef(T).
-spec current(indef(T)) ->
T.
-spec current(indef(T)) -> T.
-spec expmin(indef(T)) ->
T.
-spec expmin(indef(T)) -> T.
-spec expmax(indef(T)) ->
T.
-spec account(T, indef(T)) ->
indef(T).
-spec expmax(indef(T)) -> T.
-spec account(T, indef(T)) -> indef(T).
-spec confirm(T, indef(T)) ->
indef(T).
-spec confirm(T, indef(T)) -> indef(T).
-spec reject(T, indef(T)) ->
indef(T).
-spec reject(T, indef(T)) -> indef(T).
new(Seed) ->
#{
expected_min => Seed,
current => Seed,
current => Seed,
expected_max => Seed
}.
new(ExpMin, Current, ExpMax) ->
#{
expected_min => ExpMin,
current => Current,
current => Current,
expected_max => ExpMax
}.
@ -80,7 +73,7 @@ account(Delta, Indef = #{expected_min := ExpMin, expected_max := ExpMax}) ->
confirm(Delta, Indef = #{current := Current, expected_min := ExpMin, expected_max := ExpMax}) ->
Indef#{
current := Current + Delta,
current := Current + Delta,
expected_min := erlang:max(ExpMin + Delta, ExpMin),
expected_max := erlang:min(ExpMax + Delta, ExpMax)
}.
@ -91,9 +84,7 @@ reject(Delta, Indef = #{expected_min := ExpMin, expected_max := ExpMax}) ->
expected_max := erlang:min(ExpMax - Delta, ExpMax)
}.
-spec to_range(indef(T)) ->
ff_range:range(T).
-spec to_range(indef(T)) -> ff_range:range(T).
to_range(#{expected_min := ExpMin, expected_max := ExpMax}) ->
{{inclusive, ExpMin}, {inclusive, ExpMax}}.
@ -108,13 +99,13 @@ to_range(#{expected_min := ExpMin, expected_max := ExpMax}) ->
-spec convergency_test() -> _.
convergency_test() ->
Opset = gen_opset(3/5, 2/3, 20),
Opset = gen_opset(3 / 5, 2 / 3, 20),
#{
current := C,
current := C,
expected_min := ExpMin,
expected_max := ExpMax
} = lists:foldl(
fun ({Op, Delta}, Indef) -> Op(Delta, Indef) end,
fun({Op, Delta}, Indef) -> Op(Delta, Indef) end,
new(0),
Opset
),
@ -135,16 +126,18 @@ gen_opset(St, Acc) ->
gen_op({_, _, 0, []}) ->
done;
gen_op({Pe, Pc, N, Ds}) ->
case ff_random:from_choices([
{ Pe * sign(N) , account},
{(1 - Pe) * sign(length(Ds)) , commit}
]) of
case
ff_random:from_choices([
{Pe * sign(N), account},
{(1 - Pe) * sign(length(Ds)), commit}
])
of
account ->
Delta = ff_random:from_range(-1000, 1000),
{{fun account/2, Delta}, {Pe, Pc, N - 1, [Delta | Ds]}};
commit ->
Delta = ff_random:from_list(Ds),
Op = ff_random:from_choices([{Pc, fun confirm/2}, {1 - Pc, fun reject/2}]),
Op = ff_random:from_choices([{Pc, fun confirm/2}, {1 - Pc, fun reject/2}]),
{{Op, Delta}, {Pe, Pc, N, Ds -- [Delta]}}
end.

View File

@ -5,8 +5,8 @@
-module(ff_map).
-type result(T) ::
{ok, T} |
{error, notfound} .
{ok, T}
| {error, notfound}.
-export_type([result/1]).
@ -14,9 +14,7 @@
%%
-spec find(Key, #{Key => Value}) ->
result(Value).
-spec find(Key, #{Key => Value}) -> result(Value).
find(Key, Map) ->
case Map of
#{Key := Value} ->

View File

@ -18,37 +18,29 @@
%%
-spec from_result({ok, T} | {error, _}) ->
maybe(T).
-spec from_result({ok, T} | {error, _}) -> maybe(T).
from_result({ok, T}) ->
T;
from_result({error, _}) ->
undefined.
-spec to_list(maybe(T)) ->
[T].
-spec to_list(maybe(T)) -> [T].
to_list(undefined) ->
[];
to_list(T) ->
[T].
-spec apply(fun(), Arg :: undefined | term()) ->
term().
-spec apply(fun(), Arg :: undefined | term()) -> term().
apply(Fun, Arg) ->
ff_maybe:apply(Fun, Arg, undefined).
-spec apply(fun(), Arg :: undefined | term(), Default :: term()) ->
term().
-spec apply(fun(), Arg :: undefined | term(), Default :: term()) -> term().
apply(Fun, Arg, _Default) when Arg =/= undefined ->
Fun(Arg);
apply(_Fun, undefined, Default) ->
Default.
-spec get_defined([maybe(T)]) ->
T.
-spec get_defined([maybe(T)]) -> T.
get_defined([]) ->
erlang:error(badarg);
get_defined([Value | _Tail]) when Value =/= undefined ->
@ -56,9 +48,6 @@ get_defined([Value | _Tail]) when Value =/= undefined ->
get_defined([undefined | Tail]) ->
get_defined(Tail).
-spec get_defined(maybe(T), maybe(T)) ->
T.
-spec get_defined(maybe(T), maybe(T)) -> T.
get_defined(V1, V2) ->
get_defined([V1, V2]).

View File

@ -25,9 +25,7 @@
-type result(T, E) ::
{ok, T} | {error, E}.
-spec do(fun(() -> ok | T | thrown(E))) ->
ok | result(T, E).
-spec do(fun(() -> ok | T | thrown(E))) -> ok | result(T, E).
do(Fun) ->
try Fun() of
ok ->
@ -38,17 +36,14 @@ do(Fun) ->
Thrown -> {error, Thrown}
end.
-spec do(Tag, fun(() -> ok | T | thrown(E))) ->
ok | result(T, {Tag, E}).
-spec do(Tag, fun(() -> ok | T | thrown(E))) -> ok | result(T, {Tag, E}).
do(Tag, Fun) ->
do(fun () -> unwrap(Tag, do(Fun)) end).
do(fun() -> unwrap(Tag, do(Fun)) end).
-spec unwrap
(ok) -> ok;
({ok, V}) -> V;
(ok) -> ok;
({ok, V}) -> V;
({error, E}) -> thrown(E).
unwrap(ok) ->
ok;
unwrap({ok, V}) ->
@ -57,10 +52,9 @@ unwrap({error, E}) ->
throw(E).
-spec expect
(_E, ok) -> ok;
(_E, {ok, V}) -> V;
( E, {error, _}) -> thrown(E).
(_E, ok) -> ok;
(_E, {ok, V}) -> V;
(E, {error, _}) -> thrown(E).
expect(_, ok) ->
ok;
expect(_, {ok, V}) ->
@ -68,19 +62,16 @@ expect(_, {ok, V}) ->
expect(E, {error, _}) ->
throw(E).
-spec flip(result(T, E)) ->
result(E, T).
-spec flip(result(T, E)) -> result(E, T).
flip({ok, T}) ->
{error, T};
flip({error, E}) ->
{ok, E}.
-spec unwrap
(_Tag, ok) -> ok;
(_Tag, {ok, V}) -> V;
( Tag, {error, E}) -> thrown({Tag, E}).
(_Tag, ok) -> ok;
(_Tag, {ok, V}) -> V;
(Tag, {error, E}) -> thrown({Tag, E}).
unwrap(_, ok) ->
ok;
unwrap(_, {ok, V}) ->
@ -88,9 +79,7 @@ unwrap(_, {ok, V}) ->
unwrap(Tag, {error, E}) ->
throw({Tag, E}).
-spec valid(T, T) ->
ok | {error, T}.
-spec valid(T, T) -> ok | {error, T}.
valid(V, V) ->
ok;
valid(_, V) ->
@ -103,12 +92,10 @@ valid(_, V) ->
-type outcome(E, R) ::
{ok, [E]} | {error, R}.
-spec with(Sub, St, fun((SubSt | undefined) -> outcome(SubEv, Reason))) ->
outcome({Sub, SubEv}, {Sub, Reason}) when
Sub :: atom(),
St :: #{Sub => SubSt},
SubSt :: _.
-spec with(Sub, St, fun((SubSt | undefined) -> outcome(SubEv, Reason))) -> outcome({Sub, SubEv}, {Sub, Reason}) when
Sub :: atom(),
St :: #{Sub => SubSt},
SubSt :: _.
with(Model, St, F) ->
case F(maps:get(Model, St, undefined)) of
{ok, Events0} when is_list(Events0) ->

View File

@ -12,11 +12,9 @@
%%
-spec date() ->
calendar:date().
-spec date() -> calendar:date().
-spec time() ->
calendar:time().
-spec time() -> calendar:time().
date() ->
Y = from_range(1970, 9999),
@ -32,12 +30,11 @@ time() ->
%%
-type choice(T) :: {probability(), T}.
-type probability() :: number(). % >= 0
-spec from_choices([choice(T)]) ->
T.
-type choice(T) :: {probability(), T}.
% >= 0
-type probability() :: number().
-spec from_choices([choice(T)]) -> T.
from_choices(Choices) ->
Psum = lists:sum([assert_probability(P) || {P, _} <- Choices]),
Roll = rand:uniform() * Psum,
@ -60,14 +57,12 @@ assert_probability(P) when is_number(P), P >= 0 ->
assert_probability(_) ->
error(badarg).
-spec from_list([T, ...]) ->
T.
-spec from_list([T, ...]) -> T.
from_list(List) ->
from_choices([{1, E} || E <- List]).
-spec from_range(M :: integer(), N :: integer()) ->
integer(). % from [M; N]
% from [M; N]
integer().
from_range(M, N) when M < N ->
rand:uniform(N - M + 1) - 1 + M.

View File

@ -3,11 +3,12 @@
-module(ff_range).
-type range(T) :: {maybe(bound(T)), maybe(bound(T))}.
-type bound(T) :: {exclusive | inclusive, ord(T)}.
-type range(T) :: {maybe(bound(T)), maybe(bound(T))}.
-type bound(T) :: {exclusive | inclusive, ord(T)}.
-type maybe(T) :: infinity | T.
-type ord(T) :: T. % totally ordered
-type maybe(T) :: infinity | T.
% totally ordered
-type ord(T) :: T.
-export_type([range/1]).
-export_type([bound/1]).
@ -17,9 +18,7 @@
%%
-spec intersect(range(T), range(T)) ->
range(T) | undefined.
-spec intersect(range(T), range(T)) -> range(T) | undefined.
intersect(R1, R2) ->
B1 = max_bound(lower(R1), lower(R2)),
B2 = min_bound(upper(R1), upper(R2)),
@ -30,9 +29,7 @@ intersect(R1, R2) ->
from_bounds(B1, B2)
end.
-spec contains(range(T), range(T)) ->
boolean().
-spec contains(range(T), range(T)) -> boolean().
contains(R1, R2) ->
intersect(R1, R2) =:= R2.
@ -59,13 +56,13 @@ compare_bounds(B1, B2) ->
max_bound(B1, B2) ->
case compare_bounds(B1, B2) of
gt -> B1;
_ -> B2
_ -> B2
end.
min_bound(B1, B2) ->
case compare_bounds(B1, B2) of
lt -> B1;
_ -> B2
_ -> B2
end.
%%

View File

@ -9,20 +9,18 @@
%%
-type fragment() ::
iodata() |
char() |
atom() |
number() .
iodata()
| char()
| atom()
| number().
-spec join([fragment()]) -> binary().
join(Fragments) ->
join(<<>>, Fragments).
-spec join(Delim, [fragment()]) -> binary() when
Delim ::
char() |
iodata() .
char()
| iodata().
join(Delim, Fragments) ->
genlib_string:join(Delim, lists:map(fun genlib:to_binary/1, Fragments)).

View File

@ -10,15 +10,15 @@
-export_type([timestamp_ms/0]).
-type timestamp_ms() :: integer().
-type year() :: integer().
-type month() :: integer().
-type day() :: integer().
-type hour() :: integer().
-type minute() :: integer().
-type second() :: integer().
-type date() :: {year(), month(), day()}.
-type time() :: {hour(), minute(), second()}.
-type timestamp_ms() :: integer().
-type year() :: integer().
-type month() :: integer().
-type day() :: integer().
-type hour() :: integer().
-type minute() :: integer().
-type second() :: integer().
-type date() :: {year(), month(), day()}.
-type time() :: {hour(), minute(), second()}.
-type datetime_interval() :: {date(), time()}.
%% API
@ -35,24 +35,24 @@ to_rfc3339(Timestamp) ->
from_rfc3339(BTimestamp) ->
genlib_rfc3339:parse(BTimestamp, millisecond).
-spec add_interval(timestamp_ms(), datetime_interval()) ->
timestamp_ms().
-spec add_interval(timestamp_ms(), datetime_interval()) -> timestamp_ms().
add_interval(Timestamp, {Date, Time}) ->
Ms = Timestamp rem 1000,
TSSeconds = erlang:convert_time_unit(Timestamp, millisecond, second),
{D, T} = genlib_time:unixtime_to_daytime(TSSeconds),
NewDate = genlib_time:daytime_to_unixtime({genlib_time:shift_date(D, Date), T}),
DateTime = genlib_time:add_duration(NewDate, Time),
DateTime*1000 + Ms.
DateTime * 1000 + Ms.
%% TESTS
-ifdef(TEST).
-include_lib("eunit/include/eunit.hrl").
-spec test() -> _.
-spec rfc3339_symmetry_test() -> _.
rfc3339_symmetry_test() ->
TimestampStr = <<"2000-01-01T00:00:00Z">>,
?assertEqual(TimestampStr, to_rfc3339(from_rfc3339(TimestampStr))).
@ -67,18 +67,18 @@ add_second_interval_test() ->
add_minute_interval_test() ->
Timestamp = ff_time:now(),
NewTimestamp = add_interval(Timestamp, {{0, 0, 0}, {0, 1, 0}}),
?assertEqual(Timestamp + 60*1000, NewTimestamp).
?assertEqual(Timestamp + 60 * 1000, NewTimestamp).
-spec add_hour_interval_test() -> _.
add_hour_interval_test() ->
Timestamp = ff_time:now(),
NewTimestamp = add_interval(Timestamp, {{0, 0, 0}, {1, 0, 0}}),
?assertEqual(Timestamp + 60*60*1000, NewTimestamp).
?assertEqual(Timestamp + 60 * 60 * 1000, NewTimestamp).
-spec add_day_interval_test() -> _.
add_day_interval_test() ->
Timestamp = ff_time:now(),
NewTimestamp = add_interval(Timestamp, {{0, 0, 1}, {0, 0, 0}}),
?assertEqual(Timestamp + 24*60*60*1000, NewTimestamp).
?assertEqual(Timestamp + 24 * 60 * 60 * 1000, NewTimestamp).
-endif.

View File

@ -3,67 +3,64 @@
-include_lib("damsel/include/dmsl_domain_config_thrift.hrl").
-define(ordset(Es), ordsets:from_list(Es)).
-define(ordset(Es), ordsets:from_list(Es)).
-define(glob(), #domain_GlobalsRef{}).
-define(cur(ID), #domain_CurrencyRef{symbolic_code = ID}).
-define(pmt(C, T), #domain_PaymentMethodRef{id = {C, T}}).
-define(cat(ID), #domain_CategoryRef{id = ID}).
-define(prx(ID), #domain_ProxyRef{id = ID}).
-define(prv(ID), #domain_ProviderRef{id = ID}).
-define(trm(ID), #domain_TerminalRef{id = ID}).
-define(prv_trm(ID), #domain_ProviderTerminalRef{id = ID}).
-define(glob(), #domain_GlobalsRef{}).
-define(cur(ID), #domain_CurrencyRef{symbolic_code = ID}).
-define(pmt(C, T), #domain_PaymentMethodRef{id = {C, T}}).
-define(cat(ID), #domain_CategoryRef{id = ID}).
-define(prx(ID), #domain_ProxyRef{id = ID}).
-define(prv(ID), #domain_ProviderRef{id = ID}).
-define(trm(ID), #domain_TerminalRef{id = ID}).
-define(prv_trm(ID), #domain_ProviderTerminalRef{id = ID}).
-define(prv_trm(ID, P), #domain_ProviderTerminalRef{id = ID, priority = P}).
-define(tmpl(ID), #domain_ContractTemplateRef{id = ID}).
-define(trms(ID), #domain_TermSetHierarchyRef{id = ID}).
-define(sas(ID), #domain_SystemAccountSetRef{id = ID}).
-define(eas(ID), #domain_ExternalAccountSetRef{id = ID}).
-define(insp(ID), #domain_InspectorRef{id = ID}).
-define(p2p_insp(ID), #domain_P2PInspectorRef{id = ID}).
-define(payinst(ID), #domain_PaymentInstitutionRef{id = ID}).
-define(tmpl(ID), #domain_ContractTemplateRef{id = ID}).
-define(trms(ID), #domain_TermSetHierarchyRef{id = ID}).
-define(sas(ID), #domain_SystemAccountSetRef{id = ID}).
-define(eas(ID), #domain_ExternalAccountSetRef{id = ID}).
-define(insp(ID), #domain_InspectorRef{id = ID}).
-define(p2p_insp(ID), #domain_P2PInspectorRef{id = ID}).
-define(payinst(ID), #domain_PaymentInstitutionRef{id = ID}).
-define(cash(Amount, SymCode),
#domain_Cash{amount = Amount, currency = ?cur(SymCode)}
).
-define(cash(Amount, SymCode), #domain_Cash{amount = Amount, currency = ?cur(SymCode)}).
-define(cashrng(Lower, Upper),
#domain_CashRange{lower = Lower, upper = Upper}
).
-define(cashrng(Lower, Upper), #domain_CashRange{lower = Lower, upper = Upper}).
-define(fixed(Amount, SymCode),
{fixed, #domain_CashVolumeFixed{cash = #domain_Cash{
amount = Amount,
currency = ?cur(SymCode)
}}}
{fixed, #domain_CashVolumeFixed{
cash = #domain_Cash{
amount = Amount,
currency = ?cur(SymCode)
}
}}
).
-define(share(P, Q, C),
{share, #domain_CashVolumeShare{
parts = #'Rational'{p = P, q = Q}, 'of' = C}
}
parts = #'Rational'{p = P, q = Q},
'of' = C
}}
).
-define(share(P, Q, C, RM),
{share, #domain_CashVolumeShare{
parts = #'Rational'{p = P, q = Q}, 'of' = C, 'rounding_method' = RM}
}
parts = #'Rational'{p = P, q = Q},
'of' = C,
'rounding_method' = RM
}}
).
-define(cfpost(A1, A2, V),
#domain_CashFlowPosting{
source = A1,
destination = A2,
volume = V
}
).
-define(cfpost(A1, A2, V), #domain_CashFlowPosting{
source = A1,
destination = A2,
volume = V
}).
-define(cfpost(A1, A2, V, D),
#domain_CashFlowPosting{
source = A1,
destination = A2,
volume = V,
details = D
}
).
-define(cfpost(A1, A2, V, D), #domain_CashFlowPosting{
source = A1,
destination = A2,
volume = V,
details = D
}).
-endif.

View File

@ -8,32 +8,33 @@
-spec bank_card(binary(), {1..12, 2000..9999}, ct_helper:config()) ->
#{
token := binary(),
bin => binary(),
masked_pan => binary(),
exp_date => {integer(), integer()},
token := binary(),
bin => binary(),
masked_pan => binary(),
exp_date => {integer(), integer()},
cardholder_name => binary()
}.
bank_card(PAN, {MM, YYYY} = ExpDate, C) ->
CardData = #cds_PutCardData{
pan = PAN,
pan = PAN,
exp_date = #cds_ExpDate{month = MM, year = YYYY}
},
Client = ff_woody_client:new(maps:get('cds', ct_helper:cfg(services, C))),
WoodyCtx = ct_helper:get_woody_ctx(C),
Request = {{cds_proto_storage_thrift, 'Storage'}, 'PutCard', [CardData]},
case woody_client:call(Request, Client, WoodyCtx) of
{ok, #cds_PutCardResult{bank_card = #cds_BankCard{
token = Token,
bin = BIN,
last_digits = Masked
}}} ->
{ok, #cds_PutCardResult{
bank_card = #cds_BankCard{
token = Token,
bin = BIN,
last_digits = Masked
}
}} ->
#{
token => Token,
bin => BIN,
masked_pan => Masked,
exp_date => ExpDate,
token => Token,
bin => BIN,
masked_pan => Masked,
exp_date => ExpDate,
cardholder_name => <<"ct_cardholder_name">>
}
end.

View File

@ -35,9 +35,7 @@
-type object() ::
dmsl_domain_thrift:'DomainObject'().
-spec p2p_provider(?dtp('ProviderRef'), ?dtp('ProxyRef'), binary(), ct_helper:config()) ->
object().
-spec p2p_provider(?dtp('ProviderRef'), ?dtp('ProxyRef'), binary(), ct_helper:config()) -> object().
p2p_provider(Ref, ProxyRef, IdentityID, C) ->
AccountID = account(<<"RUB">>, C),
{provider, #domain_ProviderObject{
@ -51,44 +49,54 @@ p2p_provider(Ref, ProxyRef, IdentityID, C) ->
wallet = #domain_WalletProvisionTerms{
p2p = #domain_P2PProvisionTerms{
currencies = {value, ?ordset([?cur(<<"RUB">>)])},
cash_limit = {value, ?cashrng(
{inclusive, ?cash( 0, <<"RUB">>)},
{exclusive, ?cash(10000000, <<"RUB">>)}
)},
cash_flow = {decisions, [
#domain_CashFlowDecision{
if_ = {condition, {currency_is, ?cur(<<"RUB">>)}},
then_ = {value, [
?cfpost(
{system, settlement},
{provider, settlement},
{product, {min_of, ?ordset([
?fixed(10, <<"RUB">>),
?share(5, 100, operation_amount, round_half_towards_zero)
])}}
)
]}
}
]},
fees = {decisions, [
#domain_FeeDecision{
if_ = {condition, {currency_is, ?cur(<<"RUB">>)}},
then_ = {value, #domain_Fees{
fees = #{surplus => ?share(1, 1, operation_amount)}
}}
},
#domain_FeeDecision{
if_ = {condition, {currency_is, ?cur(<<"USD">>)}},
then_ = {value, #domain_Fees{
fees = #{surplus => ?share(1, 1, operation_amount)}
}}
}#domain_FeeDecision{
if_ = {condition, {currency_is, ?cur(<<"EUR">>)}},
then_ = {value, #domain_Fees{
fees = #{surplus => ?share(1, 1, operation_amount)}
}}
}
]}
cash_limit =
{value,
?cashrng(
{inclusive, ?cash(0, <<"RUB">>)},
{exclusive, ?cash(10000000, <<"RUB">>)}
)},
cash_flow =
{decisions, [
#domain_CashFlowDecision{
if_ = {condition, {currency_is, ?cur(<<"RUB">>)}},
then_ =
{value, [
?cfpost(
{system, settlement},
{provider, settlement},
{product,
{min_of,
?ordset([
?fixed(10, <<"RUB">>),
?share(5, 100, operation_amount, round_half_towards_zero)
])}}
)
]}
}
]},
fees =
{decisions, [
#domain_FeeDecision{
if_ = {condition, {currency_is, ?cur(<<"RUB">>)}},
then_ =
{value, #domain_Fees{
fees = #{surplus => ?share(1, 1, operation_amount)}
}}
},
#domain_FeeDecision{
if_ = {condition, {currency_is, ?cur(<<"USD">>)}},
then_ =
{value, #domain_Fees{
fees = #{surplus => ?share(1, 1, operation_amount)}
}}
}#domain_FeeDecision{
if_ = {condition, {currency_is, ?cur(<<"EUR">>)}},
then_ =
{value, #domain_Fees{
fees = #{surplus => ?share(1, 1, operation_amount)}
}}
}
]}
}
}
},
@ -98,9 +106,7 @@ p2p_provider(Ref, ProxyRef, IdentityID, C) ->
}
}}.
-spec withdrawal_provider(?dtp('ProviderRef'), ?dtp('ProxyRef'), binary(), ct_helper:config()) ->
object().
-spec withdrawal_provider(?dtp('ProviderRef'), ?dtp('ProxyRef'), binary(), ct_helper:config()) -> object().
withdrawal_provider(?prv(16) = Ref, ProxyRef, IdentityID, C) ->
AccountID = account(<<"RUB">>, C),
{provider, #domain_ProviderObject{
@ -114,15 +120,15 @@ withdrawal_provider(?prv(16) = Ref, ProxyRef, IdentityID, C) ->
accounts = #{
?cur(<<"RUB">>) => #domain_ProviderAccount{settlement = AccountID}
},
terminal = {decisions, [
#domain_TerminalDecision{
if_ = {constant, true},
then_ = {value, [?prv_trm(6)]}
}
]}
terminal =
{decisions, [
#domain_TerminalDecision{
if_ = {constant, true},
then_ = {value, [?prv_trm(6)]}
}
]}
}
}};
withdrawal_provider(Ref, ProxyRef, IdentityID, C) ->
AccountID = account(<<"RUB">>, C),
{provider, #domain_ProviderObject{
@ -137,25 +143,31 @@ withdrawal_provider(Ref, ProxyRef, IdentityID, C) ->
withdrawals = #domain_WithdrawalProvisionTerms{
currencies = {value, ?ordset([?cur(<<"RUB">>)])},
payout_methods = {value, ?ordset([])},
cash_limit = {value, ?cashrng(
{inclusive, ?cash( 0, <<"RUB">>)},
{exclusive, ?cash(10000000, <<"RUB">>)}
)},
cash_flow = {decisions, [
#domain_CashFlowDecision{
if_ = {condition, {currency_is, ?cur(<<"RUB">>)}},
then_ = {value, [
?cfpost(
{system, settlement},
{provider, settlement},
{product, {min_of, ?ordset([
?fixed(10, <<"RUB">>),
?share(5, 100, operation_amount, round_half_towards_zero)
])}}
)
]}
}
]}
cash_limit =
{value,
?cashrng(
{inclusive, ?cash(0, <<"RUB">>)},
{exclusive, ?cash(10000000, <<"RUB">>)}
)},
cash_flow =
{decisions, [
#domain_CashFlowDecision{
if_ = {condition, {currency_is, ?cur(<<"RUB">>)}},
then_ =
{value, [
?cfpost(
{system, settlement},
{provider, settlement},
{product,
{min_of,
?ordset([
?fixed(10, <<"RUB">>),
?share(5, 100, operation_amount, round_half_towards_zero)
])}}
)
]}
}
]}
}
}
},
@ -167,38 +179,44 @@ withdrawal_provider(Ref, ProxyRef, IdentityID, C) ->
?prv(9) ->
{decisions, [
#domain_TerminalDecision{
if_ = {constant, true},
if_ = {constant, true},
then_ = {value, [?prv_trm(1, 500)]}
}
]};
?prv(10) ->
{decisions, [
#domain_TerminalDecision{
if_ = {constant, true},
if_ = {constant, true},
then_ = {value, [?prv_trm(1)]}
}
]};
?prv(11) ->
{decisions, [
#domain_TerminalDecision{
if_ = {constant, true},
if_ = {constant, true},
then_ = {value, [?prv_trm(1)]}
}
]};
_ ->
{decisions, [
#domain_TerminalDecision{
if_ = {condition, {cost_in, ?cashrng(
{inclusive, ?cash( 0, <<"RUB">>)},
{exclusive, ?cash(1000000, <<"RUB">>)}
)}},
if_ =
{condition,
{cost_in,
?cashrng(
{inclusive, ?cash(0, <<"RUB">>)},
{exclusive, ?cash(1000000, <<"RUB">>)}
)}},
then_ = {value, [?prv_trm(1)]}
},
#domain_TerminalDecision{
if_ = {condition, {cost_in, ?cashrng(
{inclusive, ?cash( 3000000, <<"RUB">>)},
{exclusive, ?cash(10000000, <<"RUB">>)}
)}},
if_ =
{condition,
{cost_in,
?cashrng(
{inclusive, ?cash(3000000, <<"RUB">>)},
{exclusive, ?cash(10000000, <<"RUB">>)}
)}},
then_ = {value, [?prv_trm(7)]}
}
]}
@ -206,8 +224,7 @@ withdrawal_provider(Ref, ProxyRef, IdentityID, C) ->
}
}}.
-spec withdrawal_terminal(?dtp('TerminalRef')) ->
object().
-spec withdrawal_terminal(?dtp('TerminalRef')) -> object().
withdrawal_terminal(?trm(1) = Ref) ->
{terminal, #domain_TerminalObject{
ref = Ref,
@ -241,10 +258,12 @@ withdrawal_terminal(?trm(7) = Ref) ->
withdrawals = #domain_WithdrawalProvisionTerms{
currencies = {value, ?ordset([?cur(<<"BTC">>)])},
payout_methods = {value, ?ordset([])},
cash_limit = {value, ?cashrng(
{inclusive, ?cash( 1000000, <<"BTC">>)},
{exclusive, ?cash(10000000, <<"BTC">>)}
)},
cash_limit =
{value,
?cashrng(
{inclusive, ?cash(1000000, <<"BTC">>)},
{exclusive, ?cash(10000000, <<"BTC">>)}
)},
cash_flow = {value, ?ordset([])}
}
}
@ -252,66 +271,60 @@ withdrawal_terminal(?trm(7) = Ref) ->
}
}}.
-spec currency(?dtp('CurrencyRef')) ->
object().
-spec currency(?dtp('CurrencyRef')) -> object().
currency(?cur(<<"EUR">> = SymCode) = Ref) ->
{currency, #domain_CurrencyObject{
ref = Ref,
data = #domain_Currency{
name = <<"Europe"/utf8>>,
numeric_code = 978,
name = <<"Europe"/utf8>>,
numeric_code = 978,
symbolic_code = SymCode,
exponent = 2
exponent = 2
}
}};
currency(?cur(<<"RUB">> = SymCode) = Ref) ->
{currency, #domain_CurrencyObject{
ref = Ref,
data = #domain_Currency{
name = <<"Яussian Яuble"/utf8>>,
numeric_code = 643,
name = <<"Яussian Яuble"/utf8>>,
numeric_code = 643,
symbolic_code = SymCode,
exponent = 2
exponent = 2
}
}};
currency(?cur(<<"USD">> = SymCode) = Ref) ->
{currency, #domain_CurrencyObject{
ref = Ref,
data = #domain_Currency{
name = <<"U$ Dollar">>,
numeric_code = 840,
name = <<"U$ Dollar">>,
numeric_code = 840,
symbolic_code = SymCode,
exponent = 2
exponent = 2
}
}};
currency(?cur(<<"BTC">> = SymCode) = Ref) ->
{currency, #domain_CurrencyObject{
ref = Ref,
data = #domain_Currency{
name = <<"Bitcoin">>,
numeric_code = 999,
name = <<"Bitcoin">>,
numeric_code = 999,
symbolic_code = SymCode,
exponent = 10
exponent = 10
}
}}.
-spec category(?dtp('CategoryRef'), binary(), ?dtp('CategoryType')) ->
object().
-spec category(?dtp('CategoryRef'), binary(), ?dtp('CategoryType')) -> object().
category(Ref, Name, Type) ->
{category, #domain_CategoryObject{
ref = Ref,
data = #domain_Category{
name = Name,
name = Name,
description = <<>>,
type = Type
type = Type
}
}}.
-spec payment_method(?dtp('PaymentMethodRef')) ->
object().
-spec payment_method(?dtp('PaymentMethodRef')) -> object().
payment_method(?pmt(_Type, Name) = Ref) when is_atom(Name) ->
payment_method(Name, Ref);
payment_method(?pmt(_Type, #domain_BankCardPaymentMethod{} = PM) = Ref) ->
@ -321,14 +334,12 @@ payment_method(Name, Ref) ->
{payment_method, #domain_PaymentMethodObject{
ref = Ref,
data = #domain_PaymentMethodDefinition{
name = erlang:atom_to_binary(Name, unicode),
name = erlang:atom_to_binary(Name, unicode),
description = <<>>
}
}}.
-spec contract_template(?dtp('ContractTemplateRef'), ?dtp('TermSetHierarchyRef')) ->
object().
-spec contract_template(?dtp('ContractTemplateRef'), ?dtp('TermSetHierarchyRef')) -> object().
contract_template(Ref, TermsRef) ->
contract_template(Ref, TermsRef, undefined, undefined).
@ -342,75 +353,60 @@ contract_template(Ref, TermsRef, ValidSince, ValidUntil) ->
}
}}.
-spec inspector(?dtp('InspectorRef'), binary(), ?dtp('ProxyRef')) ->
object().
-spec inspector(?dtp('InspectorRef'), binary(), ?dtp('ProxyRef')) -> object().
inspector(Ref, Name, ProxyRef) ->
inspector(Ref, Name, ProxyRef, #{}).
-spec inspector(?dtp('InspectorRef'), binary(), ?dtp('ProxyRef'), ?dtp('ProxyOptions')) ->
object().
-spec inspector(?dtp('InspectorRef'), binary(), ?dtp('ProxyRef'), ?dtp('ProxyOptions')) -> object().
inspector(Ref, Name, ProxyRef, Additional) ->
{inspector, #domain_InspectorObject{
ref = Ref,
ref = Ref,
data = #domain_Inspector{
name = Name,
name = Name,
description = <<>>,
proxy = #domain_Proxy{
ref = ProxyRef,
ref = ProxyRef,
additional = Additional
}
}
}}.
-spec p2p_inspector(?dtp('P2PInspectorRef'), binary(), ?dtp('ProxyRef'), ?dtp('ProxyOptions')) ->
object().
-spec p2p_inspector(?dtp('P2PInspectorRef'), binary(), ?dtp('ProxyRef'), ?dtp('ProxyOptions')) -> object().
p2p_inspector(Ref, Name, ProxyRef, Additional) ->
{p2p_inspector, #domain_P2PInspectorObject{
ref = Ref,
ref = Ref,
data = #domain_P2PInspector{
name = Name,
name = Name,
description = <<>>,
fallback_risk_score = #{<<"fraud">> => high},
proxy = #domain_Proxy{
ref = ProxyRef,
ref = ProxyRef,
additional = Additional
}
}
}}.
-spec proxy(?dtp('ProxyRef'), Name :: binary()) ->
object().
-spec proxy(?dtp('ProxyRef'), Name :: binary()) -> object().
proxy(Ref, Name) ->
proxy(Ref, Name, <<>>).
-spec proxy(?dtp('ProxyRef'), Name :: binary(), URL :: binary()) ->
object().
-spec proxy(?dtp('ProxyRef'), Name :: binary(), URL :: binary()) -> object().
proxy(Ref, Name, URL) ->
proxy(Ref, Name, URL, #{}).
-spec proxy(?dtp('ProxyRef'), Name :: binary(), URL :: binary(), ?dtp('ProxyOptions')) ->
object().
-spec proxy(?dtp('ProxyRef'), Name :: binary(), URL :: binary(), ?dtp('ProxyOptions')) -> object().
proxy(Ref, Name, URL, Opts) ->
{proxy, #domain_ProxyObject{
ref = Ref,
ref = Ref,
data = #domain_ProxyDefinition{
name = Name,
name = Name,
description = <<>>,
url = URL,
options = Opts
url = URL,
options = Opts
}
}}.
-spec system_account_set(?dtp('SystemAccountSetRef'), binary(), ?dtp('CurrencyRef'), ct_helper:config()) ->
object().
-spec system_account_set(?dtp('SystemAccountSetRef'), binary(), ?dtp('CurrencyRef'), ct_helper:config()) -> object().
system_account_set(Ref, Name, ?cur(SymCode), C) ->
AccountID1 = account(SymCode, C),
AccountID2 = account(SymCode, C),
@ -430,7 +426,6 @@ system_account_set(Ref, Name, ?cur(SymCode), C) ->
-spec external_account_set(?dtp('ExternalAccountSetRef'), binary(), ?dtp('CurrencyRef'), ct_helper:config()) ->
object().
external_account_set(Ref, Name, ?cur(SymCode), C) ->
AccountID1 = account(SymCode, C),
AccountID2 = account(SymCode, C),
@ -441,29 +436,23 @@ external_account_set(Ref, Name, ?cur(SymCode), C) ->
description = <<>>,
accounts = #{
?cur(SymCode) => #domain_ExternalAccount{
income = AccountID1,
income = AccountID1,
outcome = AccountID2
}
}
}
}}.
-spec term_set_hierarchy(?dtp('TermSetHierarchyRef')) ->
object().
-spec term_set_hierarchy(?dtp('TermSetHierarchyRef')) -> object().
term_set_hierarchy(Ref) ->
term_set_hierarchy(Ref, []).
-spec term_set_hierarchy(?dtp('TermSetHierarchyRef'), [?dtp('TimedTermSet')]) ->
object().
-spec term_set_hierarchy(?dtp('TermSetHierarchyRef'), [?dtp('TimedTermSet')]) -> object().
term_set_hierarchy(Ref, TermSets) ->
term_set_hierarchy(Ref, undefined, TermSets).
-spec term_set_hierarchy(Ref, ff_maybe:maybe(Ref), [?dtp('TimedTermSet')]) ->
object() when
Ref :: ?dtp('TermSetHierarchyRef').
-spec term_set_hierarchy(Ref, ff_maybe:maybe(Ref), [?dtp('TimedTermSet')]) -> object() when
Ref :: ?dtp('TermSetHierarchyRef').
term_set_hierarchy(Ref, ParentRef, TermSets) ->
{term_set_hierarchy, #domain_TermSetHierarchyObject{
ref = Ref,
@ -473,18 +462,14 @@ term_set_hierarchy(Ref, ParentRef, TermSets) ->
}
}}.
-spec timed_term_set(?dtp('TermSet')) ->
?dtp('TimedTermSet').
-spec timed_term_set(?dtp('TermSet')) -> ?dtp('TimedTermSet').
timed_term_set(TermSet) ->
#domain_TimedTermSet{
action_time = #'TimestampInterval'{},
terms = TermSet
}.
-spec globals(?dtp('ExternalAccountSetRef'), [?dtp('PaymentInstitutionRef')]) ->
object().
-spec globals(?dtp('ExternalAccountSetRef'), [?dtp('PaymentInstitutionRef')]) -> object().
globals(EASRef, PIRefs) ->
{globals, #domain_GlobalsObject{
ref = ?glob(),
@ -494,16 +479,14 @@ globals(EASRef, PIRefs) ->
}
}}.
-spec account(binary(), ct_helper:config()) ->
shumpune_shumpune_thrift:'AccountID'().
-spec account(binary(), ct_helper:config()) -> shumpune_shumpune_thrift:'AccountID'().
account(SymCode, C) ->
Client = ff_woody_client:new(maps:get('accounter', ct_helper:cfg(services, C))),
WoodyCtx = ct_helper:get_woody_ctx(C),
Prototype = #shumpune_AccountPrototype{
currency_sym_code = SymCode,
description = <<>>,
creation_time = timestamp()
description = <<>>,
creation_time = timestamp()
},
Request = {{shumpune_shumpune_thrift, 'Accounter'}, 'CreateAccount', [Prototype]},
case woody_client:call(Request, Client, WoodyCtx) of

View File

@ -20,24 +20,21 @@
-include_lib("damsel/include/dmsl_domain_config_thrift.hrl").
-type revision() :: pos_integer().
-type ref() :: dmsl_domain_thrift:'Reference'().
-type object() :: dmsl_domain_thrift:'DomainObject'().
-type data() :: _.
-type ref() :: dmsl_domain_thrift:'Reference'().
-type object() :: dmsl_domain_thrift:'DomainObject'().
-type data() :: _.
-spec head() -> revision().
head() ->
#'Snapshot'{version = Version} = dmt_client:checkout({head, #'Head'{}}),
Version.
-spec all(revision()) -> dmsl_domain_thrift:'Domain'().
all(Revision) ->
#'Snapshot'{domain = Domain} = dmt_client:checkout({version, Revision}),
Domain.
-spec get(revision(), ref()) -> data() | no_return().
get(Revision, Ref) ->
try
extract_data(dmt_client:checkout_object({version, Revision}, Ref))
@ -47,7 +44,6 @@ get(Revision, Ref) ->
end.
-spec find(revision(), ref()) -> data() | notfound.
find(Revision, Ref) ->
try
extract_data(dmt_client:checkout_object({version, Revision}, Ref))
@ -60,14 +56,12 @@ extract_data(#'VersionedObject'{object = {_Tag, {_Name, _Ref, Data}}}) ->
Data.
-spec commit(revision(), dmt_client:commit()) -> ok | no_return().
commit(Revision, Commit) ->
Revision = dmt_client:commit(Revision, Commit) - 1,
_ = all(Revision + 1),
ok.
-spec insert(object() | [object()]) -> ok | no_return().
insert(Object) when not is_list(Object) ->
insert([Object]);
insert(Objects) ->
@ -75,14 +69,13 @@ insert(Objects) ->
ops = [
{insert, #'InsertOp'{
object = Object
}} ||
Object <- Objects
}}
|| Object <- Objects
]
},
commit(head(), Commit).
-spec update(object() | [object()]) -> ok | no_return().
update(NewObject) when not is_list(NewObject) ->
update([NewObject]);
update(NewObjects) ->
@ -93,33 +86,38 @@ update(NewObjects) ->
old_object = {Tag, {ObjectName, Ref, OldData}},
new_object = NewObject
}}
|| NewObject = {Tag, {ObjectName, Ref, _Data}} <- NewObjects,
OldData <- [get(Revision, {Tag, Ref})]
|| NewObject = {Tag, {ObjectName, Ref, _Data}} <- NewObjects,
OldData <- [get(Revision, {Tag, Ref})]
]
},
commit(Revision, Commit).
-spec upsert(object() | [object()]) -> ok | no_return().
upsert(NewObject) when not is_list(NewObject) ->
upsert([NewObject]);
upsert(NewObjects) ->
Revision = head(),
Commit = #'Commit'{
ops = lists:foldl(
fun (NewObject = {Tag, {ObjectName, Ref, NewData}}, Ops) ->
fun(NewObject = {Tag, {ObjectName, Ref, NewData}}, Ops) ->
case find(Revision, {Tag, Ref}) of
NewData ->
Ops;
notfound ->
[{insert, #'InsertOp'{
object = NewObject
}} | Ops];
[
{insert, #'InsertOp'{
object = NewObject
}}
| Ops
];
OldData ->
[{update, #'UpdateOp'{
old_object = {Tag, {ObjectName, Ref, OldData}},
new_object = NewObject
}} | Ops]
[
{update, #'UpdateOp'{
old_object = {Tag, {ObjectName, Ref, OldData}},
new_object = NewObject
}}
| Ops
]
end
end,
[],
@ -129,7 +127,6 @@ upsert(NewObjects) ->
commit(Revision, Commit).
-spec remove(object() | [object()]) -> ok | no_return().
remove(Object) when not is_list(Object) ->
remove([Object]);
remove(Objects) ->
@ -137,25 +134,22 @@ remove(Objects) ->
ops = [
{remove, #'RemoveOp'{
object = Object
}} ||
Object <- Objects
}}
|| Object <- Objects
]
},
commit(head(), Commit).
-spec reset(revision()) -> ok | no_return().
reset(Revision) ->
upsert(maps:values(all(Revision))).
-spec cleanup() -> ok | no_return().
cleanup() ->
Domain = all(head()),
remove(maps:values(Domain)).
-spec bump_revision() -> ok | no_return().
bump_revision() ->
Commit = #'Commit'{ops = []},
Revision = dmt_client:get_last_version(),

View File

@ -16,7 +16,7 @@
ff_services:service_name().
-type event() ::
ff_proto_wallet_thrift:'SinkEvent'()
ff_proto_wallet_thrift:'SinkEvent'()
| ff_proto_withdrawal_thrift:'SinkEvent'()
| ff_proto_identity_thrift:'SinkEvent'()
| ff_proto_destination_thrift:'SinkEvent'()
@ -26,11 +26,10 @@
| ff_proto_p2p_transfer_thrift:'SinkEvent'()
| ff_proto_p2p_session_thrift:'SinkEvent'()
| ff_proto_w2w_transfer_thrift:'SinkEvent'()
| ff_proto_p2p_template_thrift:'SinkEvent'()
.
| ff_proto_p2p_template_thrift:'SinkEvent'().
-type event_id() :: ff_proto_eventsink_thrift:'EventID'().
-type limit() :: non_neg_integer().
-type limit() :: non_neg_integer().
-export([last_id/1]).
@ -43,7 +42,6 @@
%%
-spec last_id(sink()) -> event_id() | 0.
last_id(Sink) ->
case call_handler('GetLastEventID', Sink, []) of
{ok, EventID} ->
@ -53,19 +51,16 @@ last_id(Sink) ->
end.
-spec events(_After :: event_id() | undefined, limit(), sink()) -> {[event()], _Last :: event_id()}.
events(After, Limit, Sink) ->
Range = #'evsink_EventRange'{'after' = After, limit = Limit},
{ok, Events} = call_handler('GetEvents', Sink, [Range]),
{Events, get_max_event_id(Events)}.
-spec consume(_ChunkSize :: limit(), sink()) -> [event()].
consume(ChunkSize, Sink) ->
fold(fun (Chunk, Acc) -> Chunk ++ Acc end, [], ChunkSize, Sink).
fold(fun(Chunk, Acc) -> Chunk ++ Acc end, [], ChunkSize, Sink).
-spec fold(fun(([event()], State) -> State), State, _ChunkSize :: limit(), sink()) -> State.
fold(FoldFun, InitialState, ChunkSize, Sink) ->
fold(FoldFun, InitialState, ChunkSize, Sink, undefined).
@ -80,12 +75,10 @@ fold(FoldFun, State0, ChunkSize, Sink, Cursor) ->
end.
-spec get_max_event_id([event()]) -> event_id().
get_max_event_id(Events) when is_list(Events) ->
lists:foldl(fun (Ev, Max) -> erlang:max(get_event_id(Ev), Max) end, 0, Events).
lists:foldl(fun(Ev, Max) -> erlang:max(get_event_id(Ev), Max) end, 0, Events).
-spec get_event_id(event()) -> event_id().
get_event_id(#'wlt_SinkEvent'{id = ID}) -> ID;
get_event_id(#'wthd_SinkEvent'{id = ID}) -> ID;
get_event_id(#'idnt_SinkEvent'{id = ID}) -> ID;
@ -102,8 +95,8 @@ call_handler(Function, ServiceName, Args) ->
Service = ff_services:get_service(ServiceName),
Path = erlang:list_to_binary(ff_services:get_service_path(ServiceName)),
Request = {Service, Function, Args},
Client = ff_woody_client:new(#{
url => <<"http://localhost:8022", Path/binary>>,
Client = ff_woody_client:new(#{
url => <<"http://localhost:8022", Path/binary>>,
event_handler => scoper_woody_event_handler
}),
ff_woody_client:call(Client, Request).

View File

@ -34,30 +34,27 @@
%%
-spec cfg(atom(), config()) -> term().
cfg(Key, Config) ->
case lists:keyfind(Key, 1, Config) of
{Key, V} -> V;
_ -> error({'ct config entry missing', Key})
_ -> error({'ct config entry missing', Key})
end.
-spec cfg(atom(), _, config()) -> config().
cfg(Key, Value, Config) ->
lists:keystore(Key, 1, Config, {Key, Value}).
%%
-type app_name() :: atom().
-type app_env() :: [{atom(), term()}].
-type app_name() :: atom().
-type app_env() :: [{atom(), term()}].
-type app_with_env() :: {app_name(), app_env()}.
-type startup_ctx() :: #{atom() => _}.
-type startup_ctx() :: #{atom() => _}.
-spec start_apps([app_name() | app_with_env()]) -> {[Started :: app_name()], startup_ctx()}.
start_apps(AppNames) ->
lists:foldl(
fun (AppName, {SAcc, CtxAcc}) ->
fun(AppName, {SAcc, CtxAcc}) ->
{Started, Ctx} = start_app(AppName),
{SAcc ++ Started, maps:merge(CtxAcc, Ctx)}
end,
@ -66,153 +63,144 @@ start_apps(AppNames) ->
).
-spec start_app(app_name() | app_with_env()) -> {[Started :: app_name()], startup_ctx()}.
start_app(scoper = AppName) ->
{start_app_with(AppName, [
{storage, scoper_storage_logger}
]), #{}};
{storage, scoper_storage_logger}
]), #{}};
start_app(woody = AppName) ->
{start_app_with(AppName, [
{acceptors_pool_size, 4}
]), #{}};
{acceptors_pool_size, 4}
]), #{}};
start_app(dmt_client = AppName) ->
{start_app_with(AppName, [
{cache_update_interval, 500}, % milliseconds
{max_cache_size, #{
elements => 1,
memory => 52428800 % 50Mb
}},
{woody_event_handlers, [
{scoper_woody_event_handler, #{}}
]},
{service_urls, #{
'Repository' => <<"http://dominant:8022/v1/domain/repository">>,
'RepositoryClient' => <<"http://dominant:8022/v1/domain/repository_client">>
}}
]), #{}};
% milliseconds
{cache_update_interval, 500},
{max_cache_size, #{
elements => 1,
% 50Mb
memory => 52428800
}},
{woody_event_handlers, [
{scoper_woody_event_handler, #{}}
]},
{service_urls, #{
'Repository' => <<"http://dominant:8022/v1/domain/repository">>,
'RepositoryClient' => <<"http://dominant:8022/v1/domain/repository_client">>
}}
]), #{}};
start_app(wapi = AppName) ->
{start_app_with(AppName, [
{ip, "::"},
{port, 8080},
{realm, <<"external">>},
{public_endpoint, <<"localhost:8080">>},
{access_conf, #{
jwt => #{
keyset => #{
wapi => {pem_file, "/opt/wapi/config/private.pem"}
{ip, "::"},
{port, 8080},
{realm, <<"external">>},
{public_endpoint, <<"localhost:8080">>},
{access_conf, #{
jwt => #{
keyset => #{
wapi => {pem_file, "/opt/wapi/config/private.pem"}
}
}
}
}},
{signee, wapi},
{lechiffre_opts, #{
encryption_key_path => "/opt/wapi/config/jwk.json",
decryption_key_paths => [
"/opt/wapi/config/jwk.json"
]
}},
{swagger_handler_opts, #{
validation_opts => #{
custom_validator => wapi_swagger_validator
}
}},
{events_fetch_limit, 32}
]), #{}};
}},
{signee, wapi},
{lechiffre_opts, #{
encryption_key_path => "/opt/wapi/config/jwk.json",
decryption_key_paths => [
"/opt/wapi/config/jwk.json"
]
}},
{swagger_handler_opts, #{
validation_opts => #{
custom_validator => wapi_swagger_validator
}
}},
{events_fetch_limit, 32}
]), #{}};
start_app(wapi_woody_client = AppName) ->
{start_app_with(AppName, [
{service_urls, #{
cds_storage => "http://cds:8022/v1/storage",
identdoc_storage => "http://cds:8022/v1/identity_document_storage",
fistful_stat => "http://fistful-magista:8022/stat",
fistful_wallet => "http://localhost:8022/v1/wallet",
fistful_identity => "http://localhost:8022/v1/identity",
fistful_destination => "http://localhost:8022/v1/destination",
fistful_withdrawal => "http://localhost:8022/v1/withdrawal",
fistful_w2w_transfer => "http://localhost:8022/v1/w2w_transfer",
fistful_p2p_template => "http://localhost:8022/v1/p2p_template",
fistful_p2p_transfer => "http://localhost:8022/v1/p2p_transfer",
fistful_p2p_session => "http://localhost:8022/v1/p2p_transfer/session"
}},
{service_retries, #{
fistful_stat => #{
'GetWallets' => {linear, 3, 1000},
'_' => finish
}
}},
{api_deadlines, #{
fistful_stat => 5000
}}
]), #{}};
{service_urls, #{
cds_storage => "http://cds:8022/v1/storage",
identdoc_storage => "http://cds:8022/v1/identity_document_storage",
fistful_stat => "http://fistful-magista:8022/stat",
fistful_wallet => "http://localhost:8022/v1/wallet",
fistful_identity => "http://localhost:8022/v1/identity",
fistful_destination => "http://localhost:8022/v1/destination",
fistful_withdrawal => "http://localhost:8022/v1/withdrawal",
fistful_w2w_transfer => "http://localhost:8022/v1/w2w_transfer",
fistful_p2p_template => "http://localhost:8022/v1/p2p_template",
fistful_p2p_transfer => "http://localhost:8022/v1/p2p_transfer",
fistful_p2p_session => "http://localhost:8022/v1/p2p_transfer/session"
}},
{service_retries, #{
fistful_stat => #{
'GetWallets' => {linear, 3, 1000},
'_' => finish
}
}},
{api_deadlines, #{
fistful_stat => 5000
}}
]), #{}};
start_app(ff_server = AppName) ->
{start_app_with(AppName, [
{ip, "::"},
{port, 8022},
{admin, #{
path => <<"/v1/admin">>
}},
{eventsink, #{
identity => #{
namespace => 'ff/identity'
},
wallet => #{
namespace => 'ff/wallet_v2'
},
withdrawal => #{
namespace => 'ff/withdrawal_v2'
},
deposit => #{
namespace => 'ff/deposit_v1'
},
destination => #{
namespace => 'ff/destination_v2'
},
source => #{
namespace => 'ff/source_v1'
},
withdrawal_session => #{
namespace => 'ff/withdrawal/session_v2'
},
p2p_transfer => #{
namespace => 'ff/p2p_transfer_v1'
},
p2p_session => #{
namespace => 'ff/p2p_transfer/session_v1'
},
w2w_transfer => #{
namespace => 'ff/w2w_transfer_v1'
},
p2p_template => #{
namespace => 'ff/p2p_template_v1'
}
}}
]), #{}};
{ip, "::"},
{port, 8022},
{admin, #{
path => <<"/v1/admin">>
}},
{eventsink, #{
identity => #{
namespace => 'ff/identity'
},
wallet => #{
namespace => 'ff/wallet_v2'
},
withdrawal => #{
namespace => 'ff/withdrawal_v2'
},
deposit => #{
namespace => 'ff/deposit_v1'
},
destination => #{
namespace => 'ff/destination_v2'
},
source => #{
namespace => 'ff/source_v1'
},
withdrawal_session => #{
namespace => 'ff/withdrawal/session_v2'
},
p2p_transfer => #{
namespace => 'ff/p2p_transfer_v1'
},
p2p_session => #{
namespace => 'ff/p2p_transfer/session_v1'
},
w2w_transfer => #{
namespace => 'ff/w2w_transfer_v1'
},
p2p_template => #{
namespace => 'ff/p2p_template_v1'
}
}}
]), #{}};
start_app(bender_client = AppName) ->
{start_app_with(AppName, [
{services, #{
'Bender' => <<"http://bender:8022/v1/bender">>,
'Generator' => <<"http://bender:8022/v1/generator">>
}},
{deadline, 60000}
]), #{}};
{services, #{
'Bender' => <<"http://bender:8022/v1/bender">>,
'Generator' => <<"http://bender:8022/v1/generator">>
}},
{deadline, 60000}
]), #{}};
start_app(p2p = AppName) ->
{start_app_with(AppName, [
{score_id, <<"fraud">>}
]), #{}};
{score_id, <<"fraud">>}
]), #{}};
start_app({AppName, AppEnv}) ->
{start_app_with(AppName, AppEnv), #{}};
start_app(AppName) ->
{start_app_with(AppName, []), #{}}.
-spec start_app_with(app_name(), app_env()) -> [app_name()].
start_app_with(AppName, Env) ->
_ = application:load(AppName),
_ = set_app_env(AppName, Env),
@ -225,19 +213,17 @@ start_app_with(AppName, Env) ->
set_app_env(AppName, Env) ->
lists:foreach(
fun ({K, V}) ->
fun({K, V}) ->
ok = application:set_env(AppName, K, V)
end,
Env
).
-spec stop_apps([app_name()]) -> ok.
stop_apps(AppNames) ->
lists:foreach(fun stop_app/1, lists:reverse(AppNames)).
-spec stop_app(app_name()) -> ok.
stop_app(AppName) ->
case application:stop(AppName) of
ok ->
@ -252,15 +238,15 @@ stop_app(AppName) ->
end.
-spec set_context(config()) -> ok.
set_context(C) ->
ok = ff_context:save(ff_context:create(#{
party_client => party_client:create_client(),
woody_context => cfg('$woody_ctx', C)
})).
ok = ff_context:save(
ff_context:create(#{
party_client => party_client:create_client(),
woody_context => cfg('$woody_ctx', C)
})
).
-spec unset_context() -> ok.
unset_context() ->
ok = ff_context:cleanup().
@ -269,14 +255,12 @@ unset_context() ->
-type config_mut_fun() :: fun((config()) -> config()).
-spec makeup_cfg([config_mut_fun()], config()) -> config().
makeup_cfg(CMFs, C0) ->
lists:foldl(fun (CMF, C) -> CMF(C) end, C0, CMFs).
lists:foldl(fun(CMF, C) -> CMF(C) end, C0, CMFs).
-spec woody_ctx() -> config_mut_fun().
woody_ctx() ->
fun (C) -> cfg('$woody_ctx', construct_woody_ctx(C), C) end.
fun(C) -> cfg('$woody_ctx', construct_woody_ctx(C), C) end.
construct_woody_ctx(C) ->
woody_context:new(construct_rpc_id(get_test_case_name(C))).
@ -289,33 +273,26 @@ construct_rpc_id(TestCaseName) ->
).
-spec get_woody_ctx(config()) -> woody_context:ctx().
get_woody_ctx(C) ->
cfg('$woody_ctx', C).
%%
-spec test_case_name(test_case_name()) -> config_mut_fun().
test_case_name(TestCaseName) ->
fun (C) -> cfg('$test_case_name', TestCaseName, C) end.
fun(C) -> cfg('$test_case_name', TestCaseName, C) end.
-spec get_test_case_name(config()) -> test_case_name().
get_test_case_name(C) ->
cfg('$test_case_name', C).
%%
-spec await(Expect, fun(() -> Expect | _)) ->
Expect.
-spec await(Expect, fun(() -> Expect | _)) -> Expect.
await(Expect, Compute) ->
await(Expect, Compute, genlib_retry:linear(3, 1000)).
-spec await(Expect, fun(() -> Expect | _), genlib_retry:strategy()) ->
Expect.
-spec await(Expect, fun(() -> Expect | _), genlib_retry:strategy()) -> Expect.
await(Expect, Compute, Retry0) ->
case Compute() of
Expect ->

View File

@ -7,22 +7,20 @@
-include_lib("identdocstore_proto/include/identdocstore_identity_document_storage_thrift.hrl").
-spec rus_domestic_passport(ct_helper:config()) ->
{rus_domestic_passport, binary()}.
-spec rus_domestic_passport(ct_helper:config()) -> {rus_domestic_passport, binary()}.
rus_domestic_passport(C) ->
Document = {
russian_domestic_passport,
#identdocstore_RussianDomesticPassport{
series = <<"1234">>,
number = <<"567890">>,
issuer = <<"Чаржбекистон УВД"/utf8>>,
series = <<"1234">>,
number = <<"567890">>,
issuer = <<"Чаржбекистон УВД"/utf8>>,
issuer_code = <<"012345">>,
issued_at = <<"2012-12-22T12:42:11Z">>,
issued_at = <<"2012-12-22T12:42:11Z">>,
family_name = <<"Котлетка"/utf8>>,
first_name = <<"С"/utf8>>,
patronymic = <<"Пюрешкой"/utf8>>,
birth_date = <<"1972-03-12T00:00:00Z">>,
first_name = <<"С"/utf8>>,
patronymic = <<"Пюрешкой"/utf8>>,
birth_date = <<"1972-03-12T00:00:00Z">>,
birth_place = <<"Чаржбечхала"/utf8>>
}
},
@ -34,9 +32,7 @@ rus_domestic_passport(C) ->
{rus_domestic_passport, Token}
end.
-spec rus_retiree_insurance_cert(_Number :: binary(), ct_helper:config()) ->
{rus_retiree_insurance_cert, binary()}.
-spec rus_retiree_insurance_cert(_Number :: binary(), ct_helper:config()) -> {rus_retiree_insurance_cert, binary()}.
rus_retiree_insurance_cert(Number, C) ->
Document = {
russian_retiree_insurance_certificate,

View File

@ -1,4 +1,5 @@
-module(ct_keyring).
-include_lib("cds_proto/include/cds_proto_keyring_thrift.hrl").
-include_lib("jose/include/jose_jwk.hrl").
@ -15,7 +16,6 @@
}.
-spec init(_) -> ok.
init(Config) ->
case get_state(Config) of
not_initialized ->
@ -77,15 +77,11 @@ call(Fun, Args, C) ->
%% DECODE
-spec decode_encrypted_shares([cds_proto_keyring_thrift:'EncryptedMasterKeyShare'()]) ->
[encrypted_master_key_share()].
-spec decode_encrypted_shares([cds_proto_keyring_thrift:'EncryptedMasterKeyShare'()]) -> [encrypted_master_key_share()].
decode_encrypted_shares(EncryptedMasterKeyShares) ->
lists:map(fun decode_encrypted_share/1, EncryptedMasterKeyShares).
-spec decode_encrypted_share(cds_proto_keyring_thrift:'EncryptedMasterKeyShare'()) ->
encrypted_master_key_share().
-spec decode_encrypted_share(cds_proto_keyring_thrift:'EncryptedMasterKeyShare'()) -> encrypted_master_key_share().
decode_encrypted_share(#cds_EncryptedMasterKeyShare{
id = Id,
owner = Owner,

File diff suppressed because it is too large Load Diff

View File

@ -6,26 +6,23 @@
%%
-behaviour(supervisor).
-export([init/1]).
%%
-spec start() -> pid().
start() ->
{ok, PID} = supervisor:start_link(?MODULE, []),
true = unlink(PID),
PID.
-spec stop(pid()) -> ok.
stop(Pid) ->
ok = proc_lib:stop(Pid).
%%
-spec init([]) ->
{ok, {supervisor:sup_flags(), [supervisor:child_spec()]}}.
-spec init([]) -> {ok, {supervisor:sup_flags(), [supervisor:child_spec()]}}.
init([]) ->
{ok, {#{strategy => one_for_all, intensity => 1, period => 1}, []}}.

View File

@ -9,12 +9,9 @@
%% API
-spec marshal(ff_codec:type_name(), ff_codec:decoded_value()) ->
ff_codec:encoded_value().
-spec marshal(ff_codec:type_name(), ff_codec:decoded_value()) -> ff_codec:encoded_value().
marshal({list, T}, V) ->
[marshal(T, E) || E <- V];
marshal(final_cash_flow, #{postings := Postings}) ->
#cashflow_FinalCashFlow{
postings = marshal({list, postings}, Postings)
@ -27,10 +24,10 @@ marshal(postings, Posting) ->
} = Posting,
Details = maps:get(details, Posting, undefined),
#cashflow_FinalCashFlowPosting{
source = marshal(final_cash_flow_account, Sender),
source = marshal(final_cash_flow_account, Sender),
destination = marshal(final_cash_flow_account, Receiver),
volume = marshal(cash, Cash),
details = marshal(string, Details)
volume = marshal(cash, Cash),
details = marshal(string, Details)
};
marshal(final_cash_flow_account, #{
account := Account,
@ -38,25 +35,20 @@ marshal(final_cash_flow_account, #{
}) ->
#{id := AccountID} = Account,
#cashflow_FinalCashFlowAccount{
account_type = marshal(account_type, AccountType),
account_id = marshal(id, AccountID), % for compatability, deprecate
account = ff_codec:marshal(account, Account)
account_type = marshal(account_type, AccountType),
% for compatability, deprecate
account_id = marshal(id, AccountID),
account = ff_codec:marshal(account, Account)
};
marshal(account_type, CashflowAccount) ->
% Mapped to thrift type WalletCashFlowAccount as is
CashflowAccount;
marshal(T, V) ->
ff_codec:marshal(T, V).
-spec unmarshal(ff_codec:type_name(), ff_codec:encoded_value()) ->
ff_codec:decoded_value().
-spec unmarshal(ff_codec:type_name(), ff_codec:encoded_value()) -> ff_codec:decoded_value().
unmarshal({list, T}, V) ->
[unmarshal(T, E) || E <- V];
unmarshal(final_cash_flow, #cashflow_FinalCashFlow{
postings = Postings
}) ->
@ -70,24 +62,22 @@ unmarshal(postings, #cashflow_FinalCashFlowPosting{
details = Details
}) ->
genlib_map:compact(#{
sender => unmarshal(final_cash_flow_account, Source),
receiver => unmarshal(final_cash_flow_account, Destination),
volume => unmarshal(cash, Cash),
details => maybe_unmarshal(string, Details)
sender => unmarshal(final_cash_flow_account, Source),
receiver => unmarshal(final_cash_flow_account, Destination),
volume => unmarshal(cash, Cash),
details => maybe_unmarshal(string, Details)
});
unmarshal(final_cash_flow_account, #cashflow_FinalCashFlowAccount{
account_type = AccountType,
account = Account
account = Account
}) ->
#{
account => ff_codec:unmarshal(account, Account),
type => unmarshal(account_type, AccountType)
type => unmarshal(account_type, AccountType)
};
unmarshal(account_type, CashflowAccount) ->
% Mapped to thrift type WalletCashFlowAccount as is
CashflowAccount;
unmarshal(T, V) ->
ff_codec:unmarshal(T, V).
@ -102,9 +92,11 @@ maybe_unmarshal(Type, Value) ->
-ifdef(TEST).
-include_lib("eunit/include/eunit.hrl").
-spec test() -> _.
-spec final_cash_flow_symmetry_test() -> _.
final_cash_flow_symmetry_test() ->
PostingFn = fun() ->
#{

View File

@ -31,56 +31,47 @@
%% Callbacks
-callback unmarshal(type_name(), encoded_value()) ->
decoded_value().
-callback marshal(type_name(), decoded_value()) ->
encoded_value().
-callback unmarshal(type_name(), encoded_value()) -> decoded_value().
-callback marshal(type_name(), decoded_value()) -> encoded_value().
%% API
-spec unmarshal(codec(), type_name(), encoded_value()) ->
decoded_value().
-spec unmarshal(codec(), type_name(), encoded_value()) -> decoded_value().
unmarshal(Codec, Type, Value) ->
Codec:unmarshal(Type, Value).
-spec marshal(codec(), type_name(), decoded_value()) ->
encoded_value().
-spec marshal(codec(), type_name(), decoded_value()) -> encoded_value().
marshal(Codec, Type, Value) ->
Codec:marshal(Type, Value).
%% Generic codec
-spec marshal(type_name(), decoded_value()) ->
encoded_value().
-spec marshal(type_name(), decoded_value()) -> encoded_value().
marshal({list, T}, V) ->
[marshal(T, E) || E <- V];
marshal({set, T}, V) ->
ordsets:from_list([marshal(T, E) || E <- ordsets:to_list(V)]);
marshal(id, V) ->
marshal(string, V);
marshal(event_id, V) ->
marshal(integer, V);
marshal(provider_id, V) ->
marshal(integer, V);
marshal(terminal_id, V) ->
marshal(integer, V);
marshal(blocking, blocked) ->
blocked;
marshal(blocking, unblocked) ->
unblocked;
marshal(identity_provider, Provider) when is_binary(Provider) ->
Provider;
marshal(transaction_info, TransactionInfo = #{
id := TransactionID,
extra := Extra
}) ->
marshal(
transaction_info,
TransactionInfo = #{
id := TransactionID,
extra := Extra
}
) ->
Timestamp = maps:get(timestamp, TransactionInfo, undefined),
AddInfo = maps:get(additional_info, TransactionInfo, undefined),
#'TransactionInfo'{
@ -89,7 +80,6 @@ marshal(transaction_info, TransactionInfo = #{
extra = Extra,
additional_info = marshal(additional_transaction_info, AddInfo)
};
marshal(additional_transaction_info, AddInfo = #{}) ->
#'AdditionalTransactionInfo'{
rrn = marshal(string, maps:get(rrn, AddInfo, undefined)),
@ -108,21 +98,19 @@ marshal(additional_transaction_info, AddInfo = #{}) ->
maps:get(three_ds_verification, AddInfo, undefined)
)
};
marshal(three_ds_verification, Value) when
Value =:= authentication_successful orelse
Value =:= attempts_processing_performed orelse
Value =:= authentication_failed orelse
Value =:= authentication_could_not_be_performed
Value =:= attempts_processing_performed orelse
Value =:= authentication_failed orelse
Value =:= authentication_could_not_be_performed
->
Value;
marshal(account_change, {created, Account}) ->
{created, marshal(account, Account)};
marshal(account, #{
id := ID,
identity := IdentityID,
currency := CurrencyID,
id := ID,
identity := IdentityID,
currency := CurrencyID,
accounter_account_id := AAID
}) ->
#'account_Account'{
@ -131,7 +119,6 @@ marshal(account, #{
currency = marshal(currency_ref, CurrencyID),
accounter_account_id = marshal(event_id, AAID)
};
marshal(resource, {bank_card, #{bank_card := BankCard} = ResourceBankCard}) ->
{bank_card, #'ResourceBankCard'{
bank_card = marshal(bank_card, BankCard),
@ -141,12 +128,10 @@ marshal(resource, {crypto_wallet, #{crypto_wallet := CryptoWallet}}) ->
{crypto_wallet, #'ResourceCryptoWallet'{
crypto_wallet = marshal(crypto_wallet, CryptoWallet)
}};
marshal(resource_descriptor, {bank_card, BinDataID}) ->
{bank_card, #'ResourceDescriptorBankCard'{
bin_data_id = marshal(msgpack, BinDataID)
}};
marshal(bank_card, BankCard = #{token := Token}) ->
Bin = maps:get(bin, BankCard, undefined),
PaymentSystem = maps:get(payment_system, BankCard, undefined),
@ -169,28 +154,23 @@ marshal(bank_card, BankCard = #{token := Token}) ->
cardholder_name = maybe_marshal(string, CardholderName),
bin_data_id = maybe_marshal(msgpack, BinDataID)
};
marshal(bank_card_auth_data, {session, #{session_id := ID}}) ->
{session_data, #'SessionAuthData'{
id = marshal(string, ID)
}};
marshal(crypto_wallet, #{id := ID, currency := Currency}) ->
#'CryptoWallet'{
id = marshal(string, ID),
id = marshal(string, ID),
currency = marshal(crypto_currency, Currency),
data = marshal(crypto_data, Currency)
data = marshal(crypto_data, Currency)
};
marshal(exp_date, {Month, Year}) ->
#'BankCardExpDate'{
month = marshal(integer, Month),
year = marshal(integer, Year)
};
marshal(crypto_currency, {Currency, _}) ->
Currency;
marshal(crypto_data, {bitcoin, #{}}) ->
{bitcoin, #'CryptoDataBitcoin'{}};
marshal(crypto_data, {litecoin, #{}}) ->
@ -207,19 +187,15 @@ marshal(crypto_data, {ripple, Data}) ->
{ripple, #'CryptoDataRipple'{
tag = maybe_marshal(string, maps:get(tag, Data, undefined))
}};
marshal(payment_system, V) when is_atom(V) ->
V;
marshal(iso_country_code, V) when is_atom(V) ->
V;
marshal(card_type, V) when is_atom(V) ->
V;
marshal(cash, {Amount, CurrencyRef}) ->
#'Cash'{
amount = marshal(amount, Amount),
amount = marshal(amount, Amount),
currency = marshal(currency_ref, CurrencyRef)
};
marshal(cash_range, {{BoundLower, CashLower}, {BoundUpper, CashUpper}}) ->
@ -233,13 +209,11 @@ marshal(currency_ref, CurrencyID) when is_binary(CurrencyID) ->
};
marshal(amount, V) ->
marshal(integer, V);
marshal(event_range, {After, Limit}) ->
#'EventRange'{
'after' = maybe_marshal(integer, After),
limit = maybe_marshal(integer, Limit)
limit = maybe_marshal(integer, Limit)
};
marshal(failure, Failure) ->
#'Failure'{
code = marshal(string, ff_failure:code(Failure)),
@ -255,7 +229,6 @@ marshal(fees, Fees) ->
#'Fees'{
fees = maps:map(fun(_Constant, Value) -> marshal(cash, Value) end, maps:get(fees, Fees))
};
marshal(timestamp, {DateTime, USec}) ->
DateTimeinSeconds = genlib_time:daytime_to_unixtime(DateTime),
{TimeinUnit, Unit} =
@ -283,35 +256,27 @@ marshal(context, V) when is_map(V) ->
ff_entity_context_codec:marshal(V);
marshal(msgpack, V) ->
ff_msgpack_codec:marshal(msgpack, V);
% Catch this up in thrift validation
marshal(_, Other) ->
Other.
-spec unmarshal(type_name(), encoded_value()) ->
decoded_value().
-spec unmarshal(type_name(), encoded_value()) -> decoded_value().
unmarshal({list, T}, V) ->
[marshal(T, E) || E <- V];
unmarshal({set, T}, V) ->
ordsets:from_list([unmarshal(T, E) || E <- ordsets:to_list(V)]);
unmarshal(id, V) ->
unmarshal(string, V);
unmarshal(event_id, V) ->
unmarshal(integer, V);
unmarshal(provider_id, V) ->
unmarshal(integer, V);
unmarshal(terminal_id, V) ->
unmarshal(integer, V);
unmarshal(blocking, blocked) ->
blocked;
unmarshal(blocking, unblocked) ->
unblocked;
unmarshal(transaction_info, #'TransactionInfo'{
id = TransactionID,
timestamp = Timestamp,
@ -324,7 +289,6 @@ unmarshal(transaction_info, #'TransactionInfo'{
extra => Extra,
additional_info => maybe_unmarshal(additional_transaction_info, AddInfo)
});
unmarshal(additional_transaction_info, #'AdditionalTransactionInfo'{
rrn = RRN,
approval_code = ApprovalCode,
@ -353,15 +317,13 @@ unmarshal(additional_transaction_info, #'AdditionalTransactionInfo'{
cavv_algorithm => maybe_unmarshal(string, CAVVAlgorithm),
three_ds_verification => maybe_unmarshal(three_ds_verification, ThreeDSVerification)
});
unmarshal(three_ds_verification, Value) when
Value =:= authentication_successful orelse
Value =:= attempts_processing_performed orelse
Value =:= authentication_failed orelse
Value =:= authentication_could_not_be_performed
Value =:= attempts_processing_performed orelse
Value =:= authentication_failed orelse
Value =:= authentication_could_not_be_performed
->
Value;
unmarshal(complex_action, #ff_repairer_ComplexAction{
timer = TimerAction,
remove = RemoveAction
@ -377,12 +339,10 @@ unmarshal(remove_action, undefined) ->
[];
unmarshal(remove_action, #ff_repairer_RemoveAction{}) ->
[remove];
unmarshal(set_timer_action, {timeout, Timeout}) ->
{timeout, unmarshal(integer, Timeout)};
unmarshal(set_timer_action, {deadline, Deadline}) ->
{deadline, unmarshal(timestamp, Deadline)};
unmarshal(account_change, {created, Account}) ->
{created, unmarshal(account, Account)};
unmarshal(account, #'account_Account'{
@ -399,28 +359,28 @@ unmarshal(account, #'account_Account'{
};
unmarshal(accounter_account_id, V) ->
unmarshal(integer, V);
unmarshal(resource, {bank_card, #'ResourceBankCard'{
bank_card = BankCard,
auth_data = AuthData
}}) ->
{bank_card, genlib_map:compact(#{
bank_card => unmarshal(bank_card, BankCard),
auth_data => maybe_unmarshal(bank_card_auth_data, AuthData)
})};
unmarshal(
resource,
{bank_card, #'ResourceBankCard'{
bank_card = BankCard,
auth_data = AuthData
}}
) ->
{bank_card,
genlib_map:compact(#{
bank_card => unmarshal(bank_card, BankCard),
auth_data => maybe_unmarshal(bank_card_auth_data, AuthData)
})};
unmarshal(resource, {crypto_wallet, #'ResourceCryptoWallet'{crypto_wallet = CryptoWallet}}) ->
{crypto_wallet, #{
crypto_wallet => unmarshal(crypto_wallet, CryptoWallet)
}};
unmarshal(resource_descriptor, {bank_card, BankCard}) ->
{bank_card, unmarshal(msgpack, BankCard#'ResourceDescriptorBankCard'.bin_data_id)};
unmarshal(bank_card_auth_data, {session_data, #'SessionAuthData'{id = ID}}) ->
{session, #{
session_id => unmarshal(string, ID)
}};
unmarshal(bank_card, #'BankCard'{
token = Token,
bin = Bin,
@ -445,22 +405,17 @@ unmarshal(bank_card, #'BankCard'{
cardholder_name => maybe_unmarshal(string, CardholderName),
bin_data_id => maybe_unmarshal(msgpack, BinDataID)
});
unmarshal(exp_date, #'BankCardExpDate'{
month = Month,
year = Year
}) ->
{unmarshal(integer, Month), unmarshal(integer, Year)};
unmarshal(payment_system, V) when is_atom(V) ->
V;
unmarshal(iso_country_code, V) when is_atom(V) ->
V;
unmarshal(card_type, V) when is_atom(V) ->
V;
unmarshal(crypto_wallet, #'CryptoWallet'{
id = CryptoWalletID,
currency = CryptoWalletCurrency,
@ -470,20 +425,17 @@ unmarshal(crypto_wallet, #'CryptoWallet'{
id => unmarshal(string, CryptoWalletID),
currency => {CryptoWalletCurrency, unmarshal(crypto_data, Data)}
});
unmarshal(crypto_data, {ripple, #'CryptoDataRipple'{tag = Tag}}) ->
genlib_map:compact(#{
tag => maybe_unmarshal(string, Tag)
});
unmarshal(crypto_data, _) ->
#{};
unmarshal(cash, #'Cash'{
amount = Amount,
amount = Amount,
currency = CurrencyRef
}) ->
{unmarshal(amount, Amount), unmarshal(currency_ref, CurrencyRef)};
unmarshal(cash_range, #'CashRange'{
lower = {BoundLower, CashLower},
upper = {BoundUpper, CashUpper}
@ -492,17 +444,14 @@ unmarshal(cash_range, #'CashRange'{
{BoundLower, unmarshal(cash, CashLower)},
{BoundUpper, unmarshal(cash, CashUpper)}
};
unmarshal(currency_ref, #'CurrencyRef'{
symbolic_code = SymbolicCode
}) ->
unmarshal(string, SymbolicCode);
unmarshal(amount, V) ->
unmarshal(integer, V);
unmarshal(event_range, #'EventRange'{'after' = After, limit = Limit}) ->
{maybe_unmarshal(integer, After), maybe_unmarshal(integer, Limit)};
unmarshal(failure, Failure) ->
genlib_map:compact(#{
code => unmarshal(string, Failure#'Failure'.code),
@ -514,20 +463,17 @@ unmarshal(sub_failure, Failure) ->
code => unmarshal(string, Failure#'SubFailure'.code),
sub => maybe_unmarshal(sub_failure, Failure#'SubFailure'.sub)
});
unmarshal(context, V) -> ff_entity_context_codec:unmarshal(V);
unmarshal(context, V) ->
ff_entity_context_codec:unmarshal(V);
unmarshal(range, #evsink_EventRange{
'after' = Cursor,
limit = Limit
limit = Limit
}) ->
{Cursor, Limit, forward};
unmarshal(fees, Fees) ->
#{
fees => maps:map(fun(_Constant, Value) -> unmarshal(cash, Value) end, Fees#'Fees'.fees)
};
unmarshal(timestamp, Timestamp) when is_binary(Timestamp) ->
parse_timestamp(Timestamp);
unmarshal(timestamp_ms, V) ->
@ -540,16 +486,13 @@ unmarshal(string, V) when is_binary(V) ->
V;
unmarshal(integer, V) when is_integer(V) ->
V;
unmarshal(msgpack, V) ->
ff_msgpack_codec:unmarshal(msgpack, V);
unmarshal(range, #'EventRange'{
'after' = Cursor,
limit = Limit
limit = Limit
}) ->
{Cursor, Limit, forward};
unmarshal(bool, V) when is_boolean(V) ->
V.
@ -563,8 +506,7 @@ maybe_marshal(_Type, undefined) ->
maybe_marshal(Type, Value) ->
marshal(Type, Value).
-spec parse_timestamp(binary()) ->
machinery:timestamp().
-spec parse_timestamp(binary()) -> machinery:timestamp().
parse_timestamp(Bin) ->
try
MicroSeconds = genlib_rfc3339:parse(Bin, microsecond),
@ -577,7 +519,7 @@ parse_timestamp(Bin) ->
{DateTime, USec}
end
catch
error:Error:St ->
error:Error:St ->
erlang:raise(error, {bad_timestamp, Bin, Error}, St)
end.
@ -585,9 +527,11 @@ parse_timestamp(Bin) ->
-ifdef(TEST).
-include_lib("eunit/include/eunit.hrl").
-spec test() -> _.
-spec bank_card_codec_test() -> _.
bank_card_codec_test() ->
BankCard = #{
token => <<"token">>,

View File

@ -9,16 +9,13 @@
%% API
-spec marshal(ff_codec:type_name(), ff_codec:decoded_value()) ->
ff_codec:encoded_value().
-spec marshal(ff_codec:type_name(), ff_codec:decoded_value()) -> ff_codec:encoded_value().
marshal(change, {created, Adjustment}) ->
{created, #dep_adj_CreatedChange{adjustment = marshal(adjustment, Adjustment)}};
marshal(change, {status_changed, Status}) ->
{status_changed, #dep_adj_StatusChange{status = marshal(status, Status)}};
marshal(change, {p_transfer, TransferChange}) ->
{transfer, #dep_adj_TransferChange{payload = ff_p_transfer_codec:marshal(change, TransferChange)}};
marshal(adjustment, Adjustment) ->
#dep_adj_Adjustment{
id = marshal(id, ff_adjustment:id(Adjustment)),
@ -47,12 +44,10 @@ marshal(adjustment_state, Adjustment) ->
operation_timestamp = marshal(timestamp_ms, ff_adjustment:operation_timestamp(Adjustment)),
external_id = maybe_marshal(id, ff_adjustment:external_id(Adjustment))
};
marshal(status, pending) ->
{pending, #dep_adj_Pending{}};
marshal(status, succeeded) ->
{succeeded, #dep_adj_Succeeded{}};
marshal(changes_plan, Plan) ->
#dep_adj_ChangesPlan{
new_cash_flow = maybe_marshal(cash_flow_change_plan, maps:get(new_cash_flow, Plan, undefined)),
@ -69,26 +64,20 @@ marshal(status_change_plan, Plan) ->
#dep_adj_StatusChangePlan{
new_status = ff_deposit_status_codec:marshal(status, maps:get(new_status, Plan))
};
marshal(change_request, {change_status, Status}) ->
{change_status, #dep_adj_ChangeStatusRequest{
new_status = ff_deposit_status_codec:marshal(status, Status)
}};
marshal(T, V) ->
ff_codec:marshal(T, V).
-spec unmarshal(ff_codec:type_name(), ff_codec:encoded_value()) ->
ff_codec:decoded_value().
-spec unmarshal(ff_codec:type_name(), ff_codec:encoded_value()) -> ff_codec:decoded_value().
unmarshal(change, {created, #dep_adj_CreatedChange{adjustment = Adjustment}}) ->
{created, unmarshal(adjustment, Adjustment)};
unmarshal(change, {status_changed, #dep_adj_StatusChange{status = Status}}) ->
{status_changed, unmarshal(status, Status)};
unmarshal(change, {transfer, #dep_adj_TransferChange{payload = TransferChange}}) ->
{p_transfer, ff_p_transfer_codec:unmarshal(change, TransferChange)};
unmarshal(adjustment, Adjustment) ->
#{
id => unmarshal(id, Adjustment#dep_adj_Adjustment.id),
@ -100,19 +89,16 @@ unmarshal(adjustment, Adjustment) ->
operation_timestamp => unmarshal(timestamp_ms, Adjustment#dep_adj_Adjustment.operation_timestamp),
external_id => maybe_unmarshal(id, Adjustment#dep_adj_Adjustment.external_id)
};
unmarshal(adjustment_params, Params) ->
genlib_map:compact(#{
id => unmarshal(id, Params#dep_adj_AdjustmentParams.id),
change => unmarshal(change_request, Params#dep_adj_AdjustmentParams.change),
external_id => maybe_unmarshal(id, Params#dep_adj_AdjustmentParams.external_id)
});
unmarshal(status, {pending, #dep_adj_Pending{}}) ->
pending;
unmarshal(status, {succeeded, #dep_adj_Succeeded{}}) ->
succeeded;
unmarshal(changes_plan, Plan) ->
genlib_map:compact(#{
new_cash_flow => maybe_unmarshal(cash_flow_change_plan, Plan#dep_adj_ChangesPlan.new_cash_flow),
@ -130,11 +116,9 @@ unmarshal(status_change_plan, Plan) ->
#{
new_status => ff_deposit_status_codec:unmarshal(status, Status)
};
unmarshal(change_request, {change_status, Request}) ->
Status = Request#dep_adj_ChangeStatusRequest.new_status,
{change_status, ff_deposit_status_codec:unmarshal(status, Status)};
unmarshal(T, V) ->
ff_codec:unmarshal(T, V).
@ -154,9 +138,11 @@ maybe_marshal(Type, Value) ->
-ifdef(TEST).
-include_lib("eunit/include/eunit.hrl").
-spec test() -> _.
-spec adjustment_codec_test() -> _.
adjustment_codec_test() ->
FinalCashFlow = #{
postings => [

View File

@ -11,11 +11,11 @@
%% Data transform
-define(to_session_event(SessionID, Payload),
{session, #{id => SessionID, payload => Payload}}).
{session, #{id => SessionID, payload => Payload}}
).
-spec marshal_deposit_state(ff_deposit:deposit_state(), ff_entity_context:context()) ->
ff_proto_deposit_thrift:'DepositState'().
marshal_deposit_state(DepositState, Context) ->
CashFlow = ff_deposit:effective_final_cash_flow(DepositState),
Reverts = ff_deposit:reverts(DepositState),
@ -39,25 +39,20 @@ marshal_deposit_state(DepositState, Context) ->
%% API
-spec marshal(ff_codec:type_name(), ff_codec:decoded_value()) ->
ff_codec:encoded_value().
-spec marshal(ff_codec:type_name(), ff_codec:decoded_value()) -> ff_codec:encoded_value().
marshal({list, T}, V) ->
[marshal(T, E) || E <- V];
marshal(event, {EventID, {ev, Timestamp, Change}}) ->
#deposit_Event{
event_id = ff_codec:marshal(event_id, EventID),
occured_at = ff_codec:marshal(timestamp, Timestamp),
change = marshal(change, Change)
};
marshal(timestamped_change, {ev, Timestamp, Change}) ->
#deposit_TimestampedChange{
change = marshal(change, Change),
occured_at = ff_codec:marshal(timestamp, Timestamp)
};
marshal(change, {created, Deposit}) ->
{created, #deposit_CreatedChange{deposit = marshal(deposit, Deposit)}};
marshal(change, {status_changed, Status}) ->
@ -76,7 +71,6 @@ marshal(change, {adjustment, #{id := ID, payload := Payload}}) ->
id = marshal(id, ID),
payload = ff_deposit_adjustment_codec:marshal(change, Payload)
}};
marshal(deposit, Deposit) ->
#deposit_Deposit{
id = marshal(id, ff_deposit:id(Deposit)),
@ -99,34 +93,26 @@ marshal(deposit_params, DepositParams) ->
external_id = maybe_marshal(id, maps:get(external_id, DepositParams, undefined)),
metadata = maybe_marshal(ctx, maps:get(metadata, DepositParams, undefined))
};
marshal(ctx, Ctx) ->
maybe_marshal(context, Ctx);
marshal(status, Status) ->
ff_deposit_status_codec:marshal(status, Status);
marshal(T, V) ->
ff_codec:marshal(T, V).
-spec unmarshal(ff_codec:type_name(), ff_codec:encoded_value()) ->
ff_codec:decoded_value().
-spec unmarshal(ff_codec:type_name(), ff_codec:encoded_value()) -> ff_codec:decoded_value().
unmarshal({list, T}, V) ->
[unmarshal(T, E) || E <- V];
unmarshal(repair_scenario, {add_events, #deposit_AddEventsRepair{events = Events, action = Action}}) ->
{add_events, genlib_map:compact(#{
events => unmarshal({list, change}, Events),
action => maybe_unmarshal(complex_action, Action)
})};
{add_events,
genlib_map:compact(#{
events => unmarshal({list, change}, Events),
action => maybe_unmarshal(complex_action, Action)
})};
unmarshal(timestamped_change, TimestampedChange) ->
Timestamp = ff_codec:unmarshal(timestamp, TimestampedChange#deposit_TimestampedChange.occured_at),
Change = unmarshal(change, TimestampedChange#deposit_TimestampedChange.change),
{ev, Timestamp, Change};
unmarshal(change, {created, #deposit_CreatedChange{deposit = Deposit}}) ->
{created, unmarshal(deposit, Deposit)};
unmarshal(change, {status_changed, #deposit_StatusChange{status = DepositStatus}}) ->
@ -145,10 +131,8 @@ unmarshal(change, {adjustment, Change}) ->
id => unmarshal(id, Change#deposit_AdjustmentChange.id),
payload => ff_deposit_adjustment_codec:unmarshal(change, Change#deposit_AdjustmentChange.payload)
}};
unmarshal(status, Status) ->
ff_deposit_status_codec:unmarshal(status, Status);
unmarshal(deposit, Deposit) ->
genlib_map:compact(#{
version => 3,
@ -166,7 +150,6 @@ unmarshal(deposit, Deposit) ->
created_at => maybe_unmarshal(timestamp_ms, Deposit#deposit_Deposit.created_at),
metadata => maybe_unmarshal(ctx, Deposit#deposit_Deposit.metadata)
});
unmarshal(deposit_params, DepositParams) ->
genlib_map:compact(#{
id => unmarshal(id, DepositParams#deposit_DepositParams.id),
@ -176,10 +159,8 @@ unmarshal(deposit_params, DepositParams) ->
metadata => maybe_unmarshal(ctx, DepositParams#deposit_DepositParams.metadata),
external_id => maybe_unmarshal(id, DepositParams#deposit_DepositParams.external_id)
});
unmarshal(ctx, Ctx) ->
maybe_unmarshal(context, Ctx);
unmarshal(T, V) ->
ff_codec:unmarshal(T, V).
@ -199,14 +180,16 @@ maybe_marshal(Type, Value) ->
-ifdef(TEST).
-include_lib("eunit/include/eunit.hrl").
-spec test() -> _.
-spec deposit_symmetry_test() -> _.
deposit_symmetry_test() ->
Encoded = #deposit_Deposit{
body = #'Cash'{
amount = 10101,
currency = #'CurrencyRef'{ symbolic_code = <<"Banana Republic">> }
currency = #'CurrencyRef'{symbolic_code = <<"Banana Republic">>}
},
source_id = genlib:unique(),
wallet_id = genlib:unique(),
@ -225,7 +208,7 @@ deposit_params_symmetry_test() ->
Encoded = #deposit_DepositParams{
body = #'Cash'{
amount = 10101,
currency = #'CurrencyRef'{ symbolic_code = <<"Banana Republic">> }
currency = #'CurrencyRef'{symbolic_code = <<"Banana Republic">>}
},
source_id = genlib:unique(),
wallet_id = genlib:unique(),
@ -263,17 +246,18 @@ deposit_timestamped_change_codec_test() ->
deposit_change_revert_codec_test() ->
Revert = #{
id => genlib:unique(),
payload => {created, #{
id => genlib:unique(),
status => pending,
body => {123, <<"RUB">>},
created_at => ff_time:now(),
domain_revision => 123,
party_revision => 321,
external_id => genlib:unique(),
wallet_id => genlib:unique(),
source_id => genlib:unique()
}}
payload =>
{created, #{
id => genlib:unique(),
status => pending,
body => {123, <<"RUB">>},
created_at => ff_time:now(),
domain_revision => 123,
party_revision => 321,
external_id => genlib:unique(),
wallet_id => genlib:unique(),
source_id => genlib:unique()
}}
},
Change = {revert, Revert},
TimestampedChange = {ev, machinery_time:now(), Change},
@ -286,24 +270,25 @@ deposit_change_revert_codec_test() ->
deposit_change_adjustment_codec_test() ->
Adjustment = #{
id => genlib:unique(),
payload => {created, #{
id => genlib:unique(),
status => pending,
changes_plan => #{
new_cash_flow => #{
old_cash_flow_inverted => #{postings => []},
new_cash_flow => #{postings => []}
payload =>
{created, #{
id => genlib:unique(),
status => pending,
changes_plan => #{
new_cash_flow => #{
old_cash_flow_inverted => #{postings => []},
new_cash_flow => #{postings => []}
},
new_status => #{
new_status => succeeded
}
},
new_status => #{
new_status => succeeded
}
},
created_at => ff_time:now(),
domain_revision => 123,
party_revision => 321,
operation_timestamp => ff_time:now(),
external_id => genlib:unique()
}}
created_at => ff_time:now(),
domain_revision => 123,
party_revision => 321,
operation_timestamp => ff_time:now(),
external_id => genlib:unique()
}}
},
Change = {adjustment, Adjustment},
TimestampedChange = {ev, machinery_time:now(), Change},

View File

@ -16,32 +16,28 @@
%% Internals
%%
-spec publish_events(list(event())) ->
list(sinkevent()).
-spec publish_events(list(event())) -> list(sinkevent()).
publish_events(Events) ->
[publish_event(Event) || Event <- Events].
-spec publish_event(event()) ->
sinkevent().
-spec publish_event(event()) -> sinkevent().
publish_event(#{
id := ID,
source_id := SourceID,
event := {
id := ID,
source_id := SourceID,
event := {
EventID,
Dt,
{ev, EventDt, Payload}
}
}) ->
#deposit_SinkEvent{
id = marshal(event_id, ID),
created_at = marshal(timestamp, Dt),
source = marshal(id, SourceID),
payload = #deposit_EventSinkPayload{
sequence = marshal(event_id, EventID),
id = marshal(event_id, ID),
created_at = marshal(timestamp, Dt),
source = marshal(id, SourceID),
payload = #deposit_EventSinkPayload{
sequence = marshal(event_id, EventID),
occured_at = marshal(timestamp, EventDt),
changes = [marshal(change, Payload)]
changes = [marshal(change, Payload)]
}
}.

View File

@ -1,4 +1,5 @@
-module(ff_deposit_handler).
-behaviour(ff_woody_wrapper).
-include_lib("fistful_proto/include/ff_proto_deposit_thrift.hrl").
@ -10,10 +11,11 @@
%% ff_woody_wrapper callbacks
%%
-spec handle_function(woody:func(), woody:args(), woody:options()) ->
{ok, woody:result()} | no_return().
-spec handle_function(woody:func(), woody:args(), woody:options()) -> {ok, woody:result()} | no_return().
handle_function(Func, Args, Opts) ->
scoper:scope(deposit, #{},
scoper:scope(
deposit,
#{},
fun() ->
handle_function_(Func, Args, Opts)
end
@ -87,11 +89,13 @@ handle_function_('GetEvents', [ID, EventRange], _Opts) ->
handle_function_('CreateAdjustment', [ID, MarshaledParams], _Opts) ->
Params = ff_deposit_adjustment_codec:unmarshal(adjustment_params, MarshaledParams),
AdjustmentID = maps:get(id, Params),
ok = scoper:add_meta(genlib_map:compact(#{
id => ID,
adjustment_id => AdjustmentID,
external_id => maps:get(external_id, Params, undefined)
})),
ok = scoper:add_meta(
genlib_map:compact(#{
id => ID,
adjustment_id => AdjustmentID,
external_id => maps:get(external_id, Params, undefined)
})
),
case ff_deposit_machine:start_adjustment(ID, Params) of
ok ->
{ok, Machine} = ff_deposit_machine:get(ID),
@ -120,11 +124,13 @@ handle_function_('CreateAdjustment', [ID, MarshaledParams], _Opts) ->
handle_function_('CreateRevert', [ID, MarshaledParams], _Opts) ->
Params = ff_deposit_revert_codec:unmarshal(revert_params, MarshaledParams),
RevertID = maps:get(id, Params),
ok = scoper:add_meta(genlib_map:compact(#{
id => ID,
revert_id => RevertID,
external_id => maps:get(external_id, Params, undefined)
})),
ok = scoper:add_meta(
genlib_map:compact(#{
id => ID,
revert_id => RevertID,
external_id => maps:get(external_id, Params, undefined)
})
),
case ff_deposit_machine:start_revert(ID, Params) of
ok ->
{ok, Machine} = ff_deposit_machine:get(ID),
@ -155,12 +161,14 @@ handle_function_('CreateRevert', [ID, MarshaledParams], _Opts) ->
handle_function_('CreateRevertAdjustment', [ID, RevertID, MarshaledParams], _Opts) ->
Params = ff_deposit_revert_adjustment_codec:unmarshal(adjustment_params, MarshaledParams),
AdjustmentID = maps:get(id, Params),
ok = scoper:add_meta(genlib_map:compact(#{
id => ID,
revert_id => RevertID,
adjustment_id => AdjustmentID,
external_id => maps:get(external_id, Params, undefined)
})),
ok = scoper:add_meta(
genlib_map:compact(#{
id => ID,
revert_id => RevertID,
adjustment_id => AdjustmentID,
external_id => maps:get(external_id, Params, undefined)
})
),
case ff_deposit_machine:start_revert_adjustment(ID, RevertID, Params) of
ok ->
{ok, Machine} = ff_deposit_machine:get(ID),

File diff suppressed because it is too large Load Diff

View File

@ -12,8 +12,7 @@
%% ff_woody_wrapper callbacks
%%
-spec handle_function(woody:func(), woody:args(), options()) ->
{ok, woody:result()} | no_return().
-spec handle_function(woody:func(), woody:args(), options()) -> {ok, woody:result()} | no_return().
handle_function('Repair', [ID, Scenario], _Opts) ->
DecodedScenario = ff_deposit_codec:unmarshal(repair_scenario, Scenario),
case ff_deposit_machine:repair(ID, DecodedScenario) of

View File

@ -9,16 +9,13 @@
%% API
-spec marshal(ff_codec:type_name(), ff_codec:decoded_value()) ->
ff_codec:encoded_value().
-spec marshal(ff_codec:type_name(), ff_codec:decoded_value()) -> ff_codec:encoded_value().
marshal(change, {created, Adjustment}) ->
{created, #dep_rev_adj_CreatedChange{adjustment = marshal(adjustment, Adjustment)}};
marshal(change, {status_changed, Status}) ->
{status_changed, #dep_rev_adj_StatusChange{status = marshal(status, Status)}};
marshal(change, {p_transfer, TransferChange}) ->
{transfer, #dep_rev_adj_TransferChange{payload = ff_p_transfer_codec:marshal(change, TransferChange)}};
marshal(adjustment, Adjustment) ->
#dep_rev_adj_Adjustment{
id = marshal(id, ff_adjustment:id(Adjustment)),
@ -47,12 +44,10 @@ marshal(adjustment_state, Adjustment) ->
operation_timestamp = marshal(timestamp_ms, ff_adjustment:operation_timestamp(Adjustment)),
external_id = maybe_marshal(id, ff_adjustment:external_id(Adjustment))
};
marshal(status, pending) ->
{pending, #dep_rev_adj_Pending{}};
marshal(status, succeeded) ->
{succeeded, #dep_rev_adj_Succeeded{}};
marshal(changes_plan, Plan) ->
#dep_rev_adj_ChangesPlan{
new_cash_flow = maybe_marshal(cash_flow_change_plan, maps:get(new_cash_flow, Plan, undefined)),
@ -69,26 +64,20 @@ marshal(status_change_plan, Plan) ->
#dep_rev_adj_StatusChangePlan{
new_status = ff_deposit_revert_status_codec:marshal(status, maps:get(new_status, Plan))
};
marshal(change_request, {change_status, Status}) ->
{change_status, #dep_rev_adj_ChangeStatusRequest{
new_status = ff_deposit_revert_status_codec:marshal(status, Status)
}};
marshal(T, V) ->
ff_codec:marshal(T, V).
-spec unmarshal(ff_codec:type_name(), ff_codec:encoded_value()) ->
ff_codec:decoded_value().
-spec unmarshal(ff_codec:type_name(), ff_codec:encoded_value()) -> ff_codec:decoded_value().
unmarshal(change, {created, #dep_rev_adj_CreatedChange{adjustment = Adjustment}}) ->
{created, unmarshal(adjustment, Adjustment)};
unmarshal(change, {status_changed, #dep_rev_adj_StatusChange{status = Status}}) ->
{status_changed, unmarshal(status, Status)};
unmarshal(change, {transfer, #dep_rev_adj_TransferChange{payload = TransferChange}}) ->
{p_transfer, ff_p_transfer_codec:unmarshal(change, TransferChange)};
unmarshal(adjustment, Adjustment) ->
#{
id => unmarshal(id, Adjustment#dep_rev_adj_Adjustment.id),
@ -100,19 +89,16 @@ unmarshal(adjustment, Adjustment) ->
operation_timestamp => unmarshal(timestamp_ms, Adjustment#dep_rev_adj_Adjustment.operation_timestamp),
external_id => maybe_unmarshal(id, Adjustment#dep_rev_adj_Adjustment.external_id)
};
unmarshal(adjustment_params, Params) ->
genlib_map:compact(#{
id => unmarshal(id, Params#dep_rev_adj_AdjustmentParams.id),
change => unmarshal(change_request, Params#dep_rev_adj_AdjustmentParams.change),
external_id => maybe_unmarshal(id, Params#dep_rev_adj_AdjustmentParams.external_id)
});
unmarshal(status, {pending, #dep_rev_adj_Pending{}}) ->
pending;
unmarshal(status, {succeeded, #dep_rev_adj_Succeeded{}}) ->
succeeded;
unmarshal(changes_plan, Plan) ->
genlib_map:compact(#{
new_cash_flow => maybe_unmarshal(cash_flow_change_plan, Plan#dep_rev_adj_ChangesPlan.new_cash_flow),
@ -130,11 +116,9 @@ unmarshal(status_change_plan, Plan) ->
#{
new_status => ff_deposit_revert_status_codec:unmarshal(status, Status)
};
unmarshal(change_request, {change_status, Request}) ->
Status = Request#dep_rev_adj_ChangeStatusRequest.new_status,
{change_status, ff_deposit_revert_status_codec:unmarshal(status, Status)};
unmarshal(T, V) ->
ff_codec:unmarshal(T, V).
@ -154,9 +138,11 @@ maybe_marshal(Type, Value) ->
-ifdef(TEST).
-include_lib("eunit/include/eunit.hrl").
-spec test() -> _.
-spec adjustment_codec_test() -> _.
adjustment_codec_test() ->
FinalCashFlow = #{
postings => [

View File

@ -10,13 +10,12 @@
%% Data transform
-define(to_session_event(SessionID, Payload),
{session, #{id => SessionID, payload => Payload}}).
{session, #{id => SessionID, payload => Payload}}
).
%% API
-spec marshal(ff_codec:type_name(), ff_codec:decoded_value()) ->
ff_codec:encoded_value().
-spec marshal(ff_codec:type_name(), ff_codec:decoded_value()) -> ff_codec:encoded_value().
marshal(change, {created, Revert}) ->
{created, #deposit_revert_CreatedChange{revert = marshal(revert, Revert)}};
marshal(change, {status_changed, Status}) ->
@ -31,7 +30,6 @@ marshal(change, {adjustment, #{id := ID, payload := Payload}}) ->
id = marshal(id, ID),
payload = ff_deposit_revert_adjustment_codec:marshal(change, Payload)
}};
marshal(revert, Revert) ->
#deposit_revert_Revert{
id = marshal(id, ff_deposit_revert:id(Revert)),
@ -69,17 +67,12 @@ marshal(revert_state, Revert) ->
effective_final_cash_flow = ff_cash_flow_codec:marshal(final_cash_flow, CashFlow),
adjustments = [ff_deposit_revert_adjustment_codec:marshal(adjustment_state, A) || A <- Adjustments]
};
marshal(status, Status) ->
ff_deposit_revert_status_codec:marshal(status, Status);
marshal(T, V) ->
ff_codec:marshal(T, V).
-spec unmarshal(ff_codec:type_name(), ff_codec:encoded_value()) ->
ff_codec:decoded_value().
-spec unmarshal(ff_codec:type_name(), ff_codec:encoded_value()) -> ff_codec:decoded_value().
unmarshal(change, {created, #deposit_revert_CreatedChange{revert = Revert}}) ->
{created, unmarshal(revert, Revert)};
unmarshal(change, {status_changed, #deposit_revert_StatusChange{status = Status}}) ->
@ -97,10 +90,8 @@ unmarshal(change, {adjustment, Change}) ->
id => unmarshal(id, ID),
payload => ff_deposit_revert_adjustment_codec:unmarshal(change, Payload)
}};
unmarshal(status, Status) ->
ff_deposit_revert_status_codec:unmarshal(status, Status);
unmarshal(revert, Revert) ->
genlib_map:compact(#{
id => unmarshal(id, Revert#deposit_revert_Revert.id),
@ -114,7 +105,6 @@ unmarshal(revert, Revert) ->
reason => maybe_unmarshal(string, Revert#deposit_revert_Revert.reason),
external_id => maybe_unmarshal(id, Revert#deposit_revert_Revert.external_id)
});
unmarshal(revert_params, Params) ->
genlib_map:compact(#{
id => unmarshal(id, Params#deposit_revert_RevertParams.id),
@ -122,7 +112,6 @@ unmarshal(revert_params, Params) ->
external_id => maybe_unmarshal(id, Params#deposit_revert_RevertParams.external_id),
reason => maybe_unmarshal(string, Params#deposit_revert_RevertParams.reason)
});
unmarshal(T, V) ->
ff_codec:unmarshal(T, V).
@ -142,14 +131,16 @@ maybe_marshal(Type, Value) ->
-ifdef(TEST).
-include_lib("eunit/include/eunit.hrl").
-spec test() -> _.
-spec revert_symmetry_test() -> _.
revert_symmetry_test() ->
Encoded = #deposit_revert_Revert{
body = #'Cash'{
amount = 10101,
currency = #'CurrencyRef'{ symbolic_code = <<"Banana Republic">> }
currency = #'CurrencyRef'{symbolic_code = <<"Banana Republic">>}
},
source_id = genlib:unique(),
wallet_id = genlib:unique(),
@ -168,7 +159,7 @@ revert_params_symmetry_test() ->
Encoded = #deposit_revert_RevertParams{
body = #'Cash'{
amount = 10101,
currency = #'CurrencyRef'{ symbolic_code = <<"Banana Republic">> }
currency = #'CurrencyRef'{symbolic_code = <<"Banana Republic">>}
},
external_id = undefined,
reason = <<"why not">>,
@ -178,26 +169,28 @@ revert_params_symmetry_test() ->
-spec change_adjustment_symmetry_test() -> _.
change_adjustment_symmetry_test() ->
Encoded = {adjustment, #deposit_revert_AdjustmentChange{
id = genlib:unique(),
payload = {created, #dep_rev_adj_CreatedChange{
adjustment = #dep_rev_adj_Adjustment{
id = genlib:unique(),
status = {pending, #dep_rev_adj_Pending{}},
changes_plan = #dep_rev_adj_ChangesPlan{
new_cash_flow = #dep_rev_adj_CashFlowChangePlan{
old_cash_flow_inverted = #cashflow_FinalCashFlow{postings = []},
new_cash_flow = #cashflow_FinalCashFlow{postings = []}
Encoded =
{adjustment, #deposit_revert_AdjustmentChange{
id = genlib:unique(),
payload =
{created, #dep_rev_adj_CreatedChange{
adjustment = #dep_rev_adj_Adjustment{
id = genlib:unique(),
status = {pending, #dep_rev_adj_Pending{}},
changes_plan = #dep_rev_adj_ChangesPlan{
new_cash_flow = #dep_rev_adj_CashFlowChangePlan{
old_cash_flow_inverted = #cashflow_FinalCashFlow{postings = []},
new_cash_flow = #cashflow_FinalCashFlow{postings = []}
}
},
created_at = <<"2000-01-01T00:00:00Z">>,
domain_revision = 123,
party_revision = 321,
operation_timestamp = <<"2000-01-01T00:00:00Z">>,
external_id = genlib:unique()
}
},
created_at = <<"2000-01-01T00:00:00Z">>,
domain_revision = 123,
party_revision = 321,
operation_timestamp = <<"2000-01-01T00:00:00Z">>,
external_id = genlib:unique()
}
}}
}},
}}
}},
?assertEqual(Encoded, marshal(change, unmarshal(change, Encoded))).
-endif.

View File

@ -9,30 +9,23 @@
%% API
-spec marshal(ff_codec:type_name(), ff_codec:decoded_value()) ->
ff_codec:encoded_value().
-spec marshal(ff_codec:type_name(), ff_codec:decoded_value()) -> ff_codec:encoded_value().
marshal(status, pending) ->
{pending, #dep_rev_status_Pending{}};
marshal(status, succeeded) ->
{succeeded, #dep_rev_status_Succeeded{}};
marshal(status, {failed, Failure}) ->
{failed, #dep_rev_status_Failed{failure = marshal(failure, Failure)}};
marshal(T, V) ->
ff_codec:marshal(T, V).
-spec unmarshal(ff_codec:type_name(), ff_codec:encoded_value()) ->
ff_codec:decoded_value().
-spec unmarshal(ff_codec:type_name(), ff_codec:encoded_value()) -> ff_codec:decoded_value().
unmarshal(status, {pending, #dep_rev_status_Pending{}}) ->
pending;
unmarshal(status, {succeeded, #dep_rev_status_Succeeded{}}) ->
succeeded;
unmarshal(status, {failed, #dep_rev_status_Failed{failure = Failure}}) ->
{failed, unmarshal(failure, Failure)};
unmarshal(T, V) ->
ff_codec:unmarshal(T, V).
@ -40,9 +33,11 @@ unmarshal(T, V) ->
-ifdef(TEST).
-include_lib("eunit/include/eunit.hrl").
-spec test() -> _.
-spec pending_symmetry_test() -> _.
pending_symmetry_test() ->
Status = pending,
?assertEqual(Status, unmarshal(status, marshal(status, Status))).
@ -54,13 +49,14 @@ succeeded_symmetry_test() ->
-spec failed_symmetry_test() -> _.
failed_symmetry_test() ->
Status = {failed, #{
code => <<"test">>,
reason => <<"why not">>,
sub => #{
code => <<"sub">>
}
}},
Status =
{failed, #{
code => <<"test">>,
reason => <<"why not">>,
sub => #{
code => <<"sub">>
}
}},
?assertEqual(Status, unmarshal(status, marshal(status, Status))).
-endif.

View File

@ -9,30 +9,23 @@
%% API
-spec marshal(ff_codec:type_name(), ff_codec:decoded_value()) ->
ff_codec:encoded_value().
-spec marshal(ff_codec:type_name(), ff_codec:decoded_value()) -> ff_codec:encoded_value().
marshal(status, pending) ->
{pending, #dep_status_Pending{}};
marshal(status, succeeded) ->
{succeeded, #dep_status_Succeeded{}};
marshal(status, {failed, Failure}) ->
{failed, #dep_status_Failed{failure = marshal(failure, Failure)}};
marshal(T, V) ->
ff_codec:marshal(T, V).
-spec unmarshal(ff_codec:type_name(), ff_codec:encoded_value()) ->
ff_codec:decoded_value().
-spec unmarshal(ff_codec:type_name(), ff_codec:encoded_value()) -> ff_codec:decoded_value().
unmarshal(status, {pending, #dep_status_Pending{}}) ->
pending;
unmarshal(status, {succeeded, #dep_status_Succeeded{}}) ->
succeeded;
unmarshal(status, {failed, #dep_status_Failed{failure = Failure}}) ->
{failed, unmarshal(failure, Failure)};
unmarshal(T, V) ->
ff_codec:unmarshal(T, V).
@ -40,9 +33,11 @@ unmarshal(T, V) ->
-ifdef(TEST).
-include_lib("eunit/include/eunit.hrl").
-spec test() -> _.
-spec pending_symmetry_test() -> _.
pending_symmetry_test() ->
Status = pending,
?assertEqual(Status, unmarshal(status, marshal(status, Status))).
@ -54,13 +49,14 @@ succeeded_symmetry_test() ->
-spec failed_symmetry_test() -> _.
failed_symmetry_test() ->
Status = {failed, #{
code => <<"test">>,
reason => <<"why not">>,
sub => #{
code => <<"sub">>
}
}},
Status =
{failed, #{
code => <<"test">>,
reason => <<"why not">>,
sub => #{
code => <<"sub">>
}
}},
?assertEqual(Status, unmarshal(status, marshal(status, Status))).
-endif.

View File

@ -14,30 +14,28 @@
%% API
-spec unmarshal_destination_params(ff_proto_destination_thrift:'DestinationParams'()) ->
ff_destination:params().
-spec unmarshal_destination_params(ff_proto_destination_thrift:'DestinationParams'()) -> ff_destination:params().
unmarshal_destination_params(Params) ->
genlib_map:compact(#{
id => unmarshal(id, Params#dst_DestinationParams.id),
identity => unmarshal(id, Params#dst_DestinationParams.identity),
name => unmarshal(string, Params#dst_DestinationParams.name),
currency => unmarshal(string, Params#dst_DestinationParams.currency),
resource => unmarshal(resource, Params#dst_DestinationParams.resource),
id => unmarshal(id, Params#dst_DestinationParams.id),
identity => unmarshal(id, Params#dst_DestinationParams.identity),
name => unmarshal(string, Params#dst_DestinationParams.name),
currency => unmarshal(string, Params#dst_DestinationParams.currency),
resource => unmarshal(resource, Params#dst_DestinationParams.resource),
external_id => maybe_unmarshal(id, Params#dst_DestinationParams.external_id),
metadata => maybe_unmarshal(ctx, Params#dst_DestinationParams.metadata)
metadata => maybe_unmarshal(ctx, Params#dst_DestinationParams.metadata)
}).
-spec marshal_destination_state(ff_destination:destination_state(), ff_entity_context:context()) ->
ff_proto_destination_thrift:'DestinationState'().
marshal_destination_state(DestinationState, Context) ->
Blocking = case ff_destination:is_accessible(DestinationState) of
{ok, accessible} ->
unblocked;
_ ->
blocked
end,
Blocking =
case ff_destination:is_accessible(DestinationState) of
{ok, accessible} ->
unblocked;
_ ->
blocked
end,
#dst_DestinationState{
id = marshal(id, ff_destination:id(DestinationState)),
name = marshal(string, ff_destination:name(DestinationState)),
@ -51,9 +49,7 @@ marshal_destination_state(DestinationState, Context) ->
context = maybe_marshal(ctx, Context)
}.
-spec marshal_event(ff_destination_machine:event()) ->
ff_proto_destination_thrift:'Event'().
-spec marshal_event(ff_destination_machine:event()) -> ff_proto_destination_thrift:'Event'().
marshal_event({EventID, {ev, Timestamp, Change}}) ->
#dst_Event{
event_id = ff_codec:marshal(event_id, EventID),
@ -61,74 +57,64 @@ marshal_event({EventID, {ev, Timestamp, Change}}) ->
change = marshal(change, Change)
}.
-spec marshal(ff_codec:type_name(), ff_codec:decoded_value()) ->
ff_codec:encoded_value().
-spec marshal(ff_codec:type_name(), ff_codec:decoded_value()) -> ff_codec:encoded_value().
marshal(timestamped_change, {ev, Timestamp, Change}) ->
#dst_TimestampedChange{
change = marshal(change, Change),
occured_at = ff_codec:marshal(timestamp, Timestamp)
};
marshal(change, {created, Destination}) ->
{created, marshal(create_change, Destination)};
marshal(change, {account, AccountChange}) ->
{account, marshal(account_change, AccountChange)};
marshal(change, {status_changed, StatusChange}) ->
{status, marshal(status_change, StatusChange)};
marshal(create_change, Destination = #{
name := Name,
resource := Resource
}) ->
marshal(
create_change,
Destination = #{
name := Name,
resource := Resource
}
) ->
#dst_Destination{
name = Name,
resource = marshal(resource, Resource),
created_at = maybe_marshal(timestamp_ms, maps:get(created_at, Destination, undefined)),
external_id = maybe_marshal(id, maps:get(external_id, Destination, undefined)),
metadata = maybe_marshal(ctx, maps:get(metadata, Destination, undefined))
created_at = maybe_marshal(timestamp_ms, maps:get(created_at, Destination, undefined)),
external_id = maybe_marshal(id, maps:get(external_id, Destination, undefined)),
metadata = maybe_marshal(ctx, maps:get(metadata, Destination, undefined))
};
marshal(status, authorized) ->
{authorized, #dst_Authorized{}};
marshal(status, unauthorized) ->
{unauthorized, #dst_Unauthorized{}};
marshal(status_change, unauthorized) ->
{changed, {unauthorized, #dst_Unauthorized{}}};
marshal(status_change, authorized) ->
{changed, {authorized, #dst_Authorized{}}};
marshal(ctx, Ctx) ->
marshal(context, Ctx);
marshal(T, V) ->
ff_codec:marshal(T, V).
-spec unmarshal(ff_codec:type_name(), ff_codec:encoded_value()) ->
ff_codec:decoded_value().
-spec unmarshal(ff_codec:type_name(), ff_codec:encoded_value()) -> ff_codec:decoded_value().
unmarshal({list, T}, V) ->
[unmarshal(T, E) || E <- V];
unmarshal(repair_scenario, {add_events, #dst_AddEventsRepair{events = Events, action = Action}}) ->
{add_events, genlib_map:compact(#{
events => unmarshal({list, change}, Events),
action => maybe_unmarshal(complex_action, Action)
})};
{add_events,
genlib_map:compact(#{
events => unmarshal({list, change}, Events),
action => maybe_unmarshal(complex_action, Action)
})};
unmarshal(timestamped_change, TimestampedChange) ->
Timestamp = ff_codec:unmarshal(timestamp, TimestampedChange#dst_TimestampedChange.occured_at),
Change = unmarshal(change, TimestampedChange#dst_TimestampedChange.change),
{ev, Timestamp, Change};
unmarshal(change, {created, Destination}) ->
{created, unmarshal(destination, Destination)};
unmarshal(change, {account, AccountChange}) ->
{account, unmarshal(account_change, AccountChange)};
unmarshal(change, {status, StatusChange}) ->
{status_changed, unmarshal(status_change, StatusChange)};
unmarshal(destination, Dest) ->
genlib_map:compact(#{
version => 3,
@ -138,20 +124,16 @@ unmarshal(destination, Dest) ->
external_id => maybe_unmarshal(id, Dest#dst_Destination.external_id),
metadata => maybe_unmarshal(ctx, Dest#dst_Destination.metadata)
});
unmarshal(status, {authorized, #dst_Authorized{}}) ->
authorized;
unmarshal(status, {unauthorized, #dst_Unauthorized{}}) ->
unauthorized;
unmarshal(status_change, {changed, {unauthorized, #dst_Unauthorized{}}}) ->
unauthorized;
unmarshal(status_change, {changed, {authorized, #dst_Authorized{}}}) ->
authorized;
unmarshal(ctx, Ctx) ->
maybe_unmarshal(context, Ctx);
unmarshal(T, V) ->
ff_codec:unmarshal(T, V).
@ -171,27 +153,35 @@ maybe_unmarshal(Type, Value) ->
-ifdef(TEST).
-include_lib("eunit/include/eunit.hrl").
-spec test() -> _.
-spec destination_test() -> _.
destination_test() ->
Resource = {bank_card, #{bank_card => #{
token => <<"token auth">>
}}},
Resource =
{bank_card, #{
bank_card => #{
token => <<"token auth">>
}
}},
In = #{
version => 3,
name => <<"Wallet">>,
resource => Resource
version => 3,
name => <<"Wallet">>,
resource => Resource
},
?assertEqual(In, unmarshal(destination, marshal(create_change, In))).
-spec crypto_wallet_resource_test() -> _.
crypto_wallet_resource_test() ->
Resource = {crypto_wallet, #{crypto_wallet => #{
id => <<"9e6245a7a6e15f75769a4d87183b090a">>,
currency => {bitcoin, #{}}
}}},
Resource =
{crypto_wallet, #{
crypto_wallet => #{
id => <<"9e6245a7a6e15f75769a4d87183b090a">>,
currency => {bitcoin, #{}}
}
}},
?assertEqual(Resource, unmarshal(resource, marshal(resource, Resource))).
-endif.

View File

@ -9,32 +9,28 @@
-type event() :: ff_eventsink_publisher:event(ff_destination:event()).
-type sinkevent() :: ff_eventsink_publisher:sinkevent(ff_proto_destination_thrift:'SinkEvent'()).
-spec publish_events(list(event())) ->
list(sinkevent()).
-spec publish_events(list(event())) -> list(sinkevent()).
publish_events(Events) ->
[publish_event(Event) || Event <- Events].
-spec publish_event(event()) ->
sinkevent().
-spec publish_event(event()) -> sinkevent().
publish_event(#{
id := ID,
source_id := SourceID,
event := {
id := ID,
source_id := SourceID,
event := {
EventID,
Dt,
{ev, EventDt, Payload}
}
}) ->
#dst_SinkEvent{
id = marshal(event_id, ID),
created_at = marshal(timestamp, Dt),
source = marshal(id, SourceID),
payload = #dst_EventSinkPayload{
sequence = marshal(event_id, EventID),
id = marshal(event_id, ID),
created_at = marshal(timestamp, Dt),
source = marshal(id, SourceID),
payload = #dst_EventSinkPayload{
sequence = marshal(event_id, EventID),
occured_at = marshal(timestamp, EventDt),
changes = [marshal(change, Payload)]
changes = [marshal(change, Payload)]
}
}.

View File

@ -1,4 +1,5 @@
-module(ff_destination_handler).
-behaviour(ff_woody_wrapper).
-include_lib("fistful_proto/include/ff_proto_destination_thrift.hrl").
@ -9,10 +10,11 @@
%%
%% ff_woody_wrapper callbacks
%%
-spec handle_function(woody:func(), woody:args(), woody:options()) ->
{ok, woody:result()} | no_return().
-spec handle_function(woody:func(), woody:args(), woody:options()) -> {ok, woody:result()} | no_return().
handle_function(Func, Args, Opts) ->
scoper:scope(destination, #{},
scoper:scope(
destination,
#{},
fun() ->
handle_function_(Func, Args, Opts)
end
@ -23,9 +25,11 @@ handle_function(Func, Args, Opts) ->
%%
handle_function_('Create', [Params, Ctx], Opts) ->
ID = Params#dst_DestinationParams.id,
case ff_destination_machine:create(
ff_destination_codec:unmarshal_destination_params(Params),
ff_destination_codec:unmarshal(ctx, Ctx))
case
ff_destination_machine:create(
ff_destination_codec:unmarshal_destination_params(Params),
ff_destination_codec:unmarshal(ctx, Ctx)
)
of
ok ->
handle_function_('Get', [ID, #'EventRange'{}], Opts);

View File

@ -21,72 +21,67 @@
-type value_type() :: machinery_mg_schema:vt().
-type context() :: machinery_mg_schema:context().
-type event() :: ff_machine:timestamped_event(ff_destination:event()).
-type event() :: ff_machine:timestamped_event(ff_destination:event()).
-type aux_state() :: ff_machine:auxst().
-type call_args() :: term().
-type call_response() :: term().
-type data() ::
aux_state() |
event() |
call_args() |
call_response().
aux_state()
| event()
| call_args()
| call_response().
%% machinery_mg_schema callbacks
-spec get_version(value_type()) ->
machinery_mg_schema:version().
-spec get_version(value_type()) -> machinery_mg_schema:version().
get_version(event) ->
?CURRENT_EVENT_FORMAT_VERSION;
get_version(aux_state) ->
undefined.
-spec marshal(type(), value(data()), context()) ->
{machinery_msgpack:t(), context()}.
-spec marshal(type(), value(data()), context()) -> {machinery_msgpack:t(), context()}.
marshal({event, Format}, TimestampedChange, Context) ->
marshal_event(Format, TimestampedChange, Context);
marshal(T, V, C) when
T =:= {args, init} orelse
T =:= {args, call} orelse
T =:= {args, repair} orelse
T =:= {aux_state, undefined} orelse
T =:= {response, call} orelse
T =:= {response, {repair, success}} orelse
T =:= {response, {repair, failure}}
T =:= {args, call} orelse
T =:= {args, repair} orelse
T =:= {aux_state, undefined} orelse
T =:= {response, call} orelse
T =:= {response, {repair, success}} orelse
T =:= {response, {repair, failure}}
->
machinery_mg_schema_generic:marshal(T, V, C).
-spec unmarshal(type(), machinery_msgpack:t(), context()) ->
{data(), context()}.
-spec unmarshal(type(), machinery_msgpack:t(), context()) -> {data(), context()}.
unmarshal({event, FormatVersion}, EncodedChange, Context) ->
unmarshal_event(FormatVersion, EncodedChange, Context);
unmarshal(T, V, C) when
T =:= {args, init} orelse
T =:= {args, call} orelse
T =:= {args, repair} orelse
T =:= {aux_state, undefined} orelse
T =:= {response, call} orelse
T =:= {response, {repair, success}} orelse
T =:= {response, {repair, failure}}
T =:= {args, call} orelse
T =:= {args, repair} orelse
T =:= {aux_state, undefined} orelse
T =:= {response, call} orelse
T =:= {response, {repair, success}} orelse
T =:= {response, {repair, failure}}
->
machinery_mg_schema_generic:unmarshal(T, V, C).
%% Internals
-spec marshal_event(machinery_mg_schema:version(), event(), context()) ->
{machinery_msgpack:t(), context()}.
-spec marshal_event(machinery_mg_schema:version(), event(), context()) -> {machinery_msgpack:t(), context()}.
%%@TODO remove post migration
%%======
marshal_event(undefined = Version, TimestampedChange, Context) ->
machinery_mg_schema_generic:marshal({event, Version}, TimestampedChange, Context);
machinery_mg_schema_generic:marshal({event, Version}, TimestampedChange, Context);
%%======
marshal_event(1, TimestampedChange, Context) ->
ThriftChange = ff_destination_codec:marshal(timestamped_change, TimestampedChange),
Type = {struct, struct, {ff_proto_destination_thrift, 'TimestampedChange'}},
{{bin, ff_proto_utils:serialize(Type, ThriftChange)}, Context}.
-spec unmarshal_event(machinery_mg_schema:version(), machinery_msgpack:t(), context()) ->
{event(), context()}.
-spec unmarshal_event(machinery_mg_schema:version(), machinery_msgpack:t(), context()) -> {event(), context()}.
unmarshal_event(1, EncodedChange, Context) ->
{bin, EncodedThriftChange} = EncodedChange,
Type = {struct, struct, {ff_proto_destination_thrift, 'TimestampedChange'}},
@ -96,84 +91,90 @@ unmarshal_event(undefined = Version, EncodedChange, Context0) ->
{Event, Context1} = machinery_mg_schema_generic:unmarshal({event, Version}, EncodedChange, Context0),
{maybe_migrate(Event), Context1}.
-spec maybe_migrate(any()) ->
event().
-spec maybe_migrate(any()) -> event().
maybe_migrate({ev, Timestamp, Change}) ->
{ev, Timestamp, ff_destination:maybe_migrate(Change, #{timestamp => Timestamp})}.
-ifdef(TEST).
-include_lib("eunit/include/eunit.hrl").
-spec test() -> _.
-spec marshal(type(), value(data())) ->
machinery_msgpack:t().
-spec marshal(type(), value(data())) -> machinery_msgpack:t().
marshal(Type, Value) ->
{Result, _Context} = marshal(Type, Value, #{}),
Result.
-spec unmarshal(type(), machinery_msgpack:t()) ->
data().
-spec unmarshal(type(), machinery_msgpack:t()) -> data().
unmarshal(Type, Value) ->
{Result, _Context} = unmarshal(Type, Value, #{}),
Result.
-spec created_v0_0_decoding_test() -> _.
created_v0_0_decoding_test() ->
Resource = {crypto_wallet, #{crypto_wallet => #{
id => <<"kek">>,
currency => {bitcoin, #{}}
}}},
Resource =
{crypto_wallet, #{
crypto_wallet => #{
id => <<"kek">>,
currency => {bitcoin, #{}}
}
}},
Destination = #{
version => 3,
resource => Resource,
name => <<"name">>,
created_at => 1590434350293,
version => 3,
resource => Resource,
name => <<"name">>,
created_at => 1590434350293,
external_id => <<"external_id">>
},
Change = {created, Destination},
Event = {ev, {{{2020, 5, 25}, {19, 19, 10}}, 293305}, Change},
LegacyResource = {arr, [
{str, <<"tup">>},
{str, <<"crypto_wallet">>},
{arr, [
{str, <<"map">>},
{obj, #{
{str, <<"currency">>} => {arr, [
{str, <<"tup">>},
{str, <<"bitcoin">>},
{arr, [{str, <<"map">>}, {obj, #{}}]}
]},
{str, <<"id">>} => {bin, <<"kek">>}
}}
]}
]},
LegacyChange = {arr, [
{str, <<"tup">>},
{str, <<"created">>},
{arr, [
{str, <<"map">>},
{obj, #{
{str, <<"resource">>} => LegacyResource,
{str, <<"name">>} => {bin, <<"name">>},
{str, <<"external_id">>} => {bin, <<"external_id">>}
}}
]}
]},
LegacyEvent = {arr, [
{str, <<"tup">>},
{str, <<"ev">>},
LegacyResource =
{arr, [
{str, <<"tup">>},
{str, <<"crypto_wallet">>},
{arr, [
{str, <<"map">>},
{obj, #{
{str, <<"currency">>} =>
{arr, [
{str, <<"tup">>},
{str, <<"bitcoin">>},
{arr, [{str, <<"map">>}, {obj, #{}}]}
]},
{str, <<"id">>} => {bin, <<"kek">>}
}}
]}
]},
LegacyChange =
{arr, [
{str, <<"tup">>},
{str, <<"created">>},
{arr, [
{str, <<"map">>},
{obj, #{
{str, <<"resource">>} => LegacyResource,
{str, <<"name">>} => {bin, <<"name">>},
{str, <<"external_id">>} => {bin, <<"external_id">>}
}}
]}
]},
LegacyEvent =
{arr, [
{str, <<"tup">>},
{str, <<"ev">>},
{arr, [
{str, <<"tup">>},
{arr, [{str, <<"tup">>}, {i, 2020}, {i, 5}, {i, 25}]},
{arr, [{str, <<"tup">>}, {i, 19}, {i, 19}, {i, 10}]}
{arr, [
{str, <<"tup">>},
{arr, [{str, <<"tup">>}, {i, 2020}, {i, 5}, {i, 25}]},
{arr, [{str, <<"tup">>}, {i, 19}, {i, 19}, {i, 10}]}
]},
{i, 293305}
]},
{i, 293305}
LegacyChange
]},
LegacyChange
]},
DecodedLegacy = unmarshal({event, undefined}, LegacyEvent),
ModernizedBinary = marshal({event, ?CURRENT_EVENT_FORMAT_VERSION}, DecodedLegacy),
@ -182,69 +183,76 @@ created_v0_0_decoding_test() ->
-spec created_v0_1_decoding_test() -> _.
created_v0_1_decoding_test() ->
Resource = {bank_card, #{bank_card => #{
token => <<"token">>,
bin_data_id => {binary, <<"ebin">>}
}}},
Resource =
{bank_card, #{
bank_card => #{
token => <<"token">>,
bin_data_id => {binary, <<"ebin">>}
}
}},
Destination = #{
version => 3,
resource => Resource,
name => <<"name">>,
created_at => 1590434350293,
version => 3,
resource => Resource,
name => <<"name">>,
created_at => 1590434350293,
external_id => <<"external_id">>
},
Change = {created, Destination},
Event = {ev, {{{2020, 5, 25}, {19, 19, 10}}, 293305}, Change},
LegacyResource = {arr, [
{str, <<"tup">>},
{str, <<"bank_card">>},
{arr, [
{str, <<"map">>},
{obj, #{
{str, <<"bank_card">>} =>
{arr, [
{str, <<"map">>},
{obj, #{
{str, <<"bin_data_id">>} => {arr, [
{str, <<"tup">>},
{str, <<"binary">>},
{bin, <<"ebin">>}
]},
{str, <<"token">>} => {bin, <<"token">>}
}}
]}
}}
]}
]},
LegacyChange = {arr, [
{str, <<"tup">>},
{str, <<"created">>},
{arr, [
{str, <<"map">>},
{obj, #{
{str, <<"version">>} => {i, 1},
{str, <<"resource">>} => LegacyResource,
{str, <<"name">>} => {bin, <<"name">>},
{str, <<"created_at">>} => {i, 1590434350293},
{str, <<"external_id">>} => {bin, <<"external_id">>}
}}
]}
]},
LegacyEvent = {arr, [
{str, <<"tup">>},
{str, <<"ev">>},
LegacyResource =
{arr, [
{str, <<"tup">>},
{str, <<"bank_card">>},
{arr, [
{str, <<"map">>},
{obj, #{
{str, <<"bank_card">>} =>
{arr, [
{str, <<"map">>},
{obj, #{
{str, <<"bin_data_id">>} =>
{arr, [
{str, <<"tup">>},
{str, <<"binary">>},
{bin, <<"ebin">>}
]},
{str, <<"token">>} => {bin, <<"token">>}
}}
]}
}}
]}
]},
LegacyChange =
{arr, [
{str, <<"tup">>},
{str, <<"created">>},
{arr, [
{str, <<"map">>},
{obj, #{
{str, <<"version">>} => {i, 1},
{str, <<"resource">>} => LegacyResource,
{str, <<"name">>} => {bin, <<"name">>},
{str, <<"created_at">>} => {i, 1590434350293},
{str, <<"external_id">>} => {bin, <<"external_id">>}
}}
]}
]},
LegacyEvent =
{arr, [
{str, <<"tup">>},
{str, <<"ev">>},
{arr, [
{str, <<"tup">>},
{arr, [{str, <<"tup">>}, {i, 2020}, {i, 5}, {i, 25}]},
{arr, [{str, <<"tup">>}, {i, 19}, {i, 19}, {i, 10}]}
{arr, [
{str, <<"tup">>},
{arr, [{str, <<"tup">>}, {i, 2020}, {i, 5}, {i, 25}]},
{arr, [{str, <<"tup">>}, {i, 19}, {i, 19}, {i, 10}]}
]},
{i, 293305}
]},
{i, 293305}
LegacyChange
]},
LegacyChange
]},
DecodedLegacy = unmarshal({event, undefined}, LegacyEvent),
ModernizedBinary = marshal({event, ?CURRENT_EVENT_FORMAT_VERSION}, DecodedLegacy),
@ -253,47 +261,51 @@ created_v0_1_decoding_test() ->
-spec account_v0_decoding_test() -> _.
account_v0_decoding_test() ->
Change = {account, {created, #{
id => <<"1">>,
identity => <<"Solo">>,
currency => <<"USD">>,
accounter_account_id => 322
}}},
Change =
{account,
{created, #{
id => <<"1">>,
identity => <<"Solo">>,
currency => <<"USD">>,
accounter_account_id => 322
}}},
Event = {ev, {{{2020, 5, 25}, {19, 19, 10}}, 293305}, Change},
LegacyChange = {arr, [
{str, <<"tup">>},
{str, <<"account">>},
{arr, [
{str, <<"tup">>},
{str, <<"created">>},
{arr, [
{str, <<"map">>},
{obj, #{
{str, <<"id">>} => {bin, <<"1">>},
{str, <<"identity">>} => {bin, <<"Solo">>},
{str, <<"currency">>} => {bin, <<"USD">>},
{str, <<"accounter_account_id">>} => {i, 322}
}}
]}
]}
]},
LegacyEvent = {arr, [
{str, <<"tup">>},
{str, <<"ev">>},
LegacyChange =
{arr, [
{str, <<"tup">>},
{str, <<"account">>},
{arr, [
{str, <<"tup">>},
{arr, [{str, <<"tup">>}, {i, 2020}, {i, 5}, {i, 25}]},
{arr, [{str, <<"tup">>}, {i, 19}, {i, 19}, {i, 10}]}
]},
{i, 293305}
{str, <<"created">>},
{arr, [
{str, <<"map">>},
{obj, #{
{str, <<"id">>} => {bin, <<"1">>},
{str, <<"identity">>} => {bin, <<"Solo">>},
{str, <<"currency">>} => {bin, <<"USD">>},
{str, <<"accounter_account_id">>} => {i, 322}
}}
]}
]}
]},
LegacyEvent =
{arr, [
{str, <<"tup">>},
{str, <<"ev">>},
{arr, [
{str, <<"tup">>},
{arr, [
{str, <<"tup">>},
{arr, [{str, <<"tup">>}, {i, 2020}, {i, 5}, {i, 25}]},
{arr, [{str, <<"tup">>}, {i, 19}, {i, 19}, {i, 10}]}
]},
{i, 293305}
]},
LegacyChange
]},
LegacyChange
]},
DecodedLegacy = unmarshal({event, undefined}, LegacyEvent),
ModernizedBinary = marshal({event, ?CURRENT_EVENT_FORMAT_VERSION}, DecodedLegacy),
@ -308,24 +320,25 @@ status_v0_decoding_test() ->
{status_changed, unauthorized}
},
LegacyEvent = {arr, [
{str, <<"tup">>},
{str, <<"ev">>},
LegacyEvent =
{arr, [
{str, <<"tup">>},
{str, <<"ev">>},
{arr, [
{str, <<"tup">>},
{arr, [{str, <<"tup">>}, {i, 2020}, {i, 5}, {i, 25}]},
{arr, [{str, <<"tup">>}, {i, 19}, {i, 19}, {i, 10}]}
{arr, [
{str, <<"tup">>},
{arr, [{str, <<"tup">>}, {i, 2020}, {i, 5}, {i, 25}]},
{arr, [{str, <<"tup">>}, {i, 19}, {i, 19}, {i, 10}]}
]},
{i, 293305}
]},
{i, 293305}
{arr, [
{str, <<"tup">>},
{str, <<"status_changed">>},
{str, <<"unauthorized">>}
]}
]},
{arr, [
{str, <<"tup">>},
{str, <<"status_changed">>},
{str, <<"unauthorized">>}
]}
]},
DecodedLegacy = unmarshal({event, undefined}, LegacyEvent),
ModernizedBinary = marshal({event, ?CURRENT_EVENT_FORMAT_VERSION}, DecodedLegacy),
@ -334,25 +347,30 @@ status_v0_decoding_test() ->
-spec created_v1_3_decoding_test() -> _.
created_v1_3_decoding_test() ->
Resource = {crypto_wallet, #{crypto_wallet => #{
id => <<"kek">>,
currency => {bitcoin, #{}}
}}},
Resource =
{crypto_wallet, #{
crypto_wallet => #{
id => <<"kek">>,
currency => {bitcoin, #{}}
}
}},
Destination = #{
version => 3,
resource => Resource,
name => <<"name">>,
created_at => 1590434350293,
version => 3,
resource => Resource,
name => <<"name">>,
created_at => 1590434350293,
external_id => <<"external_id">>
},
Change = {created, Destination},
Event = {ev, {{{2020, 5, 25}, {19, 19, 10}}, 293305}, Change},
LegacyEvent = {bin, base64:decode(<<
"CwABAAAAGzIwMjAtMDUtMjVUMTk6MTk6MTAuMjkzMzA1WgwAAgwAAQsAAQAAAARuYW1lDAA"
"CDAACDAABCwABAAAAA2tlawwAAwwAAQAACAACAAAAAAAAAAsAAwAAAAtleHRlcm5hbF9pZA"
"sABwAAABgyMDIwLTA1LTI1VDE5OjE5OjEwLjI5M1oAAAA="
>>)},
LegacyEvent =
{bin,
base64:decode(<<
"CwABAAAAGzIwMjAtMDUtMjVUMTk6MTk6MTAuMjkzMzA1WgwAAgwAAQsAAQAAAARuYW1lDAA"
"CDAACDAABCwABAAAAA2tlawwAAwwAAQAACAACAAAAAAAAAAsAAwAAAAtleHRlcm5hbF9pZA"
"sABwAAABgyMDIwLTA1LTI1VDE5OjE5OjEwLjI5M1oAAAA="
>>)},
DecodedLegacy = unmarshal({event, 1}, LegacyEvent),
ModernizedBinary = marshal({event, ?CURRENT_EVENT_FORMAT_VERSION}, DecodedLegacy),
@ -361,19 +379,23 @@ created_v1_3_decoding_test() ->
-spec account_v1_decoding_test() -> _.
account_v1_decoding_test() ->
Change = {account, {created, #{
id => <<"1">>,
identity => <<"Solo">>,
currency => <<"USD">>,
accounter_account_id => 322
}}},
Change =
{account,
{created, #{
id => <<"1">>,
identity => <<"Solo">>,
currency => <<"USD">>,
accounter_account_id => 322
}}},
Event = {ev, {{{2020, 5, 25}, {19, 19, 10}}, 293305}, Change},
LegacyEvent = {bin, base64:decode(<<
"CwABAAAAGzIwMjAtMDUtMjVUMTk6MTk6MTAuMjkzMzA1WgwAAgwAAgwAAQsAAwAAAAExCw"
"ABAAAABFNvbG8MAAILAAEAAAADVVNEAAoABAAAAAAAAAFCAAAAAA=="
>>)},
LegacyEvent =
{bin,
base64:decode(<<
"CwABAAAAGzIwMjAtMDUtMjVUMTk6MTk6MTAuMjkzMzA1WgwAAgwAAgwAAQsAAwAAAAExCw"
"ABAAAABFNvbG8MAAILAAEAAAADVVNEAAoABAAAAAAAAAFCAAAAAA=="
>>)},
DecodedLegacy = unmarshal({event, 1}, LegacyEvent),
ModernizedBinary = marshal({event, ?CURRENT_EVENT_FORMAT_VERSION}, DecodedLegacy),
@ -388,9 +410,11 @@ status_v1_decoding_test() ->
{status_changed, unauthorized}
},
LegacyEvent = {bin, base64:decode(<<
"CwABAAAAGzIwMjAtMDUtMjVUMTk6MTk6MTAuMjkzMzA1WgwAAgwAAwwAAQwAAgAAAAAA"
>>)},
LegacyEvent =
{bin,
base64:decode(<<
"CwABAAAAGzIwMjAtMDUtMjVUMTk6MTk6MTAuMjkzMzA1WgwAAgwAAwwAAQwAAgAAAAAA"
>>)},
DecodedLegacy = unmarshal({event, 1}, LegacyEvent),
ModernizedBinary = marshal({event, ?CURRENT_EVENT_FORMAT_VERSION}, DecodedLegacy),

View File

@ -2,37 +2,49 @@
-include_lib("fistful_proto/include/ff_proto_msgpack_thrift.hrl").
-type ctx()::ff_entity_context:context().
-type ctx() :: ff_entity_context:context().
-export([marshal/1]).
-export([unmarshal/1]).
%% snatch from https://github.com/rbkmoney/erlang_capi/blob/v2/apps/capi/src/capi_msgpack.erl
-spec unmarshal(map()) ->
ctx().
-spec unmarshal(map()) -> ctx().
unmarshal(Ctx) when is_map(Ctx) ->
maps:map(fun(_NS, V) -> unwrap_(V) end, Ctx).
unwrap_({nl, #msgp_Nil{}}) -> nil;
unwrap_({b, V}) when is_boolean(V) -> V;
unwrap_({i, V}) when is_integer(V) -> V;
unwrap_({flt, V}) when is_float(V) -> V;
unwrap_({str, V}) when is_binary(V) -> V; % Assuming well-formed UTF-8 bytestring.
unwrap_({bin, V}) when is_binary(V) -> {binary, V};
unwrap_({arr, V}) when is_list(V) -> [unwrap_(ListItem) || ListItem <- V];
unwrap_({obj, V}) when is_map(V) ->
unwrap_({nl, #msgp_Nil{}}) ->
nil;
unwrap_({b, V}) when is_boolean(V) ->
V;
unwrap_({i, V}) when is_integer(V) ->
V;
unwrap_({flt, V}) when is_float(V) ->
V;
% Assuming well-formed UTF-8 bytestring.
unwrap_({str, V}) when is_binary(V) ->
V;
unwrap_({bin, V}) when is_binary(V) ->
{binary, V};
unwrap_({arr, V}) when is_list(V) ->
[unwrap_(ListItem) || ListItem <- V];
unwrap_({obj, V}) when is_map(V) ->
maps:fold(fun(Key, Value, Map) -> Map#{unwrap_(Key) => unwrap_(Value)} end, #{}, V).
-spec marshal(map()) -> ctx().
marshal(Value) when is_map(Value) ->
maps:map(fun(_K, V) -> wrap_(V) end, Value).
wrap_(nil) -> {nl, #msgp_Nil{}};
wrap_(V) when is_boolean(V) -> {b, V};
wrap_(V) when is_integer(V) -> {i, V};
wrap_(V) when is_float(V) -> V;
wrap_(V) when is_binary(V) -> {str, V}; % Assuming well-formed UTF-8 bytestring.
wrap_(nil) ->
{nl, #msgp_Nil{}};
wrap_(V) when is_boolean(V) ->
{b, V};
wrap_(V) when is_integer(V) ->
{i, V};
wrap_(V) when is_float(V) ->
V;
% Assuming well-formed UTF-8 bytestring.
wrap_(V) when is_binary(V) ->
{str, V};
wrap_({binary, V}) when is_binary(V) ->
{bin, V};
wrap_(V) when is_list(V) ->
@ -48,9 +60,11 @@ wrap_(V) when is_map(V) ->
-spec test() -> _.
-spec unwrap_test() -> _.
unwrap_test() ->
K = {str, <<"Key">>},
K2 = {i, 1}, V = {str, <<"Value">>},
K2 = {i, 1},
V = {str, <<"Value">>},
Obj = {obj, #{K => V}},
Obj2 = {obj, #{K2 => Obj}},
MsgPack = {arr, [Obj2]},
@ -65,10 +79,10 @@ wrap_test() ->
Obj = #{123 => Str},
Arr = [Obj],
MsgPack = marshal(#{<<"NS">> => Arr}),
?assertEqual(#{<<"NS">> => {arr, [{obj, #{ {i, 123} => {str, Str} }}]}}, MsgPack).
?assertEqual(#{<<"NS">> => {arr, [{obj, #{{i, 123} => {str, Str}}}]}}, MsgPack).
-spec wrap_empty_obj_test() -> _.
wrap_empty_obj_test() ->
?assertEqual({obj, #{}}, wrap_(#{})).
-endif.
-endif.

View File

@ -7,20 +7,21 @@
-include_lib("fistful_proto/include/ff_proto_eventsink_thrift.hrl").
-type options() :: #{
schema := module(),
client := woody_client:options(),
ns := binary(),
publisher := module()
schema := module(),
client := woody_client:options(),
ns := binary(),
publisher := module()
}.
%%
%% ff_woody_wrapper callbacks
%%
-spec handle_function(woody:func(), woody:args(), options()) ->
{ok, woody:result()} | no_return().
-spec handle_function(woody:func(), woody:args(), options()) -> {ok, woody:result()} | no_return().
handle_function(Func, Args, Opts) ->
scoper:scope(eventsink_handler, #{},
scoper:scope(
eventsink_handler,
#{},
fun() ->
handle_function_(Func, Args, Opts)
end
@ -36,8 +37,12 @@ handle_function_('GetEvents', [#'evsink_EventRange'{'after' = After0, limit = Li
} = Options,
After = erlang:max(After0, StartEvent),
WoodyContext = ff_context:get_woody_context(ff_context:load()),
{ok, Events} = machinery_mg_eventsink:get_events(NS, After, Limit,
#{client => {Client, WoodyContext}, schema => Schema}),
{ok, Events} = machinery_mg_eventsink:get_events(
NS,
After,
Limit,
#{client => {Client, WoodyContext}, schema => Schema}
),
ff_eventsink_publisher:publish_events(Events, #{publisher => Publisher});
handle_function_('GetLastEventID', _Params, #{schema := Schema, client := Client, ns := NS}) ->
WoodyContext = ff_context:get_woody_context(ff_context:load()),

View File

@ -19,16 +19,13 @@
-export_type([sinkevent/1]).
-export_type([options/0]).
-callback publish_events(list(event(_))) ->
list(sinkevent(_)).
-callback publish_events(list(event(_))) -> list(sinkevent(_)).
%% API
-export([publish_events/2]).
-spec publish_events(list(event(_)), options()) ->
{ok, list(sinkevent(_))}.
-spec publish_events(list(event(_)), options()) -> {ok, list(sinkevent(_))}.
publish_events(Events, Opts) ->
{ok, handler_publish_events(Events, Opts)}.

View File

@ -15,72 +15,67 @@
-export([unmarshal/2]).
%% This special functions hasn't got opposite functions.
-spec unmarshal_identity_params(ff_proto_identity_thrift:'IdentityParams'()) ->
ff_identity_machine:params().
-spec unmarshal_identity_params(ff_proto_identity_thrift:'IdentityParams'()) -> ff_identity_machine:params().
unmarshal_identity_params(#idnt_IdentityParams{
id = ID,
name = Name,
party = PartyID,
provider = ProviderID,
cls = ClassID,
id = ID,
name = Name,
party = PartyID,
provider = ProviderID,
cls = ClassID,
external_id = ExternalID,
metadata = Metadata
metadata = Metadata
}) ->
genlib_map:compact(#{
id => unmarshal(id, ID),
name => unmarshal(string, Name),
party => unmarshal(id, PartyID),
provider => unmarshal(id, ProviderID),
class => unmarshal(id, ClassID),
id => unmarshal(id, ID),
name => unmarshal(string, Name),
party => unmarshal(id, PartyID),
provider => unmarshal(id, ProviderID),
class => unmarshal(id, ClassID),
external_id => maybe_unmarshal(id, ExternalID),
metadata => maybe_unmarshal(ctx, Metadata)
metadata => maybe_unmarshal(ctx, Metadata)
}).
-spec unmarshal_challenge_params(ff_proto_identity_thrift:'ChallengeParams'()) ->
ff_identity_machine:challenge_params().
unmarshal_challenge_params(#idnt_ChallengeParams{
id = ID,
cls = ClassID,
id = ID,
cls = ClassID,
proofs = Proofs
}) ->
genlib_map:compact(#{
id => unmarshal(id, ID),
class => unmarshal(id, ClassID),
id => unmarshal(id, ID),
class => unmarshal(id, ClassID),
proofs => unmarshal({list, challenge_proofs}, Proofs)
}).
-spec marshal_identity_event({integer(), ff_machine:timestamped_event(ff_identity:event())}) ->
ff_proto_identity_thrift:'Event'().
marshal_identity_event({ID, {ev, Timestamp, Ev}}) ->
#idnt_Event{
sequence = marshal(event_id, ID),
sequence = marshal(event_id, ID),
occured_at = marshal(timestamp, Timestamp),
change = marshal(change, Ev)
change = marshal(change, Ev)
}.
-spec marshal_challenge_state(ff_identity_challenge:challenge_state()) -> ff_proto_identity_thrift:'ChallengeState'().
marshal_challenge_state(ChallengeState) ->
Proofs = ff_identity_challenge:proofs(ChallengeState),
Status = ff_identity_challenge:status(ChallengeState),
#idnt_ChallengeState{
id = ff_identity_challenge:id(ChallengeState),
cls = ff_identity_challenge:class(ChallengeState),
id = ff_identity_challenge:id(ChallengeState),
cls = ff_identity_challenge:class(ChallengeState),
proofs = marshal({list, challenge_proofs}, Proofs),
status = marshal(challenge_payload_status_changed, Status)
}.
-spec marshal_identity_state(ff_identity:identity_state(), ff_entity_context:context()) ->
ff_proto_identity_thrift:'IdentityState'().
marshal_identity_state(IdentityState, Context) ->
EffectiveChallengeID = case ff_identity:effective_challenge(IdentityState) of
{ok, ID} -> maybe_marshal(id, ID);
{error, notfound} -> undefined
end,
EffectiveChallengeID =
case ff_identity:effective_challenge(IdentityState) of
{ok, ID} -> maybe_marshal(id, ID);
{error, notfound} -> undefined
end,
#idnt_IdentityState{
id = maybe_marshal(id, ff_identity:id(IdentityState)),
name = marshal(string, ff_identity:name(IdentityState)),
@ -98,28 +93,25 @@ marshal_identity_state(IdentityState, Context) ->
}.
-spec marshal(ff_codec:type_name(), ff_codec:decoded_value()) -> ff_codec:encoded_value().
marshal({list, T}, V) ->
[marshal(T, E) || E <- V];
marshal(timestamped_change, {ev, Timestamp, Change}) ->
#idnt_TimestampedChange{
#idnt_TimestampedChange{
change = marshal(change, Change),
occured_at = ff_codec:marshal(timestamp, Timestamp)
};
marshal(change, {created, Identity}) ->
{created, marshal(identity, Identity)};
marshal(change, {level_changed, LevelID}) ->
{level_changed, marshal(id, LevelID)};
marshal(change, {{challenge, ChallengeID}, ChallengeChange}) ->
{identity_challenge, marshal(challenge_change, #{
id => ChallengeID,
payload => ChallengeChange
})};
{identity_challenge,
marshal(challenge_change, #{
id => ChallengeID,
payload => ChallengeChange
})};
marshal(change, {effective_challenge_changed, ChallengeID}) ->
{effective_challenge_changed, marshal(id, ChallengeID)};
marshal(identity, Identity) ->
#idnt_Identity{
id = maybe_marshal(id, ff_identity:id(Identity)),
@ -132,22 +124,24 @@ marshal(identity, Identity) ->
external_id = maybe_marshal(id, ff_identity:external_id(Identity)),
metadata = maybe_marshal(ctx, ff_identity:metadata(Identity))
};
marshal(challenge_change, #{
id := ID,
payload := Payload
id := ID,
payload := Payload
}) ->
#idnt_ChallengeChange{
id = marshal(id, ID),
id = marshal(id, ID),
payload = marshal(challenge_payload, Payload)
};
marshal(challenge_payload, {created, Challenge}) ->
{created, marshal(challenge_payload_created, Challenge)};
marshal(challenge_payload, {status_changed, ChallengeStatus}) ->
{status_changed, marshal(challenge_payload_status_changed, ChallengeStatus)};
marshal(challenge_payload_created, Challenge = #{
id := ID
}) ->
marshal(
challenge_payload_created,
Challenge = #{
id := ID
}
) ->
Proofs = maps:get(proofs, Challenge, []),
#idnt_Challenge{
id = marshal(id, ID),
@ -155,24 +149,26 @@ marshal(challenge_payload_created, Challenge = #{
provider_id = marshal(id, maps:get(provider, Challenge)),
class_id = marshal(id, maps:get(identity_class, Challenge)),
proofs = marshal({list, challenge_proofs}, Proofs),
claim_id = marshal(id, maps:get(claim_id, Challenge)),
claimant = marshal(id, maps:get(claimant, Challenge)),
master_id = marshal(id, maps:get(master_id, Challenge))
claim_id = marshal(id, maps:get(claim_id, Challenge)),
claimant = marshal(id, maps:get(claimant, Challenge)),
master_id = marshal(id, maps:get(master_id, Challenge))
};
marshal(challenge_proofs, {Type, Token}) ->
#idnt_ChallengeProof{
type = Type,
token = Token
};
marshal(challenge_payload_status_changed, pending) ->
{pending, #idnt_ChallengePending{}};
marshal(challenge_payload_status_changed, cancelled) ->
{cancelled, #idnt_ChallengeCancelled{}};
marshal(challenge_payload_status_changed, {completed, Status = #{
resolution := Resolution
}}) ->
marshal(
challenge_payload_status_changed,
{completed,
Status = #{
resolution := Resolution
}}
) ->
ValidUntil = maps:get(valid_until, Status, undefined),
NewStatus = #idnt_ChallengeCompleted{
resolution = marshal(resolution, Resolution),
@ -181,39 +177,30 @@ marshal(challenge_payload_status_changed, {completed, Status = #{
{completed, NewStatus};
marshal(challenge_payload_status_changed, {failed, _Status}) ->
{failed, #idnt_ChallengeFailed{}};
marshal(resolution, approved) ->
approved;
marshal(resolution, denied) ->
denied;
marshal(ctx, Ctx) ->
maybe_marshal(context, Ctx);
marshal(created_at, TimeMS) ->
marshal(string, ff_time:to_rfc3339(TimeMS));
marshal(T, V) ->
ff_codec:marshal(T, V).
-spec unmarshal(ff_codec:type_name(), ff_codec:encoded_value()) ->
ff_codec:decoded_value().
-spec unmarshal(ff_codec:type_name(), ff_codec:encoded_value()) -> ff_codec:decoded_value().
unmarshal({list, T}, V) ->
[unmarshal(T, E) || E <- V];
unmarshal(timestamped_change, TimestampedChange) ->
Timestamp = ff_codec:unmarshal(timestamp, TimestampedChange#idnt_TimestampedChange.occured_at),
Change = unmarshal(change, TimestampedChange#idnt_TimestampedChange.change),
{ev, Timestamp, Change};
unmarshal(repair_scenario, {add_events, #idnt_AddEventsRepair{events = Events, action = Action}}) ->
{add_events, genlib_map:compact(#{
events => unmarshal({list, change}, Events),
action => maybe_unmarshal(complex_action, Action)
})};
{add_events,
genlib_map:compact(#{
events => unmarshal({list, change}, Events),
action => maybe_unmarshal(complex_action, Action)
})};
unmarshal(change, {created, Identity}) ->
{created, unmarshal(identity, Identity)};
unmarshal(change, {level_changed, LevelID}) ->
@ -222,31 +209,29 @@ unmarshal(change, {identity_challenge, #idnt_ChallengeChange{id = ID, payload =
{{challenge, unmarshal(id, ID)}, unmarshal(challenge_payload, Payload)};
unmarshal(change, {effective_challenge_changed, ChallengeID}) ->
{effective_challenge_changed, unmarshal(id, ChallengeID)};
unmarshal(identity, #idnt_Identity{
id = ID,
name = Name,
party = PartyID,
provider = ProviderID,
cls = ClassID,
contract = ContractID,
id = ID,
name = Name,
party = PartyID,
provider = ProviderID,
cls = ClassID,
contract = ContractID,
external_id = ExternalID,
created_at = CreatedAt,
metadata = Metadata
created_at = CreatedAt,
metadata = Metadata
}) ->
genlib_map:compact(#{
id => unmarshal(id, ID),
name => unmarshal(string, Name),
party => unmarshal(id, PartyID),
provider => unmarshal(id, ProviderID),
class => unmarshal(id, ClassID),
contract => unmarshal(id, ContractID),
id => unmarshal(id, ID),
name => unmarshal(string, Name),
party => unmarshal(id, PartyID),
provider => unmarshal(id, ProviderID),
class => unmarshal(id, ClassID),
contract => unmarshal(id, ContractID),
external_id => maybe_unmarshal(id, ExternalID),
created_at => maybe_unmarshal(created_at, CreatedAt),
metadata => maybe_unmarshal(ctx, Metadata),
version => 2
created_at => maybe_unmarshal(created_at, CreatedAt),
metadata => maybe_unmarshal(ctx, Metadata),
version => 2
});
unmarshal(challenge_payload, {created, Challenge}) ->
{created, unmarshal(challenge_payload_created, Challenge)};
unmarshal(challenge_payload, {status_changed, ChallengeStatus}) ->
@ -271,29 +256,31 @@ unmarshal(challenge_payload_created, #idnt_Challenge{
master_id => unmarshal(id, MasterID),
claimant => unmarshal(id, Claimant)
};
unmarshal(challenge_proofs, Proof) -> {
unmarshal(challenge_proofs, Proof) ->
{
unmarshal(proof_type, Proof#idnt_ChallengeProof.type),
unmarshal(id, Proof#idnt_ChallengeProof.token)
};
unmarshal(proof_type, rus_domestic_passport) ->
rus_domestic_passport;
unmarshal(proof_type, rus_retiree_insurance_cert) ->
rus_retiree_insurance_cert;
unmarshal(challenge_payload_status_changed, {pending, #idnt_ChallengePending{}}) ->
pending;
unmarshal(challenge_payload_status_changed, {cancelled, #idnt_ChallengeCancelled{}}) ->
cancelled;
unmarshal(challenge_payload_status_changed, {completed, #idnt_ChallengeCompleted{
resolution = Resolution,
valid_until = ValidUntil
}}) ->
{completed, genlib_map:compact(#{
resolution => unmarshal(resolution, Resolution),
valid_until => maybe_unmarshal(timestamp, ValidUntil)
})};
unmarshal(
challenge_payload_status_changed,
{completed, #idnt_ChallengeCompleted{
resolution = Resolution,
valid_until = ValidUntil
}}
) ->
{completed,
genlib_map:compact(#{
resolution => unmarshal(resolution, Resolution),
valid_until => maybe_unmarshal(timestamp, ValidUntil)
})};
unmarshal(challenge_payload_status_changed, {failed, #idnt_ChallengeFailed{}}) ->
% FIXME: Describe failures in protocol
{failed, unknown};
@ -301,18 +288,14 @@ unmarshal(resolution, approved) ->
approved;
unmarshal(resolution, denied) ->
denied;
unmarshal(effective_challenge, undefined) ->
{error, notfound};
unmarshal(effective_challenge, EffectiveChallengeID) ->
{ok, unmarshal(id, EffectiveChallengeID)};
unmarshal(created_at, Timestamp) ->
unmarshal(integer, ff_time:from_rfc3339(Timestamp));
unmarshal(ctx, Ctx) ->
maybe_unmarshal(context, Ctx);
unmarshal(T, V) ->
ff_codec:unmarshal(T, V).
@ -336,16 +319,17 @@ maybe_unmarshal(Type, Value) ->
-spec test() -> _.
-spec identity_test() -> _.
identity_test() ->
IdentityIn = #{
id => genlib:unique(),
name => genlib:unique(),
party => genlib:unique(),
provider => genlib:unique(),
class => genlib:unique(),
contract => genlib:unique(),
id => genlib:unique(),
name => genlib:unique(),
party => genlib:unique(),
provider => genlib:unique(),
class => genlib:unique(),
contract => genlib:unique(),
external_id => genlib:unique(),
version => 2
version => 2
},
IdentityOut = unmarshal(identity, marshal(identity, IdentityIn)),
?assertEqual(IdentityOut, IdentityIn).
@ -353,7 +337,7 @@ identity_test() ->
-spec challenge_test() -> _.
challenge_test() ->
ChallengeIn = #{
id => genlib:unique(),
id => genlib:unique(),
proofs => [{rus_retiree_insurance_cert, <<"Bananazzzz">>}],
challenge_class => <<"challenge_class">>,
claim_id => <<"claim_id">>,

View File

@ -9,32 +9,28 @@
-type event() :: ff_eventsink_publisher:event(ff_identity:event()).
-type sinkevent() :: ff_eventsink_publisher:sinkevent(ff_proto_identity_thrift:'SinkEvent'()).
-spec publish_events(list(event())) ->
list(sinkevent()).
-spec publish_events(list(event())) -> list(sinkevent()).
publish_events(Events) ->
[publish_event(Event) || Event <- Events].
-spec publish_event(event()) ->
sinkevent().
-spec publish_event(event()) -> sinkevent().
publish_event(#{
id := ID,
source_id := SourceID,
event := {
id := ID,
source_id := SourceID,
event := {
EventID,
Dt,
{ev, EventDt, Payload}
}
}) ->
#idnt_SinkEvent{
id = marshal(event_id, ID),
created_at = marshal(timestamp, Dt),
source = marshal(id, SourceID),
payload = #idnt_EventSinkPayload{
sequence = marshal(event_id, EventID),
id = marshal(event_id, ID),
created_at = marshal(timestamp, Dt),
source = marshal(id, SourceID),
payload = #idnt_EventSinkPayload{
sequence = marshal(event_id, EventID),
occured_at = marshal(timestamp, EventDt),
changes = [marshal(change, Payload)]
changes = [marshal(change, Payload)]
}
}.

View File

@ -1,4 +1,5 @@
-module(ff_identity_handler).
-behaviour(ff_woody_wrapper).
-include_lib("fistful_proto/include/ff_proto_identity_thrift.hrl").
@ -9,11 +10,12 @@
%%
%% ff_woody_wrapper callbacks
%%
-spec handle_function(woody:func(), woody:args(), woody:options()) ->
{ok, woody:result()} | no_return().
-spec handle_function(woody:func(), woody:args(), woody:options()) -> {ok, woody:result()} | no_return().
handle_function(Func, Args, Opts) ->
[IdentityID| _ ] = Args,
scoper:scope(identity, #{identity_id => IdentityID},
[IdentityID | _] = Args,
scoper:scope(
identity,
#{identity_id => IdentityID},
fun() ->
handle_function_(Func, Args, Opts)
end
@ -45,7 +47,7 @@ handle_function_('Get', [ID, EventRange], _Opts) ->
case ff_identity_machine:get(ID, ff_codec:unmarshal(event_range, EventRange)) of
{ok, Machine} ->
Identity = ff_identity:set_blocking(ff_identity_machine:identity(Machine)),
Context = ff_identity_machine:ctx(Machine),
Context = ff_identity_machine:ctx(Machine),
Response = ff_identity_codec:marshal_identity_state(Identity, Context),
{ok, Response};
{error, notfound} ->
@ -66,8 +68,8 @@ handle_function_('StartChallenge', [IdentityID, Params], _Opts) ->
case ff_identity_machine:start_challenge(IdentityID, ChallengeParams) of
ok ->
ChallengeID = maps:get(id, ChallengeParams),
{ok, Machine} = ff_identity_machine:get(IdentityID),
Identity = ff_identity_machine:identity(Machine),
{ok, Machine} = ff_identity_machine:get(IdentityID),
Identity = ff_identity_machine:identity(Machine),
{ok, Challenge} = ff_identity:challenge(ChallengeID, Identity),
{ok, ff_identity_codec:marshal_challenge_state(Challenge)};
{error, notfound} ->
@ -96,7 +98,6 @@ handle_function_('GetChallenges', [ID], _Opts) ->
{error, notfound} ->
woody_error:raise(business, #fistful_IdentityNotFound{})
end;
handle_function_('GetEvents', [IdentityID, EventRange], _Opts) ->
case ff_identity_machine:events(IdentityID, ff_codec:unmarshal(event_range, EventRange)) of
{ok, EventList} ->

View File

@ -20,7 +20,7 @@
-type value(T) :: machinery_mg_schema:v(T).
-type value_type() :: machinery_mg_schema:vt().
-type event() :: ff_machine:timestamped_event(ff_identity:event()).
-type event() :: ff_machine:timestamped_event(ff_identity:event()).
-type aux_state() :: ff_machine:auxst().
-type call_args() :: term().
-type call_response() :: term().
@ -32,71 +32,61 @@
-type thrift_change() :: ff_proto_identity_thrift:'Change'().
-type data() ::
aux_state() |
event() |
call_args() |
call_response().
aux_state()
| event()
| call_args()
| call_response().
%% machinery_mg_schema callbacks
-spec get_version(value_type()) ->
machinery_mg_schema:version().
-spec get_version(value_type()) -> machinery_mg_schema:version().
get_version(event) ->
?CURRENT_EVENT_FORMAT_VERSION;
get_version(aux_state) ->
undefined.
-spec marshal(type(), value(data()), context()) ->
{machinery_msgpack:t(), context()}.
-spec marshal(type(), value(data()), context()) -> {machinery_msgpack:t(), context()}.
marshal({event, Format}, TimestampedChange, Context) ->
marshal_event(Format, TimestampedChange, Context);
marshal(T, V, C) when
T =:= {args, init} orelse
T =:= {args, call} orelse
T =:= {args, repair} orelse
T =:= {aux_state, undefined} orelse
T =:= {response, call} orelse
T =:= {response, {repair, success}} orelse
T =:= {response, {repair, failure}}
T =:= {args, call} orelse
T =:= {args, repair} orelse
T =:= {aux_state, undefined} orelse
T =:= {response, call} orelse
T =:= {response, {repair, success}} orelse
T =:= {response, {repair, failure}}
->
machinery_mg_schema_generic:marshal(T, V, C).
-spec unmarshal(type(), machinery_msgpack:t(), context()) ->
{data(), context()}.
-spec unmarshal(type(), machinery_msgpack:t(), context()) -> {data(), context()}.
unmarshal({event, FormatVersion}, EncodedChange, Context) ->
unmarshal_event(FormatVersion, EncodedChange, Context);
unmarshal({aux_state, undefined} = T, V, C0) ->
{AuxState, C1} = machinery_mg_schema_generic:unmarshal(T, V, C0),
{AuxState, C1#{ctx => get_aux_state_ctx(AuxState)}};
unmarshal(T, V, C) when
T =:= {args, init} orelse
T =:= {args, call} orelse
T =:= {args, repair} orelse
T =:= {response, call} orelse
T =:= {response, {repair, success}} orelse
T =:= {response, {repair, failure}}
T =:= {args, call} orelse
T =:= {args, repair} orelse
T =:= {response, call} orelse
T =:= {response, {repair, success}} orelse
T =:= {response, {repair, failure}}
->
machinery_mg_schema_generic:unmarshal(T, V, C).
%% Internals
-spec marshal_event(machinery_mg_schema:version(), event(), context()) ->
{machinery_msgpack:t(), context()}.
-spec marshal_event(machinery_mg_schema:version(), event(), context()) -> {machinery_msgpack:t(), context()}.
marshal_event(undefined = Version, TimestampedChange, Context) ->
% TODO: Remove this clause after MSPF-561 finish
machinery_mg_schema_generic:marshal({event, Version}, TimestampedChange, Context);
marshal_event(Version, TimestampedChange, Context) when
Version =:= 1;
Version =:= 2
->
marshal_event(Version, TimestampedChange, Context) when Version =:= 1; Version =:= 2 ->
ThriftChange = ff_identity_codec:marshal(timestamped_change, TimestampedChange),
Type = {struct, struct, {ff_proto_identity_thrift, 'TimestampedChange'}},
{{bin, ff_proto_utils:serialize(Type, ThriftChange)}, Context}.
-spec unmarshal_event(machinery_mg_schema:version(), machinery_msgpack:t(), context()) ->
{event(), context()}.
-spec unmarshal_event(machinery_mg_schema:version(), machinery_msgpack:t(), context()) -> {event(), context()}.
unmarshal_event(2, EncodedChange, Context) ->
ThriftChange = unmashal_thrift_change(EncodedChange),
{ff_identity_codec:unmarshal(timestamped_change, ThriftChange), Context};
@ -114,49 +104,57 @@ unmarshal_event(undefined = Version, EncodedChange, Context) ->
),
{{ev, Timestamp, maybe_migrate_change(Change, Context1)}, Context1}.
-spec unmashal_thrift_change(machinery_msgpack:t()) ->
timestamped_change().
-spec unmashal_thrift_change(machinery_msgpack:t()) -> timestamped_change().
unmashal_thrift_change(EncodedChange) ->
{bin, EncodedThriftChange} = EncodedChange,
Type = {struct, struct, {ff_proto_identity_thrift, 'TimestampedChange'}},
ff_proto_utils:deserialize(Type, EncodedThriftChange).
-spec maybe_migrate_thrift_change(thrift_change(), context()) ->
thrift_change().
-spec maybe_migrate_thrift_change(thrift_change(), context()) -> thrift_change().
maybe_migrate_thrift_change({created, #idnt_Identity{name = undefined} = Identity}, MigrateContext) ->
Context = fetch_entity_context(Identity#idnt_Identity.id, MigrateContext),
{created, Identity#idnt_Identity{name = get_legacy_name(Context)}};
maybe_migrate_thrift_change(Change, _MigrateContext) ->
Change.
-spec maybe_migrate_change(legacy_change(), context()) ->
ff_identity:event().
-spec maybe_migrate_change(legacy_change(), context()) -> ff_identity:event().
maybe_migrate_change(Event = {created, #{version := 2, name := _}}, _MigrateContext) ->
Event;
maybe_migrate_change({created, Identity = #{version := 2, id := ID}}, MigrateContext) ->
Context = fetch_entity_context(ID, MigrateContext),
maybe_migrate_change({created, genlib_map:compact(Identity#{
name => get_legacy_name(Context)
})}, MigrateContext);
maybe_migrate_change(
{created,
genlib_map:compact(Identity#{
name => get_legacy_name(Context)
})},
MigrateContext
);
maybe_migrate_change({created, Identity = #{version := 1, id := ID}}, MigrateContext) ->
Context = fetch_entity_context(ID, MigrateContext),
maybe_migrate_change({created, genlib_map:compact(Identity#{
version => 2,
metadata => ff_entity_context:try_get_legacy_metadata(Context)
})}, MigrateContext);
maybe_migrate_change(
{created,
genlib_map:compact(Identity#{
version => 2,
metadata => ff_entity_context:try_get_legacy_metadata(Context)
})},
MigrateContext
);
maybe_migrate_change({created, Identity = #{created_at := _CreatedAt}}, MigrateContext) ->
maybe_migrate_change({created, Identity#{
version => 1
}}, MigrateContext);
maybe_migrate_change(
{created, Identity#{
version => 1
}},
MigrateContext
);
maybe_migrate_change({created, Identity}, MigrateContext) ->
Timestamp = maps:get(created_at, MigrateContext),
CreatedAt = ff_codec:unmarshal(timestamp_ms, ff_codec:marshal(timestamp, Timestamp)),
maybe_migrate_change({created, Identity#{
created_at => CreatedAt
}}, MigrateContext);
maybe_migrate_change(
{created, Identity#{
created_at => CreatedAt
}},
MigrateContext
);
maybe_migrate_change(Ev, _MigrateContext) ->
Ev.
@ -179,18 +177,18 @@ get_legacy_name(#{<<"com.rbkmoney.wapi">> := #{<<"name">> := Name}}) ->
-ifdef(TEST).
-include_lib("eunit/include/eunit.hrl").
-spec test() -> _.
% tests helpers
-spec marshal(type(), value(data())) ->
machinery_msgpack:t().
-spec marshal(type(), value(data())) -> machinery_msgpack:t().
marshal(Type, Value) ->
{Result, _Context} = marshal(Type, Value, #{}),
Result.
-spec unmarshal(type(), machinery_msgpack:t()) ->
data().
-spec unmarshal(type(), machinery_msgpack:t()) -> data().
unmarshal(Type, Value) ->
{Result, _Context} = unmarshal(Type, Value, #{}),
Result.
@ -211,42 +209,45 @@ created_v0_decoding_test() ->
Change = {created, Identity},
Event = {ev, {{{2020, 5, 25}, {19, 19, 10}}, 293305}, Change},
LegacyChange = {arr, [
{str, <<"tup">>},
{str, <<"created">>},
{arr, [
{str, <<"map">>},
{obj, #{
{str, <<"class">>} => {bin, <<"class">>},
{str, <<"contract">>} => {bin, <<"ContractID">>},
{str, <<"created_at">>} => {i, 1592576943762},
{str, <<"id">>} => {bin, <<"ID">>},
{str, <<"party">>} => {bin, <<"PartyID">>},
{str, <<"provider">>} => {bin, <<"good-one">>},
{str, <<"version">>} => {i, 2},
{str, <<"metadata">>} => {arr, [
{str, <<"map">>},
{obj, #{
{bin, <<"some key">>} => {bin, <<"some val">>}
}}
]}
}}
]}
]},
LegacyEvent = {arr, [
{str, <<"tup">>},
{str, <<"ev">>},
LegacyChange =
{arr, [
{str, <<"tup">>},
{str, <<"created">>},
{arr, [
{str, <<"map">>},
{obj, #{
{str, <<"class">>} => {bin, <<"class">>},
{str, <<"contract">>} => {bin, <<"ContractID">>},
{str, <<"created_at">>} => {i, 1592576943762},
{str, <<"id">>} => {bin, <<"ID">>},
{str, <<"party">>} => {bin, <<"PartyID">>},
{str, <<"provider">>} => {bin, <<"good-one">>},
{str, <<"version">>} => {i, 2},
{str, <<"metadata">>} =>
{arr, [
{str, <<"map">>},
{obj, #{
{bin, <<"some key">>} => {bin, <<"some val">>}
}}
]}
}}
]}
]},
LegacyEvent =
{arr, [
{str, <<"tup">>},
{str, <<"ev">>},
{arr, [
{str, <<"tup">>},
{arr, [{str, <<"tup">>}, {i, 2020}, {i, 5}, {i, 25}]},
{arr, [{str, <<"tup">>}, {i, 19}, {i, 19}, {i, 10}]}
{arr, [
{str, <<"tup">>},
{arr, [{str, <<"tup">>}, {i, 2020}, {i, 5}, {i, 25}]},
{arr, [{str, <<"tup">>}, {i, 19}, {i, 19}, {i, 10}]}
]},
{i, 293305}
]},
{i, 293305}
LegacyChange
]},
LegacyChange
]},
{DecodedLegacy, _} = unmarshal({event, undefined}, LegacyEvent, #{
ctx => #{<<"com.rbkmoney.wapi">> => #{<<"name">> => <<"Name">>}}
@ -262,25 +263,27 @@ level_changed_v0_decoding_test() ->
Change = {level_changed, <<"level_changed">>},
Event = {ev, {{{2020, 5, 25}, {19, 19, 10}}, 293305}, Change},
LegacyChange = {arr, [
{str, <<"tup">>},
{str, <<"level_changed">>},
{bin, <<"level_changed">>}
]},
LegacyEvent = {arr, [
{str, <<"tup">>},
{str, <<"ev">>},
LegacyChange =
{arr, [
{str, <<"tup">>},
{str, <<"level_changed">>},
{bin, <<"level_changed">>}
]},
LegacyEvent =
{arr, [
{str, <<"tup">>},
{str, <<"ev">>},
{arr, [
{str, <<"tup">>},
{arr, [{str, <<"tup">>}, {i, 2020}, {i, 5}, {i, 25}]},
{arr, [{str, <<"tup">>}, {i, 19}, {i, 19}, {i, 10}]}
{arr, [
{str, <<"tup">>},
{arr, [{str, <<"tup">>}, {i, 2020}, {i, 5}, {i, 25}]},
{arr, [{str, <<"tup">>}, {i, 19}, {i, 19}, {i, 10}]}
]},
{i, 293305}
]},
{i, 293305}
LegacyChange
]},
LegacyChange
]},
{DecodedLegacy, _} = unmarshal({event, undefined}, LegacyEvent, #{}),
ModernizedBinary = marshal({event, ?CURRENT_EVENT_FORMAT_VERSION}, DecodedLegacy),
@ -292,25 +295,27 @@ effective_challenge_changed_v0_decoding_test() ->
Change = {effective_challenge_changed, <<"effective_challenge_changed">>},
Event = {ev, {{{2020, 5, 25}, {19, 19, 10}}, 293305}, Change},
LegacyChange = {arr, [
{str, <<"tup">>},
{str, <<"effective_challenge_changed">>},
{bin, <<"effective_challenge_changed">>}
]},
LegacyEvent = {arr, [
{str, <<"tup">>},
{str, <<"ev">>},
LegacyChange =
{arr, [
{str, <<"tup">>},
{str, <<"effective_challenge_changed">>},
{bin, <<"effective_challenge_changed">>}
]},
LegacyEvent =
{arr, [
{str, <<"tup">>},
{str, <<"ev">>},
{arr, [
{str, <<"tup">>},
{arr, [{str, <<"tup">>}, {i, 2020}, {i, 5}, {i, 25}]},
{arr, [{str, <<"tup">>}, {i, 19}, {i, 19}, {i, 10}]}
{arr, [
{str, <<"tup">>},
{arr, [{str, <<"tup">>}, {i, 2020}, {i, 5}, {i, 25}]},
{arr, [{str, <<"tup">>}, {i, 19}, {i, 19}, {i, 10}]}
]},
{i, 293305}
]},
{i, 293305}
LegacyChange
]},
LegacyChange
]},
{DecodedLegacy, _} = unmarshal({event, undefined}, LegacyEvent, #{}),
ModernizedBinary = marshal({event, ?CURRENT_EVENT_FORMAT_VERSION}, DecodedLegacy),
@ -319,64 +324,69 @@ effective_challenge_changed_v0_decoding_test() ->
-spec challenge_created_v0_decoding_test() -> _.
challenge_created_v0_decoding_test() ->
Change = {{challenge, <<"challengeID">>}, {created, #{
id => <<"id">>,
claimant => <<"claimant">>,
provider => <<"provider">>,
identity_class => <<"identity_class">>,
challenge_class => <<"challenge_class">>,
proofs => [{rus_domestic_passport, <<"identdoc_token">>}],
master_id => <<"master_id">>,
claim_id => <<"claim_id">>
}}},
Change =
{{challenge, <<"challengeID">>},
{created, #{
id => <<"id">>,
claimant => <<"claimant">>,
provider => <<"provider">>,
identity_class => <<"identity_class">>,
challenge_class => <<"challenge_class">>,
proofs => [{rus_domestic_passport, <<"identdoc_token">>}],
master_id => <<"master_id">>,
claim_id => <<"claim_id">>
}}},
Event = {ev, {{{2020, 5, 25}, {19, 19, 10}}, 293305}, Change},
LegacyChange = {arr, [
{str, <<"tup">>},
{arr, [
{str, <<"tup">>},
{str, <<"challenge">>},
{bin, <<"challengeID">>}
]},
{arr, [
{str, <<"tup">>},
{str, <<"created">>},
{arr, [
{str, <<"map">>},
{obj, #{
{str, <<"id">>} => {bin, <<"id">>},
{str, <<"claimant">>} => {bin, <<"claimant">>},
{str, <<"provider">>} => {bin, <<"provider">>},
{str, <<"identity_class">>} => {bin, <<"identity_class">>},
{str, <<"challenge_class">>} => {bin, <<"challenge_class">>},
{str, <<"master_id">>} => {bin, <<"master_id">>},
{str, <<"claim_id">>} => {bin, <<"claim_id">>},
{str, <<"proofs">>} => {arr, [
{str, <<"lst">>},
{arr, [
{str, <<"tup">>},
{str, <<"rus_domestic_passport">>},
{bin, <<"identdoc_token">>}
]}
]}
}}
]}
]}
]},
LegacyEvent = {arr, [
{str, <<"tup">>},
{str, <<"ev">>},
LegacyChange =
{arr, [
{str, <<"tup">>},
{arr, [
{str, <<"tup">>},
{arr, [{str, <<"tup">>}, {i, 2020}, {i, 5}, {i, 25}]},
{arr, [{str, <<"tup">>}, {i, 19}, {i, 19}, {i, 10}]}
{str, <<"challenge">>},
{bin, <<"challengeID">>}
]},
{i, 293305}
{arr, [
{str, <<"tup">>},
{str, <<"created">>},
{arr, [
{str, <<"map">>},
{obj, #{
{str, <<"id">>} => {bin, <<"id">>},
{str, <<"claimant">>} => {bin, <<"claimant">>},
{str, <<"provider">>} => {bin, <<"provider">>},
{str, <<"identity_class">>} => {bin, <<"identity_class">>},
{str, <<"challenge_class">>} => {bin, <<"challenge_class">>},
{str, <<"master_id">>} => {bin, <<"master_id">>},
{str, <<"claim_id">>} => {bin, <<"claim_id">>},
{str, <<"proofs">>} =>
{arr, [
{str, <<"lst">>},
{arr, [
{str, <<"tup">>},
{str, <<"rus_domestic_passport">>},
{bin, <<"identdoc_token">>}
]}
]}
}}
]}
]}
]},
LegacyEvent =
{arr, [
{str, <<"tup">>},
{str, <<"ev">>},
{arr, [
{str, <<"tup">>},
{arr, [
{str, <<"tup">>},
{arr, [{str, <<"tup">>}, {i, 2020}, {i, 5}, {i, 25}]},
{arr, [{str, <<"tup">>}, {i, 19}, {i, 19}, {i, 10}]}
]},
{i, 293305}
]},
LegacyChange
]},
LegacyChange
]},
{DecodedLegacy, _} = unmarshal({event, undefined}, LegacyEvent, #{}),
ModernizedBinary = marshal({event, ?CURRENT_EVENT_FORMAT_VERSION}, DecodedLegacy),
@ -388,33 +398,35 @@ challenge_status_changed_v0_decoding_test() ->
Change = {{challenge, <<"challengeID">>}, {status_changed, pending}},
Event = {ev, {{{2020, 5, 25}, {19, 19, 10}}, 293305}, Change},
LegacyChange = {arr, [
{str, <<"tup">>},
{arr, [
{str, <<"tup">>},
{str, <<"challenge">>},
{bin, <<"challengeID">>}
]},
{arr, [
{str, <<"tup">>},
{str, <<"status_changed">>},
{str, <<"pending">>}
]}
]},
LegacyEvent = {arr, [
{str, <<"tup">>},
{str, <<"ev">>},
LegacyChange =
{arr, [
{str, <<"tup">>},
{arr, [
{str, <<"tup">>},
{arr, [{str, <<"tup">>}, {i, 2020}, {i, 5}, {i, 25}]},
{arr, [{str, <<"tup">>}, {i, 19}, {i, 19}, {i, 10}]}
{str, <<"challenge">>},
{bin, <<"challengeID">>}
]},
{i, 293305}
{arr, [
{str, <<"tup">>},
{str, <<"status_changed">>},
{str, <<"pending">>}
]}
]},
LegacyEvent =
{arr, [
{str, <<"tup">>},
{str, <<"ev">>},
{arr, [
{str, <<"tup">>},
{arr, [
{str, <<"tup">>},
{arr, [{str, <<"tup">>}, {i, 2020}, {i, 5}, {i, 25}]},
{arr, [{str, <<"tup">>}, {i, 19}, {i, 19}, {i, 10}]}
]},
{i, 293305}
]},
LegacyChange
]},
LegacyChange
]},
{DecodedLegacy, _} = unmarshal({event, undefined}, LegacyEvent, #{}),
ModernizedBinary = marshal({event, ?CURRENT_EVENT_FORMAT_VERSION}, DecodedLegacy),
@ -436,11 +448,13 @@ created_v1_decoding_test() ->
},
Change = {created, Identity},
Event = {ev, {{{2020, 5, 25}, {19, 19, 10}}, 293305}, Change},
LegacyEvent = {bin, base64:decode(<<
"CwABAAAAGzIwMjAtMDUtMjVUMTk6MTk6MTAuMjkzMzA1WgwAAgwAAQsABgAAAAJJRAsAAQAAAAd"
"QYXJ0eUlECwACAAAACGdvb2Qtb25lCwADAAAABWNsYXNzCwAEAAAACkNvbnRyYWN0SUQLAAoAAA"
"AYMjAyMC0wNi0xOVQxNDoyOTowMy43NjJaDQALCwwAAAABAAAACHNvbWUga2V5CwAFAAAACHNvbWUgdmFsAAAAAA=="
>>)},
LegacyEvent =
{bin,
base64:decode(<<
"CwABAAAAGzIwMjAtMDUtMjVUMTk6MTk6MTAuMjkzMzA1WgwAAgwAAQsABgAAAAJJRAsAAQAAAAd"
"QYXJ0eUlECwACAAAACGdvb2Qtb25lCwADAAAABWNsYXNzCwAEAAAACkNvbnRyYWN0SUQLAAoAAA"
"AYMjAyMC0wNi0xOVQxNDoyOTowMy43NjJaDQALCwwAAAABAAAACHNvbWUga2V5CwAFAAAACHNvbWUgdmFsAAAAAA=="
>>)},
{DecodedLegacy, _} = unmarshal({event, 1}, LegacyEvent, #{
ctx => #{<<"com.rbkmoney.wapi">> => #{<<"name">> => <<"Name">>}}
}),
@ -454,9 +468,11 @@ created_v1_decoding_test() ->
level_changed_v1_decoding_test() ->
Change = {level_changed, <<"level_changed">>},
Event = {ev, {{{2020, 5, 25}, {19, 19, 10}}, 293305}, Change},
LegacyEvent = {bin, base64:decode(<<
"CwABAAAAGzIwMjAtMDUtMjVUMTk6MTk6MTAuMjkzMzA1WgwAAgsAAgAAAA1sZXZlbF9jaGFuZ2VkAAA="
>>)},
LegacyEvent =
{bin,
base64:decode(<<
"CwABAAAAGzIwMjAtMDUtMjVUMTk6MTk6MTAuMjkzMzA1WgwAAgsAAgAAAA1sZXZlbF9jaGFuZ2VkAAA="
>>)},
DecodedLegacy = unmarshal({event, 1}, LegacyEvent),
ModernizedBinary = marshal({event, ?CURRENT_EVENT_FORMAT_VERSION}, DecodedLegacy),
Decoded = unmarshal({event, ?CURRENT_EVENT_FORMAT_VERSION}, ModernizedBinary),
@ -466,9 +482,11 @@ level_changed_v1_decoding_test() ->
effective_challenge_changed_v1_decoding_test() ->
Change = {effective_challenge_changed, <<"effective_challenge_changed">>},
Event = {ev, {{{2020, 5, 25}, {19, 19, 10}}, 293305}, Change},
LegacyEvent = {bin, base64:decode(<<
"CwABAAAAGzIwMjAtMDUtMjVUMTk6MTk6MTAuMjkzMzA1WgwAAgsABAAAABtlZmZlY3RpdmVfY2hhbGxlbmdlX2NoYW5nZWQAAA=="
>>)},
LegacyEvent =
{bin,
base64:decode(<<
"CwABAAAAGzIwMjAtMDUtMjVUMTk6MTk6MTAuMjkzMzA1WgwAAgsABAAAABtlZmZlY3RpdmVfY2hhbGxlbmdlX2NoYW5nZWQAAA=="
>>)},
DecodedLegacy = unmarshal({event, 1}, LegacyEvent),
ModernizedBinary = marshal({event, ?CURRENT_EVENT_FORMAT_VERSION}, DecodedLegacy),
Decoded = unmarshal({event, ?CURRENT_EVENT_FORMAT_VERSION}, ModernizedBinary),
@ -476,23 +494,27 @@ effective_challenge_changed_v1_decoding_test() ->
-spec challenge_created_v1_decoding_test() -> _.
challenge_created_v1_decoding_test() ->
Change = {{challenge, <<"challengeID">>}, {created, #{
id => <<"id">>,
claimant => <<"claimant">>,
provider => <<"provider">>,
identity_class => <<"identity_class">>,
challenge_class => <<"challenge_class">>,
proofs => [{rus_domestic_passport, <<"identdoc_token">>}],
master_id => <<"master_id">>,
claim_id => <<"claim_id">>
}}},
Change =
{{challenge, <<"challengeID">>},
{created, #{
id => <<"id">>,
claimant => <<"claimant">>,
provider => <<"provider">>,
identity_class => <<"identity_class">>,
challenge_class => <<"challenge_class">>,
proofs => [{rus_domestic_passport, <<"identdoc_token">>}],
master_id => <<"master_id">>,
claim_id => <<"claim_id">>
}}},
Event = {ev, {{{2020, 5, 25}, {19, 19, 10}}, 293305}, Change},
LegacyEvent = {bin, base64:decode(<<
"CwABAAAAGzIwMjAtMDUtMjVUMTk6MTk6MTAuMjkzMzA1WgwAAgwAAwsAAQAAAAtjaGFsbGVuZ2VJRA"
"wAAgwAAQsAAwAAAAJpZAsAAQAAAA9jaGFsbGVuZ2VfY2xhc3MPAAIMAAAAAQgAAQAAAAALAAIAAAAO"
"aWRlbnRkb2NfdG9rZW4ACwAFAAAACHByb3ZpZGVyCwAGAAAADmlkZW50aXR5X2NsYXNzCwAHAAAACG"
"NsYWltX2lkCwAIAAAACW1hc3Rlcl9pZAsACQAAAAhjbGFpbWFudAAAAAAA"
>>)},
LegacyEvent =
{bin,
base64:decode(<<
"CwABAAAAGzIwMjAtMDUtMjVUMTk6MTk6MTAuMjkzMzA1WgwAAgwAAwsAAQAAAAtjaGFsbGVuZ2VJRA"
"wAAgwAAQsAAwAAAAJpZAsAAQAAAA9jaGFsbGVuZ2VfY2xhc3MPAAIMAAAAAQgAAQAAAAALAAIAAAAO"
"aWRlbnRkb2NfdG9rZW4ACwAFAAAACHByb3ZpZGVyCwAGAAAADmlkZW50aXR5X2NsYXNzCwAHAAAACG"
"NsYWltX2lkCwAIAAAACW1hc3Rlcl9pZAsACQAAAAhjbGFpbWFudAAAAAAA"
>>)},
DecodedLegacy = unmarshal({event, 1}, LegacyEvent),
ModernizedBinary = marshal({event, ?CURRENT_EVENT_FORMAT_VERSION}, DecodedLegacy),
Decoded = unmarshal({event, ?CURRENT_EVENT_FORMAT_VERSION}, ModernizedBinary),
@ -502,9 +524,11 @@ challenge_created_v1_decoding_test() ->
challenge_status_changed_v1_decoding_test() ->
Change = {{challenge, <<"challengeID">>}, {status_changed, pending}},
Event = {ev, {{{2020, 5, 25}, {19, 19, 10}}, 293305}, Change},
LegacyEvent = {bin, base64:decode(<<
"CwABAAAAGzIwMjAtMDUtMjVUMTk6MTk6MTAuMjkzMzA1WgwAAgwAAwsAAQAAAAtjaGFsbGVuZ2VJRAwAAgwAAgwAAQAAAAAAAA=="
>>)},
LegacyEvent =
{bin,
base64:decode(<<
"CwABAAAAGzIwMjAtMDUtMjVUMTk6MTk6MTAuMjkzMzA1WgwAAgwAAwsAAQAAAAtjaGFsbGVuZ2VJRAwAAgwAAgwAAQAAAAAAAA=="
>>)},
DecodedLegacy = unmarshal({event, 1}, LegacyEvent),
ModernizedBinary = marshal({event, ?CURRENT_EVENT_FORMAT_VERSION}, DecodedLegacy),
Decoded = unmarshal({event, ?CURRENT_EVENT_FORMAT_VERSION}, ModernizedBinary),
@ -525,12 +549,14 @@ created_v2_decoding_test() ->
},
Change = {created, Identity},
Event = {ev, {{{2020, 5, 25}, {19, 19, 10}}, 293305}, Change},
LegacyEvent = {bin, base64:decode(<<
"CwABAAAAGzIwMjAtMDUtMjVUMTk6MTk6MTAuMjkzMzA1WgwAAgwAAQsABgAAAAJJRAsADAAAAAROYW1lC"
"wABAAAAB1BhcnR5SUQLAAIAAAAIZ29vZC1vbmULAAMAAAAFY2xhc3MLAAQAAAAKQ29udHJhY3RJRAsACg"
"AAABgyMDIwLTA2LTE5VDE0OjI5OjAzLjc2MloNAAsLDAAAAAEAAAAIc29tZSBrZXkLAAUAAAAIc29tZSB2"
"YWwAAAAA"
>>)},
LegacyEvent =
{bin,
base64:decode(<<
"CwABAAAAGzIwMjAtMDUtMjVUMTk6MTk6MTAuMjkzMzA1WgwAAgwAAQsABgAAAAJJRAsADAAAAAROYW1lC"
"wABAAAAB1BhcnR5SUQLAAIAAAAIZ29vZC1vbmULAAMAAAAFY2xhc3MLAAQAAAAKQ29udHJhY3RJRAsACg"
"AAABgyMDIwLTA2LTE5VDE0OjI5OjAzLjc2MloNAAsLDAAAAAEAAAAIc29tZSBrZXkLAAUAAAAIc29tZSB2"
"YWwAAAAA"
>>)},
DecodedLegacy = unmarshal({event, 2}, LegacyEvent),
ModernizedBinary = marshal({event, ?CURRENT_EVENT_FORMAT_VERSION}, DecodedLegacy),
Decoded = unmarshal({event, ?CURRENT_EVENT_FORMAT_VERSION}, ModernizedBinary),
@ -540,9 +566,11 @@ created_v2_decoding_test() ->
level_changed_v2_decoding_test() ->
Change = {level_changed, <<"level_changed">>},
Event = {ev, {{{2020, 5, 25}, {19, 19, 10}}, 293305}, Change},
LegacyEvent = {bin, base64:decode(<<
"CwABAAAAGzIwMjAtMDUtMjVUMTk6MTk6MTAuMjkzMzA1WgwAAgsAAgAAAA1sZXZlbF9jaGFuZ2VkAAA="
>>)},
LegacyEvent =
{bin,
base64:decode(<<
"CwABAAAAGzIwMjAtMDUtMjVUMTk6MTk6MTAuMjkzMzA1WgwAAgsAAgAAAA1sZXZlbF9jaGFuZ2VkAAA="
>>)},
DecodedLegacy = unmarshal({event, 2}, LegacyEvent),
ModernizedBinary = marshal({event, ?CURRENT_EVENT_FORMAT_VERSION}, DecodedLegacy),
Decoded = unmarshal({event, ?CURRENT_EVENT_FORMAT_VERSION}, ModernizedBinary),
@ -552,9 +580,11 @@ level_changed_v2_decoding_test() ->
effective_challenge_changed_v2_decoding_test() ->
Change = {effective_challenge_changed, <<"effective_challenge_changed">>},
Event = {ev, {{{2020, 5, 25}, {19, 19, 10}}, 293305}, Change},
LegacyEvent = {bin, base64:decode(<<
"CwABAAAAGzIwMjAtMDUtMjVUMTk6MTk6MTAuMjkzMzA1WgwAAgsABAAAABtlZmZlY3RpdmVfY2hhbGxlbmdlX2NoYW5nZWQAAA=="
>>)},
LegacyEvent =
{bin,
base64:decode(<<
"CwABAAAAGzIwMjAtMDUtMjVUMTk6MTk6MTAuMjkzMzA1WgwAAgsABAAAABtlZmZlY3RpdmVfY2hhbGxlbmdlX2NoYW5nZWQAAA=="
>>)},
DecodedLegacy = unmarshal({event, 2}, LegacyEvent),
ModernizedBinary = marshal({event, ?CURRENT_EVENT_FORMAT_VERSION}, DecodedLegacy),
Decoded = unmarshal({event, ?CURRENT_EVENT_FORMAT_VERSION}, ModernizedBinary),
@ -562,23 +592,27 @@ effective_challenge_changed_v2_decoding_test() ->
-spec challenge_created_v2_decoding_test() -> _.
challenge_created_v2_decoding_test() ->
Change = {{challenge, <<"challengeID">>}, {created, #{
id => <<"id">>,
claimant => <<"claimant">>,
provider => <<"provider">>,
identity_class => <<"identity_class">>,
challenge_class => <<"challenge_class">>,
proofs => [{rus_domestic_passport, <<"identdoc_token">>}],
master_id => <<"master_id">>,
claim_id => <<"claim_id">>
}}},
Change =
{{challenge, <<"challengeID">>},
{created, #{
id => <<"id">>,
claimant => <<"claimant">>,
provider => <<"provider">>,
identity_class => <<"identity_class">>,
challenge_class => <<"challenge_class">>,
proofs => [{rus_domestic_passport, <<"identdoc_token">>}],
master_id => <<"master_id">>,
claim_id => <<"claim_id">>
}}},
Event = {ev, {{{2020, 5, 25}, {19, 19, 10}}, 293305}, Change},
LegacyEvent = {bin, base64:decode(<<
"CwABAAAAGzIwMjAtMDUtMjVUMTk6MTk6MTAuMjkzMzA1WgwAAgwAAwsAAQAAAAtjaGFsbGVuZ2VJRA"
"wAAgwAAQsAAwAAAAJpZAsAAQAAAA9jaGFsbGVuZ2VfY2xhc3MPAAIMAAAAAQgAAQAAAAALAAIAAAAO"
"aWRlbnRkb2NfdG9rZW4ACwAFAAAACHByb3ZpZGVyCwAGAAAADmlkZW50aXR5X2NsYXNzCwAHAAAACG"
"NsYWltX2lkCwAIAAAACW1hc3Rlcl9pZAsACQAAAAhjbGFpbWFudAAAAAAA"
>>)},
LegacyEvent =
{bin,
base64:decode(<<
"CwABAAAAGzIwMjAtMDUtMjVUMTk6MTk6MTAuMjkzMzA1WgwAAgwAAwsAAQAAAAtjaGFsbGVuZ2VJRA"
"wAAgwAAQsAAwAAAAJpZAsAAQAAAA9jaGFsbGVuZ2VfY2xhc3MPAAIMAAAAAQgAAQAAAAALAAIAAAAO"
"aWRlbnRkb2NfdG9rZW4ACwAFAAAACHByb3ZpZGVyCwAGAAAADmlkZW50aXR5X2NsYXNzCwAHAAAACG"
"NsYWltX2lkCwAIAAAACW1hc3Rlcl9pZAsACQAAAAhjbGFpbWFudAAAAAAA"
>>)},
DecodedLegacy = unmarshal({event, 2}, LegacyEvent),
ModernizedBinary = marshal({event, ?CURRENT_EVENT_FORMAT_VERSION}, DecodedLegacy),
Decoded = unmarshal({event, ?CURRENT_EVENT_FORMAT_VERSION}, ModernizedBinary),
@ -588,9 +622,11 @@ challenge_created_v2_decoding_test() ->
challenge_status_changed_v2_decoding_test() ->
Change = {{challenge, <<"challengeID">>}, {status_changed, pending}},
Event = {ev, {{{2020, 5, 25}, {19, 19, 10}}, 293305}, Change},
LegacyEvent = {bin, base64:decode(<<
"CwABAAAAGzIwMjAtMDUtMjVUMTk6MTk6MTAuMjkzMzA1WgwAAgwAAwsAAQAAAAtjaGFsbGVuZ2VJRAwAAgwAAgwAAQAAAAAAAA=="
>>)},
LegacyEvent =
{bin,
base64:decode(<<
"CwABAAAAGzIwMjAtMDUtMjVUMTk6MTk6MTAuMjkzMzA1WgwAAgwAAwsAAQAAAAtjaGFsbGVuZ2VJRAwAAgwAAgwAAQAAAAAAAA=="
>>)},
DecodedLegacy = unmarshal({event, 2}, LegacyEvent),
ModernizedBinary = marshal({event, ?CURRENT_EVENT_FORMAT_VERSION}, DecodedLegacy),
Decoded = unmarshal({event, ?CURRENT_EVENT_FORMAT_VERSION}, ModernizedBinary),

View File

@ -10,18 +10,16 @@
%% Data transform
-define(to_session_event(SessionID, Payload),
{session, #{id => SessionID, payload => Payload}}).
{session, #{id => SessionID, payload => Payload}}
).
%% API
-spec marshal(ff_codec:type_name(), ff_codec:decoded_value()) ->
ff_codec:encoded_value().
-spec marshal(ff_codec:type_name(), ff_codec:decoded_value()) -> ff_codec:encoded_value().
marshal(details, {wallet_sender, WalletDetails}) ->
{wallet_sender, marshal(wallet_details, WalletDetails)};
marshal(details, {wallet_receiver, WalletDetails}) ->
{wallet_receiver, marshal(wallet_details, WalletDetails)};
marshal(wallet_details, ok) ->
{ok, #lim_check_WalletOk{}};
marshal(wallet_details, {failed, Details}) ->
@ -31,14 +29,11 @@ marshal(wallet_details, {failed, Details}) ->
balance = ff_codec:marshal(cash, Balance)
}}.
-spec unmarshal(ff_codec:type_name(), ff_codec:encoded_value()) ->
ff_codec:decoded_value().
-spec unmarshal(ff_codec:type_name(), ff_codec:encoded_value()) -> ff_codec:decoded_value().
unmarshal(details, {wallet_sender, WalletDetails}) ->
{wallet_sender, unmarshal(wallet_details, WalletDetails)};
unmarshal(details, {wallet_receiver, WalletDetails}) ->
{wallet_receiver, unmarshal(wallet_details, WalletDetails)};
unmarshal(wallet_details, {ok, #lim_check_WalletOk{}}) ->
ok;
unmarshal(wallet_details, {failed, Details}) ->
@ -50,9 +45,11 @@ unmarshal(wallet_details, {failed, Details}) ->
-ifdef(TEST).
-include_lib("eunit/include/eunit.hrl").
-spec test() -> _.
-spec wallet_ok_test() -> _.
wallet_ok_test() ->
Details0 = {wallet_sender, ok},
?assertEqual(Details0, unmarshal(details, (marshal(details, Details0)))),
@ -61,15 +58,19 @@ wallet_ok_test() ->
-spec wallet_fail_test() -> _.
wallet_fail_test() ->
Details0 = {wallet_sender, {failed, #{
expected_range => {{exclusive, {1, <<"RUB">>}}, {inclusive, {10, <<"RUB">>}}},
balance => {0, <<"RUB">>}
}}},
Details0 =
{wallet_sender,
{failed, #{
expected_range => {{exclusive, {1, <<"RUB">>}}, {inclusive, {10, <<"RUB">>}}},
balance => {0, <<"RUB">>}
}}},
?assertEqual(Details0, unmarshal(details, (marshal(details, Details0)))),
Details1 = {wallet_receiver, {failed, #{
expected_range => {{exclusive, {1, <<"RUB">>}}, {inclusive, {10, <<"RUB">>}}},
balance => {0, <<"RUB">>}
}}},
Details1 =
{wallet_receiver,
{failed, #{
expected_range => {{exclusive, {1, <<"RUB">>}}, {inclusive, {10, <<"RUB">>}}},
balance => {0, <<"RUB">>}
}}},
?assertEqual(Details1, unmarshal(details, (marshal(details, Details1)))).
-endif.

View File

@ -9,26 +9,32 @@
-type type_name() :: msgpack.
-type decoded_value() ::
#{decoded_value() => decoded_value()} |
[decoded_value()] |
integer() |
float() |
binary() |
{binary, binary()} |
nil.
#{decoded_value() => decoded_value()}
| [decoded_value()]
| integer()
| float()
| binary()
| {binary, binary()}
| nil.
-type encoded_value() :: ff_proto_msgpack_thrift:'Value'().
-export_type([type_name/0]).
-export_type([encoded_value/0]).
-export_type([decoded_value/0]).
-spec marshal(type_name(), decoded_value()) ->
encoded_value().
marshal(msgpack, nil) -> {nl, #msgp_Nil{}};
marshal(msgpack, V) when is_boolean(V) -> {b, V};
marshal(msgpack, V) when is_integer(V) -> {i, V};
marshal(msgpack, V) when is_float(V) -> V;
marshal(msgpack, V) when is_binary(V) -> {str, V}; % Assuming well-formed UTF-8 bytestring.
-spec marshal(type_name(), decoded_value()) -> encoded_value().
marshal(msgpack, nil) ->
{nl, #msgp_Nil{}};
marshal(msgpack, V) when is_boolean(V) ->
{b, V};
marshal(msgpack, V) when is_integer(V) ->
{i, V};
marshal(msgpack, V) when is_float(V) ->
V;
% Assuming well-formed UTF-8 bytestring.
marshal(msgpack, V) when is_binary(V) ->
{str, V};
marshal(msgpack, {binary, V}) when is_binary(V) ->
{bin, V};
marshal(msgpack, V) when is_list(V) ->
@ -36,14 +42,21 @@ marshal(msgpack, V) when is_list(V) ->
marshal(msgpack, V) when is_map(V) ->
{obj, maps:fold(fun(Key, Value, Map) -> Map#{marshal(msgpack, Key) => marshal(msgpack, Value)} end, #{}, V)}.
-spec unmarshal(type_name(), encoded_value()) ->
decoded_value().
unmarshal(msgpack, {nl, #msgp_Nil{}}) -> nil;
unmarshal(msgpack, {b, V}) when is_boolean(V) -> V;
unmarshal(msgpack, {i, V}) when is_integer(V) -> V;
unmarshal(msgpack, {flt, V}) when is_float(V) -> V;
unmarshal(msgpack, {str, V}) when is_binary(V) -> V; % Assuming well-formed UTF-8 bytestring.
unmarshal(msgpack, {bin, V}) when is_binary(V) -> {binary, V};
unmarshal(msgpack, {arr, V}) when is_list(V) -> [unmarshal(msgpack, ListItem) || ListItem <- V];
unmarshal(msgpack, {obj, V}) when is_map(V) ->
-spec unmarshal(type_name(), encoded_value()) -> decoded_value().
unmarshal(msgpack, {nl, #msgp_Nil{}}) ->
nil;
unmarshal(msgpack, {b, V}) when is_boolean(V) ->
V;
unmarshal(msgpack, {i, V}) when is_integer(V) ->
V;
unmarshal(msgpack, {flt, V}) when is_float(V) ->
V;
% Assuming well-formed UTF-8 bytestring.
unmarshal(msgpack, {str, V}) when is_binary(V) ->
V;
unmarshal(msgpack, {bin, V}) when is_binary(V) ->
{binary, V};
unmarshal(msgpack, {arr, V}) when is_list(V) ->
[unmarshal(msgpack, ListItem) || ListItem <- V];
unmarshal(msgpack, {obj, V}) when is_map(V) ->
maps:fold(fun(Key, Value, Map) -> Map#{unmarshal(msgpack, Key) => unmarshal(msgpack, Value)} end, #{}, V).

View File

@ -1,6 +1,7 @@
%% P2P adapter host
-module(ff_p2p_adapter_host).
-behaviour(ff_woody_wrapper).
-include_lib("damsel/include/dmsl_base_thrift.hrl").
@ -18,8 +19,7 @@
%% Handler
-spec handle_function(woody:func(), woody:args(), woody:options()) ->
{ok, woody:result()} | no_return().
-spec handle_function(woody:func(), woody:args(), woody:options()) -> {ok, woody:result()} | no_return().
handle_function(Func, Args, Opts) ->
scoper:scope(ff_p2p_adapter_host, #{}, fun() -> handle_function_(Func, Args, Opts) end).

View File

@ -15,7 +15,6 @@
%% API
-spec marshal_state(p2p_session:session_state(), ff_entity_context:context()) ->
ff_proto_p2p_session_thrift:'SessionState'().
marshal_state(State, Context) ->
#p2p_session_SessionState{
id = marshal(id, p2p_session:id(State)),
@ -27,9 +26,7 @@ marshal_state(State, Context) ->
context = marshal(ctx, Context)
}.
-spec marshal_event(p2p_transfer_machine:event()) ->
ff_proto_p2p_session_thrift:'Event'().
-spec marshal_event(p2p_transfer_machine:event()) -> ff_proto_p2p_session_thrift:'Event'().
marshal_event({EventID, {ev, Timestamp, Change}}) ->
#p2p_session_Event{
event = ff_codec:marshal(event_id, EventID),
@ -37,18 +34,14 @@ marshal_event({EventID, {ev, Timestamp, Change}}) ->
change = marshal(change, Change)
}.
-spec marshal(ff_codec:type_name(), ff_codec:decoded_value()) ->
ff_codec:encoded_value().
-spec marshal(ff_codec:type_name(), ff_codec:decoded_value()) -> ff_codec:encoded_value().
marshal({list, T}, V) ->
[marshal(T, E) || E <- V];
marshal(timestamped_change, {ev, Timestamp, Change}) ->
#p2p_session_TimestampedChange{
change = marshal(change, Change),
occured_at = ff_codec:marshal(timestamp, Timestamp)
};
marshal(change, {created, Session}) ->
{created, #p2p_session_CreatedChange{session = marshal(session, Session)}};
marshal(change, {next_state, AdapterState}) ->
@ -61,15 +54,17 @@ marshal(change, {callback, CallbackChange}) ->
{callback, marshal(callback_change, CallbackChange)};
marshal(change, {user_interaction, UserInteractionChange}) ->
{ui, marshal(user_interaction_change, UserInteractionChange)};
marshal(session, #{
id := ID,
status := Status,
transfer_params := TransferParams,
domain_revision := DomainRevision,
party_revision := PartyRevision,
route := Route
} = Session) ->
marshal(
session,
#{
id := ID,
status := Status,
transfer_params := TransferParams,
domain_revision := DomainRevision,
party_revision := PartyRevision,
route := Route
} = Session
) ->
#p2p_session_Session{
id = marshal(id, ID),
status = marshal(status, Status),
@ -79,23 +74,23 @@ marshal(session, #{
domain_revision = marshal(domain_revision, DomainRevision),
provider_legacy = maybe_marshal(integer, genlib_map:get(provider_id_legacy, Session))
};
marshal(status, active) ->
{active, #p2p_session_SessionActive{}};
marshal(status, {finished, SessionResult}) ->
{finished, #p2p_session_SessionFinished{result = marshal(session_result, SessionResult)}};
marshal(session_result, success) ->
{success, #p2p_session_ResultSuccess{}};
marshal(session_result, {failure, Failure}) ->
{failed, #p2p_session_ResultFailed{failure = marshal(failure, Failure)}};
marshal(p2p_transfer, Transfer = #{
id := ID,
body := Body,
sender := Sender,
receiver := Receiver
}) ->
marshal(
p2p_transfer,
Transfer = #{
id := ID,
body := Body,
sender := Sender,
receiver := Receiver
}
) ->
Deadline = maps:get(deadline, Transfer, undefined),
MerchantFees = maps:get(merchant_fees, Transfer, undefined),
ProviderFees = maps:get(provider_fees, Transfer, undefined),
@ -108,15 +103,12 @@ marshal(p2p_transfer, Transfer = #{
merchant_fees = maybe_marshal(fees, MerchantFees),
provider_fees = maybe_marshal(fees, ProviderFees)
};
marshal(route, Route) ->
#p2p_session_Route{
provider_id = marshal(provider_id, maps:get(provider_id, Route))
};
marshal(deadline, Deadline) ->
ff_time:to_rfc3339(Deadline);
marshal(fees, #{fees := Fees}) ->
#'Fees'{
fees = maps:fold(
@ -127,92 +119,77 @@ marshal(fees, #{fees := Fees}) ->
Fees
)
};
marshal(cash_flow_constant, Constant) ->
Constant;
marshal(callback_change, #{tag := Tag, payload := Payload}) ->
#p2p_session_CallbackChange{
tag = marshal(string, Tag),
payload = marshal(callback_event, Payload)
};
marshal(callback_event, {created, Callback}) ->
{created, #p2p_session_CallbackCreatedChange{callback = marshal(callback, Callback)}};
marshal(callback_event, {finished, #{payload := Response}}) ->
{finished, #p2p_session_CallbackResultChange{payload = Response}};
marshal(callback_event, {status_changed, Status}) ->
{status_changed, #p2p_session_CallbackStatusChange{status = marshal(callback_status, Status)}};
marshal(callback, #{tag := Tag}) ->
#p2p_session_Callback{tag = marshal(string, Tag)};
marshal(callback_status, pending) ->
{pending, #p2p_session_CallbackStatusPending{}};
marshal(callback_status, succeeded) ->
{succeeded, #p2p_session_CallbackStatusSucceeded{}};
marshal(user_interaction_change, #{id := ID, payload := Payload}) ->
#p2p_session_UserInteractionChange{
id = marshal(id, ID),
payload = marshal(user_interaction_event, Payload)
};
marshal(user_interaction_event, {created, UserInteraction}) ->
{created, #p2p_session_UserInteractionCreatedChange{ui = marshal(user_interaction, UserInteraction)}};
marshal(user_interaction_event, {status_changed, Status}) ->
{status_changed, #p2p_session_UserInteractionStatusChange{
status = marshal(user_interaction_status, Status)
}};
marshal(user_interaction, #{id := ID, content := Content}) ->
#p2p_session_UserInteraction{
id = marshal(id, ID),
user_interaction = marshal(user_interaction_content, Content)
};
marshal(user_interaction_content, {redirect, #{content := Redirect}}) ->
{redirect, marshal(redirect, Redirect)};
marshal(redirect, {get, URI}) ->
{get_request, #ui_BrowserGetRequest{uri = URI}};
marshal(redirect, {post, URI, Form}) ->
{post_request, #ui_BrowserPostRequest{uri = URI, form = marshal(form, Form)}};
marshal(form, Form) when is_map(Form) ->
maps:fold(fun(Key, Value, Map) ->
Map#{marshal(string, Key) => marshal(string, Value)} end,
maps:fold(
fun(Key, Value, Map) ->
Map#{marshal(string, Key) => marshal(string, Value)}
end,
#{},
Form
);
marshal(user_interaction_status, pending) ->
{pending, #p2p_session_UserInteractionStatusPending{}};
marshal(user_interaction_status, finished) ->
{finished, #p2p_session_UserInteractionStatusFinished{}};
marshal(T, V) ->
ff_codec:marshal(T, V).
-spec unmarshal(ff_codec:type_name(), ff_codec:encoded_value()) ->
ff_codec:decoded_value().
-spec unmarshal(ff_codec:type_name(), ff_codec:encoded_value()) -> ff_codec:decoded_value().
unmarshal({list, T}, V) ->
[unmarshal(T, E) || E <- V];
unmarshal(repair_scenario, {add_events, #p2p_session_AddEventsRepair{events = Events, action = Action}}) ->
{add_events, genlib_map:compact(#{
events => unmarshal({list, change}, Events),
action => maybe_unmarshal(complex_action, Action)
})};
{add_events,
genlib_map:compact(#{
events => unmarshal({list, change}, Events),
action => maybe_unmarshal(complex_action, Action)
})};
unmarshal(repair_scenario, {set_session_result, #p2p_session_SetResultRepair{result = Result}}) ->
{set_session_result, unmarshal(session_result, Result)};
unmarshal(timestamped_change, TimestampedChange) ->
Timestamp = ff_codec:unmarshal(timestamp, TimestampedChange#p2p_session_TimestampedChange.occured_at),
Change = unmarshal(change, TimestampedChange#p2p_session_TimestampedChange.change),
{ev, Timestamp, Change};
unmarshal(change, {created, #p2p_session_CreatedChange{session = Session}}) ->
{created, unmarshal(session, Session)};
unmarshal(change, {adapter_state, #p2p_session_AdapterStateChange{state = AdapterState}}) ->
@ -231,7 +208,6 @@ unmarshal(change, {ui, #p2p_session_UserInteractionChange{id = ID, payload = Pay
id => unmarshal(id, ID),
payload => unmarshal(user_interaction_event, Payload)
}};
unmarshal(session, #p2p_session_Session{
id = ID,
status = Status,
@ -251,17 +227,14 @@ unmarshal(session, #p2p_session_Session{
domain_revision => unmarshal(domain_revision, DomainRevision),
provider_id_legacy => maybe_unmarshal(integer, ProviderID)
});
unmarshal(status, {active, #p2p_session_SessionActive{}}) ->
active;
unmarshal(status, {finished, #p2p_session_SessionFinished{result = SessionResult}}) ->
{finished, unmarshal(session_result, SessionResult)};
unmarshal(session_result, {success, #p2p_session_ResultSuccess{}}) ->
success;
unmarshal(session_result, {failed, #p2p_session_ResultFailed{failure = Failure}}) ->
{failure, unmarshal(failure, Failure)};
unmarshal(p2p_transfer, #p2p_session_P2PTransfer{
id = ID,
sender = Sender,
@ -280,54 +253,53 @@ unmarshal(p2p_transfer, #p2p_session_P2PTransfer{
merchant_fees => maybe_unmarshal(fees, MerchantFees),
provider_fees => maybe_unmarshal(fees, ProviderFees)
});
unmarshal(route, Route) ->
#{
provider_id => unmarshal(provider_id, Route#p2p_session_Route.provider_id)
};
unmarshal(deadline, Deadline) ->
ff_time:from_rfc3339(Deadline);
unmarshal(fees, #'Fees'{fees = Fees}) ->
#{fees => maps:fold(
fun(Key, Value, Map) ->
Map#{unmarshal(cash_flow_constant, Key) => unmarshal(cash, Value)}
end,
#{},
Fees
)};
#{
fees => maps:fold(
fun(Key, Value, Map) ->
Map#{unmarshal(cash_flow_constant, Key) => unmarshal(cash, Value)}
end,
#{},
Fees
)
};
unmarshal(cash_flow_constant, Constant) ->
Constant;
unmarshal(callback_event, {created, #p2p_session_CallbackCreatedChange{callback = Callback}}) ->
{created, unmarshal(callback, Callback)};
unmarshal(callback_event, {finished, #p2p_session_CallbackResultChange{payload = Response}}) ->
{finished, #{payload => Response}};
unmarshal(callback_event, {status_changed, #p2p_session_CallbackStatusChange{status = Status}}) ->
{status_changed, unmarshal(callback_status, Status)};
unmarshal(callback, #p2p_session_Callback{tag = Tag}) ->
#{
version => 1,
tag => unmarshal(string, Tag)
};
unmarshal(callback_status, {pending, #p2p_session_CallbackStatusPending{}}) ->
pending;
unmarshal(callback_status, {succeeded, #p2p_session_CallbackStatusSucceeded{}}) ->
succeeded;
unmarshal(user_interaction_event, {created, #p2p_session_UserInteractionCreatedChange{
ui = UserInteraction
}}) ->
unmarshal(
user_interaction_event,
{created, #p2p_session_UserInteractionCreatedChange{
ui = UserInteraction
}}
) ->
{created, unmarshal(user_interaction, UserInteraction)};
unmarshal(user_interaction_event, {status_changed, #p2p_session_UserInteractionStatusChange{
status = Status
}}) ->
unmarshal(
user_interaction_event,
{status_changed, #p2p_session_UserInteractionStatusChange{
status = Status
}}
) ->
{status_changed, unmarshal(user_interaction_status, Status)};
unmarshal(user_interaction, #p2p_session_UserInteraction{
id = ID,
user_interaction = Content
@ -337,29 +309,26 @@ unmarshal(user_interaction, #p2p_session_UserInteraction{
id => unmarshal(id, ID),
content => unmarshal(user_interaction_content, Content)
};
unmarshal(user_interaction_content, {redirect, Redirect}) ->
{redirect, #{
content => unmarshal(redirect, Redirect)
}};
unmarshal(redirect, {get_request, #ui_BrowserGetRequest{uri = URI}}) ->
{get, URI};
unmarshal(redirect, {post_request, #ui_BrowserPostRequest{uri = URI, form = Form}}) ->
{post, URI, unmarshal(form, Form)};
unmarshal(form, Form) when is_map(Form) ->
maps:fold(fun(Key, Value, Map) ->
Map#{unmarshal(string, Key) => unmarshal(string, Value)} end,
maps:fold(
fun(Key, Value, Map) ->
Map#{unmarshal(string, Key) => unmarshal(string, Value)}
end,
#{},
Form
);
unmarshal(user_interaction_status, {pending, #p2p_session_UserInteractionStatusPending{}}) ->
pending;
unmarshal(user_interaction_status, {finished, #p2p_session_UserInteractionStatusFinished{}}) ->
finished;
unmarshal(T, V) ->
ff_codec:unmarshal(T, V).
@ -375,21 +344,23 @@ maybe_unmarshal(_Type, undefined) ->
maybe_unmarshal(Type, Value) ->
unmarshal(Type, Value).
%% TESTS
-ifdef(TEST).
-include_lib("eunit/include/eunit.hrl").
-spec test() -> _.
-spec p2p_session_codec_test() -> _.
p2p_session_codec_test() ->
UserInteraction = #{
version => 1,
id => genlib:unique(),
content => {redirect, #{
content => {get, <<"URI">>}
}}
content =>
{redirect, #{
content => {get, <<"URI">>}
}}
},
Callback = #{
@ -402,10 +373,13 @@ p2p_session_codec_test() ->
extra => #{<<"key">> => <<"Extra">>}
},
Resource = {bank_card, #{bank_card => #{
token => <<"token">>,
payment_system => visa
}}},
Resource =
{bank_card, #{
bank_card => #{
token => <<"token">>,
payment_system => visa
}
}},
TransferParams = #{
id => genlib:unique(),

View File

@ -16,32 +16,28 @@
%% Internals
%%
-spec publish_events(list(event())) ->
list(sinkevent()).
-spec publish_events(list(event())) -> list(sinkevent()).
publish_events(Events) ->
[publish_event(Event) || Event <- Events].
-spec publish_event(event()) ->
sinkevent().
-spec publish_event(event()) -> sinkevent().
publish_event(#{
id := ID,
source_id := SourceID,
event := {
id := ID,
source_id := SourceID,
event := {
EventID,
Dt,
{ev, EventDt, Payload}
}
}) ->
#p2p_session_SinkEvent{
id = marshal(event_id, ID),
created_at = marshal(timestamp, Dt),
source = marshal(id, SourceID),
payload = #p2p_session_EventSinkPayload{
sequence = marshal(event_id, EventID),
id = marshal(event_id, ID),
created_at = marshal(timestamp, Dt),
source = marshal(id, SourceID),
payload = #p2p_session_EventSinkPayload{
sequence = marshal(event_id, EventID),
occured_at = marshal(timestamp, EventDt),
changes = [marshal(change, Payload)]
changes = [marshal(change, Payload)]
}
}.

View File

@ -1,4 +1,5 @@
-module(ff_p2p_session_handler).
-behaviour(ff_woody_wrapper).
-include_lib("fistful_proto/include/ff_proto_p2p_session_thrift.hrl").
@ -9,11 +10,11 @@
%%
%% ff_woody_wrapper callbacks
%%
-spec handle_function(woody:func(), woody:args(), woody:options()) ->
{ok, woody:result()} | no_return().
-spec handle_function(woody:func(), woody:args(), woody:options()) -> {ok, woody:result()} | no_return().
handle_function(Func, Args, Opts) ->
scoper:scope(p2p_session, #{},
scoper:scope(
p2p_session,
#{},
fun() ->
handle_function_(Func, Args, Opts)
end
@ -33,7 +34,6 @@ handle_function_('Get', [ID, EventRange], _Opts) ->
{error, {unknown_p2p_session, _Ref}} ->
woody_error:raise(business, #fistful_P2PSessionNotFound{})
end;
handle_function_('GetContext', [ID], _Opts) ->
case p2p_session_machine:get(ID, {undefined, 0}) of
{ok, Machine} ->
@ -42,7 +42,6 @@ handle_function_('GetContext', [ID], _Opts) ->
{error, {unknown_p2p_session, _Ref}} ->
woody_error:raise(business, #fistful_P2PSessionNotFound{})
end;
handle_function_('GetEvents', [ID, EventRange], _Opts) ->
ok = scoper:add_meta(#{id => ID}),
case p2p_session_machine:events(ID, ff_codec:unmarshal(event_range, EventRange)) of

File diff suppressed because it is too large Load Diff

View File

@ -12,8 +12,7 @@
%% ff_woody_wrapper callbacks
%%
-spec handle_function(woody:func(), woody:args(), options()) ->
{ok, woody:result()} | no_return().
-spec handle_function(woody:func(), woody:args(), options()) -> {ok, woody:result()} | no_return().
handle_function('Repair', [ID, Scenario], _Opts) ->
DecodedScenario = ff_codec:unmarshal(ff_p2p_session_codec, repair_scenario, Scenario),
case p2p_session_machine:repair(ID, DecodedScenario) of

View File

@ -16,7 +16,6 @@
-spec marshal_p2p_template_state(p2p_template:template_state(), ff_entity_context:context()) ->
ff_proto_p2p_template_thrift:'P2PTemplateState'().
marshal_p2p_template_state(P2PTemplate, Ctx) ->
#p2p_template_P2PTemplateState{
id = marshal(id, p2p_template:id(P2PTemplate)),
@ -32,13 +31,11 @@ marshal_p2p_template_state(P2PTemplate, Ctx) ->
-spec marshal_p2p_transfer_state(p2p_transfer:p2p_transfer_state(), ff_entity_context:context()) ->
ff_proto_p2p_transfer_thrift:'P2PTransferState'().
marshal_p2p_transfer_state(P2PTransfer, Ctx) ->
ff_p2p_transfer_codec:marshal_p2p_transfer_state(P2PTransfer, Ctx).
-spec unmarshal_p2p_template_params(ff_proto_p2p_template_thrift:'P2PTemplateParams'()) ->
p2p_template_machine:params().
unmarshal_p2p_template_params(#p2p_template_P2PTemplateParams{
id = ID,
identity_id = IdentityID,
@ -54,7 +51,6 @@ unmarshal_p2p_template_params(#p2p_template_P2PTemplateParams{
-spec unmarshal_p2p_quote_params(ff_proto_p2p_template_thrift:'P2PTemplateQuoteParams'()) ->
p2p_template:quote_params().
unmarshal_p2p_quote_params(#p2p_template_P2PTemplateQuoteParams{
sender = Sender,
receiver = Receiver,
@ -68,7 +64,6 @@ unmarshal_p2p_quote_params(#p2p_template_P2PTemplateQuoteParams{
-spec unmarshal_p2p_transfer_params(ff_proto_p2p_template_thrift:'P2PTemplateTransferParams'()) ->
p2p_template:transfer_params().
unmarshal_p2p_transfer_params(#p2p_template_P2PTemplateTransferParams{
id = ID,
sender = Sender,
@ -78,7 +73,6 @@ unmarshal_p2p_transfer_params(#p2p_template_P2PTemplateTransferParams{
quote = Quote,
metadata = Metadata,
deadline = Deadline
}) ->
genlib_map:compact(#{
id => unmarshal(id, ID),
@ -91,31 +85,29 @@ unmarshal_p2p_transfer_params(#p2p_template_P2PTemplateTransferParams{
deadline => maybe_unmarshal(timestamp_ms, Deadline)
}).
-spec marshal(ff_codec:type_name(), ff_codec:decoded_value()) ->
ff_codec:encoded_value().
-spec marshal(ff_codec:type_name(), ff_codec:decoded_value()) -> ff_codec:encoded_value().
marshal({list, T}, V) ->
[marshal(T, E) || E <- V];
marshal(timestamped_change, {ev, Timestamp, Change}) ->
#p2p_template_TimestampedChange{
change = marshal(change, Change),
occured_at = ff_codec:marshal(timestamp, Timestamp)
};
marshal(change, {created, Template}) ->
{created, #p2p_template_CreatedChange{p2p_template = marshal(template, Template)}};
marshal(change, {blocking_changed, Blocking}) ->
{blocking_changed, #p2p_template_BlockingChange{blocking = marshal(blocking, Blocking)}};
marshal(template, Template = #{
id := ID,
identity_id := IdentityID,
domain_revision := DomainRevision,
party_revision := PartyRevision,
created_at := CreatedAt,
details := Details
}) ->
marshal(
template,
Template = #{
id := ID,
identity_id := IdentityID,
domain_revision := DomainRevision,
party_revision := PartyRevision,
created_at := CreatedAt,
details := Details
}
) ->
ExternalID = maps:get(external_id, Template, undefined),
#p2p_template_P2PTemplate{
@ -127,7 +119,6 @@ marshal(template, Template = #{
party_revision = marshal(party_revision, PartyRevision),
external_id = maybe_marshal(id, ExternalID)
};
marshal(details, Details) ->
Body = maps:get(body, Details),
Metadata = maps:get(metadata, Details, undefined),
@ -135,7 +126,6 @@ marshal(details, Details) ->
body = marshal(template_body, Body),
metadata = maybe_marshal(template_metadata, Metadata)
};
marshal(template_body, #{value := Body = #{currency := Currency}}) ->
Amount = maps:get(amount, Body, undefined),
#p2p_template_P2PTemplateBody{
@ -144,51 +134,40 @@ marshal(template_body, #{value := Body = #{currency := Currency}}) ->
currency = marshal(currency_ref, Currency)
}
};
marshal(template_metadata, #{value := Metadata}) ->
#p2p_template_P2PTemplateMetadata{
value = marshal(ctx, Metadata)
};
marshal(quote, Quote) ->
ff_p2p_transfer_codec:marshal(quote, Quote);
marshal(blocking, unblocked) ->
unblocked;
marshal(blocking, blocked) ->
blocked;
marshal(timestamp, Timestamp) when is_integer(Timestamp) ->
ff_time:to_rfc3339(Timestamp);
marshal(ctx, Context) ->
maybe_marshal(context, Context);
marshal(T, V) ->
ff_codec:marshal(T, V).
-spec unmarshal(ff_codec:type_name(), ff_codec:encoded_value()) ->
ff_codec:decoded_value().
-spec unmarshal(ff_codec:type_name(), ff_codec:encoded_value()) -> ff_codec:decoded_value().
unmarshal({list, T}, V) ->
[unmarshal(T, E) || E <- V];
unmarshal(timestamped_change, TimestampedChange) ->
Timestamp = ff_codec:unmarshal(timestamp, TimestampedChange#p2p_template_TimestampedChange.occured_at),
Change = unmarshal(change, TimestampedChange#p2p_template_TimestampedChange.change),
{ev, Timestamp, Change};
unmarshal(repair_scenario, {add_events, #p2p_template_AddEventsRepair{events = Events, action = Action}}) ->
{add_events, genlib_map:compact(#{
events => unmarshal({list, change}, Events),
action => maybe_unmarshal(complex_action, Action)
})};
{add_events,
genlib_map:compact(#{
events => unmarshal({list, change}, Events),
action => maybe_unmarshal(complex_action, Action)
})};
unmarshal(change, {created, #p2p_template_CreatedChange{p2p_template = Template}}) ->
{created, unmarshal(template, Template)};
unmarshal(change, {blocking_changed, #p2p_template_BlockingChange{blocking = Blocking}}) ->
{blocking_changed, unmarshal(blocking, Blocking)};
unmarshal(template, #p2p_template_P2PTemplate{
id = ID,
identity_id = IdentityID,
@ -208,7 +187,6 @@ unmarshal(template, #p2p_template_P2PTemplate{
created_at => ff_time:from_rfc3339(unmarshal(timestamp, CreatedAt)),
external_id => maybe_unmarshal(id, ExternalID)
});
unmarshal(details, #p2p_template_P2PTemplateDetails{
body = Body,
metadata = Metadata
@ -217,7 +195,6 @@ unmarshal(details, #p2p_template_P2PTemplateDetails{
body => unmarshal(template_body, Body),
metadata => maybe_unmarshal(template_metadata, Metadata)
});
unmarshal(template_body, #p2p_template_P2PTemplateBody{
value = #p2p_template_Cash{
amount = Amount,
@ -230,32 +207,24 @@ unmarshal(template_body, #p2p_template_P2PTemplateBody{
currency => unmarshal(currency_ref, Currency)
})
};
unmarshal(template_metadata, #p2p_template_P2PTemplateMetadata{
value = Metadata
}) ->
#{value => unmarshal(ctx, Metadata)};
unmarshal(quote, Quote) ->
ff_p2p_transfer_codec:unmarshal(quote, Quote);
unmarshal(participant, Participant) ->
ff_p2p_transfer_codec:unmarshal(participant, Participant);
unmarshal(client_info, ClientInfo) ->
ff_p2p_transfer_codec:unmarshal(client_info, ClientInfo);
unmarshal(ctx, Context) ->
maybe_unmarshal(context, Context);
unmarshal(blocking, unblocked) ->
unblocked;
unmarshal(blocking, blocked) ->
blocked;
unmarshal(timestamp, Timestamp) ->
unmarshal(string, Timestamp);
unmarshal(T, V) ->
ff_codec:unmarshal(T, V).
@ -275,9 +244,11 @@ maybe_marshal(Type, Value) ->
-ifdef(TEST).
-include_lib("eunit/include/eunit.hrl").
-spec test() -> _.
-spec p2p_template_codec_test() -> _.
p2p_template_codec_test() ->
Details = #{
body => #{

View File

@ -9,32 +9,28 @@
-type event() :: ff_eventsink_publisher:event(p2p_template:event()).
-type sinkevent() :: ff_eventsink_publisher:sinkevent(ff_proto_p2p_template_thrift:'SinkEvent'()).
-spec publish_events(list(event())) ->
list(sinkevent()).
-spec publish_events(list(event())) -> list(sinkevent()).
publish_events(Events) ->
[publish_event(Event) || Event <- Events].
-spec publish_event(event()) ->
sinkevent().
-spec publish_event(event()) -> sinkevent().
publish_event(#{
id := ID,
source_id := SourceID,
event := {
id := ID,
source_id := SourceID,
event := {
EventID,
Dt,
{ev, EventDt, Payload}
}
}) ->
#p2p_template_SinkEvent{
id = marshal(event_id, ID),
created_at = marshal(timestamp, Dt),
source = marshal(id, SourceID),
payload = #p2p_template_EventSinkPayload{
sequence = marshal(event_id, EventID),
id = marshal(event_id, ID),
created_at = marshal(timestamp, Dt),
source = marshal(id, SourceID),
payload = #p2p_template_EventSinkPayload{
sequence = marshal(event_id, EventID),
occured_at = marshal(timestamp, EventDt),
changes = [marshal(change, Payload)]
changes = [marshal(change, Payload)]
}
}.

View File

@ -1,4 +1,5 @@
-module(ff_p2p_template_handler).
-behaviour(ff_woody_wrapper).
-include_lib("fistful_proto/include/ff_proto_p2p_template_thrift.hrl").
@ -10,11 +11,11 @@
%%
%% ff_woody_wrapper callbacks
%%
-spec handle_function(woody:func(), woody:args(), woody:options()) ->
{ok, woody:result()} | no_return().
-spec handle_function(woody:func(), woody:args(), woody:options()) -> {ok, woody:result()} | no_return().
handle_function(Func, Args, Opts) ->
scoper:scope(p2p_template, #{},
scoper:scope(
p2p_template,
#{},
fun() ->
handle_function_(Func, Args, Opts)
end
@ -40,7 +41,6 @@ handle_function_('Create', [MarshaledParams, MarshaledContext], Opts) ->
{error, Error} ->
woody_error:raise(system, {internal, result_unexpected, woody_error:format_details(Error)})
end;
handle_function_('Get', [ID, MarshaledEventRange], _Opts) ->
ok = scoper:add_meta(#{id => ID}),
EventRange = ff_codec:unmarshal(event_range, MarshaledEventRange),
@ -53,7 +53,6 @@ handle_function_('Get', [ID, MarshaledEventRange], _Opts) ->
{error, {unknown_p2p_template, _Ref}} ->
woody_error:raise(business, #fistful_P2PTemplateNotFound{})
end;
handle_function_('GetContext', [ID], _Opts) ->
case p2p_template_machine:get(ID, {undefined, 0}) of
{ok, Machine} ->
@ -63,7 +62,6 @@ handle_function_('GetContext', [ID], _Opts) ->
{error, {unknown_p2p_template, _Ref}} ->
woody_error:raise(business, #fistful_P2PTemplateNotFound{})
end;
handle_function_('SetBlocking', [ID, MarshaledBlocking], _Opts) ->
ok = scoper:add_meta(#{id => ID}),
Blocking = ff_p2p_template_codec:unmarshal(blocking, MarshaledBlocking),
@ -73,7 +71,6 @@ handle_function_('SetBlocking', [ID, MarshaledBlocking], _Opts) ->
{error, {unknown_p2p_template, _Ref}} ->
woody_error:raise(business, #fistful_P2PTemplateNotFound{})
end;
handle_function_('GetQuote', [ID, MarshaledParams], _Opts) ->
ok = scoper:add_meta(#{id => ID}),
Params = ff_p2p_template_codec:unmarshal_p2p_quote_params(MarshaledParams),
@ -108,9 +105,7 @@ handle_function_('GetQuote', [ID, MarshaledParams], _Opts) ->
woody_error:raise(business, #p2p_transfer_NoResourceInfo{type = Type});
{error, Error} ->
woody_error:raise(system, {internal, result_unexpected, woody_error:format_details(Error)})
end;
handle_function_('CreateTransfer', [ID, MarshaledParams, MarshaledContext], Opts) ->
ok = scoper:add_meta(#{id => ID}),
TransferID = MarshaledParams#p2p_template_P2PTemplateTransferParams.id,

View File

@ -21,67 +21,62 @@
-type value_type() :: machinery_mg_schema:vt().
-type context() :: machinery_mg_schema:context().
-type event() :: ff_machine:timestamped_event(p2p_template:event()).
-type event() :: ff_machine:timestamped_event(p2p_template:event()).
-type aux_state() :: ff_machine:auxst().
-type call_args() :: term().
-type call_response() :: term().
-type data() ::
aux_state() |
event() |
call_args() |
call_response().
aux_state()
| event()
| call_args()
| call_response().
%% machinery_mg_schema callbacks
-spec get_version(value_type()) ->
machinery_mg_schema:version().
-spec get_version(value_type()) -> machinery_mg_schema:version().
get_version(event) ->
?CURRENT_EVENT_FORMAT_VERSION;
get_version(aux_state) ->
undefined.
-spec marshal(type(), value(data()), context()) ->
{machinery_msgpack:t(), context()}.
-spec marshal(type(), value(data()), context()) -> {machinery_msgpack:t(), context()}.
marshal({event, Format}, TimestampedChange, Context) ->
marshal_event(Format, TimestampedChange, Context);
marshal(T, V, C) when
T =:= {args, init} orelse
T =:= {args, call} orelse
T =:= {args, repair} orelse
T =:= {aux_state, undefined} orelse
T =:= {response, call} orelse
T =:= {response, {repair, success}} orelse
T =:= {response, {repair, failure}}
T =:= {args, call} orelse
T =:= {args, repair} orelse
T =:= {aux_state, undefined} orelse
T =:= {response, call} orelse
T =:= {response, {repair, success}} orelse
T =:= {response, {repair, failure}}
->
machinery_mg_schema_generic:marshal(T, V, C).
-spec unmarshal(type(), machinery_msgpack:t(), context()) ->
{data(), context()}.
-spec unmarshal(type(), machinery_msgpack:t(), context()) -> {data(), context()}.
unmarshal({event, FormatVersion}, EncodedChange, Context) ->
unmarshal_event(FormatVersion, EncodedChange, Context);
unmarshal(T, V, C) when
T =:= {args, init} orelse
T =:= {args, call} orelse
T =:= {args, repair} orelse
T =:= {aux_state, undefined} orelse
T =:= {response, call} orelse
T =:= {response, {repair, success}} orelse
T =:= {response, {repair, failure}}
T =:= {args, call} orelse
T =:= {args, repair} orelse
T =:= {aux_state, undefined} orelse
T =:= {response, call} orelse
T =:= {response, {repair, success}} orelse
T =:= {response, {repair, failure}}
->
machinery_mg_schema_generic:unmarshal(T, V, C).
%% Internals
-spec marshal_event(machinery_mg_schema:version(), event(), context()) ->
{machinery_msgpack:t(), context()}.
-spec marshal_event(machinery_mg_schema:version(), event(), context()) -> {machinery_msgpack:t(), context()}.
marshal_event(1, TimestampedChange, Context) ->
ThriftChange = ff_p2p_template_codec:marshal(timestamped_change, TimestampedChange),
Type = {struct, struct, {ff_proto_p2p_template_thrift, 'TimestampedChange'}},
{{bin, ff_proto_utils:serialize(Type, ThriftChange)}, Context}.
-spec unmarshal_event(machinery_mg_schema:version(), machinery_msgpack:t(), context()) ->
{event(), context()}.
-spec unmarshal_event(machinery_mg_schema:version(), machinery_msgpack:t(), context()) -> {event(), context()}.
unmarshal_event(1, EncodedChange, Context) ->
{bin, EncodedThriftChange} = EncodedChange,
Type = {struct, struct, {ff_proto_p2p_template_thrift, 'TimestampedChange'}},
@ -92,18 +87,18 @@ unmarshal_event(1, EncodedChange, Context) ->
-ifdef(TEST).
-include_lib("eunit/include/eunit.hrl").
-spec test() -> _.
% tests helpers
-spec marshal(type(), value(data())) ->
machinery_msgpack:t().
-spec marshal(type(), value(data())) -> machinery_msgpack:t().
marshal(Type, Value) ->
{Result, _Context} = marshal(Type, Value, #{}),
Result.
-spec unmarshal(type(), machinery_msgpack:t()) ->
data().
-spec unmarshal(type(), machinery_msgpack:t()) -> data().
unmarshal(Type, Value) ->
{Result, _Context} = unmarshal(Type, Value, #{}),
Result.
@ -129,11 +124,13 @@ created_v1_decoding_test() ->
},
Change = {created, P2PTemplate},
Event = {ev, {{{2020, 5, 25}, {19, 19, 10}}, 293305}, Change},
LegacyEvent = {bin, base64:decode(<<
"CwABAAAAGzIwMjAtMDUtMjVUMTk6MTk6MTAuMjkzMzA1WgwAAgwAAQwAAQsAAQAAAAh0cmFuc2Zlc"
"gsAAgAAAAtpZGVudGl0eV9pZAsAAwAAABgyMDIwLTA1LTI1VDE3OjEyOjU3Ljk4NVoKAAQAAAAAAA"
"AAewoABQAAAAAAAAFBDAAGDAABDAABCgABAAAAAAAAAHsMAAILAAEAAAADUlVCAAAAAAsABwAAAAtleHRlcm5hbF9pZAAAAAA="
>>)},
LegacyEvent =
{bin,
base64:decode(<<
"CwABAAAAGzIwMjAtMDUtMjVUMTk6MTk6MTAuMjkzMzA1WgwAAgwAAQwAAQsAAQAAAAh0cmFuc2Zlc"
"gsAAgAAAAtpZGVudGl0eV9pZAsAAwAAABgyMDIwLTA1LTI1VDE3OjEyOjU3Ljk4NVoKAAQAAAAAAA"
"AAewoABQAAAAAAAAFBDAAGDAABDAABCgABAAAAAAAAAHsMAAILAAEAAAADUlVCAAAAAAsABwAAAAtleHRlcm5hbF9pZAAAAAA="
>>)},
DecodedLegacy = unmarshal({event, 1}, LegacyEvent),
ModernizedBinary = marshal({event, ?CURRENT_EVENT_FORMAT_VERSION}, DecodedLegacy),
Decoded = unmarshal({event, ?CURRENT_EVENT_FORMAT_VERSION}, ModernizedBinary),
@ -143,12 +140,14 @@ created_v1_decoding_test() ->
blocking_v1_decoding_test() ->
Change = {blocking_changed, unblocked},
Event = {ev, {{{2020, 5, 25}, {19, 19, 10}}, 293305}, Change},
LegacyEvent = {bin, base64:decode(<<
"CwABAAAAGzIwMjAtMDUtMjVUMTk6MTk6MTAuMjkzMzA1WgwAAgwAAggAAQAAAAAAAAA="
>>)},
LegacyEvent =
{bin,
base64:decode(<<
"CwABAAAAGzIwMjAtMDUtMjVUMTk6MTk6MTAuMjkzMzA1WgwAAgwAAggAAQAAAAAAAAA="
>>)},
DecodedLegacy = unmarshal({event, 1}, LegacyEvent),
ModernizedBinary = marshal({event, ?CURRENT_EVENT_FORMAT_VERSION}, DecodedLegacy),
Decoded = unmarshal({event, ?CURRENT_EVENT_FORMAT_VERSION}, ModernizedBinary),
?assertEqual(Event, Decoded).
-endif.
-endif.

View File

@ -12,8 +12,7 @@
%% ff_woody_wrapper callbacks
%%
-spec handle_function(woody:func(), woody:args(), options()) ->
{ok, woody:result()} | no_return().
-spec handle_function(woody:func(), woody:args(), options()) -> {ok, woody:result()} | no_return().
handle_function('Repair', [ID, Scenario], _Opts) ->
DecodedScenario = ff_codec:unmarshal(ff_p2p_template_codec, repair_scenario, Scenario),
case p2p_template_machine:repair(ID, DecodedScenario) of

View File

@ -9,16 +9,13 @@
%% API
-spec marshal(ff_codec:type_name(), ff_codec:decoded_value()) ->
ff_codec:encoded_value().
-spec marshal(ff_codec:type_name(), ff_codec:decoded_value()) -> ff_codec:encoded_value().
marshal(change, {created, Adjustment}) ->
{created, #p2p_adj_CreatedChange{adjustment = marshal(adjustment, Adjustment)}};
marshal(change, {status_changed, Status}) ->
{status_changed, #p2p_adj_StatusChange{status = marshal(status, Status)}};
marshal(change, {p_transfer, TransferChange}) ->
{transfer, #p2p_adj_TransferChange{payload = ff_p_transfer_codec:marshal(change, TransferChange)}};
marshal(adjustment, Adjustment) ->
#p2p_adj_Adjustment{
id = marshal(id, ff_adjustment:id(Adjustment)),
@ -47,12 +44,10 @@ marshal(adjustment_state, Adjustment) ->
operation_timestamp = marshal(timestamp_ms, ff_adjustment:operation_timestamp(Adjustment)),
external_id = maybe_marshal(id, ff_adjustment:external_id(Adjustment))
};
marshal(status, pending) ->
{pending, #p2p_adj_Pending{}};
marshal(status, succeeded) ->
{succeeded, #p2p_adj_Succeeded{}};
marshal(changes_plan, Plan) ->
#p2p_adj_ChangesPlan{
new_cash_flow = maybe_marshal(cash_flow_change_plan, maps:get(new_cash_flow, Plan, undefined)),
@ -69,26 +64,20 @@ marshal(status_change_plan, Plan) ->
#p2p_adj_StatusChangePlan{
new_status = ff_p2p_transfer_status_codec:marshal(status, maps:get(new_status, Plan))
};
marshal(change_request, {change_status, Status}) ->
{change_status, #p2p_adj_ChangeStatusRequest{
new_status = ff_p2p_transfer_status_codec:marshal(status, Status)
}};
marshal(T, V) ->
ff_codec:marshal(T, V).
-spec unmarshal(ff_codec:type_name(), ff_codec:encoded_value()) ->
ff_codec:decoded_value().
-spec unmarshal(ff_codec:type_name(), ff_codec:encoded_value()) -> ff_codec:decoded_value().
unmarshal(change, {created, #p2p_adj_CreatedChange{adjustment = Adjustment}}) ->
{created, unmarshal(adjustment, Adjustment)};
unmarshal(change, {status_changed, #p2p_adj_StatusChange{status = Status}}) ->
{status_changed, unmarshal(status, Status)};
unmarshal(change, {transfer, #p2p_adj_TransferChange{payload = TransferChange}}) ->
{p_transfer, ff_p_transfer_codec:unmarshal(change, TransferChange)};
unmarshal(adjustment, Adjustment) ->
#{
id => unmarshal(id, Adjustment#p2p_adj_Adjustment.id),
@ -100,19 +89,16 @@ unmarshal(adjustment, Adjustment) ->
operation_timestamp => unmarshal(timestamp_ms, Adjustment#p2p_adj_Adjustment.operation_timestamp),
external_id => maybe_unmarshal(id, Adjustment#p2p_adj_Adjustment.external_id)
};
unmarshal(adjustment_params, Params) ->
genlib_map:compact(#{
id => unmarshal(id, Params#p2p_adj_AdjustmentParams.id),
change => unmarshal(change_request, Params#p2p_adj_AdjustmentParams.change),
external_id => maybe_unmarshal(id, Params#p2p_adj_AdjustmentParams.external_id)
});
unmarshal(status, {pending, #p2p_adj_Pending{}}) ->
pending;
unmarshal(status, {succeeded, #p2p_adj_Succeeded{}}) ->
succeeded;
unmarshal(changes_plan, Plan) ->
genlib_map:compact(#{
new_cash_flow => maybe_unmarshal(cash_flow_change_plan, Plan#p2p_adj_ChangesPlan.new_cash_flow),
@ -130,11 +116,9 @@ unmarshal(status_change_plan, Plan) ->
#{
new_status => ff_p2p_transfer_status_codec:unmarshal(status, Status)
};
unmarshal(change_request, {change_status, Request}) ->
Status = Request#p2p_adj_ChangeStatusRequest.new_status,
{change_status, ff_p2p_transfer_status_codec:unmarshal(status, Status)};
unmarshal(T, V) ->
ff_codec:unmarshal(T, V).
@ -154,9 +138,11 @@ maybe_marshal(Type, Value) ->
-ifdef(TEST).
-include_lib("eunit/include/eunit.hrl").
-spec test() -> _.
-spec adjustment_codec_test() -> _.
adjustment_codec_test() ->
FinalCashFlow = #{
postings => [

View File

@ -14,9 +14,7 @@
%% API
-spec unmarshal_p2p_quote_params(ff_proto_p2p_transfer_thrift:'QuoteParams'()) ->
p2p_quote:params().
-spec unmarshal_p2p_quote_params(ff_proto_p2p_transfer_thrift:'QuoteParams'()) -> p2p_quote:params().
unmarshal_p2p_quote_params(#p2p_transfer_QuoteParams{
identity_id = IdentityID,
sender = Sender,
@ -32,7 +30,6 @@ unmarshal_p2p_quote_params(#p2p_transfer_QuoteParams{
-spec marshal_p2p_transfer_state(p2p_transfer:p2p_transfer_state(), ff_entity_context:context()) ->
ff_proto_p2p_transfer_thrift:'P2PTransferState'().
marshal_p2p_transfer_state(P2PTransferState, Ctx) ->
CashFlow = p2p_transfer:effective_final_cash_flow(P2PTransferState),
Adjustments = p2p_transfer:adjustments(P2PTransferState),
@ -62,7 +59,6 @@ marshal_p2p_transfer_state(P2PTransferState, Ctx) ->
-spec unmarshal_p2p_transfer_params(ff_proto_p2p_transfer_thrift:'P2PTransferParams'()) ->
p2p_transfer_machine:params().
unmarshal_p2p_transfer_params(#p2p_transfer_P2PTransferParams{
id = ID,
identity_id = IdentityID,
@ -88,9 +84,7 @@ unmarshal_p2p_transfer_params(#p2p_transfer_P2PTransferParams{
metadata => maybe_unmarshal(ctx, Metadata)
}).
-spec marshal_event(p2p_transfer_machine:event()) ->
ff_proto_p2p_transfer_thrift:'Event'().
-spec marshal_event(p2p_transfer_machine:event()) -> ff_proto_p2p_transfer_thrift:'Event'().
marshal_event({EventID, {ev, Timestamp, Change}}) ->
#p2p_transfer_Event{
event = ff_codec:marshal(event_id, EventID),
@ -98,18 +92,14 @@ marshal_event({EventID, {ev, Timestamp, Change}}) ->
change = marshal(change, Change)
}.
-spec marshal(ff_codec:type_name(), ff_codec:decoded_value()) ->
ff_codec:encoded_value().
-spec marshal(ff_codec:type_name(), ff_codec:decoded_value()) -> ff_codec:encoded_value().
marshal({list, T}, V) ->
[marshal(T, E) || E <- V];
marshal(timestamped_change, {ev, Timestamp, Change}) ->
#p2p_transfer_TimestampedChange{
change = marshal(change, Change),
occured_at = ff_codec:marshal(timestamp, Timestamp)
};
marshal(change, {created, Transfer}) ->
{created, #p2p_transfer_CreatedChange{p2p_transfer = marshal(transfer, Transfer)}};
marshal(change, {status_changed, Status}) ->
@ -129,19 +119,21 @@ marshal(change, {adjustment, #{id := ID, payload := Payload}}) ->
id = marshal(id, ID),
payload = ff_p2p_transfer_adjustment_codec:marshal(change, Payload)
}};
marshal(transfer, Transfer = #{
id := ID,
status := Status,
owner := Owner,
sender := Sender,
receiver := Receiver,
body := Body,
domain_revision := DomainRevision,
party_revision := PartyRevision,
operation_timestamp := OperationTimestamp,
created_at := CreatedAt
}) ->
marshal(
transfer,
Transfer = #{
id := ID,
status := Status,
owner := Owner,
sender := Sender,
receiver := Receiver,
body := Body,
domain_revision := DomainRevision,
party_revision := PartyRevision,
operation_timestamp := OperationTimestamp,
created_at := CreatedAt
}
) ->
ExternalID = maps:get(external_id, Transfer, undefined),
Quote = maps:get(quote, Transfer, undefined),
Deadline = maps:get(deadline, Transfer, undefined),
@ -165,7 +157,6 @@ marshal(transfer, Transfer = #{
deadline = maybe_marshal(timestamp_ms, Deadline),
metadata = maybe_marshal(ctx, Metadata)
};
marshal(quote_state, Quote) ->
#p2p_transfer_QuoteState{
fees = maybe_marshal(fees, genlib_map:get(fees, Quote)),
@ -186,24 +177,20 @@ marshal(quote, Quote) ->
sender = marshal(compact_resource, maps:get(sender, Quote)),
receiver = marshal(compact_resource, maps:get(receiver, Quote))
};
marshal(compact_resource, {bank_card, BankCardResource}) ->
#{
token := Token,
bin_data_id := BinDataId
} = BankCardResource,
marshal(resource, {bank_card, #{bank_card => #{token => Token, bin_data_id => BinDataId}}});
marshal(status, Status) ->
ff_p2p_transfer_status_codec:marshal(status, Status);
marshal(participant, {raw, #{resource_params := ResourceParams} = Raw}) ->
ContactInfo = maps:get(contact_info, Raw),
{resource, #p2p_transfer_RawResource{
resource = marshal(resource, ResourceParams),
contact_info = marshal(contact_info, ContactInfo)
}};
marshal(contact_info, ContactInfo) ->
PhoneNumber = maps:get(phone_number, ContactInfo, undefined),
Email = maps:get(email, ContactInfo, undefined),
@ -211,7 +198,6 @@ marshal(contact_info, ContactInfo) ->
phone_number = marshal(string, PhoneNumber),
email = marshal(string, Email)
};
marshal(client_info, ClientInfo) ->
IPAddress = maps:get(ip_address, ClientInfo, undefined),
Fingerprint = maps:get(fingerprint, ClientInfo, undefined),
@ -219,25 +205,24 @@ marshal(client_info, ClientInfo) ->
ip_address = marshal(string, IPAddress),
fingerprint = marshal(string, Fingerprint)
};
marshal(resource_got, {Sender, Receiver}) ->
#p2p_transfer_ResourceChange{payload = {got, #p2p_transfer_ResourceGot{
sender = marshal(resource, Sender),
receiver = marshal(resource, Receiver)
}}};
#p2p_transfer_ResourceChange{
payload =
{got, #p2p_transfer_ResourceGot{
sender = marshal(resource, Sender),
receiver = marshal(resource, Receiver)
}}
};
marshal(risk_score, low) ->
low;
marshal(risk_score, high) ->
high;
marshal(risk_score, fatal) ->
fatal;
marshal(route, #{provider_id := ProviderID}) ->
#p2p_transfer_Route{
provider_id = marshal(integer, ProviderID)
provider_id = marshal(integer, ProviderID)
};
marshal(session, {SessionID, started}) ->
#p2p_transfer_SessionChange{
id = marshal(id, SessionID),
@ -246,53 +231,48 @@ marshal(session, {SessionID, started}) ->
marshal(session, {SessionID, {finished, SessionResult}}) ->
#p2p_transfer_SessionChange{
id = marshal(id, SessionID),
payload = {finished, #p2p_transfer_SessionFinished{
result = marshal(session_result, SessionResult)
}}
payload =
{finished, #p2p_transfer_SessionFinished{
result = marshal(session_result, SessionResult)
}}
};
marshal(session_state, Session) ->
#p2p_transfer_SessionState{
id = marshal(id, maps:get(id, Session)),
result = maybe_marshal(session_result, maps:get(result, Session, undefined))
};
marshal(session_result, success) ->
{succeeded, #p2p_transfer_SessionSucceeded{}};
marshal(session_result, {failure, Failure}) ->
{failed, #p2p_transfer_SessionFailed{failure = marshal(failure, Failure)}};
marshal(ctx, Ctx) ->
maybe_marshal(context, Ctx);
marshal(T, V) ->
ff_codec:marshal(T, V).
-spec unmarshal(ff_codec:type_name(), ff_codec:encoded_value()) ->
ff_codec:decoded_value().
-spec unmarshal(ff_codec:type_name(), ff_codec:encoded_value()) -> ff_codec:decoded_value().
unmarshal({list, T}, V) ->
[unmarshal(T, E) || E <- V];
unmarshal(repair_scenario, {add_events, #p2p_transfer_AddEventsRepair{events = Events, action = Action}}) ->
{add_events, genlib_map:compact(#{
events => unmarshal({list, change}, Events),
action => maybe_unmarshal(complex_action, Action)
})};
{add_events,
genlib_map:compact(#{
events => unmarshal({list, change}, Events),
action => maybe_unmarshal(complex_action, Action)
})};
unmarshal(timestamped_change, TimestampedChange) ->
Timestamp = ff_codec:unmarshal(timestamp, TimestampedChange#p2p_transfer_TimestampedChange.occured_at),
Change = unmarshal(change, TimestampedChange#p2p_transfer_TimestampedChange.change),
{ev, Timestamp, Change};
unmarshal(change, {created, #p2p_transfer_CreatedChange{p2p_transfer = Transfer}}) ->
{created, unmarshal(transfer, Transfer)};
unmarshal(change, {status_changed, #p2p_transfer_StatusChange{status = Status}}) ->
{status_changed, unmarshal(status, Status)};
unmarshal(change, {resource, #p2p_transfer_ResourceChange{
payload = {got, #p2p_transfer_ResourceGot{sender = Sender, receiver = Receiver}
}}}) ->
unmarshal(
change,
{resource, #p2p_transfer_ResourceChange{
payload = {got, #p2p_transfer_ResourceGot{sender = Sender, receiver = Receiver}}
}}
) ->
unmarshal(resource_got, {Sender, Receiver});
unmarshal(change, {risk_score, #p2p_transfer_RiskScoreChange{score = RiskScore}}) ->
{risk_score_changed, unmarshal(risk_score, RiskScore)};
@ -308,7 +288,6 @@ unmarshal(change, {adjustment, Change}) ->
id => unmarshal(id, Change#p2p_transfer_AdjustmentChange.id),
payload => Payload
}};
unmarshal(transfer, #p2p_transfer_P2PTransfer{
id = ID,
owner = Owner,
@ -344,7 +323,6 @@ unmarshal(transfer, #p2p_transfer_P2PTransfer{
deadline => maybe_unmarshal(timestamp_ms, Deadline),
metadata => maybe_unmarshal(ctx, Metadata)
});
unmarshal(quote_state, Quote) ->
genlib_map:compact(#{
fees => maybe_unmarshal(fees, Quote#p2p_transfer_QuoteState.fees),
@ -365,29 +343,28 @@ unmarshal(quote, Quote) ->
sender => unmarshal(compact_resource, Quote#p2p_transfer_Quote.sender),
receiver => unmarshal(compact_resource, Quote#p2p_transfer_Quote.receiver)
});
unmarshal(compact_resource, Resource) ->
{bank_card, #{bank_card := BankCard}} = unmarshal(resource, Resource),
{bank_card, #{
token => maps:get(token, BankCard),
bin_data_id => maps:get(bin_data_id, BankCard)
}};
unmarshal(status, Status) ->
ff_p2p_transfer_status_codec:unmarshal(status, Status);
unmarshal(resource_got, {Sender, Receiver}) ->
{resource_got, unmarshal(resource, Sender), unmarshal(resource, Receiver)};
unmarshal(participant, {resource, #p2p_transfer_RawResource{
resource = Resource,
contact_info = ContactInfo
}}) ->
{raw, genlib_map:compact(#{
resource_params => unmarshal(resource, Resource),
contact_info => unmarshal(contact_info, ContactInfo)
})};
unmarshal(
participant,
{resource, #p2p_transfer_RawResource{
resource = Resource,
contact_info = ContactInfo
}}
) ->
{raw,
genlib_map:compact(#{
resource_params => unmarshal(resource, Resource),
contact_info => unmarshal(contact_info, ContactInfo)
})};
unmarshal(contact_info, #'ContactInfo'{
phone_number = PhoneNumber,
email = Email
@ -396,7 +373,6 @@ unmarshal(contact_info, #'ContactInfo'{
phone_number => maybe_unmarshal(string, PhoneNumber),
email => maybe_unmarshal(string, Email)
});
unmarshal(client_info, #'ClientInfo'{
ip_address = IPAddress,
fingerprint = Fingerprint
@ -405,39 +381,32 @@ unmarshal(client_info, #'ClientInfo'{
ip_address => maybe_unmarshal(string, IPAddress),
fingerprint => maybe_unmarshal(string, Fingerprint)
});
unmarshal(risk_score, low) ->
low;
unmarshal(risk_score, high) ->
high;
unmarshal(risk_score, fatal) ->
fatal;
unmarshal(route, #p2p_transfer_Route{provider_id = ProviderID}) ->
#{
version => 1,
provider_id => unmarshal(integer, ProviderID)
};
unmarshal(session, {SessionID, {started, #p2p_transfer_SessionStarted{}}}) ->
{SessionID, started};
unmarshal(session, {SessionID, {finished, #p2p_transfer_SessionFinished{result = SessionResult}}}) ->
{SessionID, {finished, unmarshal(session_result, SessionResult)}};
unmarshal(session_state, Session) ->
genlib_map:compact(#{
id => unmarshal(id, Session#p2p_transfer_SessionState.id),
result => maybe_unmarshal(session_result, Session#p2p_transfer_SessionState.result)
});
unmarshal(session_result, {succeeded, #p2p_transfer_SessionSucceeded{}}) ->
success;
unmarshal(session_result, {failed, #p2p_transfer_SessionFailed{failure = Failure}}) ->
{failure, unmarshal(failure, Failure)};
unmarshal(ctx, Ctx) ->
maybe_unmarshal(context, Ctx);
unmarshal(T, V) ->
ff_codec:unmarshal(T, V).
@ -457,9 +426,11 @@ maybe_marshal(Type, Value) ->
-ifdef(TEST).
-include_lib("eunit/include/eunit.hrl").
-spec test() -> _.
-spec p2p_transfer_codec_test() -> _.
p2p_transfer_codec_test() ->
FinalCashFlow = #{
postings => []
@ -488,15 +459,19 @@ p2p_transfer_codec_test() ->
external_id => genlib:unique()
},
Resource = {bank_card, #{bank_card => #{
token => genlib:unique(),
bin_data_id => {binary, genlib:unique()}
}}},
Resource =
{bank_card, #{
bank_card => #{
token => genlib:unique(),
bin_data_id => {binary, genlib:unique()}
}
}},
Participant = {raw, #{
resource_params => Resource,
contact_info => #{}
}},
Participant =
{raw, #{
resource_params => Resource,
contact_info => #{}
}},
Quote = #{
fees => #{
@ -551,15 +526,19 @@ p2p_transfer_codec_test() ->
-spec p2p_timestamped_change_codec_test() -> _.
p2p_timestamped_change_codec_test() ->
Resource = {bank_card, #{bank_card => #{
token => genlib:unique(),
bin_data_id => {binary, genlib:unique()}
}}},
Resource =
{bank_card, #{
bank_card => #{
token => genlib:unique(),
bin_data_id => {binary, genlib:unique()}
}
}},
Participant = {raw, #{
resource_params => Resource,
contact_info => #{}
}},
Participant =
{raw, #{
resource_params => Resource,
contact_info => #{}
}},
P2PTransfer = #{
version => 3,

View File

@ -9,32 +9,28 @@
-type event() :: ff_eventsink_publisher:event(p2p_transfer:event()).
-type sinkevent() :: ff_eventsink_publisher:sinkevent(ff_proto_p2p_transfer_thrift:'SinkEvent'()).
-spec publish_events(list(event())) ->
list(sinkevent()).
-spec publish_events(list(event())) -> list(sinkevent()).
publish_events(Events) ->
[publish_event(Event) || Event <- Events].
-spec publish_event(event()) ->
sinkevent().
-spec publish_event(event()) -> sinkevent().
publish_event(#{
id := ID,
source_id := SourceID,
event := {
id := ID,
source_id := SourceID,
event := {
EventID,
Dt,
{ev, EventDt, Payload}
}
}) ->
#p2p_transfer_SinkEvent{
id = marshal(event_id, ID),
created_at = marshal(timestamp, Dt),
source = marshal(id, SourceID),
payload = #p2p_transfer_EventSinkPayload{
sequence = marshal(event_id, EventID),
id = marshal(event_id, ID),
created_at = marshal(timestamp, Dt),
source = marshal(id, SourceID),
payload = #p2p_transfer_EventSinkPayload{
sequence = marshal(event_id, EventID),
occured_at = marshal(timestamp, EventDt),
changes = [marshal(change, Payload)]
changes = [marshal(change, Payload)]
}
}.

View File

@ -1,4 +1,5 @@
-module(ff_p2p_transfer_handler).
-behaviour(ff_woody_wrapper).
-include_lib("fistful_proto/include/ff_proto_p2p_transfer_thrift.hrl").
@ -10,11 +11,11 @@
%%
%% ff_woody_wrapper callbacks
%%
-spec handle_function(woody:func(), woody:args(), woody:options()) ->
{ok, woody:result()} | no_return().
-spec handle_function(woody:func(), woody:args(), woody:options()) -> {ok, woody:result()} | no_return().
handle_function(Func, Args, Opts) ->
scoper:scope(p2p_transfer, #{},
scoper:scope(
p2p_transfer,
#{},
fun() ->
handle_function_(Func, Args, Opts)
end
@ -85,7 +86,6 @@ handle_function_('Create', [MarshaledParams, MarshaledContext], Opts) ->
{error, Error} ->
woody_error:raise(system, {internal, result_unexpected, woody_error:format_details(Error)})
end;
handle_function_('Get', [ID, EventRange], _Opts) ->
{After, Limit} = ff_codec:unmarshal(event_range, EventRange),
ok = scoper:add_meta(#{id => ID}),
@ -98,7 +98,6 @@ handle_function_('Get', [ID, EventRange], _Opts) ->
{error, {unknown_p2p_transfer, _Ref}} ->
woody_error:raise(business, #fistful_P2PNotFound{})
end;
handle_function_('GetContext', [ID], _Opts) ->
case p2p_transfer_machine:get(ID, {undefined, 0}) of
{ok, Machine} ->
@ -108,7 +107,6 @@ handle_function_('GetContext', [ID], _Opts) ->
{error, {unknown_p2p_transfer, _Ref}} ->
woody_error:raise(business, #fistful_P2PNotFound{})
end;
handle_function_('GetEvents', [ID, EventRange], _Opts) ->
ok = scoper:add_meta(#{id => ID}),
case p2p_transfer_machine:events(ID, ff_codec:unmarshal(event_range, EventRange)) of
@ -117,15 +115,16 @@ handle_function_('GetEvents', [ID, EventRange], _Opts) ->
{error, {unknown_p2p_transfer, ID}} ->
woody_error:raise(business, #fistful_P2PNotFound{})
end;
handle_function_('CreateAdjustment', [ID, MarshaledParams], _Opts) ->
Params = ff_p2p_transfer_adjustment_codec:unmarshal(adjustment_params, MarshaledParams),
AdjustmentID = maps:get(id, Params),
ok = scoper:add_meta(genlib_map:compact(#{
id => ID,
adjustment_id => AdjustmentID,
external_id => maps:get(external_id, Params, undefined)
})),
ok = scoper:add_meta(
genlib_map:compact(#{
id => ID,
adjustment_id => AdjustmentID,
external_id => maps:get(external_id, Params, undefined)
})
),
case p2p_transfer_machine:start_adjustment(ID, Params) of
ok ->
{ok, Machine} = p2p_transfer_machine:get(ID),

File diff suppressed because it is too large Load Diff

View File

@ -12,8 +12,7 @@
%% ff_woody_wrapper callbacks
%%
-spec handle_function(woody:func(), woody:args(), options()) ->
{ok, woody:result()} | no_return().
-spec handle_function(woody:func(), woody:args(), options()) -> {ok, woody:result()} | no_return().
handle_function('Repair', [ID, Scenario], _Opts) ->
DecodedScenario = ff_codec:unmarshal(ff_p2p_transfer_codec, repair_scenario, Scenario),
case p2p_transfer_machine:repair(ID, DecodedScenario) of

View File

@ -9,30 +9,23 @@
%% API
-spec marshal(ff_codec:type_name(), ff_codec:decoded_value()) ->
ff_codec:encoded_value().
-spec marshal(ff_codec:type_name(), ff_codec:decoded_value()) -> ff_codec:encoded_value().
marshal(status, pending) ->
{pending, #p2p_status_Pending{}};
marshal(status, succeeded) ->
{succeeded, #p2p_status_Succeeded{}};
marshal(status, {failed, Failure}) ->
{failed, #p2p_status_Failed{failure = marshal(failure, Failure)}};
marshal(T, V) ->
ff_codec:marshal(T, V).
-spec unmarshal(ff_codec:type_name(), ff_codec:encoded_value()) ->
ff_codec:decoded_value().
-spec unmarshal(ff_codec:type_name(), ff_codec:encoded_value()) -> ff_codec:decoded_value().
unmarshal(status, {pending, #p2p_status_Pending{}}) ->
pending;
unmarshal(status, {succeeded, #p2p_status_Succeeded{}}) ->
succeeded;
unmarshal(status, {failed, #p2p_status_Failed{failure = Failure}}) ->
{failed, unmarshal(failure, Failure)};
unmarshal(T, V) ->
ff_codec:unmarshal(T, V).
@ -40,9 +33,11 @@ unmarshal(T, V) ->
-ifdef(TEST).
-include_lib("eunit/include/eunit.hrl").
-spec test() -> _.
-spec pending_symmetry_test() -> _.
pending_symmetry_test() ->
Status = pending,
?assertEqual(Status, unmarshal(status, marshal(status, Status))).
@ -54,13 +49,14 @@ succeeded_symmetry_test() ->
-spec failed_symmetry_test() -> _.
failed_symmetry_test() ->
Status = {failed, #{
code => <<"test">>,
reason => <<"why not">>,
sub => #{
code => <<"sub">>
}
}},
Status =
{failed, #{
code => <<"test">>,
reason => <<"why not">>,
sub => #{
code => <<"sub">>
}
}},
?assertEqual(Status, unmarshal(status, marshal(status, Status))).
-endif.

View File

@ -11,25 +11,20 @@
%% API
-spec marshal(ff_codec:type_name(), ff_codec:decoded_value()) ->
ff_codec:encoded_value().
-spec marshal(ff_codec:type_name(), ff_codec:decoded_value()) -> ff_codec:encoded_value().
marshal({list, T}, V) ->
[marshal(T, E) || E <- V];
marshal(change, {created, Transfer}) ->
{created, #transfer_CreatedChange{transfer = marshal(transfer, Transfer)}};
marshal(change, {status_changed, Status}) ->
{status_changed, #transfer_StatusChange{status = marshal(status, Status)}};
marshal(change, {clock_updated, Clock}) ->
{clock_updated, #transfer_ClockChange{clock = marshal(clock, Clock)}};
marshal(transfer, Transfer) ->
#transfer_Transfer{
id = marshal(id, ff_postings_transfer:id(Transfer)),
cashflow = ff_cash_flow_codec:marshal(final_cash_flow, ff_postings_transfer:final_cash_flow(Transfer))
};
marshal(status, created) ->
{created, #transfer_Created{}};
marshal(status, prepared) ->
@ -38,37 +33,28 @@ marshal(status, committed) ->
{committed, #transfer_Committed{}};
marshal(status, cancelled) ->
{cancelled, #transfer_Cancelled{}};
marshal(clock, Clock) ->
ff_clock:marshal(transfer, Clock);
marshal(T, V) ->
ff_codec:marshal(T, V).
-spec unmarshal(ff_codec:type_name(), ff_codec:encoded_value()) ->
ff_codec:decoded_value().
-spec unmarshal(ff_codec:type_name(), ff_codec:encoded_value()) -> ff_codec:decoded_value().
unmarshal({list, T}, V) ->
[unmarshal(T, E) || E <- V];
unmarshal(change, {created, #transfer_CreatedChange{transfer = Transfer}}) ->
{created, unmarshal(transfer, Transfer)};
unmarshal(change, {status_changed, #transfer_StatusChange{status = Status}}) ->
{status_changed, unmarshal(status, Status)};
unmarshal(change, {clock_updated, #transfer_ClockChange{clock = Clock}}) ->
{clock_updated, unmarshal(clock, Clock)};
unmarshal(transfer, Transfer) ->
#{
id => ff_codec:unmarshal(id, Transfer#transfer_Transfer.id),
id => ff_codec:unmarshal(id, Transfer#transfer_Transfer.id),
final_cash_flow => ff_cash_flow_codec:unmarshal(final_cash_flow, Transfer#transfer_Transfer.cashflow)
};
unmarshal(account_type, CashflowAccount) ->
% Mapped to thrift type WalletCashFlowAccount as is
CashflowAccount;
unmarshal(status, {created, #transfer_Created{}}) ->
created;
unmarshal(status, {prepared, #transfer_Prepared{}}) ->
@ -77,9 +63,7 @@ unmarshal(status, {committed, #transfer_Committed{}}) ->
committed;
unmarshal(status, {cancelled, #transfer_Cancelled{}}) ->
cancelled;
unmarshal(clock, Clock) ->
ff_clock:unmarshal(transfer, Clock);
unmarshal(T, V) ->
ff_codec:unmarshal(T, V).

View File

@ -6,24 +6,24 @@
-spec serialize(thrift_type(), term()) -> binary().
-type thrift_type() ::
thrift_base_type() |
thrift_collection_type() |
thrift_enum_type() |
thrift_struct_type().
thrift_base_type()
| thrift_collection_type()
| thrift_enum_type()
| thrift_struct_type().
-type thrift_base_type() ::
bool |
double |
i8 |
i16 |
i32 |
i64 |
string.
bool
| double
| i8
| i16
| i32
| i64
| string.
-type thrift_collection_type() ::
{list, thrift_type()} |
{set, thrift_type()} |
{map, thrift_type(), thrift_type()}.
{list, thrift_type()}
| {set, thrift_type()}
| {map, thrift_type(), thrift_type()}.
-type thrift_enum_type() ::
{enum, thrift_type_ref()}.
@ -54,9 +54,7 @@ serialize(Type, Data) ->
erlang:error({thrift, {protocol, Reason}})
end.
-spec deserialize(thrift_type(), binary()) ->
term().
-spec deserialize(thrift_type(), binary()) -> term().
deserialize(Type, Data) ->
{ok, Trans} = thrift_membuffer_transport:new(Data),
{ok, Proto} = new_protocol(Trans),

View File

@ -1,4 +1,5 @@
-module(ff_provider_handler).
-behaviour(ff_woody_wrapper).
-include_lib("fistful_proto/include/ff_proto_provider_thrift.hrl").
@ -9,11 +10,11 @@
%%
%% ff_woody_wrapper callbacks
%%
-spec handle_function(woody:func(), woody:args(), woody:options()) ->
{ok, woody:result()} | no_return().
-spec handle_function(woody:func(), woody:args(), woody:options()) -> {ok, woody:result()} | no_return().
handle_function(Func, Args, Opts) ->
scoper:scope(provider, #{},
scoper:scope(
provider,
#{},
fun() ->
handle_function_(Func, Args, Opts)
end
@ -31,21 +32,16 @@ handle_function_('GetProvider', [ID], _Opts) ->
{error, notfound} ->
woody_error:raise(business, #fistful_ProviderNotFound{})
end;
handle_function_('ListProviders', _, _Opts) ->
{ok, marshal_providers(ff_provider:list())}.
%%
-spec marshal_providers([ff_provider:provider()]) ->
[ff_proto_provider_thrift:'Provider'()].
-spec marshal_providers([ff_provider:provider()]) -> [ff_proto_provider_thrift:'Provider'()].
marshal_providers(Providers) when is_list(Providers) ->
lists:map(fun(Provider) -> marshal_provider(Provider) end, Providers).
-spec marshal_provider(ff_provider:provider()) ->
ff_proto_provider_thrift:'Provider'().
-spec marshal_provider(ff_provider:provider()) -> ff_proto_provider_thrift:'Provider'().
marshal_provider(Provider) ->
ID = ff_provider:id(Provider),
Name = ff_provider:name(Provider),
@ -66,13 +62,10 @@ marshal_residence(Residence) ->
-spec marshal_identity_classes(ff_provider:identity_classes()) ->
#{ff_proto_provider_thrift:'IdentityClassID'() => ff_proto_provider_thrift:'IdentityClass'()}.
marshal_identity_classes(Map) ->
maps:map(fun(_ClassID, Class) -> marshal_identity_class(Class) end, Map).
-spec marshal_identity_class(ff_identity_class:class()) ->
ff_proto_provider_thrift:'IdentityClass'().
-spec marshal_identity_class(ff_identity_class:class()) -> ff_proto_provider_thrift:'IdentityClass'().
marshal_identity_class(Class) ->
#provider_IdentityClass{
id = ff_identity_class:id(Class),

View File

@ -25,41 +25,34 @@
-export([init/1]).
-define(DEFAULT_HANDLING_TIMEOUT, 30000). % 30 seconds
% 30 seconds
-define(DEFAULT_HANDLING_TIMEOUT, 30000).
%%
-spec start() ->
{ok, _}.
-spec start() -> {ok, _}.
start() ->
application:ensure_all_started(?MODULE).
%% Application
-spec start(normal, any()) ->
{ok, pid()} | {error, any()}.
-spec start(normal, any()) -> {ok, pid()} | {error, any()}.
start(_StartType, _StartArgs) ->
supervisor:start_link({local, ?MODULE}, ?MODULE, []).
-spec stop(any()) ->
ok.
-spec stop(any()) -> ok.
stop(_State) ->
ok.
%% Supervisor
-spec init([]) ->
{ok, {supervisor:sup_flags(), [supervisor:child_spec()]}}.
-spec init([]) -> {ok, {supervisor:sup_flags(), [supervisor:child_spec()]}}.
init([]) ->
IpEnv = genlib_app:env(?MODULE, ip, "::1"),
Port = genlib_app:env(?MODULE, port, 8022),
HealthCheck = genlib_app:env(?MODULE, health_check, #{}),
WoodyOptsEnv = genlib_app:env(?MODULE, woody_opts, #{}),
RouteOptsEnv = genlib_app:env(?MODULE, route_opts, #{}),
IpEnv = genlib_app:env(?MODULE, ip, "::1"),
Port = genlib_app:env(?MODULE, port, 8022),
HealthCheck = genlib_app:env(?MODULE, health_check, #{}),
WoodyOptsEnv = genlib_app:env(?MODULE, woody_opts, #{}),
RouteOptsEnv = genlib_app:env(?MODULE, route_opts, #{}),
PartyClient = party_client:create_client(),
DefaultTimeout = genlib_app:env(?MODULE, default_woody_handling_timeout, ?DEFAULT_HANDLING_TIMEOUT),
@ -68,51 +61,52 @@ init([]) ->
default_handling_timeout => DefaultTimeout
},
{ok, Ip} = inet:parse_address(IpEnv),
WoodyOpts = maps:with([net_opts, handler_limits], WoodyOptsEnv),
{ok, Ip} = inet:parse_address(IpEnv),
WoodyOpts = maps:with([net_opts, handler_limits], WoodyOptsEnv),
EventHandlerOpts = genlib_app:env(?MODULE, scoper_event_handler_options, #{}),
RouteOpts = RouteOptsEnv#{event_handler => {scoper_woody_event_handler, EventHandlerOpts}},
RouteOpts = RouteOptsEnv#{event_handler => {scoper_woody_event_handler, EventHandlerOpts}},
% TODO
% - Make it palatable
{Backends, MachineHandlers, ModernizerHandlers} = lists:unzip3([
contruct_backend_childspec('ff/identity' , ff_identity_machine , PartyClient),
contruct_backend_childspec('ff/wallet_v2' , ff_wallet_machine , PartyClient),
contruct_backend_childspec('ff/source_v1' , ff_source_machine , PartyClient),
contruct_backend_childspec('ff/destination_v2' , ff_destination_machine , PartyClient),
contruct_backend_childspec('ff/deposit_v1' , ff_deposit_machine , PartyClient),
contruct_backend_childspec('ff/withdrawal_v2' , ff_withdrawal_machine , PartyClient),
contruct_backend_childspec('ff/withdrawal/session_v2' , ff_withdrawal_session_machine , PartyClient),
contruct_backend_childspec('ff/p2p_transfer_v1' , p2p_transfer_machine , PartyClient),
contruct_backend_childspec('ff/p2p_transfer/session_v1' , p2p_session_machine , PartyClient),
contruct_backend_childspec('ff/w2w_transfer_v1' , w2w_transfer_machine , PartyClient),
contruct_backend_childspec('ff/p2p_template_v1' , p2p_template_machine , PartyClient)
contruct_backend_childspec('ff/identity', ff_identity_machine, PartyClient),
contruct_backend_childspec('ff/wallet_v2', ff_wallet_machine, PartyClient),
contruct_backend_childspec('ff/source_v1', ff_source_machine, PartyClient),
contruct_backend_childspec('ff/destination_v2', ff_destination_machine, PartyClient),
contruct_backend_childspec('ff/deposit_v1', ff_deposit_machine, PartyClient),
contruct_backend_childspec('ff/withdrawal_v2', ff_withdrawal_machine, PartyClient),
contruct_backend_childspec('ff/withdrawal/session_v2', ff_withdrawal_session_machine, PartyClient),
contruct_backend_childspec('ff/p2p_transfer_v1', p2p_transfer_machine, PartyClient),
contruct_backend_childspec('ff/p2p_transfer/session_v1', p2p_session_machine, PartyClient),
contruct_backend_childspec('ff/w2w_transfer_v1', w2w_transfer_machine, PartyClient),
contruct_backend_childspec('ff/p2p_template_v1', p2p_template_machine, PartyClient)
]),
ok = application:set_env(fistful, backends, maps:from_list(Backends)),
Services = [
{fistful_admin, ff_server_admin_handler},
{fistful_provider, ff_provider_handler},
{ff_p2p_adapter_host, ff_p2p_adapter_host},
{ff_withdrawal_adapter_host, ff_withdrawal_adapter_host},
{wallet_management, ff_wallet_handler},
{identity_management, ff_identity_handler},
{destination_management, ff_destination_handler},
{source_management, ff_source_handler},
{withdrawal_management, ff_withdrawal_handler},
{withdrawal_session_management, ff_withdrawal_session_handler},
{deposit_management, ff_deposit_handler},
{withdrawal_session_repairer, ff_withdrawal_session_repair},
{withdrawal_repairer, ff_withdrawal_repair},
{deposit_repairer, ff_deposit_repair},
{p2p_transfer_management, ff_p2p_transfer_handler},
{p2p_transfer_repairer, ff_p2p_transfer_repair},
{p2p_session_management, ff_p2p_session_handler},
{p2p_session_repairer, ff_p2p_session_repair},
{p2p_template_management, ff_p2p_template_handler},
{w2w_transfer_management, ff_w2w_transfer_handler},
{w2w_transfer_repairer, ff_w2w_transfer_repair}
] ++ get_eventsink_handlers(),
Services =
[
{fistful_admin, ff_server_admin_handler},
{fistful_provider, ff_provider_handler},
{ff_p2p_adapter_host, ff_p2p_adapter_host},
{ff_withdrawal_adapter_host, ff_withdrawal_adapter_host},
{wallet_management, ff_wallet_handler},
{identity_management, ff_identity_handler},
{destination_management, ff_destination_handler},
{source_management, ff_source_handler},
{withdrawal_management, ff_withdrawal_handler},
{withdrawal_session_management, ff_withdrawal_session_handler},
{deposit_management, ff_deposit_handler},
{withdrawal_session_repairer, ff_withdrawal_session_repair},
{withdrawal_repairer, ff_withdrawal_repair},
{deposit_repairer, ff_deposit_repair},
{p2p_transfer_management, ff_p2p_transfer_handler},
{p2p_transfer_repairer, ff_p2p_transfer_repair},
{p2p_session_management, ff_p2p_session_handler},
{p2p_session_repairer, ff_p2p_session_repair},
{p2p_template_management, ff_p2p_template_handler},
{w2w_transfer_management, ff_w2w_transfer_handler},
{w2w_transfer_repairer, ff_w2w_transfer_repair}
] ++ get_eventsink_handlers(),
WoodyHandlers = [get_handler(Service, Handler, WrapperOpts) || {Service, Handler} <- Services],
ServicesChildSpec = woody_server:child_spec(
@ -120,10 +114,10 @@ init([]) ->
maps:merge(
WoodyOpts,
#{
ip => Ip,
port => Port,
handlers => WoodyHandlers,
event_handler => scoper_woody_event_handler,
ip => Ip,
port => Port,
handlers => WoodyHandlers,
event_handler => scoper_woody_event_handler,
additional_routes =>
machinery_mg_backend:get_routes(MachineHandlers, RouteOpts) ++
machinery_modernizer_mg_backend:get_routes(ModernizerHandlers, RouteOpts) ++
@ -136,16 +130,12 @@ init([]) ->
% - Zero thoughts given while defining this strategy.
{ok, {#{strategy => one_for_one}, [PartyClientSpec, ServicesChildSpec]}}.
-spec enable_health_logging(erl_health:check()) ->
erl_health:check().
-spec enable_health_logging(erl_health:check()) -> erl_health:check().
enable_health_logging(Check) ->
EvHandler = {erl_health_event_handler, []},
maps:map(fun (_, V = {_, _, _}) -> #{runner => V, event_handler => EvHandler} end, Check).
-spec get_handler(ff_services:service_name(), woody:handler(_), map()) ->
woody:http_handler(woody:th_handler()).
maps:map(fun(_, V = {_, _, _}) -> #{runner => V, event_handler => EvHandler} end, Check).
-spec get_handler(ff_services:service_name(), woody:handler(_), map()) -> woody:http_handler(woody:th_handler()).
get_handler(Service, Handler, WrapperOpts) ->
{Path, ServiceSpec} = ff_services:get_service_spec(Service),
{Path, {ServiceSpec, wrap_handler(Handler, WrapperOpts)}}.
@ -159,22 +149,21 @@ contruct_backend_childspec(NS, Handler, PartyClient) ->
}.
construct_machinery_backend_spec(NS, Schema) ->
{NS, {machinery_mg_backend, #{
schema => Schema,
client => get_service_client(automaton)
}}}.
{NS,
{machinery_mg_backend, #{
schema => Schema,
client => get_service_client(automaton)
}}}.
construct_machinery_handler_spec(NS, Handler, Schema, PartyClient) ->
{{fistful, #{handler => Handler, party_client => PartyClient}},
#{
path => ff_string:join(["/v1/stateproc/", NS]),
backend_config => #{schema => Schema}
}
}.
{{fistful, #{handler => Handler, party_client => PartyClient}}, #{
path => ff_string:join(["/v1/stateproc/", NS]),
backend_config => #{schema => Schema}
}}.
construct_machinery_modernizer_spec(NS, Schema) ->
#{
path => ff_string:join(["/v1/modernizer/", NS]),
path => ff_string:join(["/v1/modernizer/", NS]),
backend_config => #{schema => Schema}
}.

View File

@ -1,4 +1,5 @@
-module(ff_server_admin_handler).
-behaviour(ff_woody_wrapper).
-include_lib("fistful_proto/include/ff_proto_fistful_admin_thrift.hrl").
@ -10,10 +11,11 @@
%% ff_woody_wrapper callbacks
%%
-spec handle_function(woody:func(), woody:args(), woody:options()) ->
{ok, woody:result()} | no_return().
-spec handle_function(woody:func(), woody:args(), woody:options()) -> {ok, woody:result()} | no_return().
handle_function(Func, Args, Opts) ->
scoper:scope(fistful_admin, #{},
scoper:scope(
fistful_admin,
#{},
fun() ->
handle_function_(Func, Args, Opts)
end
@ -25,13 +27,17 @@ handle_function(Func, Args, Opts) ->
handle_function_('CreateSource', [Params], Opts) ->
SourceID = Params#ff_admin_SourceParams.id,
case ff_source_machine:create(#{
id => SourceID,
identity => Params#ff_admin_SourceParams.identity_id,
name => Params#ff_admin_SourceParams.name,
currency => ff_codec:unmarshal(currency_ref, Params#ff_admin_SourceParams.currency),
resource => ff_source_codec:unmarshal(resource, Params#ff_admin_SourceParams.resource)
}, ff_entity_context:new())
case
ff_source_machine:create(
#{
id => SourceID,
identity => Params#ff_admin_SourceParams.identity_id,
name => Params#ff_admin_SourceParams.name,
currency => ff_codec:unmarshal(currency_ref, Params#ff_admin_SourceParams.currency),
resource => ff_source_codec:unmarshal(resource, Params#ff_admin_SourceParams.resource)
},
ff_entity_context:new()
)
of
ok ->
handle_function_('GetSource', [SourceID], Opts);
@ -53,10 +59,10 @@ handle_function_('GetSource', [ID], _Opts) ->
handle_function_('CreateDeposit', [Params], Opts) ->
DepositID = Params#ff_admin_DepositParams.id,
DepositParams = #{
id => DepositID,
source_id => Params#ff_admin_DepositParams.source,
wallet_id => Params#ff_admin_DepositParams.destination,
body => ff_codec:unmarshal(cash, Params#ff_admin_DepositParams.body)
id => DepositID,
source_id => Params#ff_admin_DepositParams.source,
wallet_id => Params#ff_admin_DepositParams.destination,
body => ff_codec:unmarshal(cash, Params#ff_admin_DepositParams.body)
},
case handle_create_result(ff_deposit_machine:create(DepositParams, ff_entity_context:new())) of
ok ->
@ -91,4 +97,3 @@ handle_create_result({error, exists}) ->
ok;
handle_create_result({error, _Reason} = Error) ->
Error.

View File

@ -10,7 +10,7 @@
%%
-type service() :: woody:service().
-type service() :: woody:service().
-type service_name() :: atom().
-type service_spec() :: {Path :: string(), service()}.

View File

@ -13,30 +13,28 @@
%% API
-spec unmarshal_source_params(ff_proto_source_thrift:'SourceParams'()) ->
ff_source:params().
-spec unmarshal_source_params(ff_proto_source_thrift:'SourceParams'()) -> ff_source:params().
unmarshal_source_params(Params) ->
genlib_map:compact(#{
id => unmarshal(id, Params#src_SourceParams.id),
identity => unmarshal(id, Params#src_SourceParams.identity_id),
name => unmarshal(string, Params#src_SourceParams.name),
currency => unmarshal(currency_ref, Params#src_SourceParams.currency),
resource => unmarshal(resource, Params#src_SourceParams.resource),
id => unmarshal(id, Params#src_SourceParams.id),
identity => unmarshal(id, Params#src_SourceParams.identity_id),
name => unmarshal(string, Params#src_SourceParams.name),
currency => unmarshal(currency_ref, Params#src_SourceParams.currency),
resource => unmarshal(resource, Params#src_SourceParams.resource),
external_id => maybe_unmarshal(id, Params#src_SourceParams.external_id),
metadata => maybe_unmarshal(ctx, Params#src_SourceParams.metadata)
metadata => maybe_unmarshal(ctx, Params#src_SourceParams.metadata)
}).
-spec marshal_source_state(ff_source:source_state(), ff_entity_context:context()) ->
ff_proto_source_thrift:'SourceState'().
marshal_source_state(SourceState, Context) ->
Blocking = case ff_source:is_accessible(SourceState) of
{ok, accessible} ->
unblocked;
_ ->
blocked
end,
Blocking =
case ff_source:is_accessible(SourceState) of
{ok, accessible} ->
unblocked;
_ ->
blocked
end,
#src_SourceState{
id = maybe_marshal(id, ff_source:id(SourceState)),
name = marshal(string, ff_source:name(SourceState)),
@ -50,9 +48,7 @@ marshal_source_state(SourceState, Context) ->
context = maybe_marshal(ctx, Context)
}.
-spec marshal_event(ff_source_machine:event()) ->
ff_proto_source_thrift:'Event'().
-spec marshal_event(ff_source_machine:event()) -> ff_proto_source_thrift:'Event'().
marshal_event({EventID, {ev, Timestamp, Change}}) ->
#src_Event{
event_id = ff_codec:marshal(event_id, EventID),
@ -60,33 +56,32 @@ marshal_event({EventID, {ev, Timestamp, Change}}) ->
change = marshal(change, Change)
}.
-spec marshal(ff_codec:type_name(), ff_codec:decoded_value()) ->
ff_codec:encoded_value().
-spec marshal(ff_codec:type_name(), ff_codec:decoded_value()) -> ff_codec:encoded_value().
marshal(timestamped_change, {ev, Timestamp, Change}) ->
#src_TimestampedChange{
change = marshal(change, Change),
occured_at = ff_codec:marshal(timestamp, Timestamp)
};
marshal(change, {created, Source}) ->
{created, marshal(source, Source)};
marshal(change, {account, AccountChange}) ->
{account, marshal(account_change, AccountChange)};
marshal(change, {status_changed, Status}) ->
{status, #src_StatusChange{status = marshal(status, Status)}};
marshal(source, Source = #{
name := Name,
resource := Resource
}) ->
marshal(
source,
Source = #{
name := Name,
resource := Resource
}
) ->
#src_Source{
id = marshal(id, ff_source:id(Source)),
status = maybe_marshal(status, ff_source:status(Source)),
name = marshal(string, Name),
resource = marshal(resource, Resource),
external_id = maybe_marshal(id, maps:get(external_id, Source, undefined)),
created_at = maybe_marshal(timestamp_ms, maps:get(created_at, Source, undefined)),
created_at = maybe_marshal(timestamp_ms, maps:get(created_at, Source, undefined)),
metadata = maybe_marshal(context, maps:get(metadata, Source, undefined))
};
marshal(resource, #{type := internal} = Internal) ->
@ -96,43 +91,34 @@ marshal(internal, Internal) ->
#src_Internal{
details = marshal(string, Details)
};
marshal(status, unauthorized) ->
{unauthorized, #src_Unauthorized{}};
marshal(status, authorized) ->
{authorized, #src_Authorized{}};
marshal(ctx, Ctx) ->
marshal(context, Ctx);
marshal(T, V) ->
ff_codec:marshal(T, V).
-spec unmarshal(ff_codec:type_name(), ff_codec:encoded_value()) ->
ff_codec:decoded_value().
-spec unmarshal(ff_codec:type_name(), ff_codec:encoded_value()) -> ff_codec:decoded_value().
unmarshal({list, T}, V) ->
[unmarshal(T, E) || E <- V];
unmarshal(repair_scenario, {add_events, #src_AddEventsRepair{events = Events, action = Action}}) ->
{add_events, genlib_map:compact(#{
events => unmarshal({list, change}, Events),
action => maybe_unmarshal(complex_action, Action)
})};
{add_events,
genlib_map:compact(#{
events => unmarshal({list, change}, Events),
action => maybe_unmarshal(complex_action, Action)
})};
unmarshal(timestamped_change, TimestampedChange) ->
Timestamp = ff_codec:unmarshal(timestamp, TimestampedChange#src_TimestampedChange.occured_at),
Change = unmarshal(change, TimestampedChange#src_TimestampedChange.change),
{ev, Timestamp, Change};
unmarshal(change, {created, Source}) ->
{created, unmarshal(source, Source)};
unmarshal(change, {account, AccountChange}) ->
{account, unmarshal(account_change, AccountChange)};
unmarshal(change, {status, #src_StatusChange{status = Status}}) ->
{status_changed, unmarshal(status, Status)};
unmarshal(source, #src_Source{
name = Name,
resource = Resource,
@ -153,15 +139,12 @@ unmarshal(resource, {internal, #src_Internal{details = Details}}) ->
type => internal,
details => unmarshal(string, Details)
});
unmarshal(status, {unauthorized, #src_Unauthorized{}}) ->
unauthorized;
unmarshal(status, {authorized, #src_Authorized{}}) ->
authorized;
unmarshal(ctx, Ctx) ->
maybe_unmarshal(context, Ctx);
unmarshal(T, V) ->
ff_codec:unmarshal(T, V).

View File

@ -9,32 +9,28 @@
-type event() :: ff_eventsink_publisher:event(ff_source:event()).
-type sinkevent() :: ff_eventsink_publisher:sinkevent(ff_proto_source_thrift:'SinkEvent'()).
-spec publish_events(list(event())) ->
list(sinkevent()).
-spec publish_events(list(event())) -> list(sinkevent()).
publish_events(Events) ->
[publish_event(Event) || Event <- Events].
-spec publish_event(event()) ->
sinkevent().
-spec publish_event(event()) -> sinkevent().
publish_event(#{
id := ID,
source_id := SourceID,
event := {
id := ID,
source_id := SourceID,
event := {
EventID,
Dt,
{ev, EventDt, Payload}
}
}) ->
#src_SinkEvent{
id = marshal(event_id, ID),
created_at = marshal(timestamp, Dt),
source = marshal(id, SourceID),
payload = #src_EventSinkPayload{
sequence = marshal(event_id, EventID),
id = marshal(event_id, ID),
created_at = marshal(timestamp, Dt),
source = marshal(id, SourceID),
payload = #src_EventSinkPayload{
sequence = marshal(event_id, EventID),
occured_at = marshal(timestamp, EventDt),
changes = [marshal(change, Payload)]
changes = [marshal(change, Payload)]
}
}.

View File

@ -1,4 +1,5 @@
-module(ff_source_handler).
-behaviour(ff_woody_wrapper).
-include_lib("fistful_proto/include/ff_proto_source_thrift.hrl").
@ -9,10 +10,11 @@
%%
%% ff_woody_wrapper callbacks
%%
-spec handle_function(woody:func(), woody:args(), woody:options()) ->
{ok, woody:result()} | no_return().
-spec handle_function(woody:func(), woody:args(), woody:options()) -> {ok, woody:result()} | no_return().
handle_function(Func, Args, Opts) ->
scoper:scope(source, #{},
scoper:scope(
source,
#{},
fun() ->
handle_function_(Func, Args, Opts)
end
@ -24,9 +26,11 @@ handle_function(Func, Args, Opts) ->
handle_function_('Create', [Params, Ctx], Opts) ->
ID = Params#src_SourceParams.id,
ok = scoper:add_meta(#{id => ID}),
case ff_source_machine:create(
ff_source_codec:unmarshal_source_params(Params),
ff_source_codec:unmarshal(ctx, Ctx))
case
ff_source_machine:create(
ff_source_codec:unmarshal_source_params(Params),
ff_source_codec:unmarshal(ctx, Ctx)
)
of
ok ->
handle_function_('Get', [ID, #'EventRange'{}], Opts);

View File

@ -21,60 +21,56 @@
-type value_type() :: machinery_mg_schema:vt().
-type context() :: machinery_mg_schema:context().
-type event() :: ff_machine:timestamped_event(ff_source:event()).
-type event() :: ff_machine:timestamped_event(ff_source:event()).
-type aux_state() :: ff_machine:auxst().
-type call_args() :: term().
-type call_response() :: term().
-type data() ::
aux_state() |
event() |
call_args() |
call_response().
aux_state()
| event()
| call_args()
| call_response().
%% machinery_mg_schema callbacks
-spec get_version(value_type()) ->
machinery_mg_schema:version().
-spec get_version(value_type()) -> machinery_mg_schema:version().
get_version(event) ->
?CURRENT_EVENT_FORMAT_VERSION;
get_version(aux_state) ->
undefined.
-spec marshal(type(), value(data()), context()) ->
{machinery_msgpack:t(), context()}.
-spec marshal(type(), value(data()), context()) -> {machinery_msgpack:t(), context()}.
marshal({event, Format}, TimestampedChange, Context) ->
marshal_event(Format, TimestampedChange, Context);
marshal(T, V, C) when
T =:= {args, init} orelse
T =:= {args, call} orelse
T =:= {args, repair} orelse
T =:= {aux_state, undefined} orelse
T =:= {response, call} orelse
T =:= {response, {repair, success}} orelse
T =:= {response, {repair, failure}}
T =:= {args, call} orelse
T =:= {args, repair} orelse
T =:= {aux_state, undefined} orelse
T =:= {response, call} orelse
T =:= {response, {repair, success}} orelse
T =:= {response, {repair, failure}}
->
machinery_mg_schema_generic:marshal(T, V, C).
-spec unmarshal(type(), machinery_msgpack:t(), context()) ->
{data(), context()}.
-spec unmarshal(type(), machinery_msgpack:t(), context()) -> {data(), context()}.
unmarshal({event, FormatVersion}, EncodedChange, Context) ->
unmarshal_event(FormatVersion, EncodedChange, Context);
unmarshal(T, V, C) when
T =:= {args, init} orelse
T =:= {args, call} orelse
T =:= {args, repair} orelse
T =:= {aux_state, undefined} orelse
T =:= {response, call} orelse
T =:= {response, {repair, success}} orelse
T =:= {response, {repair, failure}}
T =:= {args, call} orelse
T =:= {args, repair} orelse
T =:= {aux_state, undefined} orelse
T =:= {response, call} orelse
T =:= {response, {repair, success}} orelse
T =:= {response, {repair, failure}}
->
machinery_mg_schema_generic:unmarshal(T, V, C).
%% Internals
-spec marshal_event(machinery_mg_schema:version(), event(), context()) ->
{machinery_msgpack:t(), context()}.
-spec marshal_event(machinery_mg_schema:version(), event(), context()) -> {machinery_msgpack:t(), context()}.
marshal_event(undefined = Version, TimestampedChange, Context) ->
% @TODO: Remove after migration
machinery_mg_schema_generic:marshal({event, Version}, TimestampedChange, Context);
@ -83,8 +79,7 @@ marshal_event(1, TimestampedChange, Context) ->
Type = {struct, struct, {ff_proto_source_thrift, 'TimestampedChange'}},
{{bin, ff_proto_utils:serialize(Type, ThriftChange)}, Context}.
-spec unmarshal_event(machinery_mg_schema:version(), machinery_msgpack:t(), context()) ->
{event(), context()}.
-spec unmarshal_event(machinery_mg_schema:version(), machinery_msgpack:t(), context()) -> {event(), context()}.
unmarshal_event(1, EncodedChange, Context) ->
{bin, EncodedThriftChange} = EncodedChange,
Type = {struct, struct, {ff_proto_source_thrift, 'TimestampedChange'}},
@ -94,72 +89,77 @@ unmarshal_event(undefined = Version, EncodedChange, Context0) ->
{Event, Context1} = machinery_mg_schema_generic:unmarshal({event, Version}, EncodedChange, Context0),
{maybe_migrate(Event), Context1}.
-spec maybe_migrate(any()) ->
event().
-spec maybe_migrate(any()) -> event().
maybe_migrate({ev, Timestamp, Change0}) ->
Change = ff_source:maybe_migrate(Change0, #{timestamp => Timestamp}),
{ev, Timestamp, Change}.
-ifdef(TEST).
-include_lib("eunit/include/eunit.hrl").
-spec test() -> _.
-spec created_3_undef_3_1_decoding_test() -> _.
created_3_undef_3_1_decoding_test() ->
Resource = #{
type => internal,
type => internal,
details => <<"details">>
},
Source = #{
version => 3,
resource => Resource,
name => <<"name">>,
created_at => 1590434350293,
version => 3,
resource => Resource,
name => <<"name">>,
created_at => 1590434350293,
external_id => <<"external_id">>,
metadata => #{}
metadata => #{}
},
Change = {created, Source},
Event = {ev, {{{2020, 5, 25}, {19, 19, 10}}, 293305}, Change},
ResourceMsgpack = {arr, [
{str, <<"map">>},
{obj, #{
{str, <<"type">>} => {str, <<"internal">>},
{str, <<"details">>} => {bin, <<"details">>}
}}
]},
LegacyChange = {arr, [
{str, <<"tup">>},
{str, <<"created">>},
ResourceMsgpack =
{arr, [
{str, <<"map">>},
{obj, #{
{str, <<"version">>} => {i, 3},
{str, <<"resource">>} => ResourceMsgpack,
{str, <<"name">>} => {bin, <<"name">>},
{str, <<"created_at">>} => {i, 1590434350293},
{str, <<"external_id">>} => {bin, <<"external_id">>},
{str, <<"metadata">>} => {arr, [
{str, <<"map">>},
{obj, #{}}
]}
{str, <<"type">>} => {str, <<"internal">>},
{str, <<"details">>} => {bin, <<"details">>}
}}
]}
]},
LegacyEvent = {arr, [
{str, <<"tup">>},
{str, <<"ev">>},
]},
LegacyChange =
{arr, [
{str, <<"tup">>},
{str, <<"created">>},
{arr, [
{str, <<"map">>},
{obj, #{
{str, <<"version">>} => {i, 3},
{str, <<"resource">>} => ResourceMsgpack,
{str, <<"name">>} => {bin, <<"name">>},
{str, <<"created_at">>} => {i, 1590434350293},
{str, <<"external_id">>} => {bin, <<"external_id">>},
{str, <<"metadata">>} =>
{arr, [
{str, <<"map">>},
{obj, #{}}
]}
}}
]}
]},
LegacyEvent =
{arr, [
{str, <<"tup">>},
{str, <<"ev">>},
{arr, [
{str, <<"tup">>},
{arr, [{str, <<"tup">>}, {i, 2020}, {i, 5}, {i, 25}]},
{arr, [{str, <<"tup">>}, {i, 19}, {i, 19}, {i, 10}]}
{arr, [
{str, <<"tup">>},
{arr, [{str, <<"tup">>}, {i, 2020}, {i, 5}, {i, 25}]},
{arr, [{str, <<"tup">>}, {i, 19}, {i, 19}, {i, 10}]}
]},
{i, 293305}
]},
{i, 293305}
LegacyChange
]},
LegacyChange
]},
DecodedLegacy = unmarshal({event, undefined}, LegacyEvent),
ModernizedBinary = marshal({event, ?CURRENT_EVENT_FORMAT_VERSION}, DecodedLegacy),
@ -169,53 +169,56 @@ created_3_undef_3_1_decoding_test() ->
-spec created_1_undef_3_1_decoding_test() -> _.
created_1_undef_3_1_decoding_test() ->
Resource = #{
type => internal,
type => internal,
details => <<"details">>
},
Source = #{
version => 3,
resource => Resource,
name => <<"name">>,
created_at => 1590434350293,
version => 3,
resource => Resource,
name => <<"name">>,
created_at => 1590434350293,
external_id => <<"external_id">>
},
Change = {created, Source},
Event = {ev, {{{2020, 5, 25}, {19, 19, 10}}, 293305}, Change},
ResourceMsgpack = {arr, [
{str, <<"map">>},
{obj, #{
{str, <<"type">>} => {str, <<"internal">>},
{str, <<"details">>} => {bin, <<"details">>}
}}
]},
LegacyChange = {arr, [
{str, <<"tup">>},
{str, <<"created">>},
ResourceMsgpack =
{arr, [
{str, <<"map">>},
{obj, #{
{str, <<"version">>} => {i, 1},
{str, <<"resource">>} => ResourceMsgpack,
{str, <<"name">>} => {bin, <<"name">>},
{str, <<"external_id">>} => {bin, <<"external_id">>}
{str, <<"type">>} => {str, <<"internal">>},
{str, <<"details">>} => {bin, <<"details">>}
}}
]}
]},
LegacyEvent = {arr, [
{str, <<"tup">>},
{str, <<"ev">>},
]},
LegacyChange =
{arr, [
{str, <<"tup">>},
{str, <<"created">>},
{arr, [
{str, <<"map">>},
{obj, #{
{str, <<"version">>} => {i, 1},
{str, <<"resource">>} => ResourceMsgpack,
{str, <<"name">>} => {bin, <<"name">>},
{str, <<"external_id">>} => {bin, <<"external_id">>}
}}
]}
]},
LegacyEvent =
{arr, [
{str, <<"tup">>},
{str, <<"ev">>},
{arr, [
{str, <<"tup">>},
{arr, [{str, <<"tup">>}, {i, 2020}, {i, 5}, {i, 25}]},
{arr, [{str, <<"tup">>}, {i, 19}, {i, 19}, {i, 10}]}
{arr, [
{str, <<"tup">>},
{arr, [{str, <<"tup">>}, {i, 2020}, {i, 5}, {i, 25}]},
{arr, [{str, <<"tup">>}, {i, 19}, {i, 19}, {i, 10}]}
]},
{i, 293305}
]},
{i, 293305}
LegacyChange
]},
LegacyChange
]},
DecodedLegacy = unmarshal({event, undefined}, LegacyEvent),
ModernizedBinary = marshal({event, ?CURRENT_EVENT_FORMAT_VERSION}, DecodedLegacy),
@ -224,45 +227,49 @@ created_1_undef_3_1_decoding_test() ->
-spec account_undef_1_decoding_test() -> _.
account_undef_1_decoding_test() ->
Change = {account, {created, #{
id => <<"id">>,
identity => <<"identity">>,
currency => <<"USD">>,
accounter_account_id => 1
}}},
Change =
{account,
{created, #{
id => <<"id">>,
identity => <<"identity">>,
currency => <<"USD">>,
accounter_account_id => 1
}}},
Event = {ev, {{{2020, 5, 25}, {19, 19, 10}}, 293305}, Change},
LegacyChange = {arr, [
{str, <<"tup">>},
{str, <<"account">>},
{arr, [
{str, <<"tup">>},
{str, <<"created">>},
{arr, [
{str, <<"map">>},
{obj, #{
{str, <<"id">>} => {bin, <<"id">>},
{str, <<"identity">>} => {bin, <<"identity">>},
{str, <<"currency">>} => {bin, <<"USD">>},
{str, <<"accounter_account_id">>} => {i, 1}
}}
]}
]}
]},
LegacyEvent = {arr, [
{str, <<"tup">>},
{str, <<"ev">>},
LegacyChange =
{arr, [
{str, <<"tup">>},
{str, <<"account">>},
{arr, [
{str, <<"tup">>},
{arr, [{str, <<"tup">>}, {i, 2020}, {i, 5}, {i, 25}]},
{arr, [{str, <<"tup">>}, {i, 19}, {i, 19}, {i, 10}]}
]},
{i, 293305}
{str, <<"created">>},
{arr, [
{str, <<"map">>},
{obj, #{
{str, <<"id">>} => {bin, <<"id">>},
{str, <<"identity">>} => {bin, <<"identity">>},
{str, <<"currency">>} => {bin, <<"USD">>},
{str, <<"accounter_account_id">>} => {i, 1}
}}
]}
]}
]},
LegacyEvent =
{arr, [
{str, <<"tup">>},
{str, <<"ev">>},
{arr, [
{str, <<"tup">>},
{arr, [
{str, <<"tup">>},
{arr, [{str, <<"tup">>}, {i, 2020}, {i, 5}, {i, 25}]},
{arr, [{str, <<"tup">>}, {i, 19}, {i, 19}, {i, 10}]}
]},
{i, 293305}
]},
LegacyChange
]},
LegacyChange
]},
DecodedLegacy = unmarshal({event, undefined}, LegacyEvent),
ModernizedBinary = marshal({event, ?CURRENT_EVENT_FORMAT_VERSION}, DecodedLegacy),
@ -274,25 +281,27 @@ status_undef_1_decoding_test() ->
Change = {status_changed, unauthorized},
Event = {ev, {{{2020, 5, 25}, {19, 19, 10}}, 293305}, Change},
LegacyChange = {arr, [
{str, <<"tup">>},
{str, <<"status_changed">>},
{str, <<"unauthorized">>}
]},
LegacyEvent = {arr, [
{str, <<"tup">>},
{str, <<"ev">>},
LegacyChange =
{arr, [
{str, <<"tup">>},
{str, <<"status_changed">>},
{str, <<"unauthorized">>}
]},
LegacyEvent =
{arr, [
{str, <<"tup">>},
{str, <<"ev">>},
{arr, [
{str, <<"tup">>},
{arr, [{str, <<"tup">>}, {i, 2020}, {i, 5}, {i, 25}]},
{arr, [{str, <<"tup">>}, {i, 19}, {i, 19}, {i, 10}]}
{arr, [
{str, <<"tup">>},
{arr, [{str, <<"tup">>}, {i, 2020}, {i, 5}, {i, 25}]},
{arr, [{str, <<"tup">>}, {i, 19}, {i, 19}, {i, 10}]}
]},
{i, 293305}
]},
{i, 293305}
LegacyChange
]},
LegacyChange
]},
DecodedLegacy = unmarshal({event, undefined}, LegacyEvent),
ModernizedBinary = marshal({event, ?CURRENT_EVENT_FORMAT_VERSION}, DecodedLegacy),
@ -302,24 +311,26 @@ status_undef_1_decoding_test() ->
-spec created_1_decoding_test() -> _.
created_1_decoding_test() ->
Resource = #{
type => internal,
type => internal,
details => <<"details">>
},
Source = #{
version => 3,
resource => Resource,
name => <<"name">>,
created_at => 1590434350293,
version => 3,
resource => Resource,
name => <<"name">>,
created_at => 1590434350293,
external_id => <<"external_id">>
},
Change = {created, Source},
Event = {ev, {{{2020, 5, 25}, {19, 19, 10}}, 293305}, Change},
LegacyEvent = {bin, base64:decode(<<
"CwABAAAAGzIwMjAtMDUtMjVUMTk6MTk6MTAuMjkzMzA1WgwAAgwAAQsAAQAAAARuYW1lDA"
"ACDAABCwABAAAAB2RldGFpbHMAAAsAAwAAAAtleHRlcm5hbF9pZAsABgAAABgyMDIwLTA1"
"LTI1VDE5OjE5OjEwLjI5M1oAAAA="
>>)},
LegacyEvent =
{bin,
base64:decode(<<
"CwABAAAAGzIwMjAtMDUtMjVUMTk6MTk6MTAuMjkzMzA1WgwAAgwAAQsAAQAAAARuYW1lDA"
"ACDAABCwABAAAAB2RldGFpbHMAAAsAAwAAAAtleHRlcm5hbF9pZAsABgAAABgyMDIwLTA1"
"LTI1VDE5OjE5OjEwLjI5M1oAAAA="
>>)},
DecodedLegacy = unmarshal({event, 1}, LegacyEvent),
ModernizedBinary = marshal({event, ?CURRENT_EVENT_FORMAT_VERSION}, DecodedLegacy),
@ -328,18 +339,22 @@ created_1_decoding_test() ->
-spec account_1_decoding_test() -> _.
account_1_decoding_test() ->
Change = {account, {created, #{
id => <<"id">>,
identity => <<"identity">>,
currency => <<"USD">>,
accounter_account_id => 1
}}},
Change =
{account,
{created, #{
id => <<"id">>,
identity => <<"identity">>,
currency => <<"USD">>,
accounter_account_id => 1
}}},
Event = {ev, {{{2020, 5, 25}, {19, 19, 10}}, 293305}, Change},
LegacyEvent = {bin, base64:decode(<<
"CwABAAAAGzIwMjAtMDUtMjVUMTk6MTk6MTAuMjkzMzA1WgwAAgwAAgwAAQsAAwAAAAJpZA"
"sAAQAAAAhpZGVudGl0eQwAAgsAAQAAAANVU0QACgAEAAAAAAAAAAEAAAAA"
>>)},
LegacyEvent =
{bin,
base64:decode(<<
"CwABAAAAGzIwMjAtMDUtMjVUMTk6MTk6MTAuMjkzMzA1WgwAAgwAAgwAAQsAAwAAAAJpZA"
"sAAQAAAAhpZGVudGl0eQwAAgsAAQAAAANVU0QACgAEAAAAAAAAAAEAAAAA"
>>)},
DecodedLegacy = unmarshal({event, 1}, LegacyEvent),
ModernizedBinary = marshal({event, ?CURRENT_EVENT_FORMAT_VERSION}, DecodedLegacy),
@ -351,22 +366,22 @@ status_1_decoding_test() ->
Change = {status_changed, unauthorized},
Event = {ev, {{{2020, 5, 25}, {19, 19, 10}}, 293305}, Change},
LegacyEvent = {bin, base64:decode(<<
"CwABAAAAGzIwMjAtMDUtMjVUMTk6MTk6MTAuMjkzMzA1WgwAAgwAAwwAAQwAAgAAAAAA"
>>)},
LegacyEvent =
{bin,
base64:decode(<<
"CwABAAAAGzIwMjAtMDUtMjVUMTk6MTk6MTAuMjkzMzA1WgwAAgwAAwwAAQwAAgAAAAAA"
>>)},
DecodedLegacy = unmarshal({event, 1}, LegacyEvent),
ModernizedBinary = marshal({event, ?CURRENT_EVENT_FORMAT_VERSION}, DecodedLegacy),
Decoded = unmarshal({event, ?CURRENT_EVENT_FORMAT_VERSION}, ModernizedBinary),
?assertEqual(Event, Decoded).
-spec marshal(type(), value(data())) ->
machinery_msgpack:t().
-spec marshal(type(), value(data())) -> machinery_msgpack:t().
marshal(Type, Value) ->
element(1, marshal(Type, Value, #{})).
-spec unmarshal(type(), machinery_msgpack:t()) ->
data().
-spec unmarshal(type(), machinery_msgpack:t()) -> data().
unmarshal(Type, Value) ->
element(1, unmarshal(Type, Value, #{})).

View File

@ -9,16 +9,13 @@
%% API
-spec marshal(ff_codec:type_name(), ff_codec:decoded_value()) ->
ff_codec:encoded_value().
-spec marshal(ff_codec:type_name(), ff_codec:decoded_value()) -> ff_codec:encoded_value().
marshal(change, {created, Adjustment}) ->
{created, #w2w_adj_CreatedChange{adjustment = marshal(adjustment, Adjustment)}};
marshal(change, {status_changed, Status}) ->
{status_changed, #w2w_adj_StatusChange{status = marshal(status, Status)}};
marshal(change, {p_transfer, TransferChange}) ->
{transfer, #w2w_adj_TransferChange{payload = ff_p_transfer_codec:marshal(change, TransferChange)}};
marshal(adjustment, Adjustment) ->
#w2w_adj_Adjustment{
id = marshal(id, ff_adjustment:id(Adjustment)),
@ -47,12 +44,10 @@ marshal(adjustment_state, Adjustment) ->
operation_timestamp = marshal(timestamp_ms, ff_adjustment:operation_timestamp(Adjustment)),
external_id = maybe_marshal(id, ff_adjustment:external_id(Adjustment))
};
marshal(status, pending) ->
{pending, #w2w_adj_Pending{}};
marshal(status, succeeded) ->
{succeeded, #w2w_adj_Succeeded{}};
marshal(changes_plan, Plan) ->
#w2w_adj_ChangesPlan{
new_cash_flow = maybe_marshal(cash_flow_change_plan, maps:get(new_cash_flow, Plan, undefined)),
@ -69,26 +64,20 @@ marshal(status_change_plan, Plan) ->
#w2w_adj_StatusChangePlan{
new_status = ff_w2w_transfer_status_codec:marshal(status, maps:get(new_status, Plan))
};
marshal(change_request, {change_status, Status}) ->
{change_status, #w2w_adj_ChangeStatusRequest{
new_status = ff_w2w_transfer_status_codec:marshal(status, Status)
}};
marshal(T, V) ->
ff_codec:marshal(T, V).
-spec unmarshal(ff_codec:type_name(), ff_codec:encoded_value()) ->
ff_codec:decoded_value().
-spec unmarshal(ff_codec:type_name(), ff_codec:encoded_value()) -> ff_codec:decoded_value().
unmarshal(change, {created, #w2w_adj_CreatedChange{adjustment = Adjustment}}) ->
{created, unmarshal(adjustment, Adjustment)};
unmarshal(change, {status_changed, #w2w_adj_StatusChange{status = Status}}) ->
{status_changed, unmarshal(status, Status)};
unmarshal(change, {transfer, #w2w_adj_TransferChange{payload = TransferChange}}) ->
{p_transfer, ff_p_transfer_codec:unmarshal(change, TransferChange)};
unmarshal(adjustment, Adjustment) ->
#{
version => 1,
@ -101,19 +90,16 @@ unmarshal(adjustment, Adjustment) ->
operation_timestamp => unmarshal(timestamp_ms, Adjustment#w2w_adj_Adjustment.operation_timestamp),
external_id => maybe_unmarshal(id, Adjustment#w2w_adj_Adjustment.external_id)
};
unmarshal(adjustment_params, Params) ->
genlib_map:compact(#{
id => unmarshal(id, Params#w2w_adj_AdjustmentParams.id),
change => unmarshal(change_request, Params#w2w_adj_AdjustmentParams.change),
external_id => maybe_unmarshal(id, Params#w2w_adj_AdjustmentParams.external_id)
});
unmarshal(status, {pending, #w2w_adj_Pending{}}) ->
pending;
unmarshal(status, {succeeded, #w2w_adj_Succeeded{}}) ->
succeeded;
unmarshal(changes_plan, Plan) ->
genlib_map:compact(#{
new_cash_flow => maybe_unmarshal(cash_flow_change_plan, Plan#w2w_adj_ChangesPlan.new_cash_flow),
@ -131,11 +117,9 @@ unmarshal(status_change_plan, Plan) ->
#{
new_status => ff_w2w_transfer_status_codec:unmarshal(status, Status)
};
unmarshal(change_request, {change_status, Request}) ->
Status = Request#w2w_adj_ChangeStatusRequest.new_status,
{change_status, ff_w2w_transfer_status_codec:unmarshal(status, Status)};
unmarshal(T, V) ->
ff_codec:unmarshal(T, V).
@ -155,9 +139,11 @@ maybe_marshal(Type, Value) ->
-ifdef(TEST).
-include_lib("eunit/include/eunit.hrl").
-spec test() -> _.
-spec adjustment_codec_test() -> _.
adjustment_codec_test() ->
FinalCashFlow = #{
postings => [

View File

@ -13,13 +13,13 @@
%% Data transform
-define(to_session_event(SessionID, Payload),
{session, #{id => SessionID, payload => Payload}}).
{session, #{id => SessionID, payload => Payload}}
).
%% API
-spec marshal_w2w_transfer_state(w2w_transfer:w2w_transfer_state(), ff_entity_context:context()) ->
ff_proto_w2w_transfer_thrift:'W2WTransferState'().
marshal_w2w_transfer_state(W2WTransferState, Ctx) ->
CashFlow = w2w_transfer:effective_final_cash_flow(W2WTransferState),
Adjustments = w2w_transfer:adjustments(W2WTransferState),
@ -41,7 +41,6 @@ marshal_w2w_transfer_state(W2WTransferState, Ctx) ->
-spec unmarshal_w2w_transfer_params(ff_proto_w2w_transfer_thrift:'W2WTransferParams'()) ->
w2w_transfer_machine:params().
unmarshal_w2w_transfer_params(#w2w_transfer_W2WTransferParams{
id = ID,
body = Body,
@ -59,25 +58,20 @@ unmarshal_w2w_transfer_params(#w2w_transfer_W2WTransferParams{
metadata => maybe_unmarshal(ctx, Metadata)
}).
-spec marshal(ff_codec:type_name(), ff_codec:decoded_value()) ->
ff_codec:encoded_value().
-spec marshal(ff_codec:type_name(), ff_codec:decoded_value()) -> ff_codec:encoded_value().
marshal({list, T}, V) ->
[marshal(T, E) || E <- V];
marshal(timestamped_change, {ev, Timestamp, Change}) ->
#w2w_transfer_TimestampedChange{
change = marshal(change, Change),
occured_at = ff_codec:marshal(timestamp, Timestamp)
};
marshal(event, {EventID, {ev, Timestamp, Change}}) ->
#w2w_transfer_Event{
event_id = ff_codec:marshal(event_id, EventID),
occured_at = ff_codec:marshal(timestamp, Timestamp),
change = marshal(change, Change)
};
marshal(change, {created, W2WTransfer}) ->
{created, #w2w_transfer_CreatedChange{w2w_transfer = marshal(w2w_transfer, W2WTransfer)}};
marshal(change, {status_changed, Status}) ->
@ -91,7 +85,6 @@ marshal(change, {adjustment, #{id := ID, payload := Payload}}) ->
id = marshal(id, ID),
payload = ff_w2w_transfer_adjustment_codec:marshal(change, Payload)
}};
marshal(w2w_transfer, W2WTransfer) ->
#w2w_transfer_W2WTransfer{
id = marshal(id, w2w_transfer:id(W2WTransfer)),
@ -105,34 +98,26 @@ marshal(w2w_transfer, W2WTransfer) ->
created_at = maybe_marshal(timestamp_ms, w2w_transfer:created_at(W2WTransfer)),
metadata = maybe_marshal(ctx, w2w_transfer:metadata(W2WTransfer))
};
marshal(status, Status) ->
ff_w2w_transfer_status_codec:marshal(status, Status);
marshal(ctx, Ctx) ->
maybe_marshal(context, Ctx);
marshal(T, V) ->
ff_codec:marshal(T, V).
-spec unmarshal(ff_codec:type_name(), ff_codec:encoded_value()) ->
ff_codec:decoded_value().
-spec unmarshal(ff_codec:type_name(), ff_codec:encoded_value()) -> ff_codec:decoded_value().
unmarshal({list, T}, V) ->
[unmarshal(T, E) || E <- V];
unmarshal(timestamped_change, TimestampedChange) ->
Timestamp = ff_codec:unmarshal(timestamp, TimestampedChange#w2w_transfer_TimestampedChange.occured_at),
Change = unmarshal(change, TimestampedChange#w2w_transfer_TimestampedChange.change),
{ev, Timestamp, Change};
unmarshal(repair_scenario, {add_events, #w2w_transfer_AddEventsRepair{events = Events, action = Action}}) ->
{add_events, genlib_map:compact(#{
events => unmarshal({list, change}, Events),
action => maybe_unmarshal(complex_action, Action)
})};
{add_events,
genlib_map:compact(#{
events => unmarshal({list, change}, Events),
action => maybe_unmarshal(complex_action, Action)
})};
unmarshal(change, {created, #w2w_transfer_CreatedChange{w2w_transfer = W2WTransfer}}) ->
{created, unmarshal(w2w_transfer, W2WTransfer)};
unmarshal(change, {status_changed, #w2w_transfer_StatusChange{status = W2WTransferStatus}}) ->
@ -146,10 +131,8 @@ unmarshal(change, {adjustment, Change}) ->
id => unmarshal(id, Change#w2w_transfer_AdjustmentChange.id),
payload => ff_w2w_transfer_adjustment_codec:unmarshal(change, Change#w2w_transfer_AdjustmentChange.payload)
}};
unmarshal(status, Status) ->
ff_w2w_transfer_status_codec:unmarshal(status, Status);
unmarshal(w2w_transfer, W2WTransfer) ->
genlib_map:compact(#{
version => 1,
@ -164,10 +147,8 @@ unmarshal(w2w_transfer, W2WTransfer) ->
created_at => maybe_unmarshal(timestamp_ms, W2WTransfer#w2w_transfer_W2WTransfer.created_at),
metadata => maybe_unmarshal(ctx, W2WTransfer#w2w_transfer_W2WTransfer.metadata)
});
unmarshal(ctx, Ctx) ->
maybe_unmarshal(context, Ctx);
unmarshal(T, V) ->
ff_codec:unmarshal(T, V).
@ -187,14 +168,16 @@ maybe_marshal(Type, Value) ->
-ifdef(TEST).
-include_lib("eunit/include/eunit.hrl").
-spec test() -> _.
-spec w2w_transfer_symmetry_test() -> _.
w2w_transfer_symmetry_test() ->
Encoded = #w2w_transfer_W2WTransfer{
body = #'Cash'{
amount = 10101,
currency = #'CurrencyRef'{ symbolic_code = <<"Banana Republic">> }
currency = #'CurrencyRef'{symbolic_code = <<"Banana Republic">>}
},
wallet_from_id = genlib:unique(),
wallet_to_id = genlib:unique(),

View File

@ -16,32 +16,28 @@
%% Internals
%%
-spec publish_events(list(event())) ->
list(sinkevent()).
-spec publish_events(list(event())) -> list(sinkevent()).
publish_events(Events) ->
[publish_event(Event) || Event <- Events].
-spec publish_event(event()) ->
sinkevent().
-spec publish_event(event()) -> sinkevent().
publish_event(#{
id := ID,
source_id := SourceID,
event := {
id := ID,
source_id := SourceID,
event := {
EventID,
Dt,
{ev, EventDt, Payload}
}
}) ->
#w2w_transfer_SinkEvent{
id = marshal(event_id, ID),
created_at = marshal(timestamp, Dt),
source = marshal(id, SourceID),
payload = #w2w_transfer_EventSinkPayload{
sequence = marshal(event_id, EventID),
id = marshal(event_id, ID),
created_at = marshal(timestamp, Dt),
source = marshal(id, SourceID),
payload = #w2w_transfer_EventSinkPayload{
sequence = marshal(event_id, EventID),
occured_at = marshal(timestamp, EventDt),
changes = [marshal(change, Payload)]
changes = [marshal(change, Payload)]
}
}.

View File

@ -1,4 +1,5 @@
-module(ff_w2w_transfer_handler).
-behaviour(ff_woody_wrapper).
-include_lib("fistful_proto/include/ff_proto_w2w_transfer_thrift.hrl").
@ -10,11 +11,11 @@
%%
%% ff_woody_wrapper callbacks
%%
-spec handle_function(woody:func(), woody:args(), woody:options()) ->
{ok, woody:result()} | no_return().
-spec handle_function(woody:func(), woody:args(), woody:options()) -> {ok, woody:result()} | no_return().
handle_function(Func, Args, Opts) ->
scoper:scope(w2w_transfer, #{},
scoper:scope(
w2w_transfer,
#{},
fun() ->
handle_function_(Func, Args, Opts)
end
@ -69,7 +70,6 @@ handle_function_('Create', [MarshaledParams, MarshaledContext], Opts) ->
{error, Error} ->
woody_error:raise(system, {internal, result_unexpected, woody_error:format_details(Error)})
end;
handle_function_('Get', [ID, EventRange], _Opts) ->
{After, Limit} = ff_codec:unmarshal(event_range, EventRange),
ok = scoper:add_meta(#{id => ID}),
@ -82,7 +82,6 @@ handle_function_('Get', [ID, EventRange], _Opts) ->
{error, {unknown_w2w_transfer, _Ref}} ->
woody_error:raise(business, #fistful_W2WNotFound{})
end;
handle_function_('GetContext', [ID], _Opts) ->
case w2w_transfer_machine:get(ID, {undefined, 0}) of
{ok, Machine} ->
@ -92,15 +91,16 @@ handle_function_('GetContext', [ID], _Opts) ->
{error, {unknown_w2w_transfer, _Ref}} ->
woody_error:raise(business, #fistful_W2WNotFound{})
end;
handle_function_('CreateAdjustment', [ID, MarshaledParams], _Opts) ->
Params = ff_w2w_transfer_adjustment_codec:unmarshal(adjustment_params, MarshaledParams),
AdjustmentID = maps:get(id, Params),
ok = scoper:add_meta(genlib_map:compact(#{
id => ID,
adjustment_id => AdjustmentID,
external_id => maps:get(external_id, Params, undefined)
})),
ok = scoper:add_meta(
genlib_map:compact(#{
id => ID,
adjustment_id => AdjustmentID,
external_id => maps:get(external_id, Params, undefined)
})
),
case w2w_transfer_machine:start_adjustment(ID, Params) of
ok ->
{ok, Machine} = w2w_transfer_machine:get(ID),

View File

@ -21,67 +21,62 @@
-type value_type() :: machinery_mg_schema:vt().
-type context() :: machinery_mg_schema:context().
-type event() :: ff_machine:timestamped_event(w2w_transfer:event()).
-type event() :: ff_machine:timestamped_event(w2w_transfer:event()).
-type aux_state() :: ff_machine:auxst().
-type call_args() :: term().
-type call_response() :: term().
-type data() ::
aux_state() |
event() |
call_args() |
call_response().
aux_state()
| event()
| call_args()
| call_response().
%% machinery_mg_schema callbacks
-spec get_version(value_type()) ->
machinery_mg_schema:version().
-spec get_version(value_type()) -> machinery_mg_schema:version().
get_version(event) ->
?CURRENT_EVENT_FORMAT_VERSION;
get_version(aux_state) ->
undefined.
-spec marshal(type(), value(data()), context()) ->
{machinery_msgpack:t(), context()}.
-spec marshal(type(), value(data()), context()) -> {machinery_msgpack:t(), context()}.
marshal({event, Format}, TimestampedChange, Context) ->
marshal_event(Format, TimestampedChange, Context);
marshal(T, V, C) when
T =:= {args, init} orelse
T =:= {args, call} orelse
T =:= {args, repair} orelse
T =:= {aux_state, undefined} orelse
T =:= {response, call} orelse
T =:= {response, {repair, success}} orelse
T =:= {response, {repair, failure}}
T =:= {args, call} orelse
T =:= {args, repair} orelse
T =:= {aux_state, undefined} orelse
T =:= {response, call} orelse
T =:= {response, {repair, success}} orelse
T =:= {response, {repair, failure}}
->
machinery_mg_schema_generic:marshal(T, V, C).
-spec unmarshal(type(), machinery_msgpack:t(), context()) ->
{data(), context()}.
-spec unmarshal(type(), machinery_msgpack:t(), context()) -> {data(), context()}.
unmarshal({event, FormatVersion}, EncodedChange, Context) ->
unmarshal_event(FormatVersion, EncodedChange, Context);
unmarshal(T, V, C) when
T =:= {args, init} orelse
T =:= {args, call} orelse
T =:= {args, repair} orelse
T =:= {aux_state, undefined} orelse
T =:= {response, call} orelse
T =:= {response, {repair, success}} orelse
T =:= {response, {repair, failure}}
T =:= {args, call} orelse
T =:= {args, repair} orelse
T =:= {aux_state, undefined} orelse
T =:= {response, call} orelse
T =:= {response, {repair, success}} orelse
T =:= {response, {repair, failure}}
->
machinery_mg_schema_generic:unmarshal(T, V, C).
%% Internals
-spec marshal_event(machinery_mg_schema:version(), event(), context()) ->
{machinery_msgpack:t(), context()}.
-spec marshal_event(machinery_mg_schema:version(), event(), context()) -> {machinery_msgpack:t(), context()}.
marshal_event(1, TimestampedChange, Context) ->
ThriftChange = ff_w2w_transfer_codec:marshal(timestamped_change, TimestampedChange),
Type = {struct, struct, {ff_proto_w2w_transfer_thrift, 'TimestampedChange'}},
{{bin, ff_proto_utils:serialize(Type, ThriftChange)}, Context}.
-spec unmarshal_event(machinery_mg_schema:version(), machinery_msgpack:t(), context()) ->
{event(), context()}.
-spec unmarshal_event(machinery_mg_schema:version(), machinery_msgpack:t(), context()) -> {event(), context()}.
unmarshal_event(1, EncodedChange, Context) ->
{bin, EncodedThriftChange} = EncodedChange,
Type = {struct, struct, {ff_proto_w2w_transfer_thrift, 'TimestampedChange'}},
@ -92,18 +87,18 @@ unmarshal_event(1, EncodedChange, Context) ->
-ifdef(TEST).
-include_lib("eunit/include/eunit.hrl").
-spec test() -> _.
% tests helpers
-spec marshal(type(), value(data())) ->
machinery_msgpack:t().
-spec marshal(type(), value(data())) -> machinery_msgpack:t().
marshal(Type, Value) ->
{Result, _Context} = marshal(Type, Value, #{}),
Result.
-spec unmarshal(type(), machinery_msgpack:t()) ->
data().
-spec unmarshal(type(), machinery_msgpack:t()) -> data().
unmarshal(Type, Value) ->
{Result, _Context} = unmarshal(Type, Value, #{}),
Result.
@ -123,13 +118,15 @@ created_v1_decoding_test() ->
},
Change = {created, W2WTransfer},
Event = {ev, {{{2020, 5, 25}, {19, 19, 10}}, 293305}, Change},
LegacyEvent = {bin, base64:decode(<<
"CwABAAAAGzIwMjAtMDUtMjVUMTk6MTk6MTAuMjkzMzA1WgwAAgwAAQwAAQsAAQ"
"AAAAh0cmFuc2ZlcgsAAgAAAAxXYWxsZXRGcm9tSUQLAAMAAAAKV2FsbGV0VG9J"
"RAwABAoAAQAAAAAAAAB7DAACCwABAAAAA1JVQgAACwAFAAAAGDIwMjAtMDUtMj"
"VUMTc6MTI6NTcuOTg1WgoABgAAAAAAAAB7CgAHAAAAAAAAAUELAAkAAAALZXh0"
"ZXJuYWxfaWQAAAAA"
>>)},
LegacyEvent =
{bin,
base64:decode(<<
"CwABAAAAGzIwMjAtMDUtMjVUMTk6MTk6MTAuMjkzMzA1WgwAAgwAAQwAAQsAAQ"
"AAAAh0cmFuc2ZlcgsAAgAAAAxXYWxsZXRGcm9tSUQLAAMAAAAKV2FsbGV0VG9J"
"RAwABAoAAQAAAAAAAAB7DAACCwABAAAAA1JVQgAACwAFAAAAGDIwMjAtMDUtMj"
"VUMTc6MTI6NTcuOTg1WgoABgAAAAAAAAB7CgAHAAAAAAAAAUELAAkAAAALZXh0"
"ZXJuYWxfaWQAAAAA"
>>)},
DecodedLegacy = unmarshal({event, 1}, LegacyEvent),
ModernizedBinary = marshal({event, ?CURRENT_EVENT_FORMAT_VERSION}, DecodedLegacy),
Decoded = unmarshal({event, ?CURRENT_EVENT_FORMAT_VERSION}, ModernizedBinary),
@ -139,9 +136,11 @@ created_v1_decoding_test() ->
status_changes_v1_decoding_test() ->
Change = {status_changed, succeeded},
Event = {ev, {{{2020, 5, 25}, {19, 19, 10}}, 293305}, Change},
LegacyEvent = {bin, base64:decode(<<
"CwABAAAAGzIwMjAtMDUtMjVUMTk6MTk6MTAuMjkzMzA1WgwAAgwAAgwAAQwAAgAAAAAA"
>>)},
LegacyEvent =
{bin,
base64:decode(<<
"CwABAAAAGzIwMjAtMDUtMjVUMTk6MTk6MTAuMjkzMzA1WgwAAgwAAgwAAQwAAgAAAAAA"
>>)},
DecodedLegacy = unmarshal({event, 1}, LegacyEvent),
ModernizedBinary = marshal({event, ?CURRENT_EVENT_FORMAT_VERSION}, DecodedLegacy),
Decoded = unmarshal({event, ?CURRENT_EVENT_FORMAT_VERSION}, ModernizedBinary),
@ -151,9 +150,11 @@ status_changes_v1_decoding_test() ->
limit_check_v1_decoding_test() ->
Change = {limit_check, {wallet_sender, ok}},
Event = {ev, {{{2020, 5, 25}, {19, 19, 10}}, 293305}, Change},
LegacyEvent = {bin, base64:decode(<<
"CwABAAAAGzIwMjAtMDUtMjVUMTk6MTk6MTAuMjkzMzA1WgwAAgwABQwAAQwAAQwAAQAAAAAAAA=="
>>)},
LegacyEvent =
{bin,
base64:decode(<<
"CwABAAAAGzIwMjAtMDUtMjVUMTk6MTk6MTAuMjkzMzA1WgwAAgwABQwAAQwAAQwAAQAAAAAAAA=="
>>)},
DecodedLegacy = unmarshal({event, 1}, LegacyEvent),
ModernizedBinary = marshal({event, ?CURRENT_EVENT_FORMAT_VERSION}, DecodedLegacy),
Decoded = unmarshal({event, ?CURRENT_EVENT_FORMAT_VERSION}, ModernizedBinary),
@ -161,15 +162,19 @@ limit_check_v1_decoding_test() ->
-spec p_transfer_created_v1_decoding_test() -> _.
p_transfer_created_v1_decoding_test() ->
Change = {p_transfer, {created, #{
id => <<"id">>,
final_cash_flow => #{postings => []}
}}},
Change =
{p_transfer,
{created, #{
id => <<"id">>,
final_cash_flow => #{postings => []}
}}},
Event = {ev, {{{2020, 5, 25}, {19, 19, 10}}, 293305}, Change},
LegacyEvent = {bin, base64:decode(<<
"CwABAAAAGzIwMjAtMDUtMjVUMTk6MTk6MTAuMjkzMzA1WgwAAgwAAwwAAQwAAQwAAQ"
"sAAgAAAAJpZAwAAQ8AAQwAAAAAAAAAAAAAAA=="
>>)},
LegacyEvent =
{bin,
base64:decode(<<
"CwABAAAAGzIwMjAtMDUtMjVUMTk6MTk6MTAuMjkzMzA1WgwAAgwAAwwAAQwAAQwAAQ"
"sAAgAAAAJpZAwAAQ8AAQwAAAAAAAAAAAAAAA=="
>>)},
DecodedLegacy = unmarshal({event, 1}, LegacyEvent),
ModernizedBinary = marshal({event, ?CURRENT_EVENT_FORMAT_VERSION}, DecodedLegacy),
Decoded = unmarshal({event, ?CURRENT_EVENT_FORMAT_VERSION}, ModernizedBinary),
@ -177,14 +182,18 @@ p_transfer_created_v1_decoding_test() ->
-spec p_transfer_clock_updated_v1_decoding_test() -> _.
p_transfer_clock_updated_v1_decoding_test() ->
Change = {p_transfer, {clock_updated, #{
version => 1,
type => latest
}}},
Change =
{p_transfer,
{clock_updated, #{
version => 1,
type => latest
}}},
Event = {ev, {{{2020, 5, 25}, {19, 19, 10}}, 293305}, Change},
LegacyEvent = {bin, base64:decode(<<
"CwABAAAAGzIwMjAtMDUtMjVUMTk6MTk6MTAuMjkzMzA1WgwAAgwAAwwAAQwAAwwAAQwAAQAAAAAAAAA="
>>)},
LegacyEvent =
{bin,
base64:decode(<<
"CwABAAAAGzIwMjAtMDUtMjVUMTk6MTk6MTAuMjkzMzA1WgwAAgwAAwwAAQwAAwwAAQwAAQAAAAAAAAA="
>>)},
DecodedLegacy = unmarshal({event, 1}, LegacyEvent),
ModernizedBinary = marshal({event, ?CURRENT_EVENT_FORMAT_VERSION}, DecodedLegacy),
Decoded = unmarshal({event, ?CURRENT_EVENT_FORMAT_VERSION}, ModernizedBinary),
@ -194,9 +203,11 @@ p_transfer_clock_updated_v1_decoding_test() ->
p_transfer_status_changed_v1_decoding_test() ->
Change = {p_transfer, {status_changed, committed}},
Event = {ev, {{{2020, 5, 25}, {19, 19, 10}}, 293305}, Change},
LegacyEvent = {bin, base64:decode(<<
"CwABAAAAGzIwMjAtMDUtMjVUMTk6MTk6MTAuMjkzMzA1WgwAAgwAAwwAAQwAAgwAAQwAAwAAAAAAAAA="
>>)},
LegacyEvent =
{bin,
base64:decode(<<
"CwABAAAAGzIwMjAtMDUtMjVUMTk6MTk6MTAuMjkzMzA1WgwAAgwAAwwAAQwAAgwAAQwAAwAAAAAAAAA="
>>)},
DecodedLegacy = unmarshal({event, 1}, LegacyEvent),
ModernizedBinary = marshal({event, ?CURRENT_EVENT_FORMAT_VERSION}, DecodedLegacy),
Decoded = unmarshal({event, ?CURRENT_EVENT_FORMAT_VERSION}, ModernizedBinary),
@ -204,27 +215,31 @@ p_transfer_status_changed_v1_decoding_test() ->
-spec adjustment_created_v1_decoding_test() -> _.
adjustment_created_v1_decoding_test() ->
Change = {adjustment, #{
id => <<"id">>,
payload => {created, #{
version => 1,
Change =
{adjustment, #{
id => <<"id">>,
status => succeeded,
created_at => 1590426777985,
changes_plan => #{},
domain_revision => 123,
party_revision => 321,
operation_timestamp => 1590426777985,
external_id => <<"external_id">>
}}
}},
payload =>
{created, #{
version => 1,
id => <<"id">>,
status => succeeded,
created_at => 1590426777985,
changes_plan => #{},
domain_revision => 123,
party_revision => 321,
operation_timestamp => 1590426777985,
external_id => <<"external_id">>
}}
}},
Event = {ev, {{{2020, 5, 25}, {19, 19, 10}}, 293305}, Change},
LegacyEvent = {bin, base64:decode(<<
"CwABAAAAGzIwMjAtMDUtMjVUMTk6MTk6MTAuMjkzMzA1WgwAAgwABAsAAQAAAAJpZAwAAgwAAQwAAQs"
"AAQAAAAJpZAwAAgwAAgAADAADAAsABAAAABgyMDIwLTA1LTI1VDE3OjEyOjU3Ljk4NVoKAAUAAAAAAA"
"AAewoABgAAAAAAAAFBCwAHAAAAC2V4dGVybmFsX2lkCwAIAAAAGDIwMjAtMDUtMjVUMTc6MTI6NTcuO"
"Tg1WgAAAAAAAA=="
>>)},
LegacyEvent =
{bin,
base64:decode(<<
"CwABAAAAGzIwMjAtMDUtMjVUMTk6MTk6MTAuMjkzMzA1WgwAAgwABAsAAQAAAAJpZAwAAgwAAQwAAQs"
"AAQAAAAJpZAwAAgwAAgAADAADAAsABAAAABgyMDIwLTA1LTI1VDE3OjEyOjU3Ljk4NVoKAAUAAAAAAA"
"AAewoABgAAAAAAAAFBCwAHAAAAC2V4dGVybmFsX2lkCwAIAAAAGDIwMjAtMDUtMjVUMTc6MTI6NTcuO"
"Tg1WgAAAAAAAA=="
>>)},
DecodedLegacy = unmarshal({event, 1}, LegacyEvent),
ModernizedBinary = marshal({event, ?CURRENT_EVENT_FORMAT_VERSION}, DecodedLegacy),
Decoded = unmarshal({event, ?CURRENT_EVENT_FORMAT_VERSION}, ModernizedBinary),

View File

@ -12,8 +12,7 @@
%% ff_woody_wrapper callbacks
%%
-spec handle_function(woody:func(), woody:args(), options()) ->
{ok, woody:result()} | no_return().
-spec handle_function(woody:func(), woody:args(), options()) -> {ok, woody:result()} | no_return().
handle_function('Repair', [ID, Scenario], _Opts) ->
DecodedScenario = ff_w2w_transfer_codec:unmarshal(repair_scenario, Scenario),
case w2w_transfer_machine:repair(ID, DecodedScenario) of

View File

@ -9,30 +9,23 @@
%% API
-spec marshal(ff_codec:type_name(), ff_codec:decoded_value()) ->
ff_codec:encoded_value().
-spec marshal(ff_codec:type_name(), ff_codec:decoded_value()) -> ff_codec:encoded_value().
marshal(status, pending) ->
{pending, #w2w_status_Pending{}};
marshal(status, succeeded) ->
{succeeded, #w2w_status_Succeeded{}};
marshal(status, {failed, Failure}) ->
{failed, #w2w_status_Failed{failure = marshal(failure, Failure)}};
marshal(T, V) ->
ff_codec:marshal(T, V).
-spec unmarshal(ff_codec:type_name(), ff_codec:encoded_value()) ->
ff_codec:decoded_value().
-spec unmarshal(ff_codec:type_name(), ff_codec:encoded_value()) -> ff_codec:decoded_value().
unmarshal(status, {pending, #w2w_status_Pending{}}) ->
pending;
unmarshal(status, {succeeded, #w2w_status_Succeeded{}}) ->
succeeded;
unmarshal(status, {failed, #w2w_status_Failed{failure = Failure}}) ->
{failed, unmarshal(failure, Failure)};
unmarshal(T, V) ->
ff_codec:unmarshal(T, V).
@ -40,9 +33,11 @@ unmarshal(T, V) ->
-ifdef(TEST).
-include_lib("eunit/include/eunit.hrl").
-spec test() -> _.
-spec pending_symmetry_test() -> _.
pending_symmetry_test() ->
Status = pending,
?assertEqual(Status, unmarshal(status, marshal(status, Status))).
@ -54,13 +49,14 @@ succeeded_symmetry_test() ->
-spec failed_symmetry_test() -> _.
failed_symmetry_test() ->
Status = {failed, #{
code => <<"test">>,
reason => <<"why not">>,
sub => #{
code => <<"sub">>
}
}},
Status =
{failed, #{
code => <<"test">>,
reason => <<"why not">>,
sub => #{
code => <<"sub">>
}
}},
?assertEqual(Status, unmarshal(status, marshal(status, Status))).
-endif.

View File

@ -13,7 +13,6 @@
%% API
-spec marshal_wallet_state(ff_wallet:wallet_state(), ff_wallet:id(), ff_entity_context:context()) ->
ff_proto_wallet_thrift:'WalletState'().
marshal_wallet_state(WalletState, ID, Context) ->
#wlt_WalletState{
id = marshal(id, ID),
@ -26,9 +25,7 @@ marshal_wallet_state(WalletState, ID, Context) ->
context = marshal(ctx, Context)
}.
-spec unmarshal_wallet_params(ff_proto_wallet_thrift:'WalletParams'()) ->
ff_wallet_machine:params().
-spec unmarshal_wallet_params(ff_proto_wallet_thrift:'WalletParams'()) -> ff_wallet_machine:params().
unmarshal_wallet_params(#wlt_WalletParams{
id = ID,
account_params = AccountParams,
@ -38,28 +35,24 @@ unmarshal_wallet_params(#wlt_WalletParams{
}) ->
{IdentityID, Currency} = unmarshal(account_params, AccountParams),
genlib_map:compact(#{
id => unmarshal(id, ID),
name => unmarshal(string, Name),
identity => IdentityID,
currency => Currency,
id => unmarshal(id, ID),
name => unmarshal(string, Name),
identity => IdentityID,
currency => Currency,
external_id => maybe_unmarshal(id, ExternalID),
metadata => maybe_unmarshal(ctx, Metadata)
metadata => maybe_unmarshal(ctx, Metadata)
}).
-spec marshal(ff_codec:type_name(), ff_codec:decoded_value()) ->
ff_codec:encoded_value().
-spec marshal(ff_codec:type_name(), ff_codec:decoded_value()) -> ff_codec:encoded_value().
marshal(timestamped_change, {ev, Timestamp, Change}) ->
#wlt_TimestampedChange{
change = marshal(change, Change),
occured_at = ff_codec:marshal(timestamp, Timestamp)
};
marshal(change, {created, Wallet}) ->
{created, marshal(wallet, Wallet)};
marshal(change, {account, AccountChange}) ->
{account, marshal(account_change, AccountChange)};
marshal(wallet, Wallet) ->
#wlt_Wallet{
name = marshal(string, maps:get(name, Wallet, <<>>)),
@ -68,7 +61,6 @@ marshal(wallet, Wallet) ->
created_at = maybe_marshal(timestamp_ms, maps:get(created_at, Wallet, undefined)),
metadata = maybe_marshal(ctx, maps:get(metadata, Wallet, undefined))
};
marshal(wallet_account_balance, AccountBalance) ->
#account_AccountBalance{
id = marshal(id, maps:get(id, AccountBalance)),
@ -77,36 +69,28 @@ marshal(wallet_account_balance, AccountBalance) ->
current = marshal(amount, maps:get(current, AccountBalance)),
expected_max = marshal(amount, maps:get(expected_max, AccountBalance))
};
marshal(ctx, Ctx) ->
marshal(context, Ctx);
marshal(T, V) ->
ff_codec:marshal(T, V).
-spec unmarshal(ff_codec:type_name(), ff_codec:encoded_value()) ->
ff_codec:decoded_value().
-spec unmarshal(ff_codec:type_name(), ff_codec:encoded_value()) -> ff_codec:decoded_value().
unmarshal({list, T}, V) ->
[unmarshal(T, E) || E <- V];
unmarshal(timestamped_change, TimestampedChange) ->
Timestamp = ff_codec:unmarshal(timestamp, TimestampedChange#wlt_TimestampedChange.occured_at),
Change = unmarshal(change, TimestampedChange#wlt_TimestampedChange.change),
{ev, Timestamp, Change};
unmarshal(repair_scenario, {add_events, #wlt_AddEventsRepair{events = Events, action = Action}}) ->
{add_events, genlib_map:compact(#{
events => unmarshal({list, change}, Events),
action => maybe_unmarshal(complex_action, Action)
})};
{add_events,
genlib_map:compact(#{
events => unmarshal({list, change}, Events),
action => maybe_unmarshal(complex_action, Action)
})};
unmarshal(change, {created, Wallet}) ->
{created, unmarshal(wallet, Wallet)};
unmarshal(change, {account, AccountChange}) ->
{account, unmarshal(account_change, AccountChange)};
unmarshal(wallet, #wlt_Wallet{
name = Name,
blocking = Blocking,
@ -122,16 +106,13 @@ unmarshal(wallet, #wlt_Wallet{
external_id => maybe_unmarshal(id, ExternalID),
metadata => maybe_unmarshal(ctx, Metadata)
});
unmarshal(account_params, #account_AccountParams{
identity_id = IdentityID,
identity_id = IdentityID,
symbolic_code = SymbolicCode
}) ->
{unmarshal(id, IdentityID), unmarshal(string, SymbolicCode) };
{unmarshal(id, IdentityID), unmarshal(string, SymbolicCode)};
unmarshal(ctx, Ctx) ->
maybe_unmarshal(context, Ctx);
unmarshal(T, V) ->
ff_codec:unmarshal(T, V).
@ -146,4 +127,3 @@ maybe_unmarshal(_Type, undefined) ->
undefined;
maybe_unmarshal(Type, Value) ->
unmarshal(Type, Value).

View File

@ -9,32 +9,28 @@
-type event() :: ff_eventsink_publisher:event(ff_wallet:event()).
-type sinkevent() :: ff_eventsink_publisher:sinkevent(ff_proto_wallet_thrift:'SinkEvent'()).
-spec publish_events(list(event())) ->
list(sinkevent()).
-spec publish_events(list(event())) -> list(sinkevent()).
publish_events(Events) ->
[publish_event(Event) || Event <- Events].
-spec publish_event(event()) ->
sinkevent().
-spec publish_event(event()) -> sinkevent().
publish_event(#{
id := ID,
source_id := SourceID,
event := {
id := ID,
source_id := SourceID,
event := {
EventID,
Dt,
{ev, EventDt, Payload}
}
}) ->
#wlt_SinkEvent{
id = marshal(event_id, ID),
created_at = marshal(timestamp, Dt),
source = marshal(id, SourceID),
payload = #wlt_Event{
sequence = marshal(event_id, EventID),
id = marshal(event_id, ID),
created_at = marshal(timestamp, Dt),
source = marshal(id, SourceID),
payload = #wlt_Event{
sequence = marshal(event_id, EventID),
occured_at = marshal(timestamp, EventDt),
changes = [marshal(change, Payload)]
changes = [marshal(change, Payload)]
}
}.

View File

@ -1,4 +1,5 @@
-module(ff_wallet_handler).
-behaviour(ff_woody_wrapper).
-include_lib("fistful_proto/include/ff_proto_wallet_thrift.hrl").
@ -9,11 +10,11 @@
%%
%% ff_woody_wrapper callbacks
%%
-spec handle_function(woody:func(), woody:args(), woody:options()) ->
{ok, woody:result()} | no_return().
-spec handle_function(woody:func(), woody:args(), woody:options()) -> {ok, woody:result()} | no_return().
handle_function(Func, Args, Opts) ->
scoper:scope(wallet, #{},
scoper:scope(
wallet,
#{},
fun() ->
handle_function_(Func, Args, Opts)
end
@ -24,9 +25,11 @@ handle_function(Func, Args, Opts) ->
%%
handle_function_('Create', [Params, Context], Opts) ->
WalletID = Params#wlt_WalletParams.id,
case ff_wallet_machine:create(
ff_wallet_codec:unmarshal_wallet_params(Params),
ff_wallet_codec:unmarshal(ctx, Context))
case
ff_wallet_machine:create(
ff_wallet_codec:unmarshal_wallet_params(Params),
ff_wallet_codec:unmarshal(ctx, Context)
)
of
ok ->
handle_function_('Get', [WalletID, #'EventRange'{}], Opts);
@ -41,28 +44,25 @@ handle_function_('Create', [Params, Context], Opts) ->
{error, Error} ->
woody_error:raise(system, {internal, result_unexpected, woody_error:format_details(Error)})
end;
handle_function_('Get', [ID, EventRange], _Opts) ->
case ff_wallet_machine:get(ID, ff_codec:unmarshal(event_range, EventRange)) of
{ok, Machine} ->
Wallet = ff_wallet_machine:wallet(Machine),
Ctx = ff_wallet_machine:ctx(Machine),
Response = ff_wallet_codec:marshal_wallet_state(Wallet, ID, Ctx),
Wallet = ff_wallet_machine:wallet(Machine),
Ctx = ff_wallet_machine:ctx(Machine),
Response = ff_wallet_codec:marshal_wallet_state(Wallet, ID, Ctx),
{ok, Response};
{error, notfound} ->
woody_error:raise(business, #fistful_WalletNotFound{})
end;
handle_function_('GetContext', [ID], _Opts) ->
case ff_wallet_machine:get(ID, {undefined, 0}) of
{ok, Machine} ->
Ctx = ff_wallet_machine:ctx(Machine),
Response = ff_wallet_codec:marshal(ctx, Ctx),
Ctx = ff_wallet_machine:ctx(Machine),
Response = ff_wallet_codec:marshal(ctx, Ctx),
{ok, Response};
{error, notfound} ->
woody_error:raise(business, #fistful_WalletNotFound{})
end;
handle_function_('GetAccountBalance', [ID], _Opts) ->
case ff_wallet_machine:get(ID) of
{ok, Machine} ->

View File

@ -21,60 +21,56 @@
-type value_type() :: machinery_mg_schema:vt().
-type context() :: machinery_mg_schema:context().
-type event() :: ff_machine:timestamped_event(p2p_transfer:event()).
-type event() :: ff_machine:timestamped_event(p2p_transfer:event()).
-type aux_state() :: ff_machine:auxst().
-type call_args() :: term().
-type call_response() :: term().
-type data() ::
aux_state() |
event() |
call_args() |
call_response().
aux_state()
| event()
| call_args()
| call_response().
%% machinery_mg_schema callbacks
-spec get_version(value_type()) ->
machinery_mg_schema:version().
-spec get_version(value_type()) -> machinery_mg_schema:version().
get_version(event) ->
?CURRENT_EVENT_FORMAT_VERSION;
get_version(aux_state) ->
undefined.
-spec marshal(type(), value(data()), context()) ->
{machinery_msgpack:t(), context()}.
-spec marshal(type(), value(data()), context()) -> {machinery_msgpack:t(), context()}.
marshal({event, Format}, TimestampedChange, Context) ->
marshal_event(Format, TimestampedChange, Context);
marshal(T, V, C) when
T =:= {args, init} orelse
T =:= {args, call} orelse
T =:= {args, repair} orelse
T =:= {aux_state, undefined} orelse
T =:= {response, call} orelse
T =:= {response, {repair, success}} orelse
T =:= {response, {repair, failure}}
T =:= {args, call} orelse
T =:= {args, repair} orelse
T =:= {aux_state, undefined} orelse
T =:= {response, call} orelse
T =:= {response, {repair, success}} orelse
T =:= {response, {repair, failure}}
->
machinery_mg_schema_generic:marshal(T, V, C).
-spec unmarshal(type(), machinery_msgpack:t(), context()) ->
{data(), context()}.
-spec unmarshal(type(), machinery_msgpack:t(), context()) -> {data(), context()}.
unmarshal({event, FormatVersion}, EncodedChange, Context) ->
unmarshal_event(FormatVersion, EncodedChange, Context);
unmarshal(T, V, C) when
T =:= {args, init} orelse
T =:= {args, call} orelse
T =:= {args, repair} orelse
T =:= {aux_state, undefined} orelse
T =:= {response, call} orelse
T =:= {response, {repair, success}} orelse
T =:= {response, {repair, failure}}
T =:= {args, call} orelse
T =:= {args, repair} orelse
T =:= {aux_state, undefined} orelse
T =:= {response, call} orelse
T =:= {response, {repair, success}} orelse
T =:= {response, {repair, failure}}
->
machinery_mg_schema_generic:unmarshal(T, V, C).
%% Internals
-spec marshal_event(machinery_mg_schema:version(), event(), context()) ->
{machinery_msgpack:t(), context()}.
-spec marshal_event(machinery_mg_schema:version(), event(), context()) -> {machinery_msgpack:t(), context()}.
marshal_event(undefined = Version, TimestampedChange, Context) ->
% TODO: Удалить после выкатки
machinery_mg_schema_generic:marshal({event, Version}, TimestampedChange, Context);
@ -83,8 +79,7 @@ marshal_event(1, TimestampedChange, Context) ->
Type = {struct, struct, {ff_proto_wallet_thrift, 'TimestampedChange'}},
{{bin, ff_proto_utils:serialize(Type, ThriftChange)}, Context}.
-spec unmarshal_event(machinery_mg_schema:version(), machinery_msgpack:t(), context()) ->
{event(), context()}.
-spec unmarshal_event(machinery_mg_schema:version(), machinery_msgpack:t(), context()) -> {event(), context()}.
unmarshal_event(1, EncodedChange, Context) ->
{bin, EncodedThriftChange} = EncodedChange,
Type = {struct, struct, {ff_proto_wallet_thrift, 'TimestampedChange'}},
@ -95,135 +90,147 @@ unmarshal_event(undefined = Version, EncodedChange, Context0) ->
{ev, Timestamp, Change} = Event,
{{ev, Timestamp, maybe_migrate(Change, Context1)}, Context1}.
-spec maybe_migrate(any(), context()) ->
ff_wallet:event().
-spec maybe_migrate(any(), context()) -> ff_wallet:event().
maybe_migrate(Event = {created, #{version := 2}}, _MigrateContext) ->
Event;
maybe_migrate({created, Wallet = #{version := 1}}, MigrateContext) ->
Context = case maps:get(machine_ref, MigrateContext, undefined) of
undefined ->
undefined;
ID ->
{ok, State} = ff_machine:get(ff_wallet, 'ff/wallet_v2', ID, {undefined, 0, forward}),
maps:get(ctx, State, undefined)
end,
maybe_migrate({created, genlib_map:compact(Wallet#{
version => 2,
metadata => ff_entity_context:try_get_legacy_metadata(Context)
})}, MigrateContext);
Context =
case maps:get(machine_ref, MigrateContext, undefined) of
undefined ->
undefined;
ID ->
{ok, State} = ff_machine:get(ff_wallet, 'ff/wallet_v2', ID, {undefined, 0, forward}),
maps:get(ctx, State, undefined)
end,
maybe_migrate(
{created,
genlib_map:compact(Wallet#{
version => 2,
metadata => ff_entity_context:try_get_legacy_metadata(Context)
})},
MigrateContext
);
maybe_migrate({created, Wallet}, MigrateContext) ->
Timestamp = maps:get(created_at, MigrateContext),
CreatedAt = ff_codec:unmarshal(timestamp_ms, ff_codec:marshal(timestamp, Timestamp)),
maybe_migrate({created, Wallet#{
version => 1,
created_at => CreatedAt
}}, MigrateContext);
maybe_migrate(
{created, Wallet#{
version => 1,
created_at => CreatedAt
}},
MigrateContext
);
maybe_migrate(Ev, _MigrateContext) ->
Ev.
%% Tests
-ifdef(TEST).
-include_lib("eunit/include/eunit.hrl").
-spec test() -> _.
% tests helpers
-spec marshal(type(), value(data())) ->
machinery_msgpack:t().
-spec marshal(type(), value(data())) -> machinery_msgpack:t().
marshal(Type, Value) ->
{Result, _Context} = marshal(Type, Value, #{}),
Result.
-spec unmarshal(type(), machinery_msgpack:t()) ->
data().
-spec unmarshal(type(), machinery_msgpack:t()) -> data().
unmarshal(Type, Value) ->
{Result, _Context} = unmarshal(Type, Value, #{}),
Result.
-spec created_v0_2_decoding_test() -> _.
created_v0_2_decoding_test() ->
Change = {created, #{
version => 2,
name => <<"name">>,
blocking => unblocked,
created_at => 123
}},
Change =
{created, #{
version => 2,
name => <<"name">>,
blocking => unblocked,
created_at => 123
}},
Event = {ev, {{{2020, 5, 25}, {19, 19, 10}}, 293305}, Change},
LegacyChange = {arr, [
{str, <<"tup">>},
{str, <<"created">>},
{arr, [
{str, <<"map">>},
{obj, #{
{str, <<"version">>} => {i, 1},
{str, <<"name">>} => {bin, <<"name">>},
{str, <<"blocking">>} => {str, <<"unblocked">>},
{str, <<"created_at">>} => {i, 123}
}}
]}
]},
LegacyEvent = {arr, [
{str, <<"tup">>},
{str, <<"ev">>},
{arr, [
{str, <<"tup">>},
{arr, [
{str, <<"tup">>},
{arr, [{str, <<"tup">>}, {i, 2020}, {i, 5}, {i, 25}]},
{arr, [{str, <<"tup">>}, {i, 19}, {i, 19}, {i, 10}]}
]},
{i, 293305}
]},
LegacyChange
]},
DecodedLegacy = unmarshal({event, undefined}, LegacyEvent),
ModernizedBinary = marshal({event, ?CURRENT_EVENT_FORMAT_VERSION}, DecodedLegacy),
Decoded = unmarshal({event, ?CURRENT_EVENT_FORMAT_VERSION}, ModernizedBinary),
?assertEqual(Event, Decoded).
-spec created_account_v0_2_decoding_test() -> _.
created_account_v0_2_decoding_test() ->
Change = {account, {created, #{
id => <<"id">>,
identity => <<"identity_id">>,
currency => <<"RUB">>,
accounter_account_id => 123
}}},
Event = {ev, {{{2020, 5, 25}, {19, 19, 10}}, 293305}, Change},
LegacyChange = {arr, [
{str, <<"tup">>},
{str, <<"account">>},
LegacyChange =
{arr, [
{str, <<"tup">>},
{str, <<"created">>},
{arr, [
{str, <<"map">>},
{obj, #{
{str, <<"id">>} => {bin, <<"id">>},
{str, <<"identity">>} => {bin, <<"identity_id">>},
{str, <<"currency">>} => {bin, <<"RUB">>},
{str, <<"accounter_account_id">>} => {i, 123}
{str, <<"version">>} => {i, 1},
{str, <<"name">>} => {bin, <<"name">>},
{str, <<"blocking">>} => {str, <<"unblocked">>},
{str, <<"created_at">>} => {i, 123}
}}
]}
]}
]},
LegacyEvent = {arr, [
{str, <<"tup">>},
{str, <<"ev">>},
]},
LegacyEvent =
{arr, [
{str, <<"tup">>},
{str, <<"ev">>},
{arr, [
{str, <<"tup">>},
{arr, [{str, <<"tup">>}, {i, 2020}, {i, 5}, {i, 25}]},
{arr, [{str, <<"tup">>}, {i, 19}, {i, 19}, {i, 10}]}
{arr, [
{str, <<"tup">>},
{arr, [{str, <<"tup">>}, {i, 2020}, {i, 5}, {i, 25}]},
{arr, [{str, <<"tup">>}, {i, 19}, {i, 19}, {i, 10}]}
]},
{i, 293305}
]},
{i, 293305}
LegacyChange
]},
DecodedLegacy = unmarshal({event, undefined}, LegacyEvent),
ModernizedBinary = marshal({event, ?CURRENT_EVENT_FORMAT_VERSION}, DecodedLegacy),
Decoded = unmarshal({event, ?CURRENT_EVENT_FORMAT_VERSION}, ModernizedBinary),
?assertEqual(Event, Decoded).
-spec created_account_v0_2_decoding_test() -> _.
created_account_v0_2_decoding_test() ->
Change =
{account,
{created, #{
id => <<"id">>,
identity => <<"identity_id">>,
currency => <<"RUB">>,
accounter_account_id => 123
}}},
Event = {ev, {{{2020, 5, 25}, {19, 19, 10}}, 293305}, Change},
LegacyChange =
{arr, [
{str, <<"tup">>},
{str, <<"account">>},
{arr, [
{str, <<"tup">>},
{str, <<"created">>},
{arr, [
{str, <<"map">>},
{obj, #{
{str, <<"id">>} => {bin, <<"id">>},
{str, <<"identity">>} => {bin, <<"identity_id">>},
{str, <<"currency">>} => {bin, <<"RUB">>},
{str, <<"accounter_account_id">>} => {i, 123}
}}
]}
]}
]},
LegacyEvent =
{arr, [
{str, <<"tup">>},
{str, <<"ev">>},
{arr, [
{str, <<"tup">>},
{arr, [
{str, <<"tup">>},
{arr, [{str, <<"tup">>}, {i, 2020}, {i, 5}, {i, 25}]},
{arr, [{str, <<"tup">>}, {i, 19}, {i, 19}, {i, 10}]}
]},
{i, 293305}
]},
LegacyChange
]},
LegacyChange
]},
DecodedLegacy = unmarshal({event, undefined}, LegacyEvent),
ModernizedBinary = marshal({event, ?CURRENT_EVENT_FORMAT_VERSION}, DecodedLegacy),
Decoded = unmarshal({event, ?CURRENT_EVENT_FORMAT_VERSION}, ModernizedBinary),
@ -231,17 +238,20 @@ created_account_v0_2_decoding_test() ->
-spec created_v2_decoding_test() -> _.
created_v2_decoding_test() ->
Change = {created, #{
version => 2,
name => <<"name">>,
blocking => unblocked,
created_at => 123
}},
Change =
{created, #{
version => 2,
name => <<"name">>,
blocking => unblocked,
created_at => 123
}},
Event = {ev, {{{2020, 5, 25}, {19, 19, 10}}, 293305}, Change},
LegacyEvent = {bin, base64:decode(<<
"CwABAAAAGzIwMjAtMDUtMjVUMTk6MTk6MTAuMjkzMzA1WgwAAgwAAQsAAQAAAARuYW1lC"
"AAEAAAAAAsABgAAABgxOTcwLTAxLTAxVDAwOjAwOjAwLjEyM1oAAAA="
>>)},
LegacyEvent =
{bin,
base64:decode(<<
"CwABAAAAGzIwMjAtMDUtMjVUMTk6MTk6MTAuMjkzMzA1WgwAAgwAAQsAAQAAAARuYW1lC"
"AAEAAAAAAsABgAAABgxOTcwLTAxLTAxVDAwOjAwOjAwLjEyM1oAAAA="
>>)},
DecodedLegacy = unmarshal({event, 1}, LegacyEvent),
ModernizedBinary = marshal({event, ?CURRENT_EVENT_FORMAT_VERSION}, DecodedLegacy),
Decoded = unmarshal({event, ?CURRENT_EVENT_FORMAT_VERSION}, ModernizedBinary),
@ -249,17 +259,21 @@ created_v2_decoding_test() ->
-spec created_account_v2_decoding_test() -> _.
created_account_v2_decoding_test() ->
Change = {account, {created, #{
id => <<"id">>,
identity => <<"identity_id">>,
currency => <<"RUB">>,
accounter_account_id => 123
}}},
Change =
{account,
{created, #{
id => <<"id">>,
identity => <<"identity_id">>,
currency => <<"RUB">>,
accounter_account_id => 123
}}},
Event = {ev, {{{2020, 5, 25}, {19, 19, 10}}, 293305}, Change},
LegacyEvent = {bin, base64:decode(<<
"CwABAAAAGzIwMjAtMDUtMjVUMTk6MTk6MTAuMjkzMzA1WgwAAgwAAgwAAQsAAwAAAAJpZAs"
"AAQAAAAtpZGVudGl0eV9pZAwAAgsAAQAAAANSVUIACgAEAAAAAAAAAHsAAAAA"
>>)},
LegacyEvent =
{bin,
base64:decode(<<
"CwABAAAAGzIwMjAtMDUtMjVUMTk6MTk6MTAuMjkzMzA1WgwAAgwAAgwAAQsAAwAAAAJpZAs"
"AAQAAAAtpZGVudGl0eV9pZAwAAgsAAQAAAANSVUIACgAEAAAAAAAAAHsAAAAA"
>>)},
DecodedLegacy = unmarshal({event, 1}, LegacyEvent),
ModernizedBinary = marshal({event, ?CURRENT_EVENT_FORMAT_VERSION}, DecodedLegacy),
Decoded = unmarshal({event, ?CURRENT_EVENT_FORMAT_VERSION}, ModernizedBinary),

View File

@ -1,4 +1,5 @@
-module(ff_withdrawal_adapter_host).
-behaviour(ff_woody_wrapper).
-include_lib("damsel/include/dmsl_withdrawals_provider_adapter_thrift.hrl").
@ -13,8 +14,7 @@
%% Handler
-spec handle_function(woody:func(), woody:args(), woody:options()) ->
{ok, woody:result()} | no_return().
-spec handle_function(woody:func(), woody:args(), woody:options()) -> {ok, woody:result()} | no_return().
handle_function(Func, Args, Opts) ->
scoper:scope(ff_withdrawal_adapter_host, #{}, fun() -> handle_function_(Func, Args, Opts) end).

View File

@ -9,16 +9,13 @@
%% API
-spec marshal(ff_codec:type_name(), ff_codec:decoded_value()) ->
ff_codec:encoded_value().
-spec marshal(ff_codec:type_name(), ff_codec:decoded_value()) -> ff_codec:encoded_value().
marshal(change, {created, Adjustment}) ->
{created, #wthd_adj_CreatedChange{adjustment = marshal(adjustment, Adjustment)}};
marshal(change, {status_changed, Status}) ->
{status_changed, #wthd_adj_StatusChange{status = marshal(status, Status)}};
marshal(change, {p_transfer, TransferChange}) ->
{transfer, #wthd_adj_TransferChange{payload = ff_p_transfer_codec:marshal(change, TransferChange)}};
marshal(adjustment, Adjustment) ->
#wthd_adj_Adjustment{
id = marshal(id, ff_adjustment:id(Adjustment)),
@ -47,12 +44,10 @@ marshal(adjustment_state, Adjustment) ->
operation_timestamp = marshal(timestamp_ms, ff_adjustment:operation_timestamp(Adjustment)),
external_id = maybe_marshal(id, ff_adjustment:external_id(Adjustment))
};
marshal(status, pending) ->
{pending, #wthd_adj_Pending{}};
marshal(status, succeeded) ->
{succeeded, #wthd_adj_Succeeded{}};
marshal(changes_plan, Plan) ->
#wthd_adj_ChangesPlan{
new_cash_flow = maybe_marshal(cash_flow_change_plan, maps:get(new_cash_flow, Plan, undefined)),
@ -69,26 +64,20 @@ marshal(status_change_plan, Plan) ->
#wthd_adj_StatusChangePlan{
new_status = ff_withdrawal_status_codec:marshal(status, maps:get(new_status, Plan))
};
marshal(change_request, {change_status, Status}) ->
{change_status, #wthd_adj_ChangeStatusRequest{
new_status = ff_withdrawal_status_codec:marshal(status, Status)
}};
marshal(T, V) ->
ff_codec:marshal(T, V).
-spec unmarshal(ff_codec:type_name(), ff_codec:encoded_value()) ->
ff_codec:decoded_value().
-spec unmarshal(ff_codec:type_name(), ff_codec:encoded_value()) -> ff_codec:decoded_value().
unmarshal(change, {created, #wthd_adj_CreatedChange{adjustment = Adjustment}}) ->
{created, unmarshal(adjustment, Adjustment)};
unmarshal(change, {status_changed, #wthd_adj_StatusChange{status = Status}}) ->
{status_changed, unmarshal(status, Status)};
unmarshal(change, {transfer, #wthd_adj_TransferChange{payload = TransferChange}}) ->
{p_transfer, ff_p_transfer_codec:unmarshal(change, TransferChange)};
unmarshal(adjustment, Adjustment) ->
#{
id => unmarshal(id, Adjustment#wthd_adj_Adjustment.id),
@ -100,19 +89,16 @@ unmarshal(adjustment, Adjustment) ->
operation_timestamp => unmarshal(timestamp_ms, Adjustment#wthd_adj_Adjustment.operation_timestamp),
external_id => maybe_unmarshal(id, Adjustment#wthd_adj_Adjustment.external_id)
};
unmarshal(adjustment_params, Params) ->
genlib_map:compact(#{
id => unmarshal(id, Params#wthd_adj_AdjustmentParams.id),
change => unmarshal(change_request, Params#wthd_adj_AdjustmentParams.change),
external_id => maybe_unmarshal(id, Params#wthd_adj_AdjustmentParams.external_id)
});
unmarshal(status, {pending, #wthd_adj_Pending{}}) ->
pending;
unmarshal(status, {succeeded, #wthd_adj_Succeeded{}}) ->
succeeded;
unmarshal(changes_plan, Plan) ->
genlib_map:compact(#{
new_cash_flow => maybe_unmarshal(cash_flow_change_plan, Plan#wthd_adj_ChangesPlan.new_cash_flow),
@ -130,11 +116,9 @@ unmarshal(status_change_plan, Plan) ->
#{
new_status => ff_withdrawal_status_codec:unmarshal(status, Status)
};
unmarshal(change_request, {change_status, Request}) ->
Status = Request#wthd_adj_ChangeStatusRequest.new_status,
{change_status, ff_withdrawal_status_codec:unmarshal(status, Status)};
unmarshal(T, V) ->
ff_codec:unmarshal(T, V).
@ -154,9 +138,11 @@ maybe_marshal(Type, Value) ->
-ifdef(TEST).
-include_lib("eunit/include/eunit.hrl").
-spec test() -> _.
-spec adjustment_codec_test() -> _.
adjustment_codec_test() ->
FinalCashFlow = #{
postings => [

View File

@ -17,49 +17,42 @@
%% API
-spec unmarshal_quote_params(ff_proto_withdrawal_thrift:'QuoteParams'()) ->
ff_withdrawal:quote_params().
-spec unmarshal_quote_params(ff_proto_withdrawal_thrift:'QuoteParams'()) -> ff_withdrawal:quote_params().
unmarshal_quote_params(Params) ->
genlib_map:compact(#{
wallet_id => unmarshal(id, Params#wthd_QuoteParams.wallet_id),
currency_from => unmarshal(currency_ref, Params#wthd_QuoteParams.currency_from),
currency_to => unmarshal(currency_ref, Params#wthd_QuoteParams.currency_to),
body => unmarshal(cash, Params#wthd_QuoteParams.body),
wallet_id => unmarshal(id, Params#wthd_QuoteParams.wallet_id),
currency_from => unmarshal(currency_ref, Params#wthd_QuoteParams.currency_from),
currency_to => unmarshal(currency_ref, Params#wthd_QuoteParams.currency_to),
body => unmarshal(cash, Params#wthd_QuoteParams.body),
destination_id => maybe_unmarshal(id, Params#wthd_QuoteParams.destination_id),
external_id => maybe_unmarshal(id, Params#wthd_QuoteParams.external_id)
external_id => maybe_unmarshal(id, Params#wthd_QuoteParams.external_id)
}).
-spec marshal_withdrawal_params(ff_withdrawal:params()) ->
ff_proto_withdrawal_thrift:'WithdrawalParams'().
-spec marshal_withdrawal_params(ff_withdrawal:params()) -> ff_proto_withdrawal_thrift:'WithdrawalParams'().
marshal_withdrawal_params(Params) ->
#wthd_WithdrawalParams{
id = marshal(id, maps:get(id, Params)),
wallet_id = marshal(id, maps:get(wallet_id, Params)),
id = marshal(id, maps:get(id, Params)),
wallet_id = marshal(id, maps:get(wallet_id, Params)),
destination_id = marshal(id, maps:get(destination_id, Params)),
body = marshal(cash, maps:get(body, Params)),
external_id = maybe_marshal(id, maps:get(external_id, Params, undefined)),
metadata = maybe_marshal(ctx, maps:get(metadata, Params, undefined))
body = marshal(cash, maps:get(body, Params)),
external_id = maybe_marshal(id, maps:get(external_id, Params, undefined)),
metadata = maybe_marshal(ctx, maps:get(metadata, Params, undefined))
}.
-spec unmarshal_withdrawal_params(ff_proto_withdrawal_thrift:'WithdrawalParams'()) ->
ff_withdrawal:params().
-spec unmarshal_withdrawal_params(ff_proto_withdrawal_thrift:'WithdrawalParams'()) -> ff_withdrawal:params().
unmarshal_withdrawal_params(Params) ->
genlib_map:compact(#{
id => unmarshal(id, Params#wthd_WithdrawalParams.id),
wallet_id => unmarshal(id, Params#wthd_WithdrawalParams.wallet_id),
id => unmarshal(id, Params#wthd_WithdrawalParams.id),
wallet_id => unmarshal(id, Params#wthd_WithdrawalParams.wallet_id),
destination_id => unmarshal(id, Params#wthd_WithdrawalParams.destination_id),
body => unmarshal(cash, Params#wthd_WithdrawalParams.body),
quote => maybe_unmarshal(quote, Params#wthd_WithdrawalParams.quote),
external_id => maybe_unmarshal(id, Params#wthd_WithdrawalParams.external_id),
metadata => maybe_unmarshal(ctx, Params#wthd_WithdrawalParams.metadata)
body => unmarshal(cash, Params#wthd_WithdrawalParams.body),
quote => maybe_unmarshal(quote, Params#wthd_WithdrawalParams.quote),
external_id => maybe_unmarshal(id, Params#wthd_WithdrawalParams.external_id),
metadata => maybe_unmarshal(ctx, Params#wthd_WithdrawalParams.metadata)
}).
-spec marshal_withdrawal_state(ff_withdrawal:withdrawal_state(), ff_entity_context:context()) ->
ff_proto_withdrawal_thrift:'WithdrawalState'().
marshal_withdrawal_state(WithdrawalState, Context) ->
CashFlow = ff_withdrawal:effective_final_cash_flow(WithdrawalState),
Adjustments = ff_withdrawal:adjustments(WithdrawalState),
@ -84,9 +77,7 @@ marshal_withdrawal_state(WithdrawalState, Context) ->
quote = maybe_marshal(quote_state, ff_withdrawal:quote(WithdrawalState))
}.
-spec marshal_event(ff_withdrawal_machine:event()) ->
ff_proto_withdrawal_thrift:'Event'().
-spec marshal_event(ff_withdrawal_machine:event()) -> ff_proto_withdrawal_thrift:'Event'().
marshal_event({EventID, {ev, Timestamp, Change}}) ->
#wthd_Event{
event_id = ff_codec:marshal(event_id, EventID),
@ -94,18 +85,14 @@ marshal_event({EventID, {ev, Timestamp, Change}}) ->
change = marshal(change, Change)
}.
-spec marshal(ff_codec:type_name(), ff_codec:decoded_value()) ->
ff_codec:encoded_value().
-spec marshal(ff_codec:type_name(), ff_codec:decoded_value()) -> ff_codec:encoded_value().
marshal({list, T}, V) ->
[marshal(T, E) || E <- V];
marshal(timestamped_change, {ev, Timestamp, Change}) ->
#wthd_TimestampedChange{
change = marshal(change, Change),
occured_at = ff_codec:marshal(timestamp, Timestamp)
};
marshal(change, {created, Withdrawal}) ->
{created, #wthd_CreatedChange{withdrawal = marshal(withdrawal, Withdrawal)}};
marshal(change, {status_changed, Status}) ->
@ -127,7 +114,6 @@ marshal(change, {adjustment, #{id := ID, payload := Payload}}) ->
id = marshal(id, ID),
payload = ff_withdrawal_adjustment_codec:marshal(change, Payload)
}};
marshal(withdrawal, Withdrawal) ->
#wthd_Withdrawal{
id = marshal(id, ff_withdrawal:id(Withdrawal)),
@ -142,7 +128,6 @@ marshal(withdrawal, Withdrawal) ->
metadata = maybe_marshal(ctx, ff_withdrawal:metadata(Withdrawal)),
quote = maybe_marshal(quote_state, ff_withdrawal:quote(Withdrawal))
};
marshal(route, Route) ->
#{
version := 1,
@ -153,15 +138,12 @@ marshal(route, Route) ->
terminal_id = maybe_marshal(terminal_id, genlib_map:get(terminal_id, Route)),
provider_id_legacy = marshal(string, get_legacy_provider_id(Route))
};
marshal(status, Status) ->
ff_withdrawal_status_codec:marshal(status, Status);
marshal(session_event, started) ->
{started, #wthd_SessionStarted{}};
marshal(session_event, {finished, Result}) ->
{finished, #wthd_SessionFinished{result = marshal(session_result, Result)}};
marshal(session_result, success) ->
{succeeded, #wthd_SessionSucceeded{}};
marshal(session_result, {success, TransactionInfo}) ->
@ -170,21 +152,19 @@ marshal(session_result, {success, TransactionInfo}) ->
{succeeded, #wthd_SessionSucceeded{trx_info = marshal(transaction_info, TransactionInfo)}};
marshal(session_result, {failed, Failure}) ->
{failed, #wthd_SessionFailed{failure = ff_codec:marshal(failure, Failure)}};
marshal(transaction_info, TrxInfo) ->
ff_withdrawal_session_codec:marshal(transaction_info, TrxInfo);
marshal(session_state, Session) ->
#wthd_SessionState{
id = marshal(id, maps:get(id, Session)),
result = maybe_marshal(session_result, maps:get(result, Session, undefined))
};
marshal(quote_state, Quote) ->
#wthd_QuoteState{
cash_from = marshal(cash, maps:get(cash_from, Quote)),
cash_to = marshal(cash, maps:get(cash_to, Quote)),
created_at = maps:get(created_at, Quote), % already formatted
cash_from = marshal(cash, maps:get(cash_from, Quote)),
cash_to = marshal(cash, maps:get(cash_to, Quote)),
% already formatted
created_at = maps:get(created_at, Quote),
expires_on = maps:get(expires_on, Quote),
quote_data = maybe_marshal(msgpack, maps:get(quote_data, Quote, undefined)),
route = maybe_marshal(route, maps:get(route, Quote, undefined)),
@ -193,9 +173,10 @@ marshal(quote_state, Quote) ->
};
marshal(quote, Quote) ->
#wthd_Quote{
cash_from = marshal(cash, maps:get(cash_from, Quote)),
cash_to = marshal(cash, maps:get(cash_to, Quote)),
created_at = maps:get(created_at, Quote), % already formatted
cash_from = marshal(cash, maps:get(cash_from, Quote)),
cash_to = marshal(cash, maps:get(cash_to, Quote)),
% already formatted
created_at = maps:get(created_at, Quote),
expires_on = maps:get(expires_on, Quote),
quote_data = maybe_marshal(msgpack, genlib_map:get(quote_data, Quote)),
route = maybe_marshal(route, genlib_map:get(route, Quote)),
@ -204,31 +185,24 @@ marshal(quote, Quote) ->
domain_revision = maybe_marshal(domain_revision, genlib_map:get(domain_revision, Quote)),
operation_timestamp = maybe_marshal(timestamp_ms, genlib_map:get(operation_timestamp, Quote))
};
marshal(ctx, Ctx) ->
maybe_marshal(context, Ctx);
marshal(T, V) ->
ff_codec:marshal(T, V).
-spec unmarshal(ff_codec:type_name(), ff_codec:encoded_value()) ->
ff_codec:decoded_value().
-spec unmarshal(ff_codec:type_name(), ff_codec:encoded_value()) -> ff_codec:decoded_value().
unmarshal({list, T}, V) ->
[unmarshal(T, E) || E <- V];
unmarshal(timestamped_change, TimestampedChange) ->
Timestamp = ff_codec:unmarshal(timestamp, TimestampedChange#wthd_TimestampedChange.occured_at),
Change = unmarshal(change, TimestampedChange#wthd_TimestampedChange.change),
{ev, Timestamp, Change};
unmarshal(repair_scenario, {add_events, #wthd_AddEventsRepair{events = Events, action = Action}}) ->
{add_events, genlib_map:compact(#{
events => unmarshal({list, change}, Events),
action => maybe_unmarshal(complex_action, Action)
})};
{add_events,
genlib_map:compact(#{
events => unmarshal({list, change}, Events),
action => maybe_unmarshal(complex_action, Action)
})};
unmarshal(change, {created, #wthd_CreatedChange{withdrawal = Withdrawal}}) ->
{created, unmarshal(withdrawal, Withdrawal)};
unmarshal(change, {status_changed, #wthd_StatusChange{status = Status}}) ->
@ -248,7 +222,6 @@ unmarshal(change, {adjustment, Change}) ->
id => unmarshal(id, Change#wthd_AdjustmentChange.id),
payload => ff_withdrawal_adjustment_codec:unmarshal(change, Change#wthd_AdjustmentChange.payload)
}};
unmarshal(withdrawal, Withdrawal = #wthd_Withdrawal{}) ->
ff_withdrawal:gen(#{
id => unmarshal(id, Withdrawal#wthd_Withdrawal.id),
@ -266,7 +239,6 @@ unmarshal(withdrawal, Withdrawal = #wthd_Withdrawal{}) ->
transfer_type => withdrawal,
metadata => maybe_unmarshal(ctx, Withdrawal#wthd_Withdrawal.metadata)
});
unmarshal(route, Route) ->
genlib_map:compact(#{
version => 1,
@ -274,17 +246,13 @@ unmarshal(route, Route) ->
terminal_id => maybe_unmarshal(terminal_id, Route#wthd_Route.terminal_id),
provider_id_legacy => maybe_unmarshal(string, Route#wthd_Route.provider_id_legacy)
});
unmarshal(status, Status) ->
ff_withdrawal_status_codec:unmarshal(status, Status);
unmarshal(session_event, #wthd_SessionChange{id = ID, payload = {started, #wthd_SessionStarted{}}}) ->
{session_started, unmarshal(id, ID)};
unmarshal(session_event, #wthd_SessionChange{id = ID, payload = {finished, Finished}}) ->
#wthd_SessionFinished{result = Result} = Finished,
{session_finished, {unmarshal(id, ID), unmarshal(session_result, Result)}};
unmarshal(session_result, {succeeded, #wthd_SessionSucceeded{trx_info = undefined}}) ->
success;
unmarshal(session_result, {succeeded, #wthd_SessionSucceeded{trx_info = TransactionInfo}}) ->
@ -293,31 +261,27 @@ unmarshal(session_result, {succeeded, #wthd_SessionSucceeded{trx_info = Transact
{success, unmarshal(transaction_info, TransactionInfo)};
unmarshal(session_result, {failed, #wthd_SessionFailed{failure = Failure}}) ->
{failed, ff_codec:unmarshal(failure, Failure)};
unmarshal(transaction_info, TrxInfo) ->
ff_withdrawal_session_codec:unmarshal(transaction_info, TrxInfo);
unmarshal(session_state, Session) ->
genlib_map:compact(#{
id => unmarshal(id, Session#wthd_SessionState.id),
result => maybe_unmarshal(session_result, Session#wthd_SessionState.result)
});
unmarshal(quote_state, Quote) ->
genlib_map:compact(#{
cash_from => unmarshal(cash, Quote#wthd_QuoteState.cash_from),
cash_to => unmarshal(cash, Quote#wthd_QuoteState.cash_to),
cash_to => unmarshal(cash, Quote#wthd_QuoteState.cash_to),
created_at => Quote#wthd_QuoteState.created_at,
expires_on => Quote#wthd_QuoteState.expires_on,
route => maybe_unmarshal(route, Quote#wthd_QuoteState.route),
resource_descriptor => maybe_unmarshal(resource_descriptor, Quote#wthd_QuoteState.resource),
quote_data => maybe_unmarshal(msgpack, Quote#wthd_QuoteState.quote_data)
});
unmarshal(quote, Quote) ->
genlib_map:compact(#{
cash_from => unmarshal(cash, Quote#wthd_Quote.cash_from),
cash_to => unmarshal(cash, Quote#wthd_Quote.cash_to),
cash_to => unmarshal(cash, Quote#wthd_Quote.cash_to),
created_at => Quote#wthd_Quote.created_at,
expires_on => Quote#wthd_Quote.expires_on,
route => maybe_unmarshal(route, Quote#wthd_Quote.route),
@ -327,10 +291,8 @@ unmarshal(quote, Quote) ->
party_revision => maybe_unmarshal(party_revision, Quote#wthd_Quote.party_revision),
operation_timestamp => maybe_unmarshal(timestamp_ms, Quote#wthd_Quote.operation_timestamp)
});
unmarshal(ctx, Ctx) ->
maybe_unmarshal(context, Ctx);
unmarshal(T, V) ->
ff_codec:unmarshal(T, V).
@ -355,15 +317,17 @@ get_legacy_provider_id(#{provider_id := Provider}) when is_integer(Provider) ->
-ifdef(TEST).
-include_lib("eunit/include/eunit.hrl").
-spec test() -> _.
-spec withdrawal_symmetry_test() -> _.
withdrawal_symmetry_test() ->
In = #wthd_Withdrawal{
id = genlib:unique(),
body = #'Cash'{
amount = 10101,
currency = #'CurrencyRef'{ symbolic_code = <<"Banana Republic">> }
currency = #'CurrencyRef'{symbolic_code = <<"Banana Republic">>}
},
wallet_id = genlib:unique(),
destination_id = genlib:unique(),
@ -385,7 +349,7 @@ withdrawal_params_symmetry_test() ->
id = genlib:unique(),
body = #'Cash'{
amount = 10101,
currency = #'CurrencyRef'{ symbolic_code = <<"Banana Republic">> }
currency = #'CurrencyRef'{symbolic_code = <<"Banana Republic">>}
},
wallet_id = genlib:unique(),
destination_id = genlib:unique(),
@ -396,13 +360,13 @@ withdrawal_params_symmetry_test() ->
-spec quote_state_symmetry_test() -> _.
quote_state_symmetry_test() ->
In = #wthd_QuoteState{
cash_from = #'Cash'{
cash_from = #'Cash'{
amount = 10101,
currency = #'CurrencyRef'{ symbolic_code = <<"Banana Republic">> }
currency = #'CurrencyRef'{symbolic_code = <<"Banana Republic">>}
},
cash_to = #'Cash'{
cash_to = #'Cash'{
amount = 20202,
currency = #'CurrencyRef'{ symbolic_code = <<"Pineapple Empire">> }
currency = #'CurrencyRef'{symbolic_code = <<"Pineapple Empire">>}
},
created_at = genlib:unique(),
expires_on = genlib:unique(),
@ -420,13 +384,13 @@ quote_state_symmetry_test() ->
-spec quote_symmetry_test() -> _.
quote_symmetry_test() ->
In = #wthd_Quote{
cash_from = #'Cash'{
cash_from = #'Cash'{
amount = 10101,
currency = #'CurrencyRef'{ symbolic_code = <<"Banana Republic">> }
currency = #'CurrencyRef'{symbolic_code = <<"Banana Republic">>}
},
cash_to = #'Cash'{
cash_to = #'Cash'{
amount = 20202,
currency = #'CurrencyRef'{ symbolic_code = <<"Pineapple Empire">> }
currency = #'CurrencyRef'{symbolic_code = <<"Pineapple Empire">>}
},
created_at = genlib:unique(),
expires_on = genlib:unique(),
@ -443,10 +407,9 @@ quote_symmetry_test() ->
},
?assertEqual(In, marshal(quote, unmarshal(quote, In))).
-spec marshal_session_result_test_() -> _.
-spec marshal_session_result_test_() -> _.
marshal_session_result_test_() ->
TransactionInfo = #{ id => <<"ID">>, extra => #{<<"Hello">> => <<"World">>} },
TransactionInfo = #{id => <<"ID">>, extra => #{<<"Hello">> => <<"World">>}},
TransactionInfoThrift = marshal(transaction_info, TransactionInfo),
Results = [
{success, TransactionInfo},

View File

@ -16,32 +16,28 @@
%% Internals
%%
-spec publish_events(list(event())) ->
list(sinkevent()).
-spec publish_events(list(event())) -> list(sinkevent()).
publish_events(Events) ->
[publish_event(Event) || Event <- Events].
-spec publish_event(event()) ->
sinkevent().
-spec publish_event(event()) -> sinkevent().
publish_event(#{
id := ID,
source_id := SourceID,
event := {
id := ID,
source_id := SourceID,
event := {
EventID,
Dt,
{ev, EventDt, Payload}
}
}) ->
#wthd_SinkEvent{
id = marshal(event_id, ID),
created_at = marshal(timestamp, Dt),
source = marshal(id, SourceID),
payload = #wthd_EventSinkPayload{
sequence = marshal(event_id, EventID),
id = marshal(event_id, ID),
created_at = marshal(timestamp, Dt),
source = marshal(id, SourceID),
payload = #wthd_EventSinkPayload{
sequence = marshal(event_id, EventID),
occured_at = marshal(timestamp, EventDt),
changes = [marshal(change, Payload)]
changes = [marshal(change, Payload)]
}
}.

View File

@ -1,4 +1,5 @@
-module(ff_withdrawal_handler).
-behaviour(ff_woody_wrapper).
-include_lib("fistful_proto/include/ff_proto_withdrawal_thrift.hrl").
@ -9,10 +10,11 @@
%%
%% ff_woody_wrapper callbacks
%%
-spec handle_function(woody:func(), woody:args(), woody:options()) ->
{ok, woody:result()} | no_return().
-spec handle_function(woody:func(), woody:args(), woody:options()) -> {ok, woody:result()} | no_return().
handle_function(Func, Args, Opts) ->
scoper:scope(withdrawal, #{},
scoper:scope(
withdrawal,
#{},
fun() ->
handle_function_(Func, Args, Opts)
end
@ -136,11 +138,13 @@ handle_function_('GetEvents', [ID, EventRange], _Opts) ->
handle_function_('CreateAdjustment', [ID, MarshaledParams], _Opts) ->
Params = ff_withdrawal_adjustment_codec:unmarshal(adjustment_params, MarshaledParams),
AdjustmentID = maps:get(id, Params),
ok = scoper:add_meta(genlib_map:compact(#{
id => ID,
adjustment_id => AdjustmentID,
external_id => maps:get(external_id, Params, undefined)
})),
ok = scoper:add_meta(
genlib_map:compact(#{
id => ID,
adjustment_id => AdjustmentID,
external_id => maps:get(external_id, Params, undefined)
})
),
case ff_withdrawal_machine:start_adjustment(ID, Params) of
ok ->
{ok, Machine} = ff_withdrawal_machine:get(ID),

File diff suppressed because it is too large Load Diff

View File

@ -12,8 +12,7 @@
%% ff_woody_wrapper callbacks
%%
-spec handle_function(woody:func(), woody:args(), options()) ->
{ok, woody:result()} | no_return().
-spec handle_function(woody:func(), woody:args(), options()) -> {ok, woody:result()} | no_return().
handle_function('Repair', [ID, Scenario], _Opts) ->
DecodedScenario = ff_withdrawal_codec:unmarshal(repair_scenario, Scenario),
case ff_withdrawal_machine:repair(ID, DecodedScenario) of

View File

@ -13,7 +13,6 @@
%% API
-spec marshal_state(ff_withdrawal_session:session_state(), ff_withdrawal_session:id(), ff_entity_context:context()) ->
ff_proto_withdrawal_session_thrift:'SessionState'().
marshal_state(State, ID, Context) ->
#wthd_session_SessionState{
id = marshal(id, ID),
@ -23,18 +22,14 @@ marshal_state(State, ID, Context) ->
context = marshal(ctx, Context)
}.
-spec marshal(ff_codec:type_name(), ff_codec:decoded_value()) ->
ff_codec:encoded_value().
-spec marshal(ff_codec:type_name(), ff_codec:decoded_value()) -> ff_codec:encoded_value().
marshal({list, T}, V) ->
[marshal(T, E) || E <- V];
marshal(timestamped_change, {ev, Timestamp, Change}) ->
#wthd_session_TimestampedChange{
change = marshal(change, Change),
occured_at = ff_codec:marshal(timestamp, Timestamp)
};
marshal(change, {created, Session}) ->
{created, marshal(session, Session)};
marshal(change, {next_state, AdapterState}) ->
@ -45,7 +40,6 @@ marshal(change, {finished, SessionResult}) ->
{finished, marshal(session_result, SessionResult)};
marshal(change, {callback, CallbackChange}) ->
{callback, marshal(callback_change, CallbackChange)};
marshal(session, Session) ->
#{
id := SessionID,
@ -60,7 +54,6 @@ marshal(session, Session) ->
route = marshal(route, Route),
provider_legacy = marshal(string, get_legacy_provider_id(Session))
};
marshal(session_status, active) ->
{active, #wthd_session_SessionActive{}};
marshal(session_status, {finished, Result}) ->
@ -72,12 +65,14 @@ marshal(session_finished_status, success) ->
{success, #wthd_session_SessionFinishedSuccess{}};
marshal(session_finished_status, {failed, Failure}) ->
{failed, #wthd_session_SessionFinishedFailed{failure = marshal(failure, Failure)}};
marshal(withdrawal, Params = #{
id := WithdrawalID,
resource := Resource,
cash := Cash
}) ->
marshal(
withdrawal,
Params = #{
id := WithdrawalID,
resource := Resource,
cash := Cash
}
) ->
SenderIdentity = maps:get(sender, Params, undefined),
ReceiverIdentity = maps:get(receiver, Params, undefined),
SessionID = maps:get(session_id, Params, undefined),
@ -86,36 +81,31 @@ marshal(withdrawal, Params = #{
id = marshal(id, WithdrawalID),
destination_resource = marshal(resource, Resource),
cash = marshal(cash, Cash),
sender = marshal(identity, SenderIdentity),
sender = marshal(identity, SenderIdentity),
receiver = marshal(identity, ReceiverIdentity),
session_id = maybe_marshal(id, SessionID),
quote = maybe_marshal(quote, Quote)
};
marshal(identity, Identity = #{id := ID}) ->
#wthd_session_Identity{
identity_id = marshal(id, ID),
effective_challenge = maybe_marshal(challenge, maps:get(effective_challenge, Identity, undefined))
};
marshal(challenge, #{id := ID, proofs := Proofs}) ->
#wthd_session_Challenge{
id = maybe_marshal(id, ID),
proofs = maybe_marshal({list, proof}, Proofs)
};
marshal(proof, {Type, Token}) ->
#wthd_session_ChallengeProof{
type = Type,
token = Token
};
marshal(route, Route) ->
#wthd_session_Route{
provider_id = marshal(provider_id, maps:get(provider_id, Route)),
terminal_id = maybe_marshal(terminal_id, genlib_map:get(terminal_id, Route))
};
marshal(quote, #{
cash_from := CashFrom,
cash_to := CashTo,
@ -131,10 +121,8 @@ marshal(quote, #{
quote_data = maybe_marshal(msgpack, Data),
quote_data_legacy = marshal(ctx, #{})
};
marshal(ctx, Ctx) ->
maybe_marshal(context, Ctx);
marshal(session_result, success) ->
{success, #wthd_session_SessionResultSuccess{}};
marshal(session_result, {success, TransactionInfo}) ->
@ -143,50 +131,41 @@ marshal(session_result, {success, TransactionInfo}) ->
{success, #wthd_session_SessionResultSuccess{trx_info = marshal(transaction_info, TransactionInfo)}};
marshal(session_result, {failed, Failure}) ->
{failed, #wthd_session_SessionResultFailed{failure = ff_codec:marshal(failure, Failure)}};
marshal(callback_change, #{tag := Tag, payload := Payload}) ->
#wthd_session_CallbackChange{
tag = marshal(string, Tag),
payload = marshal(callback_event, Payload)
};
marshal(callback_event, {created, Callback}) ->
{created, #wthd_session_CallbackCreatedChange{callback = marshal(callback, Callback)}};
marshal(callback_event, {status_changed, Status}) ->
{status_changed, #wthd_session_CallbackStatusChange{status = marshal(callback_status, Status)}};
marshal(callback_event, {finished, #{payload := Response}}) ->
{finished, #wthd_session_CallbackResultChange{payload = Response}};
marshal(callback, #{tag := Tag}) ->
#wthd_session_Callback{tag = marshal(string, Tag)};
marshal(callback_status, pending) ->
{pending, #wthd_session_CallbackStatusPending{}};
marshal(callback_status, succeeded) ->
{succeeded, #wthd_session_CallbackStatusSucceeded{}};
marshal(T, V) ->
ff_codec:marshal(T, V).
-spec unmarshal(ff_codec:type_name(), ff_codec:encoded_value()) ->
ff_codec:decoded_value().
-spec unmarshal(ff_codec:type_name(), ff_codec:encoded_value()) -> ff_codec:decoded_value().
unmarshal({list, T}, V) ->
[unmarshal(T, E) || E <- V];
unmarshal(timestamped_change, TimestampedChange) ->
Timestamp = ff_codec:unmarshal(timestamp, TimestampedChange#wthd_session_TimestampedChange.occured_at),
Change = unmarshal(change, TimestampedChange#wthd_session_TimestampedChange.change),
{ev, Timestamp, Change};
unmarshal(repair_scenario, {add_events, #wthd_session_AddEventsRepair{events = Events, action = Action}}) ->
{add_events, genlib_map:compact(#{
events => unmarshal({list, change}, Events),
action => maybe_unmarshal(complex_action, Action)
})};
{add_events,
genlib_map:compact(#{
events => unmarshal({list, change}, Events),
action => maybe_unmarshal(complex_action, Action)
})};
unmarshal(repair_scenario, {set_session_result, #wthd_session_SetResultRepair{result = Result}}) ->
{set_session_result, unmarshal(session_result, Result)};
unmarshal(change, {created, Session}) ->
{created, unmarshal(session, Session)};
unmarshal(change, {next_state, AdapterState}) ->
@ -200,7 +179,6 @@ unmarshal(change, {callback, #wthd_session_CallbackChange{tag = Tag, payload = P
tag => unmarshal(string, Tag),
payload => unmarshal(callback_event, Payload)
}};
unmarshal(session, #wthd_session_Session{
id = SessionID,
status = SessionStatus,
@ -217,17 +195,14 @@ unmarshal(session, #wthd_session_Session{
route => Route1,
provider_legacy => ProviderLegacy
});
unmarshal(session_status, {active, #wthd_session_SessionActive{}}) ->
active;
unmarshal(session_status, {finished, #wthd_session_SessionFinished{status = Result}}) ->
{finished, unmarshal(session_finished_status, Result)};
unmarshal(session_finished_status, {success, #wthd_session_SessionFinishedSuccess{}}) ->
success;
unmarshal(session_finished_status, {failed, #wthd_session_SessionFinishedFailed{failure = Failure}}) ->
{failed, unmarshal(failure, Failure)};
unmarshal(withdrawal, #wthd_session_Withdrawal{
id = WithdrawalID,
destination_resource = Resource,
@ -246,7 +221,6 @@ unmarshal(withdrawal, #wthd_session_Withdrawal{
session_id => maybe_unmarshal(id, SessionID),
quote => maybe_unmarshal(quote, Quote)
});
unmarshal(identity, #wthd_session_Identity{
identity_id = ID,
effective_challenge = EffectiveChallenge
@ -255,7 +229,6 @@ unmarshal(identity, #wthd_session_Identity{
id => unmarshal(id, ID),
effective_challenge => maybe_unmarshal(challenge, EffectiveChallenge)
});
unmarshal(challenge, #wthd_session_Challenge{
id = ID,
proofs = Proofs
@ -264,19 +237,16 @@ unmarshal(challenge, #wthd_session_Challenge{
id => maybe_unmarshal(id, ID),
proofs => maybe_unmarshal({list, proof}, Proofs)
};
unmarshal(proof, #wthd_session_ChallengeProof{
type = Type,
token = Token
}) ->
{Type, Token};
unmarshal(route, Route) ->
genlib_map:compact(#{
provider_id => unmarshal(provider_id, Route#wthd_session_Route.provider_id),
terminal_id => maybe_unmarshal(terminal_id, Route#wthd_session_Route.terminal_id)
});
unmarshal(quote, #wthd_session_Quote{
cash_from = CashFrom,
cash_to = CashTo,
@ -291,7 +261,6 @@ unmarshal(quote, #wthd_session_Quote{
expires_on => ExpiresOn,
quote_data => maybe_unmarshal(msgpack, Data)
});
unmarshal(session_result, {success, #wthd_session_SessionResultSuccess{trx_info = undefined}}) ->
success;
unmarshal(session_result, {success, #wthd_session_SessionResultSuccess{trx_info = TransactionInfo}}) ->
@ -300,25 +269,20 @@ unmarshal(session_result, {success, #wthd_session_SessionResultSuccess{trx_info
{success, unmarshal(transaction_info, TransactionInfo)};
unmarshal(session_result, {failed, #wthd_session_SessionResultFailed{failure = Failure}}) ->
{failed, ff_codec:unmarshal(failure, Failure)};
unmarshal(callback_event, {created, #wthd_session_CallbackCreatedChange{callback = Callback}}) ->
{created, unmarshal(callback, Callback)};
unmarshal(callback_event, {finished, #wthd_session_CallbackResultChange{payload = Response}}) ->
{finished, #{payload => Response}};
unmarshal(callback_event, {status_changed, #wthd_session_CallbackStatusChange{status = Status}}) ->
{status_changed, unmarshal(callback_status, Status)};
unmarshal(callback, #wthd_session_Callback{tag = Tag}) ->
#{tag => unmarshal(string, Tag)};
unmarshal(callback_status, {pending, #wthd_session_CallbackStatusPending{}}) ->
pending;
unmarshal(callback_status, {succeeded, #wthd_session_CallbackStatusSucceeded{}}) ->
succeeded;
unmarshal(ctx, Ctx) ->
maybe_unmarshal(context, Ctx);
unmarshal(T, V) ->
ff_codec:unmarshal(T, V).
@ -344,14 +308,12 @@ get_legacy_provider_id(#{route := #{provider_id := Provider}}) when is_integer(P
-ifdef(TEST).
-include_lib("eunit/include/eunit.hrl").
-spec test() ->
_.
-spec test() -> _.
-spec marshal_change_test_() ->
_.
-spec marshal_change_test_() -> _.
marshal_change_test_() ->
TransactionInfo = #{ id => <<"ID">>, extra => #{<<"Hello">> => <<"World">>} },
TransactionInfo = #{id => <<"ID">>, extra => #{<<"Hello">> => <<"World">>}},
TransactionInfoThrift = marshal(transaction_info, TransactionInfo),
Changes = [
{finished, {success, TransactionInfo}},

View File

@ -16,32 +16,28 @@
%% Internals
%%
-spec publish_events(list(event())) ->
list(sinkevent()).
-spec publish_events(list(event())) -> list(sinkevent()).
publish_events(Events) ->
[publish_event(Event) || Event <- Events].
-spec publish_event(event()) ->
sinkevent().
-spec publish_event(event()) -> sinkevent().
publish_event(#{
id := ID,
source_id := SourceID,
event := {
id := ID,
source_id := SourceID,
event := {
EventID,
Dt,
{ev, EventDt, Payload}
}
}) ->
#wthd_session_SinkEvent{
id = marshal(event_id, ID),
created_at = marshal(timestamp, Dt),
source = marshal(id, SourceID),
payload = #wthd_session_Event{
sequence = marshal(event_id, EventID),
id = marshal(event_id, ID),
created_at = marshal(timestamp, Dt),
source = marshal(id, SourceID),
payload = #wthd_session_Event{
sequence = marshal(event_id, EventID),
occured_at = marshal(timestamp, EventDt),
changes = [marshal(change, Payload)]
changes = [marshal(change, Payload)]
}
}.

View File

@ -1,4 +1,5 @@
-module(ff_withdrawal_session_handler).
-behaviour(ff_woody_wrapper).
-include_lib("fistful_proto/include/ff_proto_withdrawal_session_thrift.hrl").
@ -9,11 +10,11 @@
%%
%% ff_woody_wrapper callbacks
%%
-spec handle_function(woody:func(), woody:args(), woody:options()) ->
{ok, woody:result()} | no_return().
-spec handle_function(woody:func(), woody:args(), woody:options()) -> {ok, woody:result()} | no_return().
handle_function(Func, Args, Opts) ->
scoper:scope(withdrawal_session, #{},
scoper:scope(
withdrawal_session,
#{},
fun() ->
handle_function_(Func, Args, Opts)
end
@ -33,7 +34,6 @@ handle_function_('Get', [ID, EventRange], _Opts) ->
{error, notfound} ->
woody_error:raise(business, #fistful_WithdrawalSessionNotFound{})
end;
handle_function_('GetContext', [ID], _Opts) ->
case ff_withdrawal_session_machine:get(ID, {undefined, 0}) of
{ok, Machine} ->

File diff suppressed because it is too large Load Diff

View File

@ -12,8 +12,7 @@
%% ff_woody_wrapper callbacks
%%
-spec handle_function(woody:func(), woody:args(), options()) ->
{ok, woody:result()} | no_return().
-spec handle_function(woody:func(), woody:args(), options()) -> {ok, woody:result()} | no_return().
handle_function('Repair', [ID, Scenario], _Opts) ->
DecodedScenario = ff_codec:unmarshal(ff_withdrawal_session_codec, repair_scenario, Scenario),
case ff_withdrawal_session_machine:repair(ID, DecodedScenario) of

View File

@ -9,30 +9,23 @@
%% API
-spec marshal(ff_codec:type_name(), ff_codec:decoded_value()) ->
ff_codec:encoded_value().
-spec marshal(ff_codec:type_name(), ff_codec:decoded_value()) -> ff_codec:encoded_value().
marshal(status, pending) ->
{pending, #wthd_status_Pending{}};
marshal(status, succeeded) ->
{succeeded, #wthd_status_Succeeded{}};
marshal(status, {failed, Failure}) ->
{failed, #wthd_status_Failed{failure = marshal(failure, Failure)}};
marshal(T, V) ->
ff_codec:marshal(T, V).
-spec unmarshal(ff_codec:type_name(), ff_codec:encoded_value()) ->
ff_codec:decoded_value().
-spec unmarshal(ff_codec:type_name(), ff_codec:encoded_value()) -> ff_codec:decoded_value().
unmarshal(status, {pending, #wthd_status_Pending{}}) ->
pending;
unmarshal(status, {succeeded, #wthd_status_Succeeded{}}) ->
succeeded;
unmarshal(status, {failed, #wthd_status_Failed{failure = Failure}}) ->
{failed, unmarshal(failure, Failure)};
unmarshal(T, V) ->
ff_codec:unmarshal(T, V).
@ -40,9 +33,11 @@ unmarshal(T, V) ->
-ifdef(TEST).
-include_lib("eunit/include/eunit.hrl").
-spec test() -> _.
-spec pending_symmetry_test() -> _.
pending_symmetry_test() ->
Status = pending,
?assertEqual(Status, unmarshal(status, marshal(status, Status))).
@ -54,13 +49,14 @@ succeeded_symmetry_test() ->
-spec failed_symmetry_test() -> _.
failed_symmetry_test() ->
Status = {failed, #{
code => <<"test">>,
reason => <<"why not">>,
sub => #{
code => <<"sub">>
}
}},
Status =
{failed, #{
code => <<"test">>,
reason => <<"why not">>,
sub => #{
code => <<"sub">>
}
}},
?assertEqual(Status, unmarshal(status, marshal(status, Status))).
-endif.

View File

@ -5,6 +5,7 @@
-behaviour(woody_server_thrift_handler).
-export([handle_function/4]).
-export_type([options/0]).
-export_type([handler/0]).
-export_type([client_opts/0]).
@ -18,22 +19,20 @@
}.
-type client_opts() :: #{
url := woody:url(),
url := woody:url(),
transport_opts => [{_, _}]
}.
-define(DEFAULT_HANDLING_TIMEOUT, 30000). % 30 seconds
% 30 seconds
-define(DEFAULT_HANDLING_TIMEOUT, 30000).
%% Callbacks
-callback(handle_function(woody:func(), woody:args(), handler_options()) ->
{ok, woody:result()} | no_return()).
-callback handle_function(woody:func(), woody:args(), handler_options()) -> {ok, woody:result()} | no_return().
%% API
-spec handle_function(woody:func(), woody:args(), woody_context:ctx(), options()) ->
{ok, woody:result()} | no_return().
-spec handle_function(woody:func(), woody:args(), woody_context:ctx(), options()) -> {ok, woody:result()} | no_return().
handle_function(Func, Args, WoodyContext0, #{handler := Handler} = Opts) ->
WoodyContext = ensure_woody_deadline_set(WoodyContext0, Opts),
{HandlerMod, HandlerOptions} = get_handler_opts(Handler),
@ -57,9 +56,7 @@ create_context(WoodyContext, Opts) ->
},
ff_context:create(ContextOptions).
-spec ensure_woody_deadline_set(woody_context:ctx(), options()) ->
woody_context:ctx().
-spec ensure_woody_deadline_set(woody_context:ctx(), options()) -> woody_context:ctx().
ensure_woody_deadline_set(WoodyContext, Opts) ->
case woody_context:get_deadline(WoodyContext) of
undefined ->
@ -70,11 +67,8 @@ ensure_woody_deadline_set(WoodyContext, Opts) ->
WoodyContext
end.
-spec get_handler_opts(handler()) ->
{module(), handler_options()}.
-spec get_handler_opts(handler()) -> {module(), handler_options()}.
get_handler_opts(Handler) when is_atom(Handler) ->
{Handler, undefined};
get_handler_opts({Handler, Options}) when is_atom(Handler) ->
{Handler, Options}.

View File

@ -41,10 +41,10 @@
%% Internal types
-type config() :: ct_helper:config().
-type config() :: ct_helper:config().
-type test_case_name() :: ct_helper:test_case_name().
-type group_name() :: ct_helper:group_name().
-type test_return() :: _ | no_return().
-type group_name() :: ct_helper:group_name().
-type test_return() :: _ | no_return().
%% Macro helpers
@ -85,10 +85,13 @@ groups() ->
-spec init_per_suite(config()) -> config().
init_per_suite(C) ->
ct_helper:makeup_cfg([
ct_helper:test_case_name(init),
ct_payment_system:setup()
], C).
ct_helper:makeup_cfg(
[
ct_helper:test_case_name(init),
ct_payment_system:setup()
],
C
).
-spec end_per_suite(config()) -> _.
end_per_suite(C) ->
@ -103,6 +106,7 @@ init_per_group(_, C) ->
-spec end_per_group(group_name(), config()) -> _.
end_per_group(_, _) ->
ok.
%%
-spec init_per_testcase(test_case_name(), config()) -> config().
@ -125,10 +129,10 @@ create_bad_amount_test(C) ->
source_id := SourceID
} = prepare_standard_environment(Body, C),
Params = #deposit_DepositParams{
id = generate_id(),
body = Body,
source_id = SourceID,
wallet_id = WalletID
id = generate_id(),
body = Body,
source_id = SourceID,
wallet_id = WalletID
},
Result = call_deposit('Create', [Params, #{}]),
ExpectedError = #fistful_InvalidOperationAmount{
@ -144,10 +148,10 @@ create_currency_validation_error_test(C) ->
source_id := SourceID
} = prepare_standard_environment(Body, C),
Params = #deposit_DepositParams{
id = generate_id(),
body = make_cash({5000, <<"EUR">>}),
source_id = SourceID,
wallet_id = WalletID
id = generate_id(),
body = make_cash({5000, <<"EUR">>}),
source_id = SourceID,
wallet_id = WalletID
},
Result = call_deposit('Create', [Params, #{}]),
ExpectedError = #fistful_ForbiddenOperationCurrency{
@ -166,10 +170,10 @@ create_source_notfound_test(C) ->
wallet_id := WalletID
} = prepare_standard_environment(Body, C),
Params = #deposit_DepositParams{
id = generate_id(),
body = Body,
source_id = <<"unknown_source">>,
wallet_id = WalletID
id = generate_id(),
body = Body,
source_id = <<"unknown_source">>,
wallet_id = WalletID
},
Result = call_deposit('Create', [Params, #{}]),
ExpectedError = #fistful_SourceNotFound{},
@ -182,16 +186,15 @@ create_wallet_notfound_test(C) ->
source_id := SourceID
} = prepare_standard_environment(Body, C),
Params = #deposit_DepositParams{
id = generate_id(),
body = Body,
source_id = SourceID,
wallet_id = <<"unknown_wallet">>
id = generate_id(),
body = Body,
source_id = SourceID,
wallet_id = <<"unknown_wallet">>
},
Result = call_deposit('Create', [Params, #{}]),
ExpectedError = #fistful_WalletNotFound{},
?assertEqual({exception, ExpectedError}, Result).
-spec create_ok_test(config()) -> test_return().
create_ok_test(C) ->
Body = make_cash({100, <<"RUB">>}),
@ -204,12 +207,12 @@ create_ok_test(C) ->
Context = #{<<"NS">> => #{generate_id() => generate_id()}},
Metadata = ff_entity_context_codec:marshal(#{<<"metadata">> => #{<<"some key">> => <<"some data">>}}),
Params = #deposit_DepositParams{
id = DepositID,
body = Body,
source_id = SourceID,
wallet_id = WalletID,
metadata = Metadata,
external_id = ExternalID
id = DepositID,
body = Body,
source_id = SourceID,
wallet_id = WalletID,
metadata = Metadata,
external_id = ExternalID
},
{ok, DepositState} = call_deposit('Create', [Params, ff_entity_context_codec:marshal(Context)]),
Expected = get_deposit(DepositID),
@ -269,9 +272,10 @@ create_adjustment_ok_test(C) ->
ExternalID = generate_id(),
Params = #dep_adj_AdjustmentParams{
id = AdjustmentID,
change = {change_status, #dep_adj_ChangeStatusRequest{
new_status = {failed, #dep_status_Failed{failure = #'Failure'{code = <<"Ooops">>}}}
}},
change =
{change_status, #dep_adj_ChangeStatusRequest{
new_status = {failed, #dep_status_Failed{failure = #'Failure'{code = <<"Ooops">>}}}
}},
external_id = ExternalID
},
{ok, AdjustmentState} = call_deposit('CreateAdjustment', [DepositID, Params]),
@ -303,9 +307,10 @@ create_adjustment_unavailable_status_error_test(C) ->
} = prepare_standard_environment_with_deposit(C),
Params = #dep_adj_AdjustmentParams{
id = generate_id(),
change = {change_status, #dep_adj_ChangeStatusRequest{
new_status = {pending, #dep_status_Pending{}}
}}
change =
{change_status, #dep_adj_ChangeStatusRequest{
new_status = {pending, #dep_status_Pending{}}
}}
},
Result = call_deposit('CreateAdjustment', [DepositID, Params]),
ExpectedError = #deposit_ForbiddenStatusChange{
@ -320,9 +325,10 @@ create_adjustment_already_has_status_error_test(C) ->
} = prepare_standard_environment_with_deposit(C),
Params = #dep_adj_AdjustmentParams{
id = generate_id(),
change = {change_status, #dep_adj_ChangeStatusRequest{
new_status = {succeeded, #dep_status_Succeeded{}}
}}
change =
{change_status, #dep_adj_ChangeStatusRequest{
new_status = {succeeded, #dep_status_Succeeded{}}
}}
},
Result = call_deposit('CreateAdjustment', [DepositID, Params]),
ExpectedError = #deposit_AlreadyHasStatus{
@ -439,9 +445,10 @@ create_revert_adjustment_ok_test(C) ->
ExternalID = generate_id(),
Params = #dep_rev_adj_AdjustmentParams{
id = AdjustmentID,
change = {change_status, #dep_rev_adj_ChangeStatusRequest{
new_status = {failed, #dep_rev_status_Failed{failure = #'Failure'{code = <<"Ooops">>}}}
}},
change =
{change_status, #dep_rev_adj_ChangeStatusRequest{
new_status = {failed, #dep_rev_status_Failed{failure = #'Failure'{code = <<"Ooops">>}}}
}},
external_id = ExternalID
},
{ok, AdjustmentState} = call_deposit('CreateRevertAdjustment', [DepositID, RevertID, Params]),
@ -474,9 +481,10 @@ create_revert_adjustment_unavailable_status_error_test(C) ->
} = prepare_standard_environment_with_revert(C),
Params = #dep_rev_adj_AdjustmentParams{
id = generate_id(),
change = {change_status, #dep_rev_adj_ChangeStatusRequest{
new_status = {pending, #dep_rev_status_Pending{}}
}}
change =
{change_status, #dep_rev_adj_ChangeStatusRequest{
new_status = {pending, #dep_rev_status_Pending{}}
}}
},
Result = call_deposit('CreateRevertAdjustment', [DepositID, RevertID, Params]),
ExpectedError = #deposit_ForbiddenRevertStatusChange{
@ -492,9 +500,10 @@ create_revert_adjustment_already_has_status_error_test(C) ->
} = prepare_standard_environment_with_revert(C),
Params = #dep_rev_adj_AdjustmentParams{
id = generate_id(),
change = {change_status, #dep_rev_adj_ChangeStatusRequest{
new_status = {succeeded, #dep_rev_status_Succeeded{}}
}}
change =
{change_status, #dep_rev_adj_ChangeStatusRequest{
new_status = {succeeded, #dep_rev_status_Succeeded{}}
}}
},
Result = call_deposit('CreateRevertAdjustment', [DepositID, RevertID, Params]),
ExpectedError = #deposit_RevertAlreadyHasStatus{
@ -510,16 +519,18 @@ deposit_state_content_test(C) ->
} = prepare_standard_environment_with_revert(C),
AdjustmentParams = #dep_adj_AdjustmentParams{
id = generate_id(),
change = {change_status, #dep_adj_ChangeStatusRequest{
new_status = {failed, #dep_status_Failed{failure = #'Failure'{code = <<"Ooops">>}}}
}}
change =
{change_status, #dep_adj_ChangeStatusRequest{
new_status = {failed, #dep_status_Failed{failure = #'Failure'{code = <<"Ooops">>}}}
}}
},
{ok, _} = call_deposit('CreateAdjustment', [DepositID, AdjustmentParams]),
RevertAdjustmentParams = #dep_rev_adj_AdjustmentParams{
id = generate_id(),
change = {change_status, #dep_rev_adj_ChangeStatusRequest{
new_status = {failed, #dep_rev_status_Failed{failure = #'Failure'{code = <<"Ooops">>}}}
}}
change =
{change_status, #dep_rev_adj_ChangeStatusRequest{
new_status = {failed, #dep_rev_status_Failed{failure = #'Failure'{code = <<"Ooops">>}}}
}}
},
{ok, _} = call_deposit('CreateRevertAdjustment', [DepositID, RevertID, RevertAdjustmentParams]),
@ -541,7 +552,7 @@ call_deposit(Fun, Args) ->
ServiceName = deposit_management,
Service = ff_services:get_service(ServiceName),
Request = {Service, Fun, Args},
Client = ff_woody_client:new(#{
Client = ff_woody_client:new(#{
url => "http://localhost:8022" ++ ff_services:get_service_path(ServiceName)
}),
ff_woody_client:call(Client, Request).
@ -638,7 +649,7 @@ get_revert_adjustment(DepositID, RevertID, AdjustmentID) ->
await_final_deposit_status(DepositID) ->
finished = ct_helper:await(
finished,
fun () ->
fun() ->
{ok, Machine} = ff_deposit_machine:get(DepositID),
Deposit = ff_deposit_machine:deposit(Machine),
case ff_deposit:is_finished(Deposit) of
@ -655,7 +666,7 @@ await_final_deposit_status(DepositID) ->
await_final_revert_status(DepositID, RevertID) ->
finished = ct_helper:await(
finished,
fun () ->
fun() ->
{ok, Machine} = ff_deposit_machine:get(DepositID),
Deposit = ff_deposit_machine:deposit(Machine),
{ok, Revert} = ff_deposit:find_revert(RevertID, Deposit),
@ -705,7 +716,7 @@ await_wallet_balance({Amount, Currency}, ID) ->
Balance = {Amount, {{inclusive, Amount}, {inclusive, Amount}}, Currency},
Balance = ct_helper:await(
Balance,
fun () -> get_wallet_balance(ID) end,
fun() -> get_wallet_balance(ID) end,
genlib_retry:linear(3, 500)
),
ok.
@ -743,7 +754,7 @@ create_source(IID, _C) ->
ok = ff_source_machine:create(Params, ff_entity_context:new()),
authorized = ct_helper:await(
authorized,
fun () ->
fun() ->
{ok, SrcM} = ff_source_machine:get(ID),
Source = ff_source_machine:source(SrcM),
ff_source:status(Source)

View File

@ -15,18 +15,16 @@
-export([create_crypto_wallet_destination_ok/1]).
-export([create_ripple_wallet_destination_ok/1]).
-type config() :: ct_helper:config().
-type config() :: ct_helper:config().
-type test_case_name() :: ct_helper:test_case_name().
-type group_name() :: ct_helper:group_name().
-type test_return() :: _ | no_return().
-type group_name() :: ct_helper:group_name().
-type test_return() :: _ | no_return().
-spec all() -> [test_case_name() | {group, group_name()}].
all() ->
[{group, default}].
-spec groups() -> [{group_name(), list(), [test_case_name()]}].
groups() ->
[
{default, [parallel], [
@ -37,69 +35,73 @@ groups() ->
].
-spec init_per_suite(config()) -> config().
init_per_suite(C) ->
ct_helper:makeup_cfg([
ct_helper:test_case_name(init),
ct_payment_system:setup()
], C).
ct_helper:makeup_cfg(
[
ct_helper:test_case_name(init),
ct_payment_system:setup()
],
C
).
-spec end_per_suite(config()) -> _.
end_per_suite(C) ->
ok = ct_payment_system:shutdown(C).
%%
-spec init_per_group(group_name(), config()) -> config().
init_per_group(_, C) ->
C.
-spec end_per_group(group_name(), config()) -> _.
end_per_group(_, _) ->
ok.
%%
-spec init_per_testcase(test_case_name(), config()) -> config().
init_per_testcase(Name, C) ->
C1 = ct_helper:makeup_cfg([ct_helper:test_case_name(Name), ct_helper:woody_ctx()], C),
ok = ct_helper:set_context(C1),
C1.
-spec end_per_testcase(test_case_name(), config()) -> _.
end_per_testcase(_Name, _C) ->
ok = ct_helper:unset_context().
-spec create_bank_card_destination_ok(config()) -> test_return().
create_bank_card_destination_ok(C) ->
Resource = {bank_card, #'ResourceBankCard'{bank_card = #'BankCard'{
token = <<"TOKEN shmOKEN">>
}}},
Resource =
{bank_card, #'ResourceBankCard'{
bank_card = #'BankCard'{
token = <<"TOKEN shmOKEN">>
}
}},
create_destination_ok(Resource, C).
-spec create_crypto_wallet_destination_ok(config()) -> test_return().
create_crypto_wallet_destination_ok(C) ->
Resource = {crypto_wallet, #'ResourceCryptoWallet'{crypto_wallet = #'CryptoWallet'{
id = <<"f195298af836f41d072cb390ee62bee8">>,
currency = bitcoin_cash,
data = {bitcoin_cash, #'CryptoDataBitcoinCash'{}}
}}},
Resource =
{crypto_wallet, #'ResourceCryptoWallet'{
crypto_wallet = #'CryptoWallet'{
id = <<"f195298af836f41d072cb390ee62bee8">>,
currency = bitcoin_cash,
data = {bitcoin_cash, #'CryptoDataBitcoinCash'{}}
}
}},
create_destination_ok(Resource, C).
-spec create_ripple_wallet_destination_ok(config()) -> test_return().
create_ripple_wallet_destination_ok(C) ->
Resource = {crypto_wallet, #'ResourceCryptoWallet'{crypto_wallet = #'CryptoWallet'{
id = <<"ab843336bf7738dc697522fbb90508de">>,
currency = ripple,
data = {ripple, #'CryptoDataRipple'{tag = undefined}}
}}},
Resource =
{crypto_wallet, #'ResourceCryptoWallet'{
crypto_wallet = #'CryptoWallet'{
id = <<"ab843336bf7738dc697522fbb90508de">>,
currency = ripple,
data = {ripple, #'CryptoDataRipple'{tag = undefined}}
}
}},
create_destination_ok(Resource, C).
%%----------------------------------------------------------------------
@ -116,21 +118,21 @@ create_destination_ok(Resource, C) ->
Ctx = ff_entity_context_codec:marshal(#{<<"NS">> => #{}}),
Metadata = ff_entity_context_codec:marshal(#{<<"metadata">> => #{<<"some key">> => <<"some data">>}}),
Params = #dst_DestinationParams{
id = ID,
identity = IdentityID,
name = DstName,
currency = Currency,
resource = Resource,
id = ID,
identity = IdentityID,
name = DstName,
currency = Currency,
resource = Resource,
external_id = ExternalId,
metadata = Metadata
metadata = Metadata
},
{ok, Dst} = call_service('Create', [Params, Ctx]),
DstName = Dst#dst_DestinationState.name,
ID = Dst#dst_DestinationState.id,
Resource = Dst#dst_DestinationState.resource,
ExternalId = Dst#dst_DestinationState.external_id,
Metadata = Dst#dst_DestinationState.metadata,
Ctx = Dst#dst_DestinationState.context,
{ok, Dst} = call_service('Create', [Params, Ctx]),
DstName = Dst#dst_DestinationState.name,
ID = Dst#dst_DestinationState.id,
Resource = Dst#dst_DestinationState.resource,
ExternalId = Dst#dst_DestinationState.external_id,
Metadata = Dst#dst_DestinationState.metadata,
Ctx = Dst#dst_DestinationState.context,
Account = Dst#dst_DestinationState.account,
IdentityID = Account#account_Account.identity,
@ -140,9 +142,9 @@ create_destination_ok(Resource, C) ->
{authorized, #dst_Authorized{}} = ct_helper:await(
{authorized, #dst_Authorized{}},
fun () ->
{ok, #dst_DestinationState{status = Status}}
= call_service('Get', [ID, #'EventRange'{}]),
fun() ->
{ok, #dst_DestinationState{status = Status}} =
call_service('Get', [ID, #'EventRange'{}]),
Status
end,
genlib_retry:linear(15, 1000)
@ -153,13 +155,12 @@ create_destination_ok(Resource, C) ->
call_service(Fun, Args) ->
Service = {ff_proto_destination_thrift, 'Management'},
Request = {Service, Fun, Args},
Client = ff_woody_client:new(#{
url => <<"http://localhost:8022/v1/destination">>,
Client = ff_woody_client:new(#{
url => <<"http://localhost:8022/v1/destination">>,
event_handler => scoper_woody_event_handler
}),
ff_woody_client:call(Client, Request).
create_party(_C) ->
ID = genlib:bsuuid(),
_ = ff_party:create(ID),

Some files were not shown because too many files have changed in this diff Show More