mirror of
https://github.com/valitydev/wapi-lib.git
synced 2024-11-06 18:25:17 +00:00
fixed and refactored
This commit is contained in:
parent
a8cb04b608
commit
ed35865617
@ -19,6 +19,7 @@
|
|||||||
file_storage_proto,
|
file_storage_proto,
|
||||||
swag_server_wallet,
|
swag_server_wallet,
|
||||||
swag_client_payres,
|
swag_client_payres,
|
||||||
|
party_client,
|
||||||
scoper,
|
scoper,
|
||||||
jose,
|
jose,
|
||||||
jsx,
|
jsx,
|
||||||
@ -29,7 +30,6 @@
|
|||||||
snowflake,
|
snowflake,
|
||||||
woody_user_identity,
|
woody_user_identity,
|
||||||
payproc_errors,
|
payproc_errors,
|
||||||
ff_server,
|
|
||||||
uac
|
uac
|
||||||
]},
|
]},
|
||||||
{env, []}
|
{env, []}
|
||||||
|
@ -97,7 +97,7 @@ get_context_from_state(withdrawal, #wthd_WithdrawalState{context = Context}) ->
|
|||||||
Context.
|
Context.
|
||||||
|
|
||||||
get_owner(ContextThrift) ->
|
get_owner(ContextThrift) ->
|
||||||
Context = ff_codec:unmarshal(context, ContextThrift),
|
Context = wapi_codec:unmarshal(context, ContextThrift),
|
||||||
wapi_backend_utils:get_from_ctx(<<"owner">>, Context).
|
wapi_backend_utils:get_from_ctx(<<"owner">>, Context).
|
||||||
|
|
||||||
is_resource_owner(Owner, HandlerCtx) ->
|
is_resource_owner(Owner, HandlerCtx) ->
|
||||||
|
@ -6,8 +6,18 @@
|
|||||||
-define(BENDER_DOMAIN, <<"wapi">>).
|
-define(BENDER_DOMAIN, <<"wapi">>).
|
||||||
|
|
||||||
%% Context
|
%% Context
|
||||||
-type md() :: ff_entity_context:md().
|
-type context() :: #{namespace() => md()}.
|
||||||
-type context() :: ff_entity_context:context().
|
-type namespace() :: binary().
|
||||||
|
-type md() :: %% as stolen from `machinery_msgpack`
|
||||||
|
nil |
|
||||||
|
boolean() |
|
||||||
|
integer() |
|
||||||
|
float() |
|
||||||
|
binary() | %% string
|
||||||
|
{binary, binary()} | %% binary
|
||||||
|
[md()] |
|
||||||
|
#{md() => md()} .
|
||||||
|
|
||||||
-type handler_context() :: wapi_handler:context().
|
-type handler_context() :: wapi_handler:context().
|
||||||
-type id() :: binary().
|
-type id() :: binary().
|
||||||
-type hash() :: integer().
|
-type hash() :: integer().
|
||||||
|
601
apps/wapi/src/wapi_codec.erl
Normal file
601
apps/wapi/src/wapi_codec.erl
Normal file
@ -0,0 +1,601 @@
|
|||||||
|
-module(wapi_codec).
|
||||||
|
|
||||||
|
-include_lib("fistful_proto/include/ff_proto_base_thrift.hrl").
|
||||||
|
-include_lib("fistful_proto/include/ff_proto_repairer_thrift.hrl").
|
||||||
|
-include_lib("fistful_proto/include/ff_proto_account_thrift.hrl").
|
||||||
|
-include_lib("fistful_proto/include/ff_proto_msgpack_thrift.hrl").
|
||||||
|
|
||||||
|
-export([unmarshal/2]).
|
||||||
|
-export([unmarshal/3]).
|
||||||
|
|
||||||
|
-export([marshal/2]).
|
||||||
|
-export([marshal/3]).
|
||||||
|
|
||||||
|
%% Types
|
||||||
|
|
||||||
|
-type type_name() :: atom() | {list, atom()} | {set, atom()}.
|
||||||
|
-type codec() :: module().
|
||||||
|
|
||||||
|
-type encoded_value() :: encoded_value(any()).
|
||||||
|
-type encoded_value(T) :: T.
|
||||||
|
|
||||||
|
-type decoded_value() :: decoded_value(any()).
|
||||||
|
-type decoded_value(T) :: T.
|
||||||
|
|
||||||
|
-export_type([codec/0]).
|
||||||
|
-export_type([type_name/0]).
|
||||||
|
-export_type([encoded_value/0]).
|
||||||
|
-export_type([encoded_value/1]).
|
||||||
|
-export_type([decoded_value/0]).
|
||||||
|
-export_type([decoded_value/1]).
|
||||||
|
|
||||||
|
%% Callbacks
|
||||||
|
|
||||||
|
-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().
|
||||||
|
unmarshal(Codec, Type, Value) ->
|
||||||
|
Codec:unmarshal(Type, 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().
|
||||||
|
|
||||||
|
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
|
||||||
|
}) ->
|
||||||
|
Timestamp = maps:get(timestamp, TransactionInfo, undefined),
|
||||||
|
AddInfo = maps:get(additional_info, TransactionInfo, undefined),
|
||||||
|
#'TransactionInfo'{
|
||||||
|
id = marshal(id, TransactionID),
|
||||||
|
timestamp = marshal(timestamp, Timestamp),
|
||||||
|
extra = Extra,
|
||||||
|
additional_info = marshal(additional_transaction_info, AddInfo)
|
||||||
|
};
|
||||||
|
|
||||||
|
marshal(additional_transaction_info, AddInfo = #{}) ->
|
||||||
|
#'AdditionalTransactionInfo'{
|
||||||
|
rrn = marshal(string, maps:get(rrn, AddInfo, undefined)),
|
||||||
|
approval_code = marshal(string, maps:get(approval_code, AddInfo, undefined)),
|
||||||
|
acs_url = marshal(string, maps:get(acs_url, AddInfo, undefined)),
|
||||||
|
pareq = marshal(string, maps:get(pareq, AddInfo, undefined)),
|
||||||
|
md = marshal(string, maps:get(md, AddInfo, undefined)),
|
||||||
|
term_url = marshal(string, maps:get(term_url, AddInfo, undefined)),
|
||||||
|
pares = marshal(string, maps:get(pares, AddInfo, undefined)),
|
||||||
|
eci = marshal(string, maps:get(eci, AddInfo, undefined)),
|
||||||
|
cavv = marshal(string, maps:get(cavv, AddInfo, undefined)),
|
||||||
|
xid = marshal(string, maps:get(xid, AddInfo, undefined)),
|
||||||
|
cavv_algorithm = marshal(string, maps:get(cavv_algorithm, AddInfo, undefined)),
|
||||||
|
three_ds_verification = marshal(
|
||||||
|
three_ds_verification,
|
||||||
|
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;
|
||||||
|
|
||||||
|
marshal(account_change, {created, Account}) ->
|
||||||
|
{created, marshal(account, Account)};
|
||||||
|
marshal(account, #{
|
||||||
|
id := ID,
|
||||||
|
identity := IdentityID,
|
||||||
|
currency := CurrencyID,
|
||||||
|
accounter_account_id := AAID
|
||||||
|
}) ->
|
||||||
|
#'account_Account'{
|
||||||
|
id = marshal(id, ID),
|
||||||
|
identity = marshal(id, IdentityID),
|
||||||
|
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),
|
||||||
|
auth_data = maybe_marshal(bank_card_auth_data, maps:get(auth_data, ResourceBankCard, undefined))
|
||||||
|
}};
|
||||||
|
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),
|
||||||
|
MaskedPan = maps:get(masked_pan, BankCard, undefined),
|
||||||
|
BankName = maps:get(bank_name, BankCard, undefined),
|
||||||
|
IsoCountryCode = maps:get(iso_country_code, BankCard, undefined),
|
||||||
|
CardType = maps:get(card_type, BankCard, undefined),
|
||||||
|
ExpDate = maps:get(exp_date, BankCard, undefined),
|
||||||
|
CardholderName = maps:get(cardholder_name, BankCard, undefined),
|
||||||
|
BinDataID = maps:get(bin_data_id, BankCard, undefined),
|
||||||
|
#'BankCard'{
|
||||||
|
token = marshal(string, Token),
|
||||||
|
bin = marshal(string, Bin),
|
||||||
|
masked_pan = marshal(string, MaskedPan),
|
||||||
|
bank_name = marshal(string, BankName),
|
||||||
|
payment_system = maybe_marshal(payment_system, PaymentSystem),
|
||||||
|
issuer_country = maybe_marshal(iso_country_code, IsoCountryCode),
|
||||||
|
card_type = maybe_marshal(card_type, CardType),
|
||||||
|
exp_date = maybe_marshal(exp_date, ExpDate),
|
||||||
|
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),
|
||||||
|
currency = marshal(crypto_currency, 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, #{}}) ->
|
||||||
|
{litecoin, #'CryptoDataLitecoin'{}};
|
||||||
|
marshal(crypto_data, {bitcoin_cash, #{}}) ->
|
||||||
|
{bitcoin_cash, #'CryptoDataBitcoinCash'{}};
|
||||||
|
marshal(crypto_data, {ethereum, #{}}) ->
|
||||||
|
{ethereum, #'CryptoDataEthereum'{}};
|
||||||
|
marshal(crypto_data, {zcash, #{}}) ->
|
||||||
|
{zcash, #'CryptoDataZcash'{}};
|
||||||
|
marshal(crypto_data, {usdt, #{}}) ->
|
||||||
|
{usdt, #'CryptoDataUSDT'{}};
|
||||||
|
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),
|
||||||
|
currency = marshal(currency_ref, CurrencyRef)
|
||||||
|
};
|
||||||
|
marshal(cash_range, {{BoundLower, CashLower}, {BoundUpper, CashUpper}}) ->
|
||||||
|
#'CashRange'{
|
||||||
|
lower = {BoundLower, marshal(cash, CashLower)},
|
||||||
|
upper = {BoundUpper, marshal(cash, CashUpper)}
|
||||||
|
};
|
||||||
|
marshal(currency_ref, CurrencyID) when is_binary(CurrencyID) ->
|
||||||
|
#'CurrencyRef'{
|
||||||
|
symbolic_code = CurrencyID
|
||||||
|
};
|
||||||
|
marshal(amount, V) ->
|
||||||
|
marshal(integer, V);
|
||||||
|
|
||||||
|
marshal(event_range, {After, Limit}) ->
|
||||||
|
#'EventRange'{
|
||||||
|
'after' = maybe_marshal(integer, After),
|
||||||
|
limit = maybe_marshal(integer, Limit)
|
||||||
|
};
|
||||||
|
|
||||||
|
marshal(failure, Failure) ->
|
||||||
|
#'Failure'{
|
||||||
|
code = marshal(string, wapi_failure:code(Failure)),
|
||||||
|
reason = maybe_marshal(string, wapi_failure:reason(Failure)),
|
||||||
|
sub = maybe_marshal(sub_failure, wapi_failure:sub_failure(Failure))
|
||||||
|
};
|
||||||
|
marshal(sub_failure, Failure) ->
|
||||||
|
#'SubFailure'{
|
||||||
|
code = marshal(string, wapi_failure:code(Failure)),
|
||||||
|
sub = maybe_marshal(sub_failure, wapi_failure:sub_failure(Failure))
|
||||||
|
};
|
||||||
|
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} =
|
||||||
|
case USec of
|
||||||
|
0 ->
|
||||||
|
{DateTimeinSeconds, second};
|
||||||
|
USec ->
|
||||||
|
MicroSec = erlang:convert_time_unit(DateTimeinSeconds, second, microsecond),
|
||||||
|
{MicroSec + USec, microsecond}
|
||||||
|
end,
|
||||||
|
genlib_rfc3339:format_relaxed(TimeinUnit, Unit);
|
||||||
|
marshal(timestamp_ms, V) ->
|
||||||
|
wapi_time:to_rfc3339(V);
|
||||||
|
marshal(domain_revision, V) when is_integer(V) ->
|
||||||
|
V;
|
||||||
|
marshal(party_revision, V) when is_integer(V) ->
|
||||||
|
V;
|
||||||
|
marshal(string, V) when is_binary(V) ->
|
||||||
|
V;
|
||||||
|
marshal(integer, V) when is_integer(V) ->
|
||||||
|
V;
|
||||||
|
marshal(bool, V) when is_boolean(V) ->
|
||||||
|
V;
|
||||||
|
marshal(context, Ctx) when is_map(Ctx) ->
|
||||||
|
maps:map(fun(_NS, V) -> marshal(msgpack, V) end, Ctx);
|
||||||
|
marshal(msgpack, V) ->
|
||||||
|
wapi_msgpack_codec:marshal(msgpack, V);
|
||||||
|
|
||||||
|
% Catch this up in thrift validation
|
||||||
|
marshal(_, Other) ->
|
||||||
|
Other.
|
||||||
|
|
||||||
|
-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,
|
||||||
|
extra = Extra,
|
||||||
|
additional_info = AddInfo
|
||||||
|
}) ->
|
||||||
|
genlib_map:compact(#{
|
||||||
|
id => unmarshal(string, TransactionID),
|
||||||
|
timestamp => maybe_unmarshal(string, Timestamp),
|
||||||
|
extra => Extra,
|
||||||
|
additional_info => maybe_unmarshal(additional_transaction_info, AddInfo)
|
||||||
|
});
|
||||||
|
|
||||||
|
unmarshal(additional_transaction_info, #'AdditionalTransactionInfo'{
|
||||||
|
rrn = RRN,
|
||||||
|
approval_code = ApprovalCode,
|
||||||
|
acs_url = AcsURL,
|
||||||
|
pareq = Pareq,
|
||||||
|
md = MD,
|
||||||
|
term_url = TermURL,
|
||||||
|
pares = Pares,
|
||||||
|
eci = ECI,
|
||||||
|
cavv = CAVV,
|
||||||
|
xid = XID,
|
||||||
|
cavv_algorithm = CAVVAlgorithm,
|
||||||
|
three_ds_verification = ThreeDSVerification
|
||||||
|
}) ->
|
||||||
|
genlib_map:compact(#{
|
||||||
|
rrn => maybe_unmarshal(string, RRN),
|
||||||
|
approval_code => maybe_unmarshal(string, ApprovalCode),
|
||||||
|
acs_url => maybe_unmarshal(string, AcsURL),
|
||||||
|
pareq => maybe_unmarshal(string, Pareq),
|
||||||
|
md => maybe_unmarshal(string, MD),
|
||||||
|
term_url => maybe_unmarshal(string, TermURL),
|
||||||
|
pares => maybe_unmarshal(string, Pares),
|
||||||
|
eci => maybe_unmarshal(string, ECI),
|
||||||
|
cavv => maybe_unmarshal(string, CAVV),
|
||||||
|
xid => maybe_unmarshal(string, XID),
|
||||||
|
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;
|
||||||
|
|
||||||
|
unmarshal(complex_action, #ff_repairer_ComplexAction{
|
||||||
|
timer = TimerAction,
|
||||||
|
remove = RemoveAction
|
||||||
|
}) ->
|
||||||
|
unmarshal(timer_action, TimerAction) ++ unmarshal(remove_action, RemoveAction);
|
||||||
|
unmarshal(timer_action, undefined) ->
|
||||||
|
[];
|
||||||
|
unmarshal(timer_action, {set_timer, SetTimer}) ->
|
||||||
|
[{set_timer, unmarshal(set_timer_action, SetTimer)}];
|
||||||
|
unmarshal(timer_action, {unset_timer, #ff_repairer_UnsetTimerAction{}}) ->
|
||||||
|
[unset_timer];
|
||||||
|
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'{
|
||||||
|
id = ID,
|
||||||
|
identity = IdentityID,
|
||||||
|
currency = CurrencyRef,
|
||||||
|
accounter_account_id = AAID
|
||||||
|
}) ->
|
||||||
|
#{
|
||||||
|
id => unmarshal(id, ID),
|
||||||
|
identity => unmarshal(id, IdentityID),
|
||||||
|
currency => unmarshal(currency_ref, CurrencyRef),
|
||||||
|
accounter_account_id => unmarshal(accounter_account_id, AAID)
|
||||||
|
};
|
||||||
|
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, {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,
|
||||||
|
masked_pan = MaskedPan,
|
||||||
|
bank_name = BankName,
|
||||||
|
payment_system = PaymentSystem,
|
||||||
|
issuer_country = IsoCountryCode,
|
||||||
|
card_type = CardType,
|
||||||
|
bin_data_id = BinDataID,
|
||||||
|
exp_date = ExpDate,
|
||||||
|
cardholder_name = CardholderName
|
||||||
|
}) ->
|
||||||
|
genlib_map:compact(#{
|
||||||
|
token => unmarshal(string, Token),
|
||||||
|
payment_system => maybe_unmarshal(payment_system, PaymentSystem),
|
||||||
|
bin => maybe_unmarshal(string, Bin),
|
||||||
|
masked_pan => maybe_unmarshal(string, MaskedPan),
|
||||||
|
bank_name => maybe_unmarshal(string, BankName),
|
||||||
|
iso_country_code => maybe_unmarshal(iso_country_code, IsoCountryCode),
|
||||||
|
card_type => maybe_unmarshal(card_type, CardType),
|
||||||
|
exp_date => maybe_unmarshal(exp_date, ExpDate),
|
||||||
|
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,
|
||||||
|
data = Data
|
||||||
|
}) ->
|
||||||
|
genlib_map:compact(#{
|
||||||
|
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,
|
||||||
|
currency = CurrencyRef
|
||||||
|
}) ->
|
||||||
|
{unmarshal(amount, Amount), unmarshal(currency_ref, CurrencyRef)};
|
||||||
|
|
||||||
|
unmarshal(cash_range, #'CashRange'{
|
||||||
|
lower = {BoundLower, CashLower},
|
||||||
|
upper = {BoundUpper, CashUpper}
|
||||||
|
}) ->
|
||||||
|
{
|
||||||
|
{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),
|
||||||
|
reason => maybe_unmarshal(string, Failure#'Failure'.reason),
|
||||||
|
sub => maybe_unmarshal(sub_failure, Failure#'Failure'.sub)
|
||||||
|
});
|
||||||
|
unmarshal(sub_failure, Failure) ->
|
||||||
|
genlib_map:compact(#{
|
||||||
|
code => unmarshal(string, Failure#'SubFailure'.code),
|
||||||
|
sub => maybe_unmarshal(sub_failure, Failure#'SubFailure'.sub)
|
||||||
|
});
|
||||||
|
|
||||||
|
unmarshal(range, #evsink_EventRange{
|
||||||
|
'after' = Cursor,
|
||||||
|
limit = Limit
|
||||||
|
}) ->
|
||||||
|
{Cursor, Limit, forward};
|
||||||
|
|
||||||
|
unmarshal(fees, Fees) ->
|
||||||
|
#{
|
||||||
|
fees => maps:map(fun(_Constant, Value) -> unmarshal(cash, Value) end, Fees#'Fees'.fees)
|
||||||
|
};
|
||||||
|
|
||||||
|
unmarshal(timestamp_ms, V) ->
|
||||||
|
wapi_time:from_rfc3339(V);
|
||||||
|
unmarshal(domain_revision, V) when is_integer(V) ->
|
||||||
|
V;
|
||||||
|
unmarshal(party_revision, V) when is_integer(V) ->
|
||||||
|
V;
|
||||||
|
unmarshal(string, V) when is_binary(V) ->
|
||||||
|
V;
|
||||||
|
unmarshal(integer, V) when is_integer(V) ->
|
||||||
|
V;
|
||||||
|
|
||||||
|
unmarshal(context, Ctx) when is_map(Ctx) ->
|
||||||
|
maps:map(fun(_K, V) -> unmarshal(msgpack, V) end, Ctx);
|
||||||
|
|
||||||
|
unmarshal(msgpack, V) ->
|
||||||
|
wapi_msgpack_codec:unmarshal(msgpack, V);
|
||||||
|
|
||||||
|
unmarshal(range, #'EventRange'{
|
||||||
|
'after' = Cursor,
|
||||||
|
limit = Limit
|
||||||
|
}) ->
|
||||||
|
{Cursor, Limit, forward};
|
||||||
|
|
||||||
|
unmarshal(bool, V) when is_boolean(V) ->
|
||||||
|
V.
|
||||||
|
|
||||||
|
maybe_unmarshal(_Type, undefined) ->
|
||||||
|
undefined;
|
||||||
|
maybe_unmarshal(Type, Value) ->
|
||||||
|
unmarshal(Type, Value).
|
||||||
|
|
||||||
|
maybe_marshal(_Type, undefined) ->
|
||||||
|
undefined;
|
||||||
|
maybe_marshal(Type, Value) ->
|
||||||
|
marshal(Type, Value).
|
||||||
|
|
||||||
|
-ifdef(TEST).
|
||||||
|
-include_lib("eunit/include/eunit.hrl").
|
||||||
|
-spec test() -> _.
|
||||||
|
|
||||||
|
-spec bank_card_codec_test() -> _.
|
||||||
|
bank_card_codec_test() ->
|
||||||
|
BankCard = #{
|
||||||
|
token => <<"token">>,
|
||||||
|
payment_system => visa,
|
||||||
|
bin => <<"12345">>,
|
||||||
|
masked_pan => <<"7890">>,
|
||||||
|
bank_name => <<"bank">>,
|
||||||
|
iso_country_code => zmb,
|
||||||
|
card_type => credit_or_debit,
|
||||||
|
exp_date => {12, 3456},
|
||||||
|
cardholder_name => <<"name">>,
|
||||||
|
bin_data_id => #{<<"foo">> => 1}
|
||||||
|
},
|
||||||
|
Type = {struct, struct, {ff_proto_base_thrift, 'BankCard'}},
|
||||||
|
Binary = wapi_thrift_utils:serialize(Type, marshal(bank_card, BankCard)),
|
||||||
|
Decoded = wapi_thrift_utils:deserialize(Type, Binary),
|
||||||
|
?assertEqual(BankCard, unmarshal(bank_card, Decoded)).
|
||||||
|
|
||||||
|
-spec fees_codec_test() -> _.
|
||||||
|
fees_codec_test() ->
|
||||||
|
Expected = #{
|
||||||
|
fees => #{
|
||||||
|
operation_amount => {100, <<"RUB">>},
|
||||||
|
surplus => {200, <<"RUB">>}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
Type = {struct, struct, {ff_proto_base_thrift, 'Fees'}},
|
||||||
|
Binary = wapi_thrift_utils:serialize(Type, marshal(fees, Expected)),
|
||||||
|
Decoded = wapi_thrift_utils:deserialize(Type, Binary),
|
||||||
|
?assertEqual(Expected, unmarshal(fees, Decoded)).
|
||||||
|
|
||||||
|
-endif.
|
@ -119,7 +119,7 @@ when Type =:= <<"BankCardDestinationResource">> ->
|
|||||||
cardholder_name => BankCard#'BankCard'.cardholder_name,
|
cardholder_name => BankCard#'BankCard'.cardholder_name,
|
||||||
exp_date => {Month, Year}
|
exp_date => {Month, Year}
|
||||||
}}},
|
}}},
|
||||||
{ok, ff_codec:marshal(resource, CostructedResource)};
|
{ok, wapi_codec:marshal(resource, CostructedResource)};
|
||||||
{error, {decryption_failed, _} = Error} ->
|
{error, {decryption_failed, _} = Error} ->
|
||||||
logger:warning("Resource token decryption failed: ~p", [Error]),
|
logger:warning("Resource token decryption failed: ~p", [Error]),
|
||||||
{error, invalid_resource_token}
|
{error, invalid_resource_token}
|
||||||
@ -133,7 +133,7 @@ when Type =:= <<"CryptoWalletDestinationResource">> ->
|
|||||||
id => CryptoWalletID,
|
id => CryptoWalletID,
|
||||||
currency => marshal_crypto_currency_data(Resource)
|
currency => marshal_crypto_currency_data(Resource)
|
||||||
})}},
|
})}},
|
||||||
{ok, ff_codec:marshal(resource, CostructedResource)}.
|
{ok, wapi_codec:marshal(resource, CostructedResource)}.
|
||||||
|
|
||||||
service_call(Params, Context) ->
|
service_call(Params, Context) ->
|
||||||
wapi_handler_utils:service_call(Params, Context).
|
wapi_handler_utils:service_call(Params, Context).
|
||||||
@ -167,13 +167,13 @@ marshal(resource, #{
|
|||||||
bin => maps:get(<<"bin">>, BankCard),
|
bin => maps:get(<<"bin">>, BankCard),
|
||||||
masked_pan => maps:get(<<"lastDigits">>, BankCard)
|
masked_pan => maps:get(<<"lastDigits">>, BankCard)
|
||||||
}}},
|
}}},
|
||||||
ff_codec:marshal(resource, Resource);
|
wapi_codec:marshal(resource, Resource);
|
||||||
|
|
||||||
marshal(context, Context) ->
|
marshal(context, Context) ->
|
||||||
ff_codec:marshal(context, Context);
|
wapi_codec:marshal(context, Context);
|
||||||
|
|
||||||
marshal(T, V) ->
|
marshal(T, V) ->
|
||||||
ff_codec:marshal(T, V).
|
wapi_codec:marshal(T, V).
|
||||||
|
|
||||||
maybe_marshal(_, undefined) ->
|
maybe_marshal(_, undefined) ->
|
||||||
undefined;
|
undefined;
|
||||||
@ -243,10 +243,10 @@ unmarshal(resource, {crypto_wallet, #'ResourceCryptoWallet'{crypto_wallet = #'Cr
|
|||||||
});
|
});
|
||||||
|
|
||||||
unmarshal(context, Context) ->
|
unmarshal(context, Context) ->
|
||||||
ff_codec:unmarshal(context, Context);
|
wapi_codec:unmarshal(context, Context);
|
||||||
|
|
||||||
unmarshal(T, V) ->
|
unmarshal(T, V) ->
|
||||||
ff_codec:unmarshal(T, V).
|
wapi_codec:unmarshal(T, V).
|
||||||
|
|
||||||
maybe_unmarshal(_, undefined) ->
|
maybe_unmarshal(_, undefined) ->
|
||||||
undefined;
|
undefined;
|
||||||
|
@ -69,10 +69,10 @@ process_request(Tag, OperationID, Req, SwagContext, Opts, WoodyContext) ->
|
|||||||
_ = logger:info("Processing request ~p", [OperationID]),
|
_ = logger:info("Processing request ~p", [OperationID]),
|
||||||
try
|
try
|
||||||
%% TODO remove this fistful specific step, when separating the wapi service.
|
%% TODO remove this fistful specific step, when separating the wapi service.
|
||||||
ok = ff_context:save(create_ff_context(WoodyContext, Opts)),
|
ok = wapi_context:save(create_wapi_context(WoodyContext, Opts)),
|
||||||
|
|
||||||
Context = create_handler_context(SwagContext, WoodyContext),
|
Context = create_handler_context(SwagContext, WoodyContext),
|
||||||
Handler = get_handler(Tag, genlib_app:env(?APP, transport)),
|
Handler = get_handler(Tag),
|
||||||
case wapi_auth:authorize_operation(OperationID, Req, Context) of
|
case wapi_auth:authorize_operation(OperationID, Req, Context) of
|
||||||
ok ->
|
ok ->
|
||||||
ok = logger:debug("Operation ~p authorized", [OperationID]),
|
ok = logger:debug("Operation ~p authorized", [OperationID]),
|
||||||
@ -87,7 +87,7 @@ process_request(Tag, OperationID, Req, SwagContext, Opts, WoodyContext) ->
|
|||||||
error:{woody_error, {Source, Class, Details}} ->
|
error:{woody_error, {Source, Class, Details}} ->
|
||||||
process_woody_error(Source, Class, Details)
|
process_woody_error(Source, Class, Details)
|
||||||
after
|
after
|
||||||
ff_context:cleanup()
|
wapi_context:cleanup()
|
||||||
end.
|
end.
|
||||||
|
|
||||||
-spec throw_result(request_result()) ->
|
-spec throw_result(request_result()) ->
|
||||||
@ -95,9 +95,8 @@ process_request(Tag, OperationID, Req, SwagContext, Opts, WoodyContext) ->
|
|||||||
throw_result(Res) ->
|
throw_result(Res) ->
|
||||||
erlang:throw({?request_result, Res}).
|
erlang:throw({?request_result, Res}).
|
||||||
|
|
||||||
get_handler(wallet, thrift) -> wapi_wallet_thrift_handler;
|
get_handler(wallet) -> wapi_wallet_handler;
|
||||||
get_handler(wallet, _) -> wapi_wallet_handler;
|
get_handler(payres) -> wapi_payres_handler.
|
||||||
get_handler(payres, _) -> wapi_payres_handler.
|
|
||||||
|
|
||||||
-spec create_woody_context(tag(), req_data(), wapi_auth:context(), opts()) ->
|
-spec create_woody_context(tag(), req_data(), wapi_auth:context(), opts()) ->
|
||||||
woody_context:ctx().
|
woody_context:ctx().
|
||||||
@ -142,11 +141,11 @@ process_woody_error(_Source, resource_unavailable, _Details) ->
|
|||||||
process_woody_error(_Source, result_unknown, _Details) ->
|
process_woody_error(_Source, result_unknown, _Details) ->
|
||||||
wapi_handler_utils:reply_error(504).
|
wapi_handler_utils:reply_error(504).
|
||||||
|
|
||||||
-spec create_ff_context(woody_context:ctx(), opts()) ->
|
-spec create_wapi_context(woody_context:ctx(), opts()) ->
|
||||||
ff_context:context().
|
wapi_context:context().
|
||||||
create_ff_context(WoodyContext, Opts) ->
|
create_wapi_context(WoodyContext, Opts) ->
|
||||||
ContextOptions = #{
|
ContextOptions = #{
|
||||||
woody_context => WoodyContext,
|
woody_context => WoodyContext,
|
||||||
party_client => maps:get(party_client, Opts)
|
party_client => maps:get(party_client, Opts)
|
||||||
},
|
},
|
||||||
ff_context:create(ContextOptions).
|
wapi_context:create(ContextOptions).
|
||||||
|
@ -355,7 +355,7 @@ marshal(event_range, {Cursor, Limit}) ->
|
|||||||
};
|
};
|
||||||
|
|
||||||
marshal(context, Ctx) ->
|
marshal(context, Ctx) ->
|
||||||
ff_codec:marshal(context, Ctx);
|
wapi_codec:marshal(context, Ctx);
|
||||||
|
|
||||||
marshal(proof_type, <<"RUSDomesticPassport">>) ->
|
marshal(proof_type, <<"RUSDomesticPassport">>) ->
|
||||||
rus_domestic_passport;
|
rus_domestic_passport;
|
||||||
@ -363,7 +363,7 @@ marshal(proof_type, <<"RUSRetireeInsuranceCertificate">>) ->
|
|||||||
rus_retiree_insurance_cert;
|
rus_retiree_insurance_cert;
|
||||||
|
|
||||||
marshal(T, V) ->
|
marshal(T, V) ->
|
||||||
ff_codec:marshal(T, V).
|
wapi_codec:marshal(T, V).
|
||||||
|
|
||||||
%%
|
%%
|
||||||
|
|
||||||
@ -463,7 +463,7 @@ unmarshal(blocking, blocked) ->
|
|||||||
true;
|
true;
|
||||||
|
|
||||||
unmarshal(T, V) ->
|
unmarshal(T, V) ->
|
||||||
ff_codec:unmarshal(T, V).
|
wapi_codec:unmarshal(T, V).
|
||||||
|
|
||||||
maybe_unmarshal(_, undefined) ->
|
maybe_unmarshal(_, undefined) ->
|
||||||
undefined;
|
undefined;
|
||||||
|
49
apps/wapi/src/wapi_msgpack_codec.erl
Normal file
49
apps/wapi/src/wapi_msgpack_codec.erl
Normal file
@ -0,0 +1,49 @@
|
|||||||
|
-module(wapi_msgpack_codec).
|
||||||
|
|
||||||
|
-include_lib("fistful_proto/include/ff_proto_msgpack_thrift.hrl").
|
||||||
|
|
||||||
|
-export([unmarshal/2]).
|
||||||
|
-export([marshal/2]).
|
||||||
|
|
||||||
|
%% Types
|
||||||
|
|
||||||
|
-type type_name() :: msgpack.
|
||||||
|
-type decoded_value() ::
|
||||||
|
#{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.
|
||||||
|
marshal(msgpack, {binary, V}) when is_binary(V) ->
|
||||||
|
{bin, V};
|
||||||
|
marshal(msgpack, V) when is_list(V) ->
|
||||||
|
{arr, [marshal(msgpack, ListItem) || ListItem <- 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) ->
|
||||||
|
maps:fold(fun(Key, Value, Map) -> Map#{unmarshal(msgpack, Key) => unmarshal(msgpack, Value)} end, #{}, V).
|
@ -222,7 +222,7 @@ unmarshal_response(identities, Response) ->
|
|||||||
<<"id">> => Response#fistfulstat_StatIdentity.id,
|
<<"id">> => Response#fistfulstat_StatIdentity.id,
|
||||||
<<"name">> => Response#fistfulstat_StatIdentity.name,
|
<<"name">> => Response#fistfulstat_StatIdentity.name,
|
||||||
<<"createdAt">> => Response#fistfulstat_StatIdentity.created_at,
|
<<"createdAt">> => Response#fistfulstat_StatIdentity.created_at,
|
||||||
<<"provider">> => Response#fistfulstat_StatIdentity.provider,
|
<<"provider">> => unmarshal_identity_stat_provider(Response#fistfulstat_StatIdentity.provider),
|
||||||
<<"class">> => Response#fistfulstat_StatIdentity.identity_class,
|
<<"class">> => Response#fistfulstat_StatIdentity.identity_class,
|
||||||
<<"level">> => Response#fistfulstat_StatIdentity.identity_level,
|
<<"level">> => Response#fistfulstat_StatIdentity.identity_level,
|
||||||
<<"effectiveChallenge">> => Response#fistfulstat_StatIdentity.effective_challenge,
|
<<"effectiveChallenge">> => Response#fistfulstat_StatIdentity.effective_challenge,
|
||||||
@ -294,3 +294,6 @@ unmarshal_crypto_currency_name({ripple, _}) -> <<"Ripple">>;
|
|||||||
unmarshal_crypto_currency_name({ethereum, _}) -> <<"Ethereum">>;
|
unmarshal_crypto_currency_name({ethereum, _}) -> <<"Ethereum">>;
|
||||||
unmarshal_crypto_currency_name({usdt, _}) -> <<"USDT">>;
|
unmarshal_crypto_currency_name({usdt, _}) -> <<"USDT">>;
|
||||||
unmarshal_crypto_currency_name({zcash, _}) -> <<"Zcash">>.
|
unmarshal_crypto_currency_name({zcash, _}) -> <<"Zcash">>.
|
||||||
|
|
||||||
|
unmarshal_identity_stat_provider(Provider) ->
|
||||||
|
genlib:to_binary(Provider).
|
||||||
|
@ -238,7 +238,7 @@ clamp_max_request_deadline(Value) when is_integer(Value)->
|
|||||||
|
|
||||||
-spec get_unique_id() -> binary().
|
-spec get_unique_id() -> binary().
|
||||||
get_unique_id() ->
|
get_unique_id() ->
|
||||||
ff_id:generate_snowflake_id().
|
wapi_id:generate_snowflake_id().
|
||||||
%%
|
%%
|
||||||
|
|
||||||
-ifdef(TEST).
|
-ifdef(TEST).
|
||||||
@ -273,25 +273,25 @@ get_path_test() ->
|
|||||||
|
|
||||||
-spec mask_test() -> _.
|
-spec mask_test() -> _.
|
||||||
mask_test() ->
|
mask_test() ->
|
||||||
?assertEqual(<<"Жур">>, mask(leading, 0, $*, <<"Жур">>)),
|
?assertEqual(<<"Хуй">>, mask(leading, 0, $*, <<"Хуй">>)),
|
||||||
?assertEqual(<<"*ур">>, mask(leading, 1, $*, <<"Жур">>)),
|
?assertEqual(<<"*уй">>, mask(leading, 1, $*, <<"Хуй">>)),
|
||||||
?assertEqual(<<"**р">>, mask(leading, 2, $*, <<"Жур">>)),
|
?assertEqual(<<"**й">>, mask(leading, 2, $*, <<"Хуй">>)),
|
||||||
?assertEqual(<<"***">>, mask(leading, 3, $*, <<"Жур">>)),
|
?assertEqual(<<"***">>, mask(leading, 3, $*, <<"Хуй">>)),
|
||||||
?assertEqual(<<"Жур">>, mask(trailing, 0, $*, <<"Жур">>)),
|
?assertEqual(<<"Хуй">>, mask(trailing, 0, $*, <<"Хуй">>)),
|
||||||
?assertEqual(<<"Жу*">>, mask(trailing, 1, $*, <<"Жур">>)),
|
?assertEqual(<<"Ху*">>, mask(trailing, 1, $*, <<"Хуй">>)),
|
||||||
?assertEqual(<<"Ж**">>, mask(trailing, 2, $*, <<"Жур">>)),
|
?assertEqual(<<"Х**">>, mask(trailing, 2, $*, <<"Хуй">>)),
|
||||||
?assertEqual(<<"***">>, mask(trailing, 3, $*, <<"Жур">>)).
|
?assertEqual(<<"***">>, mask(trailing, 3, $*, <<"Хуй">>)).
|
||||||
|
|
||||||
-spec mask_and_keep_test() -> _.
|
-spec mask_and_keep_test() -> _.
|
||||||
mask_and_keep_test() ->
|
mask_and_keep_test() ->
|
||||||
?assertEqual(<<"***">>, mask_and_keep(leading, 0, $*, <<"Жур">>)),
|
?assertEqual(<<"***">>, mask_and_keep(leading, 0, $*, <<"Хуй">>)),
|
||||||
?assertEqual(<<"Ж**">>, mask_and_keep(leading, 1, $*, <<"Жур">>)),
|
?assertEqual(<<"Х**">>, mask_and_keep(leading, 1, $*, <<"Хуй">>)),
|
||||||
?assertEqual(<<"Жу*">>, mask_and_keep(leading, 2, $*, <<"Жур">>)),
|
?assertEqual(<<"Ху*">>, mask_and_keep(leading, 2, $*, <<"Хуй">>)),
|
||||||
?assertEqual(<<"Жур">>, mask_and_keep(leading, 3, $*, <<"Жур">>)),
|
?assertEqual(<<"Хуй">>, mask_and_keep(leading, 3, $*, <<"Хуй">>)),
|
||||||
?assertEqual(<<"***">>, mask_and_keep(trailing, 0, $*, <<"Жур">>)),
|
?assertEqual(<<"***">>, mask_and_keep(trailing, 0, $*, <<"Хуй">>)),
|
||||||
?assertEqual(<<"**р">>, mask_and_keep(trailing, 1, $*, <<"Жур">>)),
|
?assertEqual(<<"**й">>, mask_and_keep(trailing, 1, $*, <<"Хуй">>)),
|
||||||
?assertEqual(<<"*ур">>, mask_and_keep(trailing, 2, $*, <<"Жур">>)),
|
?assertEqual(<<"*уй">>, mask_and_keep(trailing, 2, $*, <<"Хуй">>)),
|
||||||
?assertEqual(<<"Жур">>, mask_and_keep(trailing, 3, $*, <<"Жур">>)).
|
?assertEqual(<<"Хуй">>, mask_and_keep(trailing, 3, $*, <<"Хуй">>)).
|
||||||
|
|
||||||
-spec parse_deadline_test() -> _.
|
-spec parse_deadline_test() -> _.
|
||||||
parse_deadline_test() ->
|
parse_deadline_test() ->
|
||||||
|
@ -113,10 +113,10 @@ marshal(body, #{
|
|||||||
};
|
};
|
||||||
|
|
||||||
marshal(context, Ctx) ->
|
marshal(context, Ctx) ->
|
||||||
ff_codec:marshal(context, Ctx);
|
wapi_codec:marshal(context, Ctx);
|
||||||
|
|
||||||
marshal(T, V) ->
|
marshal(T, V) ->
|
||||||
ff_codec:marshal(T, V).
|
wapi_codec:marshal(T, V).
|
||||||
|
|
||||||
unmarshal(transfer, #w2w_transfer_W2WTransferState{
|
unmarshal(transfer, #w2w_transfer_W2WTransferState{
|
||||||
id = ID,
|
id = ID,
|
||||||
@ -157,7 +157,7 @@ unmarshal(transfer_status, {failed, #w2w_status_Failed{failure = Failure}}) ->
|
|||||||
};
|
};
|
||||||
|
|
||||||
unmarshal(T, V) ->
|
unmarshal(T, V) ->
|
||||||
ff_codec:unmarshal(T, V).
|
wapi_codec:unmarshal(T, V).
|
||||||
|
|
||||||
maybe_unmarshal(_T, undefined) ->
|
maybe_unmarshal(_T, undefined) ->
|
||||||
undefined;
|
undefined;
|
||||||
|
@ -141,10 +141,10 @@ marshal(account_params, {IdentityID, CurrencyID}) ->
|
|||||||
};
|
};
|
||||||
|
|
||||||
marshal(context, Ctx) ->
|
marshal(context, Ctx) ->
|
||||||
ff_codec:marshal(context, Ctx);
|
wapi_codec:marshal(context, Ctx);
|
||||||
|
|
||||||
marshal(T, V) ->
|
marshal(T, V) ->
|
||||||
ff_codec:marshal(T, V).
|
wapi_codec:marshal(T, V).
|
||||||
|
|
||||||
%%
|
%%
|
||||||
|
|
||||||
@ -197,10 +197,10 @@ unmarshal(wallet_account_balance, #account_AccountBalance{
|
|||||||
};
|
};
|
||||||
|
|
||||||
unmarshal(context, Ctx) ->
|
unmarshal(context, Ctx) ->
|
||||||
ff_codec:unmarshal(context, Ctx);
|
wapi_codec:unmarshal(context, Ctx);
|
||||||
|
|
||||||
unmarshal(T, V) ->
|
unmarshal(T, V) ->
|
||||||
ff_codec:unmarshal(T, V).
|
wapi_codec:unmarshal(T, V).
|
||||||
|
|
||||||
maybe_unmarshal(_, undefined) ->
|
maybe_unmarshal(_, undefined) ->
|
||||||
undefined;
|
undefined;
|
||||||
|
@ -25,13 +25,13 @@
|
|||||||
-spec authorize_api_key(operation_id(), api_key(), handler_opts()) ->
|
-spec authorize_api_key(operation_id(), api_key(), handler_opts()) ->
|
||||||
false | {true, wapi_auth:context()}.
|
false | {true, wapi_auth:context()}.
|
||||||
authorize_api_key(OperationID, ApiKey, _Opts) ->
|
authorize_api_key(OperationID, ApiKey, _Opts) ->
|
||||||
ok = scoper:add_scope('swag.server', #{api => wallet, operation_id => OperationID}),
|
ok = scoper:add_meta(#{api => wallet, operation_id => OperationID}),
|
||||||
case uac:authorize_api_key(ApiKey, wapi_auth:get_verification_options()) of
|
case uac:authorize_api_key(ApiKey, wapi_auth:get_verification_options()) of
|
||||||
{ok, Context0} ->
|
{ok, Context0} ->
|
||||||
Context = wapi_auth:create_wapi_context(Context0),
|
Context = wapi_auth:create_wapi_context(Context0),
|
||||||
{true, Context};
|
{true, Context};
|
||||||
{error, Error} ->
|
{error, Error} ->
|
||||||
_ = logger:info("API Key authorization failed for ~p due to ~p", [OperationID, Error]),
|
_ = logger:info("API Key authorization failed: ~p", [Error]),
|
||||||
false
|
false
|
||||||
end.
|
end.
|
||||||
|
|
||||||
@ -40,50 +40,8 @@ authorize_api_key(OperationID, ApiKey, _Opts) ->
|
|||||||
handle_request(OperationID, Req, SwagContext, Opts) ->
|
handle_request(OperationID, Req, SwagContext, Opts) ->
|
||||||
wapi_handler:handle_request(wallet, OperationID, Req, SwagContext, Opts).
|
wapi_handler:handle_request(wallet, OperationID, Req, SwagContext, Opts).
|
||||||
|
|
||||||
|
|
||||||
%% Providers
|
|
||||||
-spec process_request(operation_id(), req_data(), handler_context(), handler_opts()) ->
|
-spec process_request(operation_id(), req_data(), handler_context(), handler_opts()) ->
|
||||||
request_result().
|
request_result().
|
||||||
process_request('ListProviders', #{'residence' := Residence}, Context, _Opts) ->
|
|
||||||
Providers = wapi_wallet_ff_backend:get_providers(ff_maybe:to_list(Residence), Context),
|
|
||||||
wapi_handler_utils:reply_ok(200, Providers);
|
|
||||||
process_request('GetProvider', #{'providerID' := Id}, Context, _Opts) ->
|
|
||||||
case wapi_wallet_ff_backend:get_provider(Id, Context) of
|
|
||||||
{ok, Provider} -> wapi_handler_utils:reply_ok(200, Provider);
|
|
||||||
{error, notfound} -> wapi_handler_utils:reply_ok(404)
|
|
||||||
end;
|
|
||||||
process_request('ListProviderIdentityClasses', #{'providerID' := Id}, Context, _Opts) ->
|
|
||||||
case wapi_wallet_ff_backend:get_provider_identity_classes(Id, Context) of
|
|
||||||
{ok, Classes} -> wapi_handler_utils:reply_ok(200, Classes);
|
|
||||||
{error, notfound} -> wapi_handler_utils:reply_ok(404)
|
|
||||||
end;
|
|
||||||
process_request('GetProviderIdentityClass', #{
|
|
||||||
'providerID' := ProviderId,
|
|
||||||
'identityClassID' := ClassId
|
|
||||||
}, Context, _Opts) ->
|
|
||||||
case wapi_wallet_ff_backend:get_provider_identity_class(ProviderId, ClassId, Context) of
|
|
||||||
{ok, Class} -> wapi_handler_utils:reply_ok(200, Class);
|
|
||||||
{error, notfound} -> wapi_handler_utils:reply_ok(404)
|
|
||||||
end;
|
|
||||||
process_request('ListProviderIdentityLevels', #{
|
|
||||||
'providerID' := _ProviderId,
|
|
||||||
'identityClassID' := _ClassId
|
|
||||||
}, _Context, _Opts) ->
|
|
||||||
%% case wapi_wallet_ff_backend:get_provider_identity_class_levels(ProviderId, ClassId, Context) of
|
|
||||||
%% {ok, Levels} -> wapi_handler_utils:reply_ok(200, Levels);
|
|
||||||
%% {error, notfound} -> wapi_handler_utils:reply_ok(404)
|
|
||||||
%% end;
|
|
||||||
not_implemented();
|
|
||||||
process_request('GetProviderIdentityLevel', #{
|
|
||||||
'providerID' := _ProviderId,
|
|
||||||
'identityClassID' := _ClassId,
|
|
||||||
'identityLevelID' := _LevelId
|
|
||||||
}, _Context, _Opts) ->
|
|
||||||
%% case wapi_wallet_ff_backend:get_provider_identity_class_level(ProviderId, ClassId, LevelId, Context) of
|
|
||||||
%% {ok, Level} -> wapi_handler_utils:reply_ok(200, Level);
|
|
||||||
%% {error, notfound} -> wapi_handler_utils:reply_ok(404)
|
|
||||||
%% end;
|
|
||||||
not_implemented();
|
|
||||||
|
|
||||||
%% Identities
|
%% Identities
|
||||||
process_request('ListIdentities', Params, Context, _Opts) ->
|
process_request('ListIdentities', Params, Context, _Opts) ->
|
||||||
@ -101,32 +59,26 @@ process_request('ListIdentities', Params, Context, _Opts) ->
|
|||||||
})
|
})
|
||||||
end;
|
end;
|
||||||
process_request('GetIdentity', #{'identityID' := IdentityId}, Context, _Opts) ->
|
process_request('GetIdentity', #{'identityID' := IdentityId}, Context, _Opts) ->
|
||||||
case wapi_wallet_ff_backend:get_identity(IdentityId, Context) of
|
case wapi_identity_backend:get_identity(IdentityId, Context) of
|
||||||
{ok, Identity} -> wapi_handler_utils:reply_ok(200, Identity);
|
{ok, Identity} -> wapi_handler_utils:reply_ok(200, Identity);
|
||||||
{error, {identity, notfound}} -> wapi_handler_utils:reply_ok(404);
|
{error, {identity, notfound}} -> wapi_handler_utils:reply_ok(404);
|
||||||
{error, {identity, unauthorized}} -> wapi_handler_utils:reply_ok(404)
|
{error, {identity, unauthorized}} -> wapi_handler_utils:reply_ok(404)
|
||||||
end;
|
end;
|
||||||
process_request('CreateIdentity', #{'Identity' := Params}, Context, Opts) ->
|
process_request('CreateIdentity', #{'Identity' := Params}, Context, Opts) ->
|
||||||
case wapi_wallet_ff_backend:create_identity(Params, Context) of
|
case wapi_identity_backend:create_identity(Params, Context) of
|
||||||
{ok, Identity = #{<<"id">> := IdentityId}} ->
|
{ok, Identity = #{<<"id">> := IdentityId}} ->
|
||||||
wapi_handler_utils:reply_ok(201, Identity, get_location('GetIdentity', [IdentityId], Opts));
|
wapi_handler_utils:reply_ok(201, Identity, get_location('GetIdentity', [IdentityId], Opts));
|
||||||
{error, {inaccessible, _}} ->
|
|
||||||
wapi_handler_utils:reply_ok(422, wapi_handler_utils:get_error_msg(<<"Party inaccessible">>));
|
|
||||||
{error, {provider, notfound}} ->
|
{error, {provider, notfound}} ->
|
||||||
wapi_handler_utils:reply_ok(422, wapi_handler_utils:get_error_msg(<<"No such provider">>));
|
wapi_handler_utils:reply_ok(422, wapi_handler_utils:get_error_msg(<<"No such provider">>));
|
||||||
{error, {identity_class, notfound}} ->
|
{error, {identity_class, notfound}} ->
|
||||||
wapi_handler_utils:reply_ok(422, wapi_handler_utils:get_error_msg(<<"No such identity class">>));
|
wapi_handler_utils:reply_ok(422, wapi_handler_utils:get_error_msg(<<"No such identity class">>));
|
||||||
{error, {external_id_conflict, ID, ExternalID}} ->
|
{error, inaccessible} ->
|
||||||
wapi_handler_utils:logic_error(external_id_conflict, {ID, ExternalID});
|
wapi_handler_utils:reply_ok(422, wapi_handler_utils:get_error_msg(<<"Identity inaccessible">>));
|
||||||
{error, {email, notfound}} ->
|
{error, {external_id_conflict, ID}} ->
|
||||||
wapi_handler_utils:reply_error(400, #{
|
wapi_handler_utils:reply_error(409, #{<<"id">> => ID})
|
||||||
<<"errorType">> => <<"NotFound">>,
|
|
||||||
<<"name">> => <<"email">>,
|
|
||||||
<<"description">> => <<"No email in JWT">>
|
|
||||||
})
|
|
||||||
end;
|
end;
|
||||||
process_request('ListIdentityChallenges', #{'identityID' := Id, 'status' := Status}, Context, _Opts) ->
|
process_request('ListIdentityChallenges', #{'identityID' := Id, 'status' := Status}, Context, _Opts) ->
|
||||||
case wapi_wallet_ff_backend:get_identity_challenges(Id, ff_maybe:to_list(Status), Context) of
|
case wapi_identity_backend:get_identity_challenges(Id, Status, Context) of
|
||||||
{ok, Challenges} -> wapi_handler_utils:reply_ok(200, Challenges);
|
{ok, Challenges} -> wapi_handler_utils:reply_ok(200, Challenges);
|
||||||
{error, {identity, notfound}} -> wapi_handler_utils:reply_ok(404);
|
{error, {identity, notfound}} -> wapi_handler_utils:reply_ok(404);
|
||||||
{error, {identity, unauthorized}} -> wapi_handler_utils:reply_ok(404)
|
{error, {identity, unauthorized}} -> wapi_handler_utils:reply_ok(404)
|
||||||
@ -135,7 +87,7 @@ process_request('StartIdentityChallenge', #{
|
|||||||
'identityID' := IdentityId,
|
'identityID' := IdentityId,
|
||||||
'IdentityChallenge' := Params
|
'IdentityChallenge' := Params
|
||||||
}, Context, Opts) ->
|
}, Context, Opts) ->
|
||||||
case wapi_wallet_ff_backend:create_identity_challenge(IdentityId, Params, Context) of
|
case wapi_identity_backend:create_identity_challenge(IdentityId, Params, Context) of
|
||||||
{ok, Challenge = #{<<"id">> := ChallengeId}} ->
|
{ok, Challenge = #{<<"id">> := ChallengeId}} ->
|
||||||
wapi_handler_utils:reply_ok(202, Challenge, get_location('GetIdentityChallenge', [ChallengeId], Opts));
|
wapi_handler_utils:reply_ok(202, Challenge, get_location('GetIdentityChallenge', [ChallengeId], Opts));
|
||||||
{error, {identity, notfound}} ->
|
{error, {identity, notfound}} ->
|
||||||
@ -144,7 +96,9 @@ process_request('StartIdentityChallenge', #{
|
|||||||
wapi_handler_utils:reply_ok(404);
|
wapi_handler_utils:reply_ok(404);
|
||||||
{error, {challenge, conflict}} ->
|
{error, {challenge, conflict}} ->
|
||||||
wapi_handler_utils:reply_ok(409);
|
wapi_handler_utils:reply_ok(409);
|
||||||
{error, {challenge, {pending, _}}} ->
|
{error, {external_id_conflict, ID}} ->
|
||||||
|
wapi_handler_utils:reply_ok(409, #{<<"id">> => ID});
|
||||||
|
{error, {challenge, pending}} ->
|
||||||
wapi_handler_utils:reply_ok(409);
|
wapi_handler_utils:reply_ok(409);
|
||||||
{error, {challenge, {class, notfound}}} ->
|
{error, {challenge, {class, notfound}}} ->
|
||||||
wapi_handler_utils:reply_ok(422, wapi_handler_utils:get_error_msg(<<"No such challenge type">>));
|
wapi_handler_utils:reply_ok(422, wapi_handler_utils:get_error_msg(<<"No such challenge type">>));
|
||||||
@ -152,7 +106,7 @@ process_request('StartIdentityChallenge', #{
|
|||||||
wapi_handler_utils:reply_ok(422, wapi_handler_utils:get_error_msg(<<"Proof not found">>));
|
wapi_handler_utils:reply_ok(422, wapi_handler_utils:get_error_msg(<<"Proof not found">>));
|
||||||
{error, {challenge, {proof, insufficient}}} ->
|
{error, {challenge, {proof, insufficient}}} ->
|
||||||
wapi_handler_utils:reply_ok(422, wapi_handler_utils:get_error_msg(<<"Insufficient proof">>));
|
wapi_handler_utils:reply_ok(422, wapi_handler_utils:get_error_msg(<<"Insufficient proof">>));
|
||||||
{error, {challenge, {level, _}}} ->
|
{error, {challenge, level}} ->
|
||||||
wapi_handler_utils:reply_ok(422,
|
wapi_handler_utils:reply_ok(422,
|
||||||
wapi_handler_utils:get_error_msg(<<"Illegal identification type for current identity level">>)
|
wapi_handler_utils:get_error_msg(<<"Illegal identification type for current identity level">>)
|
||||||
)
|
)
|
||||||
@ -162,20 +116,20 @@ process_request('GetIdentityChallenge', #{
|
|||||||
'identityID' := IdentityId,
|
'identityID' := IdentityId,
|
||||||
'challengeID' := ChallengeId
|
'challengeID' := ChallengeId
|
||||||
}, Context, _Opts) ->
|
}, Context, _Opts) ->
|
||||||
case wapi_wallet_ff_backend:get_identity_challenge(IdentityId, ChallengeId, Context) of
|
case wapi_identity_backend:get_identity_challenge(IdentityId, ChallengeId, Context) of
|
||||||
{ok, Challenge} -> wapi_handler_utils:reply_ok(200, Challenge);
|
{ok, Challenge} -> wapi_handler_utils:reply_ok(200, Challenge);
|
||||||
{error, {identity, notfound}} -> wapi_handler_utils:reply_ok(404);
|
{error, {identity, notfound}} -> wapi_handler_utils:reply_ok(404);
|
||||||
{error, {identity, unauthorized}} -> wapi_handler_utils:reply_ok(404);
|
{error, {identity, unauthorized}} -> wapi_handler_utils:reply_ok(404);
|
||||||
{error, {challenge, notfound}} -> wapi_handler_utils:reply_ok(404)
|
{error, {challenge, notfound}} -> wapi_handler_utils:reply_ok(404)
|
||||||
end;
|
end;
|
||||||
process_request('PollIdentityChallengeEvents', Params, Context, _Opts) ->
|
process_request('PollIdentityChallengeEvents', Params, Context, _Opts) ->
|
||||||
case wapi_wallet_ff_backend:get_identity_challenge_events(Params, Context) of
|
case wapi_identity_backend:get_identity_challenge_events(Params, Context) of
|
||||||
{ok, Events} -> wapi_handler_utils:reply_ok(200, Events);
|
{ok, Events} -> wapi_handler_utils:reply_ok(200, Events);
|
||||||
{error, {identity, notfound}} -> wapi_handler_utils:reply_ok(404);
|
{error, {identity, notfound}} -> wapi_handler_utils:reply_ok(404);
|
||||||
{error, {identity, unauthorized}} -> wapi_handler_utils:reply_ok(404)
|
{error, {identity, unauthorized}} -> wapi_handler_utils:reply_ok(404)
|
||||||
end;
|
end;
|
||||||
process_request('GetIdentityChallengeEvent', Params, Context, _Opts) ->
|
process_request('GetIdentityChallengeEvent', Params, Context, _Opts) ->
|
||||||
case wapi_wallet_ff_backend:get_identity_challenge_event(Params, Context) of
|
case wapi_identity_backend:get_identity_challenge_event(Params, Context) of
|
||||||
{ok, Event} -> wapi_handler_utils:reply_ok(200, Event);
|
{ok, Event} -> wapi_handler_utils:reply_ok(200, Event);
|
||||||
{error, {identity, notfound}} -> wapi_handler_utils:reply_ok(404);
|
{error, {identity, notfound}} -> wapi_handler_utils:reply_ok(404);
|
||||||
{error, {identity, unauthorized}} -> wapi_handler_utils:reply_ok(404);
|
{error, {identity, unauthorized}} -> wapi_handler_utils:reply_ok(404);
|
||||||
@ -183,25 +137,36 @@ process_request('GetIdentityChallengeEvent', Params, Context, _Opts) ->
|
|||||||
end;
|
end;
|
||||||
|
|
||||||
%% Wallets
|
%% Wallets
|
||||||
|
|
||||||
process_request('ListWallets', Params, Context, _Opts) ->
|
process_request('ListWallets', Params, Context, _Opts) ->
|
||||||
case wapi_wallet_ff_backend:list_wallets(Params, Context) of
|
case wapi_stat_backend:list_wallets(Params, Context) of
|
||||||
{ok, {200, _, List}} -> wapi_handler_utils:reply_ok(200, List);
|
{ok, List} -> wapi_handler_utils:reply_ok(200, List);
|
||||||
{error, {Code, _, Error}} -> wapi_handler_utils:reply_error(Code, Error)
|
{error, {invalid, Errors}} ->
|
||||||
|
wapi_handler_utils:reply_error(400, #{
|
||||||
|
<<"errorType">> => <<"NoMatch">>,
|
||||||
|
<<"description">> => Errors
|
||||||
|
});
|
||||||
|
{error, {bad_token, Reason}} ->
|
||||||
|
wapi_handler_utils:reply_error(400, #{
|
||||||
|
<<"errorType">> => <<"InvalidToken">>,
|
||||||
|
<<"description">> => Reason
|
||||||
|
})
|
||||||
end;
|
end;
|
||||||
process_request('GetWallet', #{'walletID' := WalletId}, Context, _Opts) ->
|
process_request('GetWallet', #{'walletID' := WalletId}, Context, _Opts) ->
|
||||||
case wapi_wallet_ff_backend:get_wallet(WalletId, Context) of
|
case wapi_wallet_backend:get(WalletId, Context) of
|
||||||
{ok, Wallet} -> wapi_handler_utils:reply_ok(200, Wallet);
|
{ok, Wallet} -> wapi_handler_utils:reply_ok(200, Wallet);
|
||||||
{error, {wallet, notfound}} -> wapi_handler_utils:reply_ok(404);
|
{error, {wallet, notfound}} -> wapi_handler_utils:reply_ok(404);
|
||||||
{error, {wallet, unauthorized}} -> wapi_handler_utils:reply_ok(404)
|
{error, {wallet, unauthorized}} -> wapi_handler_utils:reply_ok(404)
|
||||||
end;
|
end;
|
||||||
process_request('GetWalletByExternalID', #{externalID := ExternalID}, Context, _Opts) ->
|
process_request('GetWalletByExternalID', #{externalID := ExternalID}, Context, _Opts) ->
|
||||||
case wapi_wallet_ff_backend:get_wallet_by_external_id(ExternalID, Context) of
|
case wapi_wallet_backend:get_by_external_id(ExternalID, Context) of
|
||||||
{ok, Wallet} -> wapi_handler_utils:reply_ok(200, Wallet);
|
{ok, Wallet} -> wapi_handler_utils:reply_ok(200, Wallet);
|
||||||
{error, {wallet, notfound}} -> wapi_handler_utils:reply_ok(404);
|
{error, {wallet, notfound}} -> wapi_handler_utils:reply_ok(404);
|
||||||
{error, {wallet, unauthorized}} -> wapi_handler_utils:reply_ok(404)
|
{error, {wallet, unauthorized}} -> wapi_handler_utils:reply_ok(404);
|
||||||
|
{error, {external_id, {unknown_external_id, ExternalID}}} -> wapi_handler_utils:reply_ok(404)
|
||||||
end;
|
end;
|
||||||
process_request('CreateWallet', #{'Wallet' := Params}, Context, Opts) ->
|
process_request('CreateWallet', #{'Wallet' := Params}, Context, Opts) ->
|
||||||
case wapi_wallet_ff_backend:create_wallet(Params, Context) of
|
case wapi_wallet_backend:create(Params, Context) of
|
||||||
{ok, Wallet = #{<<"id">> := WalletId}} ->
|
{ok, Wallet = #{<<"id">> := WalletId}} ->
|
||||||
wapi_handler_utils:reply_ok(201, Wallet, get_location('GetWallet', [WalletId], Opts));
|
wapi_handler_utils:reply_ok(201, Wallet, get_location('GetWallet', [WalletId], Opts));
|
||||||
{error, {identity, unauthorized}} ->
|
{error, {identity, unauthorized}} ->
|
||||||
@ -210,46 +175,20 @@ process_request('CreateWallet', #{'Wallet' := Params}, Context, Opts) ->
|
|||||||
wapi_handler_utils:reply_ok(422, wapi_handler_utils:get_error_msg(<<"No such identity">>));
|
wapi_handler_utils:reply_ok(422, wapi_handler_utils:get_error_msg(<<"No such identity">>));
|
||||||
{error, {currency, notfound}} ->
|
{error, {currency, notfound}} ->
|
||||||
wapi_handler_utils:reply_ok(422, wapi_handler_utils:get_error_msg(<<"Currency not supported">>));
|
wapi_handler_utils:reply_ok(422, wapi_handler_utils:get_error_msg(<<"Currency not supported">>));
|
||||||
{error, {inaccessible, _}} ->
|
{error, inaccessible} ->
|
||||||
wapi_handler_utils:reply_ok(422, wapi_handler_utils:get_error_msg(<<"Identity inaccessible">>));
|
wapi_handler_utils:reply_ok(422, wapi_handler_utils:get_error_msg(<<"Identity inaccessible">>));
|
||||||
{error, {external_id_conflict, ID, ExternalID}} ->
|
{error, {external_id_conflict, ID}} ->
|
||||||
wapi_handler_utils:logic_error(external_id_conflict, {ID, ExternalID});
|
wapi_handler_utils:reply_error(409, #{<<"id">> => ID})
|
||||||
{error, invalid} ->
|
|
||||||
wapi_handler_utils:reply_ok(422, wapi_handler_utils:get_error_msg(<<"Invalid currency">>));
|
|
||||||
{error, {terms, {terms_violation, {not_allowed_currency, _Data}}}} ->
|
|
||||||
wapi_handler_utils:reply_ok(422, wapi_handler_utils:get_error_msg(<<"Currency not allowed">>))
|
|
||||||
end;
|
end;
|
||||||
process_request('GetWalletAccount', #{'walletID' := WalletId}, Context, _Opts) ->
|
process_request('GetWalletAccount', #{'walletID' := WalletId}, Context, _Opts) ->
|
||||||
case wapi_wallet_ff_backend:get_wallet_account(WalletId, Context) of
|
case wapi_wallet_backend:get_account(WalletId, Context) of
|
||||||
{ok, WalletAccount} -> wapi_handler_utils:reply_ok(200, WalletAccount);
|
{ok, WalletAccount} -> wapi_handler_utils:reply_ok(200, WalletAccount);
|
||||||
{error, {wallet, notfound}} -> wapi_handler_utils:reply_ok(404);
|
{error, {wallet, notfound}} -> wapi_handler_utils:reply_ok(404);
|
||||||
{error, {wallet, unauthorized}} -> wapi_handler_utils:reply_ok(404)
|
{error, {wallet, unauthorized}} -> wapi_handler_utils:reply_ok(404)
|
||||||
end;
|
end;
|
||||||
process_request('IssueWalletGrant', #{
|
|
||||||
'walletID' := WalletId,
|
|
||||||
'WalletGrantRequest' := #{<<"validUntil">> := Expiration, <<"asset">> := Asset}
|
|
||||||
}, Context, _Opts) ->
|
|
||||||
case wapi_wallet_ff_backend:get_wallet(WalletId, Context) of
|
|
||||||
{ok, _} ->
|
|
||||||
case wapi_backend_utils:issue_grant_token({wallets, WalletId, Asset}, Expiration, Context) of
|
|
||||||
{ok, Token} ->
|
|
||||||
wapi_handler_utils:reply_ok(201, #{
|
|
||||||
<<"token">> => Token,
|
|
||||||
<<"validUntil">> => Expiration,
|
|
||||||
<<"asset">> => Asset
|
|
||||||
});
|
|
||||||
{error, expired} ->
|
|
||||||
wapi_handler_utils:reply_ok(422,
|
|
||||||
wapi_handler_utils:get_error_msg(<<"Invalid expiration: already expired">>)
|
|
||||||
)
|
|
||||||
end;
|
|
||||||
{error, {wallet, notfound}} ->
|
|
||||||
wapi_handler_utils:reply_ok(404);
|
|
||||||
{error, {wallet, unauthorized}} ->
|
|
||||||
wapi_handler_utils:reply_ok(404)
|
|
||||||
end;
|
|
||||||
|
|
||||||
%% Withdrawals
|
%% Destinations
|
||||||
|
|
||||||
process_request('ListDestinations', Params, Context, _Opts) ->
|
process_request('ListDestinations', Params, Context, _Opts) ->
|
||||||
case wapi_stat_backend:list_destinations(Params, Context) of
|
case wapi_stat_backend:list_destinations(Params, Context) of
|
||||||
{ok, Result} -> wapi_handler_utils:reply_ok(200, Result);
|
{ok, Result} -> wapi_handler_utils:reply_ok(200, Result);
|
||||||
@ -265,13 +204,13 @@ process_request('ListDestinations', Params, Context, _Opts) ->
|
|||||||
})
|
})
|
||||||
end;
|
end;
|
||||||
process_request('GetDestination', #{'destinationID' := DestinationId}, Context, _Opts) ->
|
process_request('GetDestination', #{'destinationID' := DestinationId}, Context, _Opts) ->
|
||||||
case wapi_wallet_ff_backend:get_destination(DestinationId, Context) of
|
case wapi_destination_backend:get(DestinationId, Context) of
|
||||||
{ok, Destination} -> wapi_handler_utils:reply_ok(200, Destination);
|
{ok, Destination} -> wapi_handler_utils:reply_ok(200, Destination);
|
||||||
{error, {destination, notfound}} -> wapi_handler_utils:reply_ok(404);
|
{error, {destination, notfound}} -> wapi_handler_utils:reply_ok(404);
|
||||||
{error, {destination, unauthorized}} -> wapi_handler_utils:reply_ok(404)
|
{error, {destination, unauthorized}} -> wapi_handler_utils:reply_ok(404)
|
||||||
end;
|
end;
|
||||||
process_request('GetDestinationByExternalID', #{'externalID' := ExternalID}, Context, _Opts) ->
|
process_request('GetDestinationByExternalID', #{'externalID' := ExternalID}, Context, _Opts) ->
|
||||||
case wapi_wallet_ff_backend:get_destination_by_external_id(ExternalID, Context) of
|
case wapi_destination_backend:get_by_external_id(ExternalID, Context) of
|
||||||
{ok, Destination} ->
|
{ok, Destination} ->
|
||||||
wapi_handler_utils:reply_ok(200, Destination);
|
wapi_handler_utils:reply_ok(200, Destination);
|
||||||
{error, {external_id, {unknown_external_id, ExternalID}}} ->
|
{error, {external_id, {unknown_external_id, ExternalID}}} ->
|
||||||
@ -282,7 +221,7 @@ process_request('GetDestinationByExternalID', #{'externalID' := ExternalID}, Con
|
|||||||
wapi_handler_utils:reply_ok(404)
|
wapi_handler_utils:reply_ok(404)
|
||||||
end;
|
end;
|
||||||
process_request('CreateDestination', #{'Destination' := Params}, Context, Opts) ->
|
process_request('CreateDestination', #{'Destination' := Params}, Context, Opts) ->
|
||||||
case wapi_wallet_ff_backend:create_destination(Params, Context) of
|
case wapi_destination_backend:create(Params, Context) of
|
||||||
{ok, Destination = #{<<"id">> := DestinationId}} ->
|
{ok, Destination = #{<<"id">> := DestinationId}} ->
|
||||||
wapi_handler_utils:reply_ok(201, Destination, get_location('GetDestination', [DestinationId], Opts));
|
wapi_handler_utils:reply_ok(201, Destination, get_location('GetDestination', [DestinationId], Opts));
|
||||||
{error, {identity, unauthorized}} ->
|
{error, {identity, unauthorized}} ->
|
||||||
@ -291,16 +230,14 @@ process_request('CreateDestination', #{'Destination' := Params}, Context, Opts)
|
|||||||
wapi_handler_utils:reply_ok(422, wapi_handler_utils:get_error_msg(<<"No such identity">>));
|
wapi_handler_utils:reply_ok(422, wapi_handler_utils:get_error_msg(<<"No such identity">>));
|
||||||
{error, {currency, notfound}} ->
|
{error, {currency, notfound}} ->
|
||||||
wapi_handler_utils:reply_ok(422, wapi_handler_utils:get_error_msg(<<"Currency not supported">>));
|
wapi_handler_utils:reply_ok(422, wapi_handler_utils:get_error_msg(<<"Currency not supported">>));
|
||||||
{error, {inaccessible, _}} ->
|
{error, inaccessible} ->
|
||||||
wapi_handler_utils:reply_ok(422, wapi_handler_utils:get_error_msg(<<"Identity inaccessible">>));
|
wapi_handler_utils:reply_ok(422, wapi_handler_utils:get_error_msg(<<"Identity inaccessible">>));
|
||||||
{error, {external_id_conflict, ID, ExternalID}} ->
|
{error, {external_id_conflict, {ID, ExternalID}}} ->
|
||||||
wapi_handler_utils:logic_error(external_id_conflict, {ID, ExternalID});
|
wapi_handler_utils:logic_error(external_id_conflict, {ID, ExternalID});
|
||||||
{error, invalid} ->
|
{error, invalid_resource_token} ->
|
||||||
wapi_handler_utils:reply_ok(422, wapi_handler_utils:get_error_msg(<<"Invalid currency">>));
|
|
||||||
{error, {invalid_resource_token, Type}} ->
|
|
||||||
wapi_handler_utils:reply_error(400, #{
|
wapi_handler_utils:reply_error(400, #{
|
||||||
<<"errorType">> => <<"InvalidResourceToken">>,
|
<<"errorType">> => <<"InvalidResourceToken">>,
|
||||||
<<"name">> => Type,
|
<<"name">> => <<"BankCardDestinationResource">>,
|
||||||
<<"description">> => <<"Specified resource token is invalid">>
|
<<"description">> => <<"Specified resource token is invalid">>
|
||||||
})
|
})
|
||||||
end;
|
end;
|
||||||
@ -308,9 +245,9 @@ process_request('IssueDestinationGrant', #{
|
|||||||
'destinationID' := DestinationId,
|
'destinationID' := DestinationId,
|
||||||
'DestinationGrantRequest' := #{<<"validUntil">> := Expiration}
|
'DestinationGrantRequest' := #{<<"validUntil">> := Expiration}
|
||||||
}, Context, _Opts) ->
|
}, Context, _Opts) ->
|
||||||
case wapi_wallet_ff_backend:get_destination(DestinationId, Context) of
|
case wapi_destination_backend:get(DestinationId, Context) of
|
||||||
{ok, _} ->
|
{ok, _} ->
|
||||||
case wapi_backend_utils:issue_grant_token({destinations, DestinationId}, Expiration, Context) of
|
case issue_grant_token({destinations, DestinationId}, Expiration, Context) of
|
||||||
{ok, Token} ->
|
{ok, Token} ->
|
||||||
wapi_handler_utils:reply_ok(201, #{
|
wapi_handler_utils:reply_ok(201, #{
|
||||||
<<"token">> => Token,
|
<<"token">> => Token,
|
||||||
@ -326,40 +263,59 @@ process_request('IssueDestinationGrant', #{
|
|||||||
{error, {destination, unauthorized}} ->
|
{error, {destination, unauthorized}} ->
|
||||||
wapi_handler_utils:reply_ok(404)
|
wapi_handler_utils:reply_ok(404)
|
||||||
end;
|
end;
|
||||||
process_request('CreateWithdrawal', #{'WithdrawalParameters' := Params}, Context, Opts) ->
|
|
||||||
case wapi_wallet_ff_backend:create_withdrawal(Params, Context) of
|
%% Withdrawals
|
||||||
{ok, Withdrawal = #{<<"id">> := WithdrawalId}} ->
|
|
||||||
wapi_handler_utils:reply_ok(202, Withdrawal, get_location('GetWithdrawal', [WithdrawalId], Opts));
|
process_request('CreateQuote', Params, Context, _Opts) ->
|
||||||
{error, {source, notfound}} ->
|
case wapi_withdrawal_backend:create_quote(Params, Context) of
|
||||||
wapi_handler_utils:reply_ok(422, wapi_handler_utils:get_error_msg(<<"No such wallet">>));
|
{ok, Quote} ->
|
||||||
|
wapi_handler_utils:reply_ok(202, Quote);
|
||||||
{error, {destination, notfound}} ->
|
{error, {destination, notfound}} ->
|
||||||
wapi_handler_utils:reply_ok(422, wapi_handler_utils:get_error_msg(<<"No such destination">>));
|
wapi_handler_utils:reply_ok(422, wapi_handler_utils:get_error_msg(<<"No such destination">>));
|
||||||
{error, {destination, {unauthorized, _}}} ->
|
|
||||||
wapi_handler_utils:reply_ok(422,
|
|
||||||
wapi_handler_utils:get_error_msg(<<"Destination unauthorized">>)
|
|
||||||
);
|
|
||||||
{error, {destination, unauthorized}} ->
|
{error, {destination, unauthorized}} ->
|
||||||
wapi_handler_utils:reply_ok(422, wapi_handler_utils:get_error_msg(<<"Destination unauthorized">>));
|
wapi_handler_utils:reply_ok(422, wapi_handler_utils:get_error_msg(<<"Destination unauthorized">>));
|
||||||
{error, {provider, notfound}} ->
|
{error, {wallet, notfound}} ->
|
||||||
wapi_handler_utils:reply_ok(422, wapi_handler_utils:get_error_msg(<<"No such provider">>));
|
wapi_handler_utils:reply_ok(422, wapi_handler_utils:get_error_msg(<<"No such wallet">>));
|
||||||
{error, {external_id_conflict, ID, ExternalID}} ->
|
{error, {wallet, unauthorized}} ->
|
||||||
|
wapi_handler_utils:reply_ok(422, wapi_handler_utils:get_error_msg(<<"Wallet unauthorized">>));
|
||||||
|
{error, {forbidden_currency, _}} ->
|
||||||
|
wapi_handler_utils:reply_ok(422,
|
||||||
|
wapi_handler_utils:get_error_msg(<<"Forbidden currency">>)
|
||||||
|
);
|
||||||
|
{error, {forbidden_amount, _}} ->
|
||||||
|
wapi_handler_utils:reply_ok(422,
|
||||||
|
wapi_handler_utils:get_error_msg(<<"Invalid cash amount">>)
|
||||||
|
);
|
||||||
|
{error, {inconsistent_currency, _}} ->
|
||||||
|
wapi_handler_utils:reply_ok(422,
|
||||||
|
wapi_handler_utils:get_error_msg(<<"Invalid currency">>)
|
||||||
|
);
|
||||||
|
{error, {identity_providers_mismatch, _}} ->
|
||||||
|
wapi_handler_utils:reply_ok(422,
|
||||||
|
wapi_handler_utils:get_error_msg(
|
||||||
|
<<"This wallet and destination cannot be used together">>
|
||||||
|
)
|
||||||
|
);
|
||||||
|
{error, {destination_resource, {bin_data, not_found}}} ->
|
||||||
|
wapi_handler_utils:reply_ok(422,
|
||||||
|
wapi_handler_utils:get_error_msg(<<"Unknown card issuer">>)
|
||||||
|
)
|
||||||
|
end;
|
||||||
|
process_request('CreateWithdrawal', #{'WithdrawalParameters' := Params}, Context, Opts) ->
|
||||||
|
case wapi_withdrawal_backend:create(Params, Context) of
|
||||||
|
{ok, Withdrawal = #{<<"id">> := WithdrawalId}} ->
|
||||||
|
wapi_handler_utils:reply_ok(202, Withdrawal, get_location('GetWithdrawal', [WithdrawalId], Opts));
|
||||||
|
{error, {destination, notfound}} ->
|
||||||
|
wapi_handler_utils:reply_ok(422, wapi_handler_utils:get_error_msg(<<"No such destination">>));
|
||||||
|
{error, {destination, unauthorized}} ->
|
||||||
|
wapi_handler_utils:reply_ok(422, wapi_handler_utils:get_error_msg(<<"Destination unauthorized">>));
|
||||||
|
{error, {external_id_conflict, ID}} ->
|
||||||
|
ExternalID = maps:get(<<"externalID">>, Params, undefined),
|
||||||
wapi_handler_utils:logic_error(external_id_conflict, {ID, ExternalID});
|
wapi_handler_utils:logic_error(external_id_conflict, {ID, ExternalID});
|
||||||
{error, {wallet, notfound}} ->
|
{error, {wallet, notfound}} ->
|
||||||
wapi_handler_utils:reply_ok(422, wapi_handler_utils:get_error_msg(<<"No such wallet">>));
|
wapi_handler_utils:reply_ok(422, wapi_handler_utils:get_error_msg(<<"No such wallet">>));
|
||||||
{error, {wallet, {unauthorized, _}}} ->
|
{error, {wallet, unauthorized}} ->
|
||||||
wapi_handler_utils:reply_ok(422, wapi_handler_utils:get_error_msg(<<"Wallet unauthorized">>));
|
wapi_handler_utils:reply_ok(422, wapi_handler_utils:get_error_msg(<<"Wallet unauthorized">>));
|
||||||
{error, {wallet, {inaccessible, _}}} ->
|
|
||||||
wapi_handler_utils:reply_ok(422,
|
|
||||||
wapi_handler_utils:get_error_msg(<<"Inaccessible source or destination">>)
|
|
||||||
);
|
|
||||||
{error, {wallet, {currency, invalid}}} ->
|
|
||||||
wapi_handler_utils:reply_ok(422,
|
|
||||||
wapi_handler_utils:get_error_msg(<<"Invalid currency for source or destination">>)
|
|
||||||
);
|
|
||||||
{error, {wallet, {provider, invalid}}} ->
|
|
||||||
wapi_handler_utils:reply_ok(422,
|
|
||||||
wapi_handler_utils:get_error_msg(<<"Invalid provider for source or destination">>)
|
|
||||||
);
|
|
||||||
{error, {quote_invalid_party, _}} ->
|
{error, {quote_invalid_party, _}} ->
|
||||||
wapi_handler_utils:reply_ok(422,
|
wapi_handler_utils:reply_ok(422,
|
||||||
wapi_handler_utils:get_error_msg(<<"Withdrawal owner differs from quote`s one">>)
|
wapi_handler_utils:get_error_msg(<<"Withdrawal owner differs from quote`s one">>)
|
||||||
@ -376,52 +332,68 @@ process_request('CreateWithdrawal', #{'WithdrawalParameters' := Params}, Context
|
|||||||
wapi_handler_utils:reply_ok(422,
|
wapi_handler_utils:reply_ok(422,
|
||||||
wapi_handler_utils:get_error_msg(<<"Withdrawal body differs from quote`s one">>)
|
wapi_handler_utils:get_error_msg(<<"Withdrawal body differs from quote`s one">>)
|
||||||
);
|
);
|
||||||
{error, {terms, {terms_violation, {cash_range, _}}}} ->
|
{error, {forbidden_currency, _}} ->
|
||||||
|
wapi_handler_utils:reply_ok(422,
|
||||||
|
wapi_handler_utils:get_error_msg(<<"Forbidden currency">>)
|
||||||
|
);
|
||||||
|
{error, {forbidden_amount, _}} ->
|
||||||
wapi_handler_utils:reply_ok(422,
|
wapi_handler_utils:reply_ok(422,
|
||||||
wapi_handler_utils:get_error_msg(<<"Invalid cash amount">>)
|
wapi_handler_utils:get_error_msg(<<"Invalid cash amount">>)
|
||||||
);
|
);
|
||||||
|
{error, {inconsistent_currency, _}} ->
|
||||||
|
wapi_handler_utils:reply_ok(422,
|
||||||
|
wapi_handler_utils:get_error_msg(<<"Invalid currency">>)
|
||||||
|
);
|
||||||
|
{error, {identity_providers_mismatch, _}} ->
|
||||||
|
wapi_handler_utils:reply_ok(422,
|
||||||
|
wapi_handler_utils:get_error_msg(
|
||||||
|
<<"This wallet and destination cannot be used together">>
|
||||||
|
)
|
||||||
|
);
|
||||||
{error, {destination_resource, {bin_data, not_found}}} ->
|
{error, {destination_resource, {bin_data, not_found}}} ->
|
||||||
wapi_handler_utils:reply_ok(422,
|
wapi_handler_utils:reply_ok(422,
|
||||||
wapi_handler_utils:get_error_msg(<<"Unknown card issuer">>)
|
wapi_handler_utils:get_error_msg(<<"Unknown card issuer">>)
|
||||||
);
|
|
||||||
{error, {destination_resource, {bin_data, {unknown_payment_system, _PaymentSystem}}}} ->
|
|
||||||
wapi_handler_utils:reply_ok(422,
|
|
||||||
wapi_handler_utils:get_error_msg(<<"Unknown card payment system">>)
|
|
||||||
);
|
|
||||||
{error, {destination_resource, {bin_data, {unknown_residence, _Residence}}}} ->
|
|
||||||
wapi_handler_utils:reply_ok(422,
|
|
||||||
wapi_handler_utils:get_error_msg(<<"Unknown card issuer residence">>)
|
|
||||||
);
|
|
||||||
{error, {identity_providers_mismatch, _}} ->
|
|
||||||
wapi_handler_utils:reply_ok(422,
|
|
||||||
wapi_handler_utils:get_error_msg(<<"This wallet and destination cannot be used together">>)
|
|
||||||
)
|
)
|
||||||
end;
|
end;
|
||||||
process_request('GetWithdrawal', #{'withdrawalID' := WithdrawalId}, Context, _Opts) ->
|
process_request('GetWithdrawal', #{'withdrawalID' := WithdrawalId}, Context, _Opts) ->
|
||||||
case wapi_wallet_ff_backend:get_withdrawal(WithdrawalId, Context) of
|
case wapi_withdrawal_backend:get(WithdrawalId, Context) of
|
||||||
{ok, Withdrawal} ->
|
{ok, Withdrawal} ->
|
||||||
wapi_handler_utils:reply_ok(200, Withdrawal);
|
wapi_handler_utils:reply_ok(200, Withdrawal);
|
||||||
{error, {withdrawal, {unknown_withdrawal, WithdrawalId}}} ->
|
{error, {withdrawal, notfound}} ->
|
||||||
wapi_handler_utils:reply_ok(404);
|
wapi_handler_utils:reply_ok(404);
|
||||||
{error, {withdrawal, unauthorized}} ->
|
{error, {withdrawal, unauthorized}} ->
|
||||||
wapi_handler_utils:reply_ok(404)
|
wapi_handler_utils:reply_ok(404)
|
||||||
end;
|
end;
|
||||||
process_request('GetWithdrawalByExternalID', #{'externalID' := ExternalID}, Context, _Opts) ->
|
process_request('GetWithdrawalByExternalID', #{'externalID' := ExternalID}, Context, _Opts) ->
|
||||||
case wapi_wallet_ff_backend:get_withdrawal_by_external_id(ExternalID, Context) of
|
case wapi_withdrawal_backend:get_by_external_id(ExternalID, Context) of
|
||||||
{ok, Withdrawal} ->
|
{ok, Withdrawal} ->
|
||||||
wapi_handler_utils:reply_ok(200, Withdrawal);
|
wapi_handler_utils:reply_ok(200, Withdrawal);
|
||||||
{error, {external_id, {unknown_external_id, ExternalID}}} ->
|
{error, {external_id, {unknown_external_id, ExternalID}}} ->
|
||||||
wapi_handler_utils:reply_ok(404);
|
wapi_handler_utils:reply_ok(404);
|
||||||
{error, {withdrawal, {unknown_withdrawal, _WithdrawalId}}} ->
|
{error, {withdrawal, notfound}} ->
|
||||||
wapi_handler_utils:reply_ok(404);
|
wapi_handler_utils:reply_ok(404);
|
||||||
{error, {withdrawal, unauthorized}} ->
|
{error, {withdrawal, unauthorized}} ->
|
||||||
wapi_handler_utils:reply_ok(404)
|
wapi_handler_utils:reply_ok(404)
|
||||||
end;
|
end;
|
||||||
|
process_request('ListWithdrawals', Params, Context, _Opts) ->
|
||||||
|
case wapi_stat_backend:list_withdrawals(Params, Context) of
|
||||||
|
{ok, List} -> wapi_handler_utils:reply_ok(200, List);
|
||||||
|
{error, {invalid, Errors}} ->
|
||||||
|
wapi_handler_utils:reply_error(400, #{
|
||||||
|
<<"errorType">> => <<"NoMatch">>,
|
||||||
|
<<"description">> => Errors
|
||||||
|
});
|
||||||
|
{error, {bad_token, Reason}} ->
|
||||||
|
wapi_handler_utils:reply_error(400, #{
|
||||||
|
<<"errorType">> => <<"InvalidToken">>,
|
||||||
|
<<"description">> => Reason
|
||||||
|
})
|
||||||
|
end;
|
||||||
process_request('PollWithdrawalEvents', Params, Context, _Opts) ->
|
process_request('PollWithdrawalEvents', Params, Context, _Opts) ->
|
||||||
case wapi_wallet_ff_backend:get_withdrawal_events(Params, Context) of
|
case wapi_withdrawal_backend:get_events(Params, Context) of
|
||||||
{ok, Events} ->
|
{ok, Events} ->
|
||||||
wapi_handler_utils:reply_ok(200, Events);
|
wapi_handler_utils:reply_ok(200, Events);
|
||||||
{error, {withdrawal, {unknown_withdrawal, _WithdrawalId}}} ->
|
{error, {withdrawal, notfound}} ->
|
||||||
wapi_handler_utils:reply_ok(404);
|
wapi_handler_utils:reply_ok(404);
|
||||||
{error, {withdrawal, unauthorized}} ->
|
{error, {withdrawal, unauthorized}} ->
|
||||||
wapi_handler_utils:reply_ok(404)
|
wapi_handler_utils:reply_ok(404)
|
||||||
@ -430,457 +402,62 @@ process_request('GetWithdrawalEvents', #{
|
|||||||
'withdrawalID' := WithdrawalId,
|
'withdrawalID' := WithdrawalId,
|
||||||
'eventID' := EventId
|
'eventID' := EventId
|
||||||
}, Context, _Opts) ->
|
}, Context, _Opts) ->
|
||||||
case wapi_wallet_ff_backend:get_withdrawal_event(WithdrawalId, EventId, Context) of
|
case wapi_withdrawal_backend:get_event(WithdrawalId, EventId, Context) of
|
||||||
{ok, Event} ->
|
{ok, Event} ->
|
||||||
wapi_handler_utils:reply_ok(200, Event);
|
wapi_handler_utils:reply_ok(200, Event);
|
||||||
{error, {withdrawal, {unknown_withdrawal, WithdrawalId}}} ->
|
{error, {withdrawal, notfound}} ->
|
||||||
wapi_handler_utils:reply_ok(404);
|
wapi_handler_utils:reply_ok(404);
|
||||||
{error, {withdrawal, unauthorized}} ->
|
{error, {withdrawal, unauthorized}} ->
|
||||||
wapi_handler_utils:reply_ok(404);
|
wapi_handler_utils:reply_ok(404);
|
||||||
{error, {event, notfound}} ->
|
{error, {event, notfound}} ->
|
||||||
wapi_handler_utils:reply_ok(404)
|
wapi_handler_utils:reply_ok(404)
|
||||||
end;
|
end;
|
||||||
process_request('ListWithdrawals', Params, Context, _Opts) ->
|
|
||||||
case wapi_wallet_ff_backend:list_withdrawals(Params, Context) of
|
|
||||||
{ok, {200, _, List}} -> wapi_handler_utils:reply_ok(200, List);
|
|
||||||
{error, {Code, _, Error}} -> wapi_handler_utils:reply_error(Code, Error)
|
|
||||||
end;
|
|
||||||
process_request('CreateQuote', Params, Context, _Opts) ->
|
|
||||||
case wapi_wallet_ff_backend:create_quote(Params, Context) of
|
|
||||||
{ok, Promise} -> wapi_handler_utils:reply_ok(202, Promise);
|
|
||||||
{error, {destination, notfound}} ->
|
|
||||||
wapi_handler_utils:reply_ok(422,
|
|
||||||
wapi_handler_utils:get_error_msg(<<"Destination not found">>)
|
|
||||||
);
|
|
||||||
{error, {destination, unauthorized}} ->
|
|
||||||
wapi_handler_utils:reply_ok(422,
|
|
||||||
wapi_handler_utils:get_error_msg(<<"Destination unauthorized">>)
|
|
||||||
);
|
|
||||||
{error, {route, route_not_found}} ->
|
|
||||||
wapi_handler_utils:reply_ok(422,
|
|
||||||
wapi_handler_utils:get_error_msg(<<"Provider not found">>)
|
|
||||||
);
|
|
||||||
{error, {wallet, notfound}} ->
|
|
||||||
wapi_handler_utils:reply_ok(422,
|
|
||||||
wapi_handler_utils:get_error_msg(<<"Wallet not found">>)
|
|
||||||
);
|
|
||||||
{error, {identity_providers_mismatch, _}} ->
|
|
||||||
wapi_handler_utils:reply_ok(422,
|
|
||||||
wapi_handler_utils:get_error_msg(
|
|
||||||
<<"This wallet and destination cannot be used together">>
|
|
||||||
)
|
|
||||||
)
|
|
||||||
end;
|
|
||||||
|
|
||||||
%% Residences
|
|
||||||
process_request('GetResidence', #{'residence' := ResidenceId}, Context, _Opts) ->
|
|
||||||
case wapi_wallet_ff_backend:get_residence(ResidenceId, Context) of
|
|
||||||
{ok, Residence} -> wapi_handler_utils:reply_ok(200, Residence);
|
|
||||||
{error, notfound} -> wapi_handler_utils:reply_ok(404)
|
|
||||||
end;
|
|
||||||
|
|
||||||
%% Currencies
|
|
||||||
process_request('GetCurrency', #{'currencyID' := CurrencyId}, Context, _Opts) ->
|
|
||||||
case wapi_wallet_ff_backend:get_currency(CurrencyId, Context) of
|
|
||||||
{ok, Currency} -> wapi_handler_utils:reply_ok(200, Currency);
|
|
||||||
{error, notfound} -> wapi_handler_utils:reply_ok(404)
|
|
||||||
end;
|
|
||||||
|
|
||||||
%% Reports
|
|
||||||
process_request('CreateReport', Params, Context, _Opts) ->
|
|
||||||
case wapi_wallet_ff_backend:create_report(Params, Context) of
|
|
||||||
{ok, Report} -> wapi_handler_utils:reply_ok(201, Report);
|
|
||||||
{error, {identity, notfound}} -> wapi_handler_utils:reply_ok(400, #{
|
|
||||||
<<"errorType">> => <<"NotFound">>,
|
|
||||||
<<"name">> => <<"identity">>,
|
|
||||||
<<"description">> => <<"identity not found">>
|
|
||||||
});
|
|
||||||
{error, {identity, unauthorized}} -> wapi_handler_utils:reply_ok(400, #{
|
|
||||||
<<"errorType">> => <<"NotFound">>,
|
|
||||||
<<"name">> => <<"identity">>,
|
|
||||||
<<"description">> => <<"identity not found">>
|
|
||||||
});
|
|
||||||
{error, invalid_request} -> wapi_handler_utils:reply_ok(400, #{
|
|
||||||
<<"errorType">> => <<"NoMatch">>,
|
|
||||||
<<"name">> => <<"timestamps">>,
|
|
||||||
<<"description">> => <<"invalid time range">>
|
|
||||||
});
|
|
||||||
{error, invalid_contract} -> wapi_handler_utils:reply_ok(400, #{
|
|
||||||
<<"errorType">> => <<"NotFound">>,
|
|
||||||
<<"name">> => <<"contractID">>,
|
|
||||||
<<"description">> => <<"contract not found">>
|
|
||||||
})
|
|
||||||
end;
|
|
||||||
process_request('GetReport', #{
|
|
||||||
identityID := IdentityID,
|
|
||||||
reportID := ReportId
|
|
||||||
}, Context, _Opts) ->
|
|
||||||
case wapi_wallet_ff_backend:get_report(ReportId, IdentityID, Context) of
|
|
||||||
{ok, Report} -> wapi_handler_utils:reply_ok(200, Report);
|
|
||||||
{error, {identity, notfound}} -> wapi_handler_utils:reply_ok(400, #{
|
|
||||||
<<"errorType">> => <<"NotFound">>,
|
|
||||||
<<"name">> => <<"identity">>,
|
|
||||||
<<"description">> => <<"identity not found">>
|
|
||||||
});
|
|
||||||
{error, {identity, unauthorized}} -> wapi_handler_utils:reply_ok(400, #{
|
|
||||||
<<"errorType">> => <<"NotFound">>,
|
|
||||||
<<"name">> => <<"identity">>,
|
|
||||||
<<"description">> => <<"identity not found">>
|
|
||||||
});
|
|
||||||
{error, notfound} -> wapi_handler_utils:reply_ok(404)
|
|
||||||
end;
|
|
||||||
process_request('GetReports', Params, Context, _Opts) ->
|
|
||||||
case wapi_wallet_ff_backend:get_reports(Params, Context) of
|
|
||||||
{ok, ReportList} -> wapi_handler_utils:reply_ok(200, ReportList);
|
|
||||||
{error, {identity, notfound}} -> wapi_handler_utils:reply_ok(400, #{
|
|
||||||
<<"errorType">> => <<"NotFound">>,
|
|
||||||
<<"name">> => <<"identity">>,
|
|
||||||
<<"description">> => <<"identity not found">>
|
|
||||||
});
|
|
||||||
{error, {identity, unauthorized}} -> wapi_handler_utils:reply_ok(400, #{
|
|
||||||
<<"errorType">> => <<"NotFound">>,
|
|
||||||
<<"name">> => <<"identity">>,
|
|
||||||
<<"description">> => <<"identity not found">>
|
|
||||||
});
|
|
||||||
{error, invalid_request} -> wapi_handler_utils:reply_ok(400, #{
|
|
||||||
<<"errorType">> => <<"NoMatch">>,
|
|
||||||
<<"name">> => <<"timestamps">>,
|
|
||||||
<<"description">> => <<"invalid time range">>
|
|
||||||
});
|
|
||||||
{error, {dataset_too_big, Limit}} -> wapi_handler_utils:reply_ok(400, #{
|
|
||||||
<<"errorType">> => <<"WrongLength">>,
|
|
||||||
<<"name">> => <<"limitExceeded">>,
|
|
||||||
<<"description">> => io_lib:format("Max limit: ~p", [Limit])
|
|
||||||
})
|
|
||||||
end;
|
|
||||||
process_request('DownloadFile', #{fileID := FileId}, Context, _Opts) ->
|
|
||||||
ExpiresAt = get_default_url_lifetime(),
|
|
||||||
case wapi_wallet_ff_backend:download_file(FileId, ExpiresAt, Context) of
|
|
||||||
{ok, URL} ->
|
|
||||||
wapi_handler_utils:reply_ok(201, #{<<"url">> => URL, <<"expiresAt">> => ExpiresAt});
|
|
||||||
{error, notfound} ->
|
|
||||||
wapi_handler_utils:reply_ok(404)
|
|
||||||
end;
|
|
||||||
|
|
||||||
%% Deposits
|
%% Deposits
|
||||||
|
|
||||||
process_request('ListDeposits', Params, Context, _Opts) ->
|
process_request('ListDeposits', Params, Context, _Opts) ->
|
||||||
case wapi_wallet_ff_backend:list_deposits(Params, Context) of
|
case wapi_stat_backend:list_deposits(Params, Context) of
|
||||||
{ok, {200, _, List}} -> wapi_handler_utils:reply_ok(200, List);
|
{ok, List} -> wapi_handler_utils:reply_ok(200, List);
|
||||||
{error, {Code, _, Error}} -> wapi_handler_utils:reply_error(Code, Error)
|
{error, {invalid, Errors}} ->
|
||||||
end;
|
|
||||||
|
|
||||||
%% Webhooks
|
|
||||||
process_request('CreateWebhook', Params, Context, _Opts) ->
|
|
||||||
case wapi_wallet_ff_backend:create_webhook(Params, Context) of
|
|
||||||
{ok, Webhook} -> wapi_handler_utils:reply_ok(201, Webhook);
|
|
||||||
{error, {identity, unauthorized}} ->
|
|
||||||
wapi_handler_utils:reply_ok(422, wapi_handler_utils:get_error_msg(<<"No such identity">>));
|
|
||||||
{error, {identity, notfound}} ->
|
|
||||||
wapi_handler_utils:reply_ok(422, wapi_handler_utils:get_error_msg(<<"No such identity">>));
|
|
||||||
{error, {wallet, unauthorized}} ->
|
|
||||||
wapi_handler_utils:reply_ok(422, wapi_handler_utils:get_error_msg(<<"No such wallet">>));
|
|
||||||
{error, {wallet, notfound}} ->
|
|
||||||
wapi_handler_utils:reply_ok(422, wapi_handler_utils:get_error_msg(<<"No such wallet">>))
|
|
||||||
end;
|
|
||||||
process_request('GetWebhooks', #{identityID := IdentityID}, Context, _Opts) ->
|
|
||||||
case wapi_wallet_ff_backend:get_webhooks(IdentityID, Context) of
|
|
||||||
{ok, Webhooks} -> wapi_handler_utils:reply_ok(200, Webhooks);
|
|
||||||
{error, {identity, unauthorized}} ->
|
|
||||||
wapi_handler_utils:reply_ok(422, wapi_handler_utils:get_error_msg(<<"No such identity">>));
|
|
||||||
{error, {identity, notfound}} ->
|
|
||||||
wapi_handler_utils:reply_ok(422, wapi_handler_utils:get_error_msg(<<"No such identity">>))
|
|
||||||
end;
|
|
||||||
process_request('GetWebhookByID', #{identityID := IdentityID, webhookID := WebhookID}, Context, _Opts) ->
|
|
||||||
case wapi_wallet_ff_backend:get_webhook(WebhookID, IdentityID, Context) of
|
|
||||||
{ok, Webhook} -> wapi_handler_utils:reply_ok(200, Webhook);
|
|
||||||
{error, notfound} ->
|
|
||||||
wapi_handler_utils:reply_ok(404);
|
|
||||||
{error, {identity, unauthorized}} ->
|
|
||||||
wapi_handler_utils:reply_ok(422, wapi_handler_utils:get_error_msg(<<"No such identity">>));
|
|
||||||
{error, {identity, notfound}} ->
|
|
||||||
wapi_handler_utils:reply_ok(422, wapi_handler_utils:get_error_msg(<<"No such identity">>))
|
|
||||||
end;
|
|
||||||
process_request('DeleteWebhookByID', #{identityID := IdentityID, webhookID := WebhookID}, Context, _Opts) ->
|
|
||||||
case wapi_wallet_ff_backend:delete_webhook(WebhookID, IdentityID, Context) of
|
|
||||||
ok -> wapi_handler_utils:reply_ok(204);
|
|
||||||
{error, notfound} ->
|
|
||||||
wapi_handler_utils:reply_ok(404);
|
|
||||||
{error, {identity, unauthorized}} ->
|
|
||||||
wapi_handler_utils:reply_ok(422, wapi_handler_utils:get_error_msg(<<"No such identity">>));
|
|
||||||
{error, {identity, notfound}} ->
|
|
||||||
wapi_handler_utils:reply_ok(422, wapi_handler_utils:get_error_msg(<<"No such identity">>))
|
|
||||||
end;
|
|
||||||
|
|
||||||
%% P2P
|
|
||||||
process_request('QuoteP2PTransfer', #{'QuoteParameters' := Params}, Context, _Opts) ->
|
|
||||||
case wapi_wallet_ff_backend:quote_p2p_transfer(Params, Context) of
|
|
||||||
{ok, Quote} ->
|
|
||||||
wapi_handler_utils:reply_ok(201, Quote);
|
|
||||||
{error, {identity, not_found}} ->
|
|
||||||
wapi_handler_utils:reply_ok(422,
|
|
||||||
wapi_handler_utils:get_error_msg(<<"No such identity">>));
|
|
||||||
{error, {party, _PartyContractError}} ->
|
|
||||||
wapi_handler_utils:reply_ok(422,
|
|
||||||
wapi_handler_utils:get_error_msg(<<"No such party">>));
|
|
||||||
{error, {sender, {bin_data, _}}} ->
|
|
||||||
wapi_handler_utils:reply_ok(422,
|
|
||||||
wapi_handler_utils:get_error_msg(<<"Invalid sender resource">>));
|
|
||||||
{error, {receiver, {bin_data, _}}} ->
|
|
||||||
wapi_handler_utils:reply_ok(422,
|
|
||||||
wapi_handler_utils:get_error_msg(<<"Invalid receiver resource">>));
|
|
||||||
{error, {terms, {terms_violation, {not_allowed_currency, _Details}}}} ->
|
|
||||||
wapi_handler_utils:reply_ok(422,
|
|
||||||
wapi_handler_utils:get_error_msg(<<"Currency not allowed">>));
|
|
||||||
{error, {terms, {terms_violation, {cash_range, {_Cash, _CashRange}}}}} ->
|
|
||||||
wapi_handler_utils:reply_ok(422,
|
|
||||||
wapi_handler_utils:get_error_msg(<<"Transfer amount is out of allowed range">>));
|
|
||||||
{error, {terms, {terms_violation, p2p_forbidden}}} ->
|
|
||||||
wapi_handler_utils:reply_ok(422,
|
|
||||||
wapi_handler_utils:get_error_msg(<<"P2P transfer not allowed">>));
|
|
||||||
{error, {invalid_resource_token, Type}} ->
|
|
||||||
wapi_handler_utils:reply_error(400, #{
|
wapi_handler_utils:reply_error(400, #{
|
||||||
<<"errorType">> => <<"InvalidResourceToken">>,
|
<<"errorType">> => <<"NoMatch">>,
|
||||||
<<"name">> => Type,
|
<<"description">> => Errors
|
||||||
<<"description">> => <<"Specified resource token is invalid">>
|
|
||||||
})
|
|
||||||
end;
|
|
||||||
process_request('CreateP2PTransfer', #{'P2PTransferParameters' := Params}, Context, _Opts) ->
|
|
||||||
case wapi_wallet_ff_backend:create_p2p_transfer(Params, Context) of
|
|
||||||
{ok, P2PTransfer} ->
|
|
||||||
wapi_handler_utils:reply_ok(202, P2PTransfer);
|
|
||||||
{error, {identity, notfound}} ->
|
|
||||||
wapi_handler_utils:reply_ok(422,
|
|
||||||
wapi_handler_utils:get_error_msg(<<"No such identity">>));
|
|
||||||
{error, {external_id_conflict, ID, ExternalID}} ->
|
|
||||||
wapi_handler_utils:logic_error(external_id_conflict, {ID, ExternalID});
|
|
||||||
{error, {identity, unauthorized}} ->
|
|
||||||
wapi_handler_utils:reply_ok(422,
|
|
||||||
wapi_handler_utils:get_error_msg(<<"No such identity">>));
|
|
||||||
{error, {sender, {bin_data, _}}} ->
|
|
||||||
wapi_handler_utils:reply_ok(422,
|
|
||||||
wapi_handler_utils:get_error_msg(<<"Invalid sender resource">>));
|
|
||||||
{error, {sender, different_resource}} ->
|
|
||||||
wapi_handler_utils:reply_ok(422,
|
|
||||||
wapi_handler_utils:get_error_msg(<<"Invalid sender resource">>));
|
|
||||||
{error, {receiver, {bin_data, _}}} ->
|
|
||||||
wapi_handler_utils:reply_ok(422,
|
|
||||||
wapi_handler_utils:get_error_msg(<<"Invalid receiver resource">>));
|
|
||||||
{error, {receiver, different_resource}} ->
|
|
||||||
wapi_handler_utils:reply_ok(422,
|
|
||||||
wapi_handler_utils:get_error_msg(<<"Invalid receiver resource">>));
|
|
||||||
{error, {terms, {terms_violation, {not_allowed_currency, _Details}}}} ->
|
|
||||||
wapi_handler_utils:reply_ok(422,
|
|
||||||
wapi_handler_utils:get_error_msg(<<"Currency not allowed">>));
|
|
||||||
{error, {terms, {terms_violation, {cash_range, {_Cash, _CashRange}}}}} ->
|
|
||||||
wapi_handler_utils:reply_ok(422,
|
|
||||||
wapi_handler_utils:get_error_msg(<<"Transfer amount is out of allowed range">>));
|
|
||||||
{error, {terms, {terms_violation, p2p_forbidden}}} ->
|
|
||||||
wapi_handler_utils:reply_ok(422,
|
|
||||||
wapi_handler_utils:get_error_msg(<<"P2P transfer not allowed">>));
|
|
||||||
{error, {token, {not_verified, _}}} ->
|
|
||||||
wapi_handler_utils:reply_ok(422,
|
|
||||||
wapi_handler_utils:get_error_msg(<<"Token can't be verified">>));
|
|
||||||
{error, {invalid_resource_token, Type}} ->
|
|
||||||
wapi_handler_utils:reply_error(400, #{
|
|
||||||
<<"errorType">> => <<"InvalidResourceToken">>,
|
|
||||||
<<"name">> => Type,
|
|
||||||
<<"description">> => <<"Specified resource token is invalid">>
|
|
||||||
})
|
|
||||||
end;
|
|
||||||
process_request('GetP2PTransfer', #{p2pTransferID := ID}, Context, _Opts) ->
|
|
||||||
case wapi_wallet_ff_backend:get_p2p_transfer(ID, Context) of
|
|
||||||
{ok, P2PTransfer} ->
|
|
||||||
wapi_handler_utils:reply_ok(200, P2PTransfer);
|
|
||||||
{error, {p2p_transfer, unauthorized}} ->
|
|
||||||
wapi_handler_utils:reply_ok(404);
|
|
||||||
{error, {p2p_transfer, {unknown_p2p_transfer, _ID}}} ->
|
|
||||||
wapi_handler_utils:reply_ok(404)
|
|
||||||
end;
|
|
||||||
process_request('GetP2PTransferEvents', #{p2pTransferID := ID, continuationToken := CT}, Context, _Opts) ->
|
|
||||||
case wapi_wallet_ff_backend:get_p2p_transfer_events({ID, CT}, Context) of
|
|
||||||
{ok, P2PTransferEvents} ->
|
|
||||||
wapi_handler_utils:reply_ok(200, P2PTransferEvents);
|
|
||||||
{error, {p2p_transfer, unauthorized}} ->
|
|
||||||
wapi_handler_utils:reply_ok(404);
|
|
||||||
{error, {p2p_transfer, not_found}} ->
|
|
||||||
wapi_handler_utils:reply_ok(404);
|
|
||||||
{error, {token, {not_verified, invalid_signature}}} ->
|
|
||||||
wapi_handler_utils:reply_ok(422,
|
|
||||||
wapi_handler_utils:get_error_msg(<<"Continuation Token can't be verified">>))
|
|
||||||
end;
|
|
||||||
|
|
||||||
%% P2P Templates
|
|
||||||
process_request('CreateP2PTransferTemplate', #{'P2PTransferTemplateParameters' := Params}, Context, _Opts) ->
|
|
||||||
case wapi_wallet_ff_backend:create_p2p_template(Params, Context) of
|
|
||||||
{ok, P2PTemplate} ->
|
|
||||||
wapi_handler_utils:reply_ok(201, P2PTemplate);
|
|
||||||
{error, {identity, notfound}} ->
|
|
||||||
wapi_handler_utils:reply_ok(422,
|
|
||||||
wapi_handler_utils:get_error_msg(<<"No such identity">>));
|
|
||||||
{error, {external_id_conflict, ID, ExternalID}} ->
|
|
||||||
wapi_handler_utils:logic_error(external_id_conflict, {ID, ExternalID});
|
|
||||||
{error, {identity, unauthorized}} ->
|
|
||||||
wapi_handler_utils:reply_ok(422,
|
|
||||||
wapi_handler_utils:get_error_msg(<<"No such identity">>));
|
|
||||||
{error, {terms, {terms_violation, p2p_template_forbidden}}} ->
|
|
||||||
wapi_handler_utils:reply_ok(422,
|
|
||||||
wapi_handler_utils:get_error_msg(<<"P2P template not allowed">>))
|
|
||||||
end;
|
|
||||||
process_request('GetP2PTransferTemplateByID', #{p2pTransferTemplateID := ID}, Context, _Opts) ->
|
|
||||||
case wapi_wallet_ff_backend:get_p2p_template(ID, Context) of
|
|
||||||
{ok, P2PTemplate} ->
|
|
||||||
wapi_handler_utils:reply_ok(200, P2PTemplate);
|
|
||||||
{error, {unknown_p2p_template, _ID}} ->
|
|
||||||
wapi_handler_utils:reply_ok(404)
|
|
||||||
end;
|
|
||||||
process_request('BlockP2PTransferTemplate', #{p2pTransferTemplateID := ID}, Context, _Opts) ->
|
|
||||||
case wapi_wallet_ff_backend:block_p2p_template(ID, Context) of
|
|
||||||
ok ->
|
|
||||||
wapi_handler_utils:reply_ok(204);
|
|
||||||
{error, {unknown_p2p_template, _ID}} ->
|
|
||||||
wapi_handler_utils:reply_ok(404);
|
|
||||||
{error, {p2p_template, unauthorized}} ->
|
|
||||||
wapi_handler_utils:reply_ok(404)
|
|
||||||
end;
|
|
||||||
process_request('IssueP2PTransferTemplateAccessToken', #{
|
|
||||||
p2pTransferTemplateID := ID,
|
|
||||||
'P2PTransferTemplateTokenRequest' := #{<<"validUntil">> := Expiration}
|
|
||||||
}, Context, _Opts) ->
|
|
||||||
case wapi_wallet_ff_backend:issue_p2p_template_access_token(ID, Expiration, Context) of
|
|
||||||
{ok, Token} ->
|
|
||||||
wapi_handler_utils:reply_ok(201, #{
|
|
||||||
<<"token">> => Token,
|
|
||||||
<<"validUntil">> => Expiration
|
|
||||||
});
|
});
|
||||||
{error, {p2p_template, unauthorized}} ->
|
{error, {bad_token, Reason}} ->
|
||||||
wapi_handler_utils:reply_ok(404);
|
|
||||||
{error, expired} ->
|
|
||||||
wapi_handler_utils:reply_ok(422,
|
|
||||||
wapi_handler_utils:get_error_msg(<<"Invalid expiration: already expired">>)
|
|
||||||
);
|
|
||||||
{error, {unknown_p2p_template, _ID}} ->
|
|
||||||
wapi_handler_utils:reply_ok(404)
|
|
||||||
end;
|
|
||||||
process_request('IssueP2PTransferTicket', #{
|
|
||||||
p2pTransferTemplateID := ID,
|
|
||||||
'P2PTransferTemplateTicketRequest' := #{<<"validUntil">> := Expiration0}
|
|
||||||
}, Context, _Opts) ->
|
|
||||||
case wapi_wallet_ff_backend:issue_p2p_transfer_ticket(ID, Expiration0, Context) of
|
|
||||||
{ok, {Token, Expiration1}} ->
|
|
||||||
wapi_handler_utils:reply_ok(201, #{
|
|
||||||
<<"token">> => Token,
|
|
||||||
<<"validUntil">> => Expiration1
|
|
||||||
});
|
|
||||||
{error, expired} ->
|
|
||||||
wapi_handler_utils:reply_ok(422,
|
|
||||||
wapi_handler_utils:get_error_msg(<<"Invalid expiration: already expired">>)
|
|
||||||
);
|
|
||||||
{error, {unknown_p2p_template, _ID}} ->
|
|
||||||
wapi_handler_utils:reply_ok(404)
|
|
||||||
end;
|
|
||||||
process_request('CreateP2PTransferWithTemplate', #{
|
|
||||||
p2pTransferTemplateID := TemplateID,
|
|
||||||
'P2PTransferWithTemplateParameters' := Params
|
|
||||||
}, Context, _Opts) ->
|
|
||||||
case wapi_wallet_ff_backend:create_p2p_transfer_with_template(TemplateID, Params, Context) of
|
|
||||||
{ok, P2PTransfer} ->
|
|
||||||
wapi_handler_utils:reply_ok(202, P2PTransfer);
|
|
||||||
{error, {unknown_p2p_template, _ID}} ->
|
|
||||||
wapi_handler_utils:reply_ok(404);
|
|
||||||
{error, {external_id_conflict, ID, ExternalID}} ->
|
|
||||||
wapi_handler_utils:logic_error(external_id_conflict, {ID, ExternalID});
|
|
||||||
{error, {identity, notfound}} ->
|
|
||||||
wapi_handler_utils:reply_ok(422,
|
|
||||||
wapi_handler_utils:get_error_msg(<<"No such identity">>));
|
|
||||||
{error, {sender, {bin_data, _}}} ->
|
|
||||||
wapi_handler_utils:reply_ok(422,
|
|
||||||
wapi_handler_utils:get_error_msg(<<"Invalid sender resource">>));
|
|
||||||
{error, {receiver, {bin_data, _}}} ->
|
|
||||||
wapi_handler_utils:reply_ok(422,
|
|
||||||
wapi_handler_utils:get_error_msg(<<"Invalid receiver resource">>));
|
|
||||||
{error, {terms, {terms_violation, {not_allowed_currency, _Details}}}} ->
|
|
||||||
wapi_handler_utils:reply_ok(422,
|
|
||||||
wapi_handler_utils:get_error_msg(<<"Currency not allowed">>));
|
|
||||||
{error, {terms, {terms_violation, {cash_range, {_Cash, _CashRange}}}}} ->
|
|
||||||
wapi_handler_utils:reply_ok(422,
|
|
||||||
wapi_handler_utils:get_error_msg(<<"Transfer amount is out of allowed range">>));
|
|
||||||
{error, {terms, {terms_violation, p2p_forbidden}}} ->
|
|
||||||
wapi_handler_utils:reply_ok(422,
|
|
||||||
wapi_handler_utils:get_error_msg(<<"P2P transfer not allowed">>));
|
|
||||||
{error, {token, {not_verified, _}}} ->
|
|
||||||
wapi_handler_utils:reply_ok(422,
|
|
||||||
wapi_handler_utils:get_error_msg(<<"Token can't be verified">>));
|
|
||||||
{error, {invalid_resource_token, Type}} ->
|
|
||||||
wapi_handler_utils:reply_error(400, #{
|
wapi_handler_utils:reply_error(400, #{
|
||||||
<<"errorType">> => <<"InvalidResourceToken">>,
|
<<"errorType">> => <<"InvalidToken">>,
|
||||||
<<"name">> => Type,
|
<<"description">> => Reason
|
||||||
<<"description">> => <<"Specified resource token is invalid">>
|
|
||||||
})
|
|
||||||
end;
|
|
||||||
process_request('QuoteP2PTransferWithTemplate', #{
|
|
||||||
p2pTransferTemplateID := TemplateID,
|
|
||||||
'P2PTransferTemplateQuoteParameters' := Params
|
|
||||||
}, Context, _Opts) ->
|
|
||||||
case wapi_wallet_ff_backend:quote_p2p_transfer_with_template(TemplateID, Params, Context) of
|
|
||||||
{ok, Quote} ->
|
|
||||||
wapi_handler_utils:reply_ok(201, Quote);
|
|
||||||
{error, {unknown_p2p_template, _ID}} ->
|
|
||||||
wapi_handler_utils:reply_ok(404);
|
|
||||||
{error, {identity, not_found}} ->
|
|
||||||
wapi_handler_utils:reply_ok(422,
|
|
||||||
wapi_handler_utils:get_error_msg(<<"No such identity">>));
|
|
||||||
{error, {party, _PartyContractError}} ->
|
|
||||||
wapi_handler_utils:reply_ok(422,
|
|
||||||
wapi_handler_utils:get_error_msg(<<"No such party">>));
|
|
||||||
{error, {sender, {bin_data, _}}} ->
|
|
||||||
wapi_handler_utils:reply_ok(422,
|
|
||||||
wapi_handler_utils:get_error_msg(<<"Invalid sender resource">>));
|
|
||||||
{error, {receiver, {bin_data, _}}} ->
|
|
||||||
wapi_handler_utils:reply_ok(422,
|
|
||||||
wapi_handler_utils:get_error_msg(<<"Invalid receiver resource">>));
|
|
||||||
{error, {terms, {terms_violation, {not_allowed_currency, _Details}}}} ->
|
|
||||||
wapi_handler_utils:reply_ok(422,
|
|
||||||
wapi_handler_utils:get_error_msg(<<"Currency not allowed">>));
|
|
||||||
{error, {terms, {terms_violation, {cash_range, {_Cash, _CashRange}}}}} ->
|
|
||||||
wapi_handler_utils:reply_ok(422,
|
|
||||||
wapi_handler_utils:get_error_msg(<<"Transfer amount is out of allowed range">>));
|
|
||||||
{error, {terms, {terms_violation, p2p_forbidden}}} ->
|
|
||||||
wapi_handler_utils:reply_ok(422,
|
|
||||||
wapi_handler_utils:get_error_msg(<<"P2P transfer not allowed">>));
|
|
||||||
{error, {invalid_resource_token, Type}} ->
|
|
||||||
wapi_handler_utils:reply_error(400, #{
|
|
||||||
<<"errorType">> => <<"InvalidResourceToken">>,
|
|
||||||
<<"name">> => Type,
|
|
||||||
<<"description">> => <<"Specified resource token is invalid">>
|
|
||||||
})
|
})
|
||||||
end;
|
end;
|
||||||
|
|
||||||
%% W2W
|
%% W2W
|
||||||
|
|
||||||
process_request('CreateW2WTransfer', #{'W2WTransferParameters' := Params}, Context, _Opts) ->
|
process_request('CreateW2WTransfer', #{'W2WTransferParameters' := Params}, Context, _Opts) ->
|
||||||
case wapi_wallet_ff_backend:create_w2w_transfer(Params, Context) of
|
case wapi_w2w_backend:create_transfer(Params, Context) of
|
||||||
{ok, W2WTransfer} ->
|
{ok, W2WTransfer} ->
|
||||||
wapi_handler_utils:reply_ok(202, W2WTransfer);
|
wapi_handler_utils:reply_ok(202, W2WTransfer);
|
||||||
{error, {wallet_from, notfound}} ->
|
{error, {wallet_from, notfound}} ->
|
||||||
wapi_handler_utils:reply_ok(422,
|
wapi_handler_utils:reply_ok(422,
|
||||||
wapi_handler_utils:get_error_msg(<<"No such wallet sender">>));
|
wapi_handler_utils:get_error_msg(<<"No such wallet sender">>));
|
||||||
|
{error, {wallet_from, unauthorized}} ->
|
||||||
|
wapi_handler_utils:reply_ok(422,
|
||||||
|
wapi_handler_utils:get_error_msg(<<"No such wallet sender">>));
|
||||||
{error, {wallet_to, notfound}} ->
|
{error, {wallet_to, notfound}} ->
|
||||||
wapi_handler_utils:reply_ok(422,
|
wapi_handler_utils:reply_ok(422,
|
||||||
wapi_handler_utils:get_error_msg(<<"No such wallet receiver">>));
|
wapi_handler_utils:get_error_msg(<<"No such wallet receiver">>));
|
||||||
{error, {wallet_from, {inaccessible, _}}} ->
|
{error, not_allowed_currency} ->
|
||||||
wapi_handler_utils:reply_ok(422,
|
|
||||||
wapi_handler_utils:get_error_msg(<<"Sender wallet is unaccessible">>));
|
|
||||||
{error, {wallet_to, {inaccessible, _}}} ->
|
|
||||||
wapi_handler_utils:reply_ok(422,
|
|
||||||
wapi_handler_utils:get_error_msg(<<"Receiver wallet is unaccessible">>));
|
|
||||||
{error, {terms, {terms_violation, {not_allowed_currency, _Details}}}} ->
|
|
||||||
wapi_handler_utils:reply_ok(422,
|
wapi_handler_utils:reply_ok(422,
|
||||||
wapi_handler_utils:get_error_msg(<<"Currency not allowed">>));
|
wapi_handler_utils:get_error_msg(<<"Currency not allowed">>));
|
||||||
{error, {terms, {terms_violation, w2w_forbidden}}} ->
|
{error, bad_w2w_transfer_amount} ->
|
||||||
wapi_handler_utils:reply_ok(422,
|
wapi_handler_utils:reply_ok(422,
|
||||||
wapi_handler_utils:get_error_msg(<<"W2W transfer not allowed">>))
|
wapi_handler_utils:get_error_msg(<<"Bad transfer amount">>));
|
||||||
|
{error, inconsistent_currency} ->
|
||||||
|
wapi_handler_utils:reply_ok(422,
|
||||||
|
wapi_handler_utils:get_error_msg(<<"Inconsistent currency">>))
|
||||||
end;
|
end;
|
||||||
|
|
||||||
process_request('GetW2WTransfer', #{w2wTransferID := ID}, Context, _Opts) ->
|
process_request('GetW2WTransfer', #{w2wTransferID := ID}, Context, _Opts) ->
|
||||||
case wapi_wallet_ff_backend:get_w2w_transfer(ID, Context) of
|
case wapi_w2w_backend:get_transfer(ID, Context) of
|
||||||
{ok, W2WTransfer} ->
|
{ok, W2WTransfer} ->
|
||||||
wapi_handler_utils:reply_ok(200, W2WTransfer);
|
wapi_handler_utils:reply_ok(200, W2WTransfer);
|
||||||
{error, {w2w_transfer, unauthorized}} ->
|
{error, {w2w_transfer, unauthorized}} ->
|
||||||
@ -895,13 +472,20 @@ get_location(OperationId, Params, Opts) ->
|
|||||||
#{path := PathSpec} = swag_server_wallet_router:get_operation(OperationId),
|
#{path := PathSpec} = swag_server_wallet_router:get_operation(OperationId),
|
||||||
wapi_handler_utils:get_location(PathSpec, Params, Opts).
|
wapi_handler_utils:get_location(PathSpec, Params, Opts).
|
||||||
|
|
||||||
-spec not_implemented() -> no_return().
|
issue_grant_token(TokenSpec, Expiration, Context) ->
|
||||||
not_implemented() ->
|
case get_expiration_deadline(Expiration) of
|
||||||
wapi_handler_utils:throw_not_implemented().
|
{ok, Deadline} ->
|
||||||
|
{ok, wapi_auth:issue_access_token(wapi_handler_utils:get_owner(Context), TokenSpec, {deadline, Deadline})};
|
||||||
|
Error = {error, _} ->
|
||||||
|
Error
|
||||||
|
end.
|
||||||
|
|
||||||
-define(DEFAULT_URL_LIFETIME, 60). % seconds
|
get_expiration_deadline(Expiration) ->
|
||||||
|
{DateTime, MilliSec} = woody_deadline:from_binary(wapi_utils:to_universal_time(Expiration)),
|
||||||
get_default_url_lifetime() ->
|
Deadline = genlib_time:daytime_to_unixtime(DateTime) + MilliSec div 1000,
|
||||||
Now = erlang:system_time(second),
|
case genlib_time:unow() - Deadline < 0 of
|
||||||
Lifetime = application:get_env(wapi, file_storage_url_lifetime, ?DEFAULT_URL_LIFETIME),
|
true ->
|
||||||
genlib_rfc3339:format(Now + Lifetime, second).
|
{ok, Deadline};
|
||||||
|
false ->
|
||||||
|
{error, expired}
|
||||||
|
end.
|
||||||
|
@ -1,6 +1,13 @@
|
|||||||
-module(wapi_withdrawal_backend).
|
-module(wapi_withdrawal_backend).
|
||||||
|
|
||||||
-define(DOMAIN, <<"wallet-api">>).
|
-define(DOMAIN, <<"wallet-api">>).
|
||||||
|
-define(event(ID, Timestamp, Change), #wthd_Event{
|
||||||
|
event_id = ID,
|
||||||
|
occured_at = Timestamp,
|
||||||
|
change = Change
|
||||||
|
}).
|
||||||
|
-define(statusChange(Status), {status_changed, #wthd_StatusChange{status = Status}}).
|
||||||
|
|
||||||
|
|
||||||
-type req_data() :: wapi_handler:req_data().
|
-type req_data() :: wapi_handler:req_data().
|
||||||
-type handler_context() :: wapi_handler:context().
|
-type handler_context() :: wapi_handler:context().
|
||||||
@ -16,20 +23,34 @@
|
|||||||
{quote_invalid_wallet, _} |
|
{quote_invalid_wallet, _} |
|
||||||
{quote, {invalid_body, _}} |
|
{quote, {invalid_body, _}} |
|
||||||
{quote, {invalid_destination, _}} |
|
{quote, {invalid_destination, _}} |
|
||||||
|
{quote, token_expired} |
|
||||||
{forbidden_currency, _} |
|
{forbidden_currency, _} |
|
||||||
{forbidden_amount, _} |
|
{forbidden_amount, _} |
|
||||||
{inconsistent_currency, _} |
|
{inconsistent_currency, _} |
|
||||||
|
{identity_providers_mismatch, {id(), id()}} |
|
||||||
|
{destination_resource, {bin_data, not_found}}.
|
||||||
|
|
||||||
|
-type create_quote_error() ::
|
||||||
|
{destination, notfound | unauthorized} |
|
||||||
|
{wallet, notfound | unauthorized} |
|
||||||
|
{forbidden_currency, _} |
|
||||||
|
{forbidden_amount, _} |
|
||||||
|
{inconsistent_currency, _} |
|
||||||
|
{identity_providers_mismatch, {id(), id()}} |
|
||||||
{destination_resource, {bin_data, not_found}}.
|
{destination_resource, {bin_data, not_found}}.
|
||||||
|
|
||||||
-export([create/2]).
|
-export([create/2]).
|
||||||
-export([get/2]).
|
-export([get/2]).
|
||||||
-export([get_by_external_id/2]).
|
-export([get_by_external_id/2]).
|
||||||
|
-export([create_quote/2]).
|
||||||
|
-export([get_events/2]).
|
||||||
|
-export([get_event/3]).
|
||||||
|
|
||||||
-include_lib("fistful_proto/include/ff_proto_withdrawal_thrift.hrl").
|
-include_lib("fistful_proto/include/ff_proto_withdrawal_thrift.hrl").
|
||||||
|
|
||||||
%% Pipeline
|
%% Pipeline
|
||||||
|
|
||||||
-import(ff_pipeline, [do/1, unwrap/1, unwrap/2]).
|
-import(wapi_pipeline, [do/1, unwrap/1, unwrap/2]).
|
||||||
|
|
||||||
-spec create(req_data(), handler_context()) ->
|
-spec create(req_data(), handler_context()) ->
|
||||||
{ok, response_data()} | {error, create_error()}.
|
{ok, response_data()} | {error, create_error()}.
|
||||||
@ -76,9 +97,7 @@ create(Params, Context, HandlerContext) ->
|
|||||||
}} ->
|
}} ->
|
||||||
{error, {identity_providers_mismatch, {WalletProvider, DestinationProvider}}};
|
{error, {identity_providers_mismatch, {WalletProvider, DestinationProvider}}};
|
||||||
{exception, #wthd_NoDestinationResourceInfo{}} ->
|
{exception, #wthd_NoDestinationResourceInfo{}} ->
|
||||||
{error, {destination_resource, {bin_data, not_found}}};
|
{error, {destination_resource, {bin_data, not_found}}}
|
||||||
{exception, Details} ->
|
|
||||||
{error, Details}
|
|
||||||
end.
|
end.
|
||||||
|
|
||||||
-spec get(id(), handler_context()) ->
|
-spec get(id(), handler_context()) ->
|
||||||
@ -116,15 +135,165 @@ get_by_external_id(ExternalID, HandlerContext = #{woody_context := WoodyContext}
|
|||||||
{error, {external_id, {unknown_external_id, ExternalID}}}
|
{error, {external_id, {unknown_external_id, ExternalID}}}
|
||||||
end.
|
end.
|
||||||
|
|
||||||
|
-spec create_quote(req_data(), handler_context()) ->
|
||||||
|
{ok, response_data()} | {error, create_quote_error()}.
|
||||||
|
|
||||||
|
create_quote(#{'WithdrawalQuoteParams' := Params}, HandlerContext) ->
|
||||||
|
case authorize_quote(Params, HandlerContext) of
|
||||||
|
ok ->
|
||||||
|
create_quote_(Params, HandlerContext);
|
||||||
|
{error, _} = Error ->
|
||||||
|
Error
|
||||||
|
end.
|
||||||
|
|
||||||
|
create_quote_(Params, HandlerContext) ->
|
||||||
|
CreateQuoteParams = marshal(create_quote_params, Params),
|
||||||
|
Request = {fistful_withdrawal, 'GetQuote', [CreateQuoteParams]},
|
||||||
|
case service_call(Request, HandlerContext) of
|
||||||
|
{ok, QuoteThrift} ->
|
||||||
|
Token = create_quote_token(
|
||||||
|
QuoteThrift,
|
||||||
|
maps:get(<<"walletID">>, Params),
|
||||||
|
maps:get(<<"destinationID">>, Params, undefined),
|
||||||
|
wapi_handler_utils:get_owner(HandlerContext)
|
||||||
|
),
|
||||||
|
UnmarshaledQuote = unmarshal(quote, QuoteThrift),
|
||||||
|
{ok, UnmarshaledQuote#{<<"quoteToken">> => Token}};
|
||||||
|
{exception, #fistful_WalletNotFound{}} ->
|
||||||
|
{error, {wallet, notfound}};
|
||||||
|
{exception, #fistful_DestinationNotFound{}} ->
|
||||||
|
{error, {destination, notfound}};
|
||||||
|
{exception, #fistful_DestinationUnauthorized{}} ->
|
||||||
|
{error, {destination, unauthorized}};
|
||||||
|
{exception, #fistful_ForbiddenOperationCurrency{currency = Currency}} ->
|
||||||
|
{error, {forbidden_currency, unmarshal_currency_ref(Currency)}};
|
||||||
|
{exception, #fistful_ForbiddenOperationAmount{amount = Amount}} ->
|
||||||
|
{error, {forbidden_amount, unmarshal_body(Amount)}};
|
||||||
|
{exception, #wthd_InconsistentWithdrawalCurrency{
|
||||||
|
withdrawal_currency = WithdrawalCurrency,
|
||||||
|
destination_currency = DestinationCurrency,
|
||||||
|
wallet_currency = WalletCurrency
|
||||||
|
}} ->
|
||||||
|
{error, {inconsistent_currency, {
|
||||||
|
unmarshal_currency_ref(WithdrawalCurrency),
|
||||||
|
unmarshal_currency_ref(DestinationCurrency),
|
||||||
|
unmarshal_currency_ref(WalletCurrency)
|
||||||
|
}}};
|
||||||
|
{exception, #wthd_IdentityProvidersMismatch{
|
||||||
|
wallet_provider = WalletProvider,
|
||||||
|
destination_provider = DestinationProvider
|
||||||
|
}} ->
|
||||||
|
{error, {identity_providers_mismatch, {WalletProvider, DestinationProvider}}};
|
||||||
|
{exception, #wthd_NoDestinationResourceInfo{}} ->
|
||||||
|
{error, {destination_resource, {bin_data, not_found}}}
|
||||||
|
end.
|
||||||
|
|
||||||
|
-spec get_events(req_data(), handler_context()) ->
|
||||||
|
{ok, response_data()} |
|
||||||
|
{error, {withdrawal, notfound}} |
|
||||||
|
{error, {withdrawal, unauthorized}}.
|
||||||
|
|
||||||
|
get_events(Params = #{'withdrawalID' := WithdrawalId, 'limit' := Limit}, HandlerContext) ->
|
||||||
|
Cursor = maps:get('eventCursor', Params, undefined),
|
||||||
|
case get_events(WithdrawalId, {Cursor, Limit}, HandlerContext) of
|
||||||
|
{ok, Events} ->
|
||||||
|
{ok, Events};
|
||||||
|
{error, {withdrawal, unauthorized}} = Error ->
|
||||||
|
Error;
|
||||||
|
{error, {withdrawal, notfound}} = Error ->
|
||||||
|
Error;
|
||||||
|
{exception, #fistful_WithdrawalNotFound{}} ->
|
||||||
|
{error, {withdrawal, notfound}}
|
||||||
|
end.
|
||||||
|
|
||||||
|
-spec get_event(id(), integer(), handler_context()) ->
|
||||||
|
{ok, response_data()} |
|
||||||
|
{error, {withdrawal, notfound}} |
|
||||||
|
{error, {withdrawal, unauthorized}} |
|
||||||
|
{error, {event, notfound}}.
|
||||||
|
|
||||||
|
get_event(WithdrawalId, EventId, HandlerContext) ->
|
||||||
|
case get_events(WithdrawalId, {EventId - 1, 1}, HandlerContext) of
|
||||||
|
{ok, [Event]} ->
|
||||||
|
{ok, Event};
|
||||||
|
{ok, []} ->
|
||||||
|
{error, {event, notfound}};
|
||||||
|
{error, {withdrawal, unauthorized}} = Error ->
|
||||||
|
Error;
|
||||||
|
{error, {withdrawal, notfound}} = Error ->
|
||||||
|
Error;
|
||||||
|
{exception, #fistful_WithdrawalNotFound{}} ->
|
||||||
|
{error, {withdrawal, notfound}}
|
||||||
|
end.
|
||||||
|
|
||||||
%%
|
%%
|
||||||
%% Internal
|
%% Internal
|
||||||
%%
|
%%
|
||||||
|
|
||||||
service_call(Params, Context) ->
|
create_quote_token(Quote, WalletID, DestinationID, PartyID) ->
|
||||||
wapi_handler_utils:service_call(Params, Context).
|
Payload = wapi_withdrawal_quote:create_token_payload(Quote, WalletID, DestinationID, PartyID),
|
||||||
|
{ok, Token} = issue_quote_token(PartyID, Payload),
|
||||||
|
Token.
|
||||||
|
|
||||||
|
issue_quote_token(PartyID, Data) ->
|
||||||
|
uac_authorizer_jwt:issue(wapi_utils:get_unique_id(), PartyID, Data, wapi_auth:get_signee()).
|
||||||
|
|
||||||
|
service_call(Params, HandlerContext) ->
|
||||||
|
wapi_handler_utils:service_call(Params, HandlerContext).
|
||||||
|
|
||||||
|
get_events(WithdrawalId, EventRange, HandlerContext) ->
|
||||||
|
case get_events_(WithdrawalId, EventRange, HandlerContext) of
|
||||||
|
{ok, Events0} ->
|
||||||
|
Events1 = lists:filter(fun event_filter/1, Events0),
|
||||||
|
{ok, unmarshal({list, event}, Events1)};
|
||||||
|
{error, _} = Error ->
|
||||||
|
Error;
|
||||||
|
{exception, _} = Exception ->
|
||||||
|
Exception
|
||||||
|
end.
|
||||||
|
|
||||||
|
get_events_(WithdrawalId, EventRange, HandlerContext) ->
|
||||||
|
case authorize_resource_by_bearer(withdrawal, WithdrawalId, HandlerContext) of
|
||||||
|
ok ->
|
||||||
|
collect_events(WithdrawalId, EventRange, HandlerContext, []);
|
||||||
|
{error, _} = Error ->
|
||||||
|
Error
|
||||||
|
end.
|
||||||
|
|
||||||
|
collect_events(WithdrawalId, {Cursor, Limit}, HandlerContext, AccEvents) ->
|
||||||
|
Request = {fistful_withdrawal, 'GetEvents', [WithdrawalId, marshal_event_range(Cursor, Limit)]},
|
||||||
|
case service_call(Request, HandlerContext) of
|
||||||
|
{exception, _} = Exception ->
|
||||||
|
Exception;
|
||||||
|
{ok, []} ->
|
||||||
|
{ok, AccEvents};
|
||||||
|
{ok, Events} ->
|
||||||
|
?event(NewCursor, _, _) = lists:last(Events),
|
||||||
|
collect_events(WithdrawalId, {NewCursor, Limit - length(Events)}, HandlerContext, AccEvents ++ Events)
|
||||||
|
end.
|
||||||
|
|
||||||
|
event_filter(?event(_, _, ?statusChange(_)))->
|
||||||
|
true;
|
||||||
|
event_filter(_) ->
|
||||||
|
false.
|
||||||
|
|
||||||
%% Validators
|
%% Validators
|
||||||
|
|
||||||
|
authorize_quote(Params = #{<<"walletID">> := WalletID}, HandlerContext) ->
|
||||||
|
do(fun() ->
|
||||||
|
unwrap(wallet, wapi_access_backend:check_resource_by_id(wallet, WalletID, HandlerContext)),
|
||||||
|
case maps:get(<<"destinationID">>, Params, undefined) of
|
||||||
|
undefined ->
|
||||||
|
ok;
|
||||||
|
DestinationID ->
|
||||||
|
unwrap(destination, wapi_access_backend:check_resource_by_id(
|
||||||
|
destination,
|
||||||
|
DestinationID,
|
||||||
|
HandlerContext
|
||||||
|
))
|
||||||
|
end
|
||||||
|
end).
|
||||||
|
|
||||||
check_withdrawal_params(Params0, HandlerContext) ->
|
check_withdrawal_params(Params0, HandlerContext) ->
|
||||||
do(fun() ->
|
do(fun() ->
|
||||||
Params1 = unwrap(try_decode_quote_token(Params0)),
|
Params1 = unwrap(try_decode_quote_token(Params0)),
|
||||||
@ -137,7 +306,7 @@ check_withdrawal_params(Params0, HandlerContext) ->
|
|||||||
try_decode_quote_token(Params = #{<<"quoteToken">> := QuoteToken}) ->
|
try_decode_quote_token(Params = #{<<"quoteToken">> := QuoteToken}) ->
|
||||||
do(fun() ->
|
do(fun() ->
|
||||||
{_, _, Data} = unwrap(uac_authorizer_jwt:verify(QuoteToken, #{})),
|
{_, _, Data} = unwrap(uac_authorizer_jwt:verify(QuoteToken, #{})),
|
||||||
{ok, Quote, WalletID, DestinationID, PartyID} = wapi_withdrawal_quote:decode_token_payload(Data),
|
{Quote, WalletID, DestinationID, PartyID} = unwrap(quote, wapi_withdrawal_quote:decode_token_payload(Data)),
|
||||||
Params#{<<"quoteToken">> => #{
|
Params#{<<"quoteToken">> => #{
|
||||||
quote => Quote,
|
quote => Quote,
|
||||||
wallet_id => WalletID,
|
wallet_id => WalletID,
|
||||||
@ -239,12 +408,12 @@ maybe_check_quote_token(Params = #{<<"quoteToken">> := #{
|
|||||||
wallet_id := WalletID,
|
wallet_id := WalletID,
|
||||||
destination_id := DestinationID,
|
destination_id := DestinationID,
|
||||||
party_id := PartyID
|
party_id := PartyID
|
||||||
}}, Context) ->
|
}}, HandlerContext) ->
|
||||||
do(fun() ->
|
do(fun() ->
|
||||||
unwrap(quote_invalid_party, valid(PartyID, wapi_handler_utils:get_owner(Context))),
|
unwrap(quote_invalid_party, valid(PartyID, wapi_handler_utils:get_owner(HandlerContext))),
|
||||||
unwrap(quote_invalid_wallet, valid(WalletID, maps:get(<<"wallet">>, Params))),
|
unwrap(quote_invalid_wallet, valid(WalletID, maps:get(<<"wallet">>, Params))),
|
||||||
unwrap(check_quote_withdrawal(DestinationID, maps:get(<<"destination">>, Params))),
|
unwrap(check_quote_withdrawal(DestinationID, maps:get(<<"destination">>, Params))),
|
||||||
unwrap(check_quote_body(maps:get(cash_from, Quote), marshal_quote_body(maps:get(<<"body">>, Params)))),
|
unwrap(check_quote_body(Quote#wthd_Quote.cash_from, marshal_body(maps:get(<<"body">>, Params)))),
|
||||||
Params#{<<"quote">> => Quote}
|
Params#{<<"quote">> => Quote}
|
||||||
end);
|
end);
|
||||||
maybe_check_quote_token(Params, _Context) ->
|
maybe_check_quote_token(Params, _Context) ->
|
||||||
@ -267,9 +436,6 @@ check_quote_withdrawal(DestinationID, DestinationID) ->
|
|||||||
check_quote_withdrawal(_, DestinationID) ->
|
check_quote_withdrawal(_, DestinationID) ->
|
||||||
{error, {quote, {invalid_destination, DestinationID}}}.
|
{error, {quote, {invalid_destination, DestinationID}}}.
|
||||||
|
|
||||||
marshal_quote_body(Body) ->
|
|
||||||
{genlib:to_int(maps:get(<<"amount">>, Body)), maps:get(<<"currency">>, Body)}.
|
|
||||||
|
|
||||||
%% Marshaling
|
%% Marshaling
|
||||||
|
|
||||||
marshal(withdrawal_params, Params = #{
|
marshal(withdrawal_params, Params = #{
|
||||||
@ -286,34 +452,61 @@ marshal(withdrawal_params, Params = #{
|
|||||||
wallet_id = marshal(id, WalletID),
|
wallet_id = marshal(id, WalletID),
|
||||||
destination_id = marshal(id, DestinationID),
|
destination_id = marshal(id, DestinationID),
|
||||||
body = marshal_body(Body),
|
body = marshal_body(Body),
|
||||||
quote = marshal_quote(Quote),
|
quote = Quote,
|
||||||
external_id = maybe_marshal(id, ExternalID),
|
external_id = maybe_marshal(id, ExternalID),
|
||||||
metadata = maybe_marshal(context, Metadata)
|
metadata = maybe_marshal(context, Metadata)
|
||||||
};
|
};
|
||||||
|
|
||||||
|
marshal(create_quote_params, Params = #{
|
||||||
|
<<"walletID">> := WalletID,
|
||||||
|
<<"currencyFrom">> := CurrencyFrom,
|
||||||
|
<<"currencyTo">> := CurrencyTo,
|
||||||
|
<<"cash">> := Body
|
||||||
|
}) ->
|
||||||
|
ExternalID = maps:get(<<"externalID">>, Params, undefined),
|
||||||
|
DestinationID = maps:get(<<"destinationID">>, Params, undefined),
|
||||||
|
#wthd_QuoteParams{
|
||||||
|
wallet_id = marshal(id, WalletID),
|
||||||
|
body = marshal_body(Body),
|
||||||
|
currency_from = marshal_currency_ref(CurrencyFrom),
|
||||||
|
currency_to = marshal_currency_ref(CurrencyTo),
|
||||||
|
destination_id = maybe_marshal(id, DestinationID),
|
||||||
|
external_id = maybe_marshal(id, ExternalID)
|
||||||
|
};
|
||||||
|
|
||||||
marshal(context, Context) ->
|
marshal(context, Context) ->
|
||||||
ff_codec:marshal(context, Context);
|
wapi_codec:marshal(context, Context);
|
||||||
|
|
||||||
marshal(T, V) ->
|
marshal(T, V) ->
|
||||||
ff_codec:marshal(T, V).
|
wapi_codec:marshal(T, V).
|
||||||
|
|
||||||
maybe_marshal(_, undefined) ->
|
maybe_marshal(_, undefined) ->
|
||||||
undefined;
|
undefined;
|
||||||
maybe_marshal(T, V) ->
|
maybe_marshal(T, V) ->
|
||||||
marshal(T, V).
|
marshal(T, V).
|
||||||
|
|
||||||
|
marshal_event_range(Cursor, Limit) when
|
||||||
|
(is_integer(Cursor) orelse Cursor =:= undefined) andalso
|
||||||
|
(is_integer(Limit) orelse Limit =:= undefined)
|
||||||
|
->
|
||||||
|
#'EventRange'{
|
||||||
|
'after' = Cursor,
|
||||||
|
'limit' = Limit
|
||||||
|
}.
|
||||||
|
|
||||||
marshal_body(Body) ->
|
marshal_body(Body) ->
|
||||||
#'Cash'{
|
#'Cash'{
|
||||||
amount = genlib:to_int(maps:get(<<"amount">>, Body)),
|
amount = genlib:to_int(maps:get(<<"amount">>, Body)),
|
||||||
currency = #'CurrencyRef'{
|
currency = marshal_currency_ref(maps:get(<<"currency">>, Body))
|
||||||
symbolic_code = maps:get(<<"currency">>, Body)
|
|
||||||
}
|
|
||||||
}.
|
}.
|
||||||
|
|
||||||
marshal_quote(undefined) ->
|
marshal_currency_ref(Currency) ->
|
||||||
undefined;
|
#'CurrencyRef'{
|
||||||
marshal_quote(Quote) ->
|
symbolic_code = Currency
|
||||||
ff_withdrawal_codec:marshal(quote, Quote).
|
}.
|
||||||
|
|
||||||
|
unmarshal({list, Type}, List) ->
|
||||||
|
lists:map(fun(V) -> unmarshal(Type, V) end, List);
|
||||||
|
|
||||||
unmarshal(withdrawal, #wthd_WithdrawalState{
|
unmarshal(withdrawal, #wthd_WithdrawalState{
|
||||||
id = ID,
|
id = ID,
|
||||||
@ -336,8 +529,31 @@ unmarshal(withdrawal, #wthd_WithdrawalState{
|
|||||||
<<"metadata">> => UnmarshaledMetadata
|
<<"metadata">> => UnmarshaledMetadata
|
||||||
}, unmarshal_status(Status)));
|
}, unmarshal_status(Status)));
|
||||||
|
|
||||||
|
unmarshal(quote, #wthd_Quote{
|
||||||
|
cash_from = CashFrom,
|
||||||
|
cash_to = CashTo,
|
||||||
|
created_at = CreatedAt,
|
||||||
|
expires_on = ExpiresOn
|
||||||
|
}) ->
|
||||||
|
#{
|
||||||
|
<<"cashFrom">> => unmarshal_body(CashFrom),
|
||||||
|
<<"cashTo">> => unmarshal_body(CashTo),
|
||||||
|
<<"createdAt">> => CreatedAt,
|
||||||
|
<<"expiresOn">> => ExpiresOn
|
||||||
|
};
|
||||||
|
|
||||||
|
unmarshal(event, ?event(EventId, OccuredAt, ?statusChange(Status))) ->
|
||||||
|
genlib_map:compact(#{
|
||||||
|
<<"eventID">> => EventId,
|
||||||
|
<<"occuredAt">> => OccuredAt,
|
||||||
|
<<"changes">> => [maps:merge(
|
||||||
|
#{<<"type">> => <<"WithdrawalStatusChanged">>},
|
||||||
|
unmarshal_status(Status)
|
||||||
|
)]
|
||||||
|
});
|
||||||
|
|
||||||
unmarshal(T, V) ->
|
unmarshal(T, V) ->
|
||||||
ff_codec:unmarshal(T, V).
|
wapi_codec:unmarshal(T, V).
|
||||||
|
|
||||||
maybe_unmarshal(_, undefined) ->
|
maybe_unmarshal(_, undefined) ->
|
||||||
undefined;
|
undefined;
|
||||||
|
@ -1,5 +1,7 @@
|
|||||||
-module(wapi_withdrawal_quote).
|
-module(wapi_withdrawal_quote).
|
||||||
|
|
||||||
|
-include_lib("fistful_proto/include/ff_proto_withdrawal_thrift.hrl").
|
||||||
|
|
||||||
-export([create_token_payload/4]).
|
-export([create_token_payload/4]).
|
||||||
-export([decode_token_payload/1]).
|
-export([decode_token_payload/1]).
|
||||||
|
|
||||||
@ -17,23 +19,26 @@
|
|||||||
-type wallet_id() :: binary().
|
-type wallet_id() :: binary().
|
||||||
-type destination_id() :: binary() | undefined.
|
-type destination_id() :: binary() | undefined.
|
||||||
-type party_id() :: binary().
|
-type party_id() :: binary().
|
||||||
-type quote() :: ff_withdrawal:quote().
|
-type quote() :: ff_proto_withdrawal_thrift:'Quote'().
|
||||||
|
|
||||||
%% API
|
%% API
|
||||||
|
|
||||||
-spec create_token_payload(quote(), wallet_id(), destination_id(), party_id()) ->
|
-spec create_token_payload(quote(), wallet_id(), destination_id(), party_id()) ->
|
||||||
token_payload().
|
token_payload().
|
||||||
create_token_payload(Quote, WalletID, DestinationID, PartyID) ->
|
create_token_payload(Quote, WalletID, DestinationID, PartyID) ->
|
||||||
|
do_create_token_payload(encode_quote(Quote), WalletID, DestinationID, PartyID).
|
||||||
|
|
||||||
|
do_create_token_payload(Quote, WalletID, DestinationID, PartyID) ->
|
||||||
genlib_map:compact(#{
|
genlib_map:compact(#{
|
||||||
<<"version">> => 2,
|
<<"version">> => 2,
|
||||||
<<"walletID">> => WalletID,
|
<<"walletID">> => WalletID,
|
||||||
<<"destinationID">> => DestinationID,
|
<<"destinationID">> => DestinationID,
|
||||||
<<"partyID">> => PartyID,
|
<<"partyID">> => PartyID,
|
||||||
<<"quote">> => encode_quote(Quote)
|
<<"quote">> => Quote
|
||||||
}).
|
}).
|
||||||
|
|
||||||
-spec decode_token_payload(token_payload()) ->
|
-spec decode_token_payload(token_payload()) ->
|
||||||
{ok, quote(), wallet_id(), destination_id(), party_id()}.
|
{ok, {quote(), wallet_id(), destination_id(), party_id()}} | {error, token_expired}.
|
||||||
decode_token_payload(#{<<"version">> := 2} = Payload) ->
|
decode_token_payload(#{<<"version">> := 2} = Payload) ->
|
||||||
#{
|
#{
|
||||||
<<"version">> := 2,
|
<<"version">> := 2,
|
||||||
@ -43,42 +48,9 @@ decode_token_payload(#{<<"version">> := 2} = Payload) ->
|
|||||||
} = Payload,
|
} = Payload,
|
||||||
Quote = decode_quote(EncodedQuote),
|
Quote = decode_quote(EncodedQuote),
|
||||||
DestinationID = maps:get(<<"destinationID">>, Payload, undefined),
|
DestinationID = maps:get(<<"destinationID">>, Payload, undefined),
|
||||||
{ok, Quote, WalletID, DestinationID, PartyID};
|
{ok, {Quote, WalletID, DestinationID, PartyID}};
|
||||||
decode_token_payload(#{<<"version">> := 1} = Payload) ->
|
decode_token_payload(#{<<"version">> := 1} = _Payload) ->
|
||||||
#{
|
{error, token_expired}.
|
||||||
<<"version">> := 1,
|
|
||||||
<<"walletID">> := WalletID,
|
|
||||||
<<"partyID">> := PartyID,
|
|
||||||
<<"cashFrom">> := CashFrom,
|
|
||||||
<<"cashTo">> := CashTo,
|
|
||||||
<<"createdAt">> := CreatedAt,
|
|
||||||
<<"expiresOn">> := ExpiresOn,
|
|
||||||
<<"quoteData">> := LegacyQuote
|
|
||||||
} = Payload,
|
|
||||||
DestinationID = maps:get(<<"destinationID">>, Payload, undefined),
|
|
||||||
#{
|
|
||||||
<<"version">> := 1,
|
|
||||||
<<"quote_data">> := QuoteData,
|
|
||||||
<<"provider_id">> := ProviderID,
|
|
||||||
<<"resource_id">> := ResourceID,
|
|
||||||
<<"timestamp">> := Timestamp,
|
|
||||||
<<"domain_revision">> := DomainRevision,
|
|
||||||
<<"party_revision">> := PartyRevision
|
|
||||||
} = LegacyQuote,
|
|
||||||
TerminalID = maps:get(<<"terminal_id">>, LegacyQuote, undefined),
|
|
||||||
Quote = genlib_map:compact(#{
|
|
||||||
cash_from => decode_legacy_cash(CashFrom),
|
|
||||||
cash_to => decode_legacy_cash(CashTo),
|
|
||||||
created_at => CreatedAt,
|
|
||||||
expires_on => ExpiresOn,
|
|
||||||
quote_data => QuoteData,
|
|
||||||
route => ff_withdrawal_routing:make_route(ProviderID, TerminalID),
|
|
||||||
operation_timestamp => Timestamp,
|
|
||||||
resource_descriptor => decode_legacy_resource_id(ResourceID),
|
|
||||||
domain_revision => DomainRevision,
|
|
||||||
party_revision => PartyRevision
|
|
||||||
}),
|
|
||||||
{ok, Quote, WalletID, DestinationID, PartyID}.
|
|
||||||
|
|
||||||
%% Internals
|
%% Internals
|
||||||
|
|
||||||
@ -86,7 +58,7 @@ decode_token_payload(#{<<"version">> := 1} = Payload) ->
|
|||||||
token_payload().
|
token_payload().
|
||||||
encode_quote(Quote) ->
|
encode_quote(Quote) ->
|
||||||
Type = {struct, struct, {ff_proto_withdrawal_thrift, 'Quote'}},
|
Type = {struct, struct, {ff_proto_withdrawal_thrift, 'Quote'}},
|
||||||
Bin = ff_proto_utils:serialize(Type, ff_withdrawal_codec:marshal(quote, Quote)),
|
Bin = wapi_thrift_utils:serialize(Type, Quote),
|
||||||
base64:encode(Bin).
|
base64:encode(Bin).
|
||||||
|
|
||||||
-spec decode_quote(token_payload()) ->
|
-spec decode_quote(token_payload()) ->
|
||||||
@ -94,15 +66,7 @@ encode_quote(Quote) ->
|
|||||||
decode_quote(Encoded) ->
|
decode_quote(Encoded) ->
|
||||||
Type = {struct, struct, {ff_proto_withdrawal_thrift, 'Quote'}},
|
Type = {struct, struct, {ff_proto_withdrawal_thrift, 'Quote'}},
|
||||||
Bin = base64:decode(Encoded),
|
Bin = base64:decode(Encoded),
|
||||||
Thrift = ff_proto_utils:deserialize(Type, Bin),
|
wapi_thrift_utils:deserialize(Type, Bin).
|
||||||
ff_withdrawal_codec:unmarshal(quote, Thrift).
|
|
||||||
|
|
||||||
decode_legacy_cash(Body) ->
|
|
||||||
{genlib:to_int(maps:get(<<"amount">>, Body)), maps:get(<<"currency">>, Body)}.
|
|
||||||
|
|
||||||
decode_legacy_resource_id(#{<<"bank_card">> := ID}) ->
|
|
||||||
{bank_card, ID}.
|
|
||||||
|
|
||||||
|
|
||||||
-ifdef(TEST).
|
-ifdef(TEST).
|
||||||
-include_lib("eunit/include/eunit.hrl").
|
-include_lib("eunit/include/eunit.hrl").
|
||||||
@ -130,9 +94,10 @@ payload_symmetry_test() ->
|
|||||||
domain_revision => 1,
|
domain_revision => 1,
|
||||||
party_revision => 2
|
party_revision => 2
|
||||||
},
|
},
|
||||||
Payload = create_token_payload(Quote, WalletID, DestinationID, PartyID),
|
ThriftQuote = ff_withdrawal_codec:marshal(quote, Quote),
|
||||||
{ok, Decoded, WalletID, DestinationID, PartyID} = decode_token_payload(Payload),
|
Payload = create_token_payload(ThriftQuote, WalletID, DestinationID, PartyID),
|
||||||
?assertEqual(Quote, Decoded).
|
{ok, {Decoded, WalletID, DestinationID, PartyID}} = decode_token_payload(Payload),
|
||||||
|
?assertEqual(ThriftQuote, Decoded).
|
||||||
|
|
||||||
-spec payload_v2_decoding_test() -> _.
|
-spec payload_v2_decoding_test() -> _.
|
||||||
payload_v2_decoding_test() ->
|
payload_v2_decoding_test() ->
|
||||||
@ -156,6 +121,7 @@ payload_v2_decoding_test() ->
|
|||||||
domain_revision => 1,
|
domain_revision => 1,
|
||||||
party_revision => 2
|
party_revision => 2
|
||||||
},
|
},
|
||||||
|
ExpectedThriftQuote = ff_withdrawal_codec:marshal(quote, ExpectedQuote),
|
||||||
Payload = #{
|
Payload = #{
|
||||||
<<"version">> => 2,
|
<<"version">> => 2,
|
||||||
<<"walletID">> => WalletID,
|
<<"walletID">> => WalletID,
|
||||||
@ -171,7 +137,7 @@ payload_v2_decoding_test() ->
|
|||||||
>>
|
>>
|
||||||
},
|
},
|
||||||
?assertEqual(
|
?assertEqual(
|
||||||
{ok, ExpectedQuote, WalletID, DestinationID, PartyID},
|
{ok, {ExpectedThriftQuote, WalletID, DestinationID, PartyID}},
|
||||||
decode_token_payload(Payload)
|
decode_token_payload(Payload)
|
||||||
).
|
).
|
||||||
|
|
||||||
@ -180,18 +146,6 @@ payload_v1_decoding_test() ->
|
|||||||
PartyID = <<"party">>,
|
PartyID = <<"party">>,
|
||||||
WalletID = <<"wallet">>,
|
WalletID = <<"wallet">>,
|
||||||
DestinationID = <<"destination">>,
|
DestinationID = <<"destination">>,
|
||||||
ExpectedQuote = #{
|
|
||||||
cash_from => {1000000, <<"RUB">>},
|
|
||||||
cash_to => {1, <<"USD">>},
|
|
||||||
created_at => <<"1970-01-01T00:00:00.123Z">>,
|
|
||||||
expires_on => <<"1970-01-01T00:00:00.321Z">>,
|
|
||||||
quote_data => 6,
|
|
||||||
route => ff_withdrawal_routing:make_route(1000, 2),
|
|
||||||
operation_timestamp => 234,
|
|
||||||
resource_descriptor => {bank_card, 5},
|
|
||||||
domain_revision => 1,
|
|
||||||
party_revision => 2
|
|
||||||
},
|
|
||||||
Payload = #{
|
Payload = #{
|
||||||
<<"version">> => 1,
|
<<"version">> => 1,
|
||||||
<<"walletID">> => WalletID,
|
<<"walletID">> => WalletID,
|
||||||
@ -213,7 +167,7 @@ payload_v1_decoding_test() ->
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
?assertEqual(
|
?assertEqual(
|
||||||
{ok, ExpectedQuote, WalletID, DestinationID, PartyID},
|
{error, token_expired},
|
||||||
decode_token_payload(Payload)
|
decode_token_payload(Payload)
|
||||||
).
|
).
|
||||||
|
|
||||||
|
@ -4,10 +4,17 @@
|
|||||||
-include_lib("wapi_wallet_dummy_data.hrl").
|
-include_lib("wapi_wallet_dummy_data.hrl").
|
||||||
-include_lib("damsel/include/dmsl_domain_config_thrift.hrl").
|
-include_lib("damsel/include/dmsl_domain_config_thrift.hrl").
|
||||||
|
|
||||||
|
-export([cfg/2]).
|
||||||
|
-export([cfg/3]).
|
||||||
|
-export([makeup_cfg/2]).
|
||||||
|
-export([woody_ctx/0]).
|
||||||
|
-export([get_woody_ctx/1]).
|
||||||
|
-export([test_case_name/1]).
|
||||||
|
-export([get_test_case_name/1]).
|
||||||
|
|
||||||
-export([init_suite/2]).
|
-export([init_suite/2]).
|
||||||
-export([start_app/1]).
|
-export([start_app/1]).
|
||||||
-export([start_app/2]).
|
-export([start_app/2]).
|
||||||
-export([start_wapi/1]).
|
|
||||||
-export([issue_token/4]).
|
-export([issue_token/4]).
|
||||||
-export([get_context/1]).
|
-export([get_context/1]).
|
||||||
-export([get_keysource/2]).
|
-export([get_keysource/2]).
|
||||||
@ -25,58 +32,120 @@
|
|||||||
-define(DOMAIN, <<"wallet-api">>).
|
-define(DOMAIN, <<"wallet-api">>).
|
||||||
|
|
||||||
%%
|
%%
|
||||||
-type config() :: [{atom(), any()}].
|
-type config() :: [{atom(), any()}].
|
||||||
|
-type test_case_name() :: atom().
|
||||||
-type app_name() :: atom().
|
-type app_name() :: atom().
|
||||||
|
-type app_env() :: [{atom(), term()}].
|
||||||
|
% -type app_with_env() :: {app_name(), app_env()}.
|
||||||
|
% -type startup_ctx() :: #{atom() => _}.
|
||||||
|
|
||||||
-define(SIGNEE, wapi).
|
-define(SIGNEE, wapi).
|
||||||
|
|
||||||
|
-spec cfg(atom(), config()) -> term().
|
||||||
|
|
||||||
|
cfg(Key, Config) ->
|
||||||
|
case lists:keyfind(Key, 1, Config) of
|
||||||
|
{Key, V} -> V;
|
||||||
|
_ -> error({'ct config entry missing', Key})
|
||||||
|
end.
|
||||||
|
|
||||||
|
-spec cfg(atom(), _, config()) -> config().
|
||||||
|
|
||||||
|
cfg(Key, Value, Config) ->
|
||||||
|
lists:keystore(Key, 1, Config, {Key, Value}).
|
||||||
|
|
||||||
|
-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).
|
||||||
|
|
||||||
|
-spec woody_ctx() -> config_mut_fun().
|
||||||
|
|
||||||
|
woody_ctx() ->
|
||||||
|
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))).
|
||||||
|
|
||||||
|
construct_rpc_id(TestCaseName) ->
|
||||||
|
woody_context:new_rpc_id(
|
||||||
|
<<"undefined">>,
|
||||||
|
list_to_binary(lists:sublist(atom_to_list(TestCaseName), 32)),
|
||||||
|
woody_context:new_req_id()
|
||||||
|
).
|
||||||
|
|
||||||
|
-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.
|
||||||
|
|
||||||
|
-spec get_test_case_name(config()) -> test_case_name().
|
||||||
|
|
||||||
|
get_test_case_name(C) ->
|
||||||
|
cfg('$test_case_name', C).
|
||||||
|
|
||||||
|
%
|
||||||
|
|
||||||
-spec init_suite(module(), config()) ->
|
-spec init_suite(module(), config()) ->
|
||||||
config().
|
config().
|
||||||
init_suite(Module, Config) ->
|
init_suite(Module, Config) ->
|
||||||
SupPid = start_mocked_service_sup(Module),
|
SupPid = start_mocked_service_sup(Module),
|
||||||
Apps1 =
|
Apps1 =
|
||||||
start_app(scoper) ++
|
start_app(scoper) ++
|
||||||
start_app(woody),
|
start_app(woody) ++
|
||||||
ServiceURLs = mock_services_([
|
start_app({wapi, Config}),
|
||||||
{
|
[{apps, lists:reverse(Apps1)}, {suite_test_sup, SupPid} | Config].
|
||||||
'Repository',
|
|
||||||
{dmsl_domain_config_thrift, 'Repository'},
|
|
||||||
fun('Checkout', _) -> {ok, ?SNAPSHOT} end
|
|
||||||
}
|
|
||||||
], SupPid),
|
|
||||||
Apps2 =
|
|
||||||
start_app(dmt_client, [{max_cache_size, #{}}, {service_urls, ServiceURLs}, {cache_update_interval, 50000}]) ++
|
|
||||||
start_wapi(Config),
|
|
||||||
[{apps, lists:reverse(Apps2 ++ Apps1)}, {suite_test_sup, SupPid} | Config].
|
|
||||||
|
|
||||||
-spec start_app(app_name()) ->
|
-spec start_app(app_name()) ->
|
||||||
[app_name()].
|
[app_name()].
|
||||||
|
|
||||||
start_app(scoper = AppName) ->
|
start_app(scoper = AppName) ->
|
||||||
start_app(AppName, []);
|
start_app_with(AppName, [
|
||||||
|
{storage, scoper_storage_logger}
|
||||||
|
]);
|
||||||
|
|
||||||
start_app(woody = AppName) ->
|
start_app(woody = AppName) ->
|
||||||
start_app(AppName, [
|
start_app_with(AppName, [
|
||||||
{acceptors_pool_size, 4}
|
{acceptors_pool_size, 4}
|
||||||
]);
|
]);
|
||||||
|
|
||||||
start_app(wapi_woody_client = AppName) ->
|
start_app({wapi = AppName, Config}) ->
|
||||||
start_app(AppName, [
|
JwkPath = get_keysource("jwk.json", Config),
|
||||||
{service_urls, #{
|
start_app_with(AppName, [
|
||||||
cds_storage => "http://cds:8022/v2/storage",
|
{ip, "::"},
|
||||||
identdoc_storage => "http://cds:8022/v1/identity_document_storage",
|
{port, 8080},
|
||||||
fistful_stat => "http://fistful-magista:8022/stat"
|
{realm, <<"external">>},
|
||||||
|
{public_endpoint, <<"localhost:8080">>},
|
||||||
|
{access_conf, #{
|
||||||
|
jwt => #{
|
||||||
|
keyset => #{
|
||||||
|
wapi => {pem_file, get_keysource("private.pem", Config)}
|
||||||
|
}
|
||||||
|
}
|
||||||
}},
|
}},
|
||||||
{service_retries, #{
|
{signee, wapi},
|
||||||
fistful_stat => #{
|
{lechiffre_opts, #{
|
||||||
'GetWallets' => {linear, 3, 1000},
|
encryption_key_path => JwkPath,
|
||||||
'_' => finish
|
decryption_key_paths => [JwkPath]
|
||||||
|
}},
|
||||||
|
{swagger_handler_opts, #{
|
||||||
|
validation_opts => #{
|
||||||
|
custom_validator => wapi_swagger_validator
|
||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
]);
|
]);
|
||||||
|
|
||||||
start_app(AppName) ->
|
start_app(AppName) ->
|
||||||
genlib_app:start_application(AppName).
|
[genlib_app:start_application(AppName)].
|
||||||
|
|
||||||
-spec start_app(app_name(), list()) ->
|
-spec start_app(app_name(), list()) ->
|
||||||
[app_name()].
|
[app_name()].
|
||||||
@ -84,23 +153,25 @@ start_app(AppName) ->
|
|||||||
start_app(AppName, Env) ->
|
start_app(AppName, Env) ->
|
||||||
genlib_app:start_application_with(AppName, Env).
|
genlib_app:start_application_with(AppName, Env).
|
||||||
|
|
||||||
-spec start_wapi(config()) ->
|
-spec start_app_with(app_name(), app_env()) -> [app_name()].
|
||||||
[app_name()].
|
|
||||||
start_wapi(Config) ->
|
start_app_with(AppName, Env) ->
|
||||||
start_app(wapi, [
|
_ = application:load(AppName),
|
||||||
{ip, ?WAPI_IP},
|
_ = set_app_env(AppName, Env),
|
||||||
{port, ?WAPI_PORT},
|
case application:ensure_all_started(AppName) of
|
||||||
{realm, <<"external">>},
|
{ok, Apps} ->
|
||||||
{public_endpoint, <<"localhost:8080">>},
|
Apps;
|
||||||
{access_conf, #{
|
{error, Reason} ->
|
||||||
jwt => #{
|
exit({start_app_failed, AppName, Reason})
|
||||||
keyset => #{
|
end.
|
||||||
wapi => {pem_file, get_keysource("keys/local/private.pem", Config)}
|
|
||||||
}
|
set_app_env(AppName, Env) ->
|
||||||
}
|
lists:foreach(
|
||||||
}},
|
fun ({K, V}) ->
|
||||||
{signee, ?SIGNEE}
|
ok = application:set_env(AppName, K, V)
|
||||||
]).
|
end,
|
||||||
|
Env
|
||||||
|
).
|
||||||
|
|
||||||
-spec get_keysource(_, config()) ->
|
-spec get_keysource(_, config()) ->
|
||||||
_.
|
_.
|
||||||
|
@ -51,14 +51,14 @@ init([]) ->
|
|||||||
[test_case_name()].
|
[test_case_name()].
|
||||||
all() ->
|
all() ->
|
||||||
[
|
[
|
||||||
{group, default}
|
{group, base}
|
||||||
].
|
].
|
||||||
|
|
||||||
-spec groups() ->
|
-spec groups() ->
|
||||||
[{group_name(), list(), [test_case_name()]}].
|
[{group_name(), list(), [test_case_name()]}].
|
||||||
groups() ->
|
groups() ->
|
||||||
[
|
[
|
||||||
{default, [], [
|
{base, [], [
|
||||||
bank_card_resource_test,
|
bank_card_resource_test,
|
||||||
bitcoin_resource_test,
|
bitcoin_resource_test,
|
||||||
litecoin_resource_test,
|
litecoin_resource_test,
|
||||||
@ -73,38 +73,27 @@ groups() ->
|
|||||||
%%
|
%%
|
||||||
%% starting/stopping
|
%% starting/stopping
|
||||||
%%
|
%%
|
||||||
-spec init_per_suite(config()) ->
|
-spec init_per_suite(config()) -> config().
|
||||||
config().
|
|
||||||
init_per_suite(Config0) ->
|
init_per_suite(C) ->
|
||||||
%% TODO remove this after cut off wapi
|
wapi_ct_helper:init_suite(?MODULE, C).
|
||||||
ok = application:set_env(wapi, transport, thrift),
|
|
||||||
ct_helper:makeup_cfg([
|
-spec end_per_suite(config()) -> _.
|
||||||
ct_helper:test_case_name(init),
|
|
||||||
ct_payment_system:setup(#{
|
|
||||||
optional_apps => [
|
|
||||||
bender_client,
|
|
||||||
wapi_woody_client,
|
|
||||||
wapi
|
|
||||||
]
|
|
||||||
})
|
|
||||||
], Config0).
|
|
||||||
|
|
||||||
-spec end_per_suite(config()) ->
|
|
||||||
_.
|
|
||||||
end_per_suite(C) ->
|
end_per_suite(C) ->
|
||||||
%% TODO remove this after cut off wapi
|
_ = wapi_ct_helper:stop_mocked_service_sup(?config(suite_test_sup, C)),
|
||||||
ok = application:unset_env(wapi, transport),
|
_ = [application:stop(App) || App <- ?config(apps, C)],
|
||||||
ok = ct_payment_system:shutdown(C).
|
ok.
|
||||||
|
|
||||||
-spec init_per_group(group_name(), config()) ->
|
-spec init_per_group(group_name(), config()) ->
|
||||||
config().
|
config().
|
||||||
init_per_group(default = Group, Config) ->
|
init_per_group(Group, Config) when Group =:= base ->
|
||||||
ok = ff_context:save(ff_context:create(#{
|
ok = wapi_context:save(wapi_context:create(#{
|
||||||
party_client => party_client:create_client(),
|
party_client => party_client:create_client(),
|
||||||
woody_context => woody_context:new(<<"init_per_group/", (atom_to_binary(Group, utf8))/binary>>)
|
woody_context => woody_context:new(<<"init_per_group/", (atom_to_binary(Group, utf8))/binary>>)
|
||||||
})),
|
})),
|
||||||
Party = create_party(Config),
|
Party = genlib:bsuuid(),
|
||||||
{ok, Token} = wapi_ct_helper:issue_token(Party, [{[party], write}], {deadline, 10}, ?DOMAIN),
|
{ok, Token} = wapi_ct_helper:issue_token(Party, [{[party], write}], unlimited, ?DOMAIN),
|
||||||
Config1 = [{party, Party} | Config],
|
Config1 = [{party, Party} | Config],
|
||||||
[{context, wapi_ct_helper:get_context(Token)} | Config1];
|
[{context, wapi_ct_helper:get_context(Token)} | Config1];
|
||||||
init_per_group(_, Config) ->
|
init_per_group(_, Config) ->
|
||||||
@ -118,14 +107,14 @@ end_per_group(_Group, _C) ->
|
|||||||
-spec init_per_testcase(test_case_name(), config()) ->
|
-spec init_per_testcase(test_case_name(), config()) ->
|
||||||
config().
|
config().
|
||||||
init_per_testcase(Name, C) ->
|
init_per_testcase(Name, C) ->
|
||||||
C1 = ct_helper:makeup_cfg([ct_helper:test_case_name(Name), ct_helper:woody_ctx()], C),
|
C1 = wapi_ct_helper:makeup_cfg([wapi_ct_helper:test_case_name(Name), wapi_ct_helper:woody_ctx()], C),
|
||||||
ok = ct_helper:set_context(C1),
|
ok = wapi_context:save(C1),
|
||||||
[{test_sup, wapi_ct_helper:start_mocked_service_sup(?MODULE)} | C1].
|
[{test_sup, wapi_ct_helper:start_mocked_service_sup(?MODULE)} | C1].
|
||||||
|
|
||||||
-spec end_per_testcase(test_case_name(), config()) ->
|
-spec end_per_testcase(test_case_name(), config()) ->
|
||||||
config().
|
config().
|
||||||
end_per_testcase(_Name, C) ->
|
end_per_testcase(_Name, C) ->
|
||||||
ok = ct_helper:unset_context(),
|
ok = wapi_context:cleanup(),
|
||||||
wapi_ct_helper:stop_mocked_service_sup(?config(test_sup, C)),
|
wapi_ct_helper:stop_mocked_service_sup(?config(test_sup, C)),
|
||||||
ok.
|
ok.
|
||||||
|
|
||||||
@ -205,12 +194,18 @@ zcash_resource_test(C) ->
|
|||||||
%%
|
%%
|
||||||
|
|
||||||
do_destination_lifecycle(ResourceType, C) ->
|
do_destination_lifecycle(ResourceType, C) ->
|
||||||
PartyID = ?config(party, C),
|
PartyID = wapi_ct_helper:cfg(party, C),
|
||||||
Identity = generate_identity(PartyID),
|
Identity = generate_identity(PartyID),
|
||||||
Resource = generate_resource(ResourceType),
|
Resource = generate_resource(ResourceType),
|
||||||
Context = generate_context(PartyID),
|
Context = generate_context(PartyID),
|
||||||
Destination = generate_destination(Identity#idnt_IdentityState.id, Resource, Context),
|
Destination = generate_destination(Identity#idnt_IdentityState.id, Resource, Context),
|
||||||
wapi_ct_helper:mock_services([
|
wapi_ct_helper:mock_services([
|
||||||
|
{bender_thrift,
|
||||||
|
fun
|
||||||
|
('GenerateID', _) -> {ok, ?GENERATE_ID_RESULT};
|
||||||
|
('GetInternalID', _) -> {ok, ?GET_INTERNAL_ID_RESULT}
|
||||||
|
end
|
||||||
|
},
|
||||||
{fistful_identity, fun('GetContext', _) -> {ok, ?DEFAULT_CONTEXT(PartyID)} end},
|
{fistful_identity, fun('GetContext', _) -> {ok, ?DEFAULT_CONTEXT(PartyID)} end},
|
||||||
{fistful_destination,
|
{fistful_destination,
|
||||||
fun
|
fun
|
||||||
@ -224,7 +219,7 @@ do_destination_lifecycle(ResourceType, C) ->
|
|||||||
#{
|
#{
|
||||||
body => build_destination_spec(Destination)
|
body => build_destination_spec(Destination)
|
||||||
},
|
},
|
||||||
ct_helper:cfg(context, C)
|
wapi_ct_helper:cfg(context, C)
|
||||||
),
|
),
|
||||||
{ok, GetResult} = call_api(
|
{ok, GetResult} = call_api(
|
||||||
fun swag_client_wallet_withdrawals_api:get_destination/3,
|
fun swag_client_wallet_withdrawals_api:get_destination/3,
|
||||||
@ -233,7 +228,7 @@ do_destination_lifecycle(ResourceType, C) ->
|
|||||||
<<"destinationID">> => ?STRING
|
<<"destinationID">> => ?STRING
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
ct_helper:cfg(context, C)
|
wapi_ct_helper:cfg(context, C)
|
||||||
),
|
),
|
||||||
?assertEqual(CreateResult, GetResult),
|
?assertEqual(CreateResult, GetResult),
|
||||||
{ok, GetByIDResult} = call_api(
|
{ok, GetByIDResult} = call_api(
|
||||||
@ -243,7 +238,7 @@ do_destination_lifecycle(ResourceType, C) ->
|
|||||||
<<"externalID">> => Destination#dst_DestinationState.external_id
|
<<"externalID">> => Destination#dst_DestinationState.external_id
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
ct_helper:cfg(context, C)
|
wapi_ct_helper:cfg(context, C)
|
||||||
),
|
),
|
||||||
?assertEqual(GetResult, GetByIDResult),
|
?assertEqual(GetResult, GetByIDResult),
|
||||||
?assertEqual(Destination#dst_DestinationState.id, maps:get(<<"id">>, CreateResult)),
|
?assertEqual(Destination#dst_DestinationState.id, maps:get(<<"id">>, CreateResult)),
|
||||||
@ -266,11 +261,6 @@ call_api(F, Params, Context) ->
|
|||||||
Response = F(Url, PreparedParams, Opts),
|
Response = F(Url, PreparedParams, Opts),
|
||||||
wapi_client_lib:handle_response(Response).
|
wapi_client_lib:handle_response(Response).
|
||||||
|
|
||||||
create_party(_C) ->
|
|
||||||
ID = genlib:bsuuid(),
|
|
||||||
_ = ff_party:create(ID),
|
|
||||||
ID.
|
|
||||||
|
|
||||||
build_destination_spec(D) ->
|
build_destination_spec(D) ->
|
||||||
#{
|
#{
|
||||||
<<"name">> => D#dst_DestinationState.name,
|
<<"name">> => D#dst_DestinationState.name,
|
||||||
|
@ -0,0 +1,7 @@
|
|||||||
|
{
|
||||||
|
"use": "enc",
|
||||||
|
"kty": "oct",
|
||||||
|
"kid": "1",
|
||||||
|
"alg": "dir",
|
||||||
|
"k": "M3VKOExvQVdhWUtXekduVGt1eDdrUmtwTTNBSko1a2M"
|
||||||
|
}
|
@ -0,0 +1,9 @@
|
|||||||
|
-----BEGIN RSA PRIVATE KEY-----
|
||||||
|
MIIBOwIBAAJBAK9fx7qOJT7Aoseu7KKgaLagBh3wvDzg7F/ZMtGbPFikJnnvRWvF
|
||||||
|
B5oEGbMPblvtF0/fjqfu+eqjP3Z1tUSn7TkCAwEAAQJABUY5KIgr4JZEjwLYxQ9T
|
||||||
|
9uIbLP1Xe/E7yqoqmBk2GGhSrPY0OeRkYnUVLcP96UPQhF63iuG8VF6uZ7oAPsq+
|
||||||
|
gQIhANZy3jSCzPjXYHRU1kRqQzpt2S+OqoEiqQ6YG1HrC/VxAiEA0Vq6JlQK2tOX
|
||||||
|
37SS00dK0Qog4Qi8dN73GliFQNP18EkCIQC4epSA48zkfJMzQBAbRraSuxDNApPX
|
||||||
|
BzQbo+pMrEDbYQIgY4AncQgIkLB4Qk5kah48JNYXglzQlQtTjiX8Ty9ueGECIQCM
|
||||||
|
GD3UbQKiA0gf5plBA24I4wFVKxxa4wXbW/7SfP6XmQ==
|
||||||
|
-----END RSA PRIVATE KEY-----
|
@ -75,37 +75,26 @@ groups() ->
|
|||||||
%%
|
%%
|
||||||
%% starting/stopping
|
%% starting/stopping
|
||||||
%%
|
%%
|
||||||
-spec init_per_suite(config()) ->
|
-spec init_per_suite(config()) -> config().
|
||||||
config().
|
|
||||||
init_per_suite(Config) ->
|
init_per_suite(C) ->
|
||||||
%% TODO remove this after cut off wapi
|
wapi_ct_helper:init_suite(?MODULE, C).
|
||||||
ok = application:set_env(wapi, transport, thrift),
|
|
||||||
ct_helper:makeup_cfg([
|
-spec end_per_suite(config()) -> _.
|
||||||
ct_helper:test_case_name(init),
|
|
||||||
ct_payment_system:setup(#{
|
|
||||||
optional_apps => [
|
|
||||||
bender_client,
|
|
||||||
wapi,
|
|
||||||
wapi_woody_client
|
|
||||||
]
|
|
||||||
})
|
|
||||||
], Config).
|
|
||||||
|
|
||||||
-spec end_per_suite(config()) ->
|
|
||||||
_.
|
|
||||||
end_per_suite(C) ->
|
end_per_suite(C) ->
|
||||||
%% TODO remove this after cut off wapi
|
_ = wapi_ct_helper:stop_mocked_service_sup(?config(suite_test_sup, C)),
|
||||||
ok = application:unset_env(wapi, transport),
|
_ = [application:stop(App) || App <- ?config(apps, C)],
|
||||||
ok = ct_payment_system:shutdown(C).
|
ok.
|
||||||
|
|
||||||
-spec init_per_group(group_name(), config()) ->
|
-spec init_per_group(group_name(), config()) ->
|
||||||
config().
|
config().
|
||||||
init_per_group(Group, Config) when Group =:= base ->
|
init_per_group(Group, Config) when Group =:= base ->
|
||||||
ok = ff_context:save(ff_context:create(#{
|
ok = wapi_context:save(wapi_context:create(#{
|
||||||
party_client => party_client:create_client(),
|
party_client => party_client:create_client(),
|
||||||
woody_context => woody_context:new(<<"init_per_group/", (atom_to_binary(Group, utf8))/binary>>)
|
woody_context => woody_context:new(<<"init_per_group/", (atom_to_binary(Group, utf8))/binary>>)
|
||||||
})),
|
})),
|
||||||
Party = create_party(Config),
|
Party = genlib:bsuuid(),
|
||||||
{ok, Token} = wapi_ct_helper:issue_token(Party, [{[party], write}], unlimited, ?DOMAIN),
|
{ok, Token} = wapi_ct_helper:issue_token(Party, [{[party], write}], unlimited, ?DOMAIN),
|
||||||
Config1 = [{party, Party} | Config],
|
Config1 = [{party, Party} | Config],
|
||||||
[{context, wapi_ct_helper:get_context(Token)} | Config1];
|
[{context, wapi_ct_helper:get_context(Token)} | Config1];
|
||||||
@ -120,14 +109,14 @@ end_per_group(_Group, _C) ->
|
|||||||
-spec init_per_testcase(test_case_name(), config()) ->
|
-spec init_per_testcase(test_case_name(), config()) ->
|
||||||
config().
|
config().
|
||||||
init_per_testcase(Name, C) ->
|
init_per_testcase(Name, C) ->
|
||||||
C1 = ct_helper:makeup_cfg([ct_helper:test_case_name(Name), ct_helper:woody_ctx()], C),
|
C1 = wapi_ct_helper:makeup_cfg([wapi_ct_helper:test_case_name(Name), wapi_ct_helper:woody_ctx()], C),
|
||||||
ok = ct_helper:set_context(C1),
|
ok = wapi_context:save(C1),
|
||||||
[{test_sup, wapi_ct_helper:start_mocked_service_sup(?MODULE)} | C1].
|
[{test_sup, wapi_ct_helper:start_mocked_service_sup(?MODULE)} | C1].
|
||||||
|
|
||||||
-spec end_per_testcase(test_case_name(), config()) ->
|
-spec end_per_testcase(test_case_name(), config()) ->
|
||||||
config().
|
config().
|
||||||
end_per_testcase(_Name, C) ->
|
end_per_testcase(_Name, C) ->
|
||||||
ok = ct_helper:unset_context(),
|
ok = wapi_context:cleanup(),
|
||||||
wapi_ct_helper:stop_mocked_service_sup(?config(test_sup, C)),
|
wapi_ct_helper:stop_mocked_service_sup(?config(test_sup, C)),
|
||||||
ok.
|
ok.
|
||||||
|
|
||||||
@ -137,6 +126,7 @@ end_per_testcase(_Name, C) ->
|
|||||||
create_identity(C) ->
|
create_identity(C) ->
|
||||||
PartyID = ?config(party, C),
|
PartyID = ?config(party, C),
|
||||||
wapi_ct_helper:mock_services([
|
wapi_ct_helper:mock_services([
|
||||||
|
{bender_thrift, fun('GenerateID', _) -> {ok, ?GENERATE_ID_RESULT} end},
|
||||||
{fistful_identity, fun('Create', _) -> {ok, ?IDENTITY(PartyID)} end}
|
{fistful_identity, fun('Create', _) -> {ok, ?IDENTITY(PartyID)} end}
|
||||||
], C),
|
], C),
|
||||||
{ok, _} = call_api(
|
{ok, _} = call_api(
|
||||||
@ -151,7 +141,7 @@ create_identity(C) ->
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
ct_helper:cfg(context, C)
|
wapi_ct_helper:cfg(context, C)
|
||||||
).
|
).
|
||||||
|
|
||||||
-spec get_identity(config()) ->
|
-spec get_identity(config()) ->
|
||||||
@ -168,7 +158,7 @@ get_identity(C) ->
|
|||||||
<<"identityID">> => ?STRING
|
<<"identityID">> => ?STRING
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
ct_helper:cfg(context, C)
|
wapi_ct_helper:cfg(context, C)
|
||||||
).
|
).
|
||||||
|
|
||||||
-spec create_identity_challenge(config()) ->
|
-spec create_identity_challenge(config()) ->
|
||||||
@ -180,6 +170,7 @@ create_identity_challenge(C) ->
|
|||||||
('GetContext', _) -> {ok, ?DEFAULT_CONTEXT(PartyID)};
|
('GetContext', _) -> {ok, ?DEFAULT_CONTEXT(PartyID)};
|
||||||
('StartChallenge', _) -> {ok, ?IDENTITY_CHALLENGE(?IDENTITY_CHALLENGE_STATUS_COMPLETED)}
|
('StartChallenge', _) -> {ok, ?IDENTITY_CHALLENGE(?IDENTITY_CHALLENGE_STATUS_COMPLETED)}
|
||||||
end},
|
end},
|
||||||
|
{bender_thrift, fun('GenerateID', _) -> {ok, ?GENERATE_ID_RESULT} end},
|
||||||
{identdoc_storage, fun('Get', _) -> {ok, ?IDENT_DOC} end}
|
{identdoc_storage, fun('Get', _) -> {ok, ?IDENT_DOC} end}
|
||||||
], C),
|
], C),
|
||||||
{ok, _} = call_api(
|
{ok, _} = call_api(
|
||||||
@ -206,7 +197,7 @@ create_identity_challenge(C) ->
|
|||||||
]
|
]
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
ct_helper:cfg(context, C)
|
wapi_ct_helper:cfg(context, C)
|
||||||
).
|
).
|
||||||
|
|
||||||
-spec get_identity_challenge(config()) ->
|
-spec get_identity_challenge(config()) ->
|
||||||
@ -228,7 +219,7 @@ get_identity_challenge(C) ->
|
|||||||
<<"challengeID">> => ?STRING
|
<<"challengeID">> => ?STRING
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
ct_helper:cfg(context, C)
|
wapi_ct_helper:cfg(context, C)
|
||||||
).
|
).
|
||||||
|
|
||||||
-spec list_identity_challenges(config()) ->
|
-spec list_identity_challenges(config()) ->
|
||||||
@ -252,7 +243,7 @@ list_identity_challenges(C) ->
|
|||||||
<<"status">> => <<"Completed">>
|
<<"status">> => <<"Completed">>
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
ct_helper:cfg(context, C)
|
wapi_ct_helper:cfg(context, C)
|
||||||
).
|
).
|
||||||
|
|
||||||
-spec get_identity_challenge_event(config()) ->
|
-spec get_identity_challenge_event(config()) ->
|
||||||
@ -274,7 +265,7 @@ get_identity_challenge_event(C) ->
|
|||||||
<<"eventID">> => ?INTEGER
|
<<"eventID">> => ?INTEGER
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
ct_helper:cfg(context, C)
|
wapi_ct_helper:cfg(context, C)
|
||||||
).
|
).
|
||||||
|
|
||||||
-spec poll_identity_challenge_events(config()) ->
|
-spec poll_identity_challenge_events(config()) ->
|
||||||
@ -299,7 +290,7 @@ poll_identity_challenge_events(C) ->
|
|||||||
<<"eventCursor">> => ?INTEGER
|
<<"eventCursor">> => ?INTEGER
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
ct_helper:cfg(context, C)
|
wapi_ct_helper:cfg(context, C)
|
||||||
).
|
).
|
||||||
|
|
||||||
%%
|
%%
|
||||||
@ -310,8 +301,3 @@ call_api(F, Params, Context) ->
|
|||||||
{Url, PreparedParams, Opts} = wapi_client_lib:make_request(Context, Params),
|
{Url, PreparedParams, Opts} = wapi_client_lib:make_request(Context, Params),
|
||||||
Response = F(Url, PreparedParams, Opts),
|
Response = F(Url, PreparedParams, Opts),
|
||||||
wapi_client_lib:handle_response(Response).
|
wapi_client_lib:handle_response(Response).
|
||||||
|
|
||||||
create_party(_C) ->
|
|
||||||
ID = genlib:bsuuid(),
|
|
||||||
_ = ff_party:create(ID),
|
|
||||||
ID.
|
|
||||||
|
7
apps/wapi/test/wapi_identity_tests_SUITE_data/jwk.json
Normal file
7
apps/wapi/test/wapi_identity_tests_SUITE_data/jwk.json
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
{
|
||||||
|
"use": "enc",
|
||||||
|
"kty": "oct",
|
||||||
|
"kid": "1",
|
||||||
|
"alg": "dir",
|
||||||
|
"k": "M3VKOExvQVdhWUtXekduVGt1eDdrUmtwTTNBSko1a2M"
|
||||||
|
}
|
@ -0,0 +1,9 @@
|
|||||||
|
-----BEGIN RSA PRIVATE KEY-----
|
||||||
|
MIIBOwIBAAJBAK9fx7qOJT7Aoseu7KKgaLagBh3wvDzg7F/ZMtGbPFikJnnvRWvF
|
||||||
|
B5oEGbMPblvtF0/fjqfu+eqjP3Z1tUSn7TkCAwEAAQJABUY5KIgr4JZEjwLYxQ9T
|
||||||
|
9uIbLP1Xe/E7yqoqmBk2GGhSrPY0OeRkYnUVLcP96UPQhF63iuG8VF6uZ7oAPsq+
|
||||||
|
gQIhANZy3jSCzPjXYHRU1kRqQzpt2S+OqoEiqQ6YG1HrC/VxAiEA0Vq6JlQK2tOX
|
||||||
|
37SS00dK0Qog4Qi8dN73GliFQNP18EkCIQC4epSA48zkfJMzQBAbRraSuxDNApPX
|
||||||
|
BzQbo+pMrEDbYQIgY4AncQgIkLB4Qk5kah48JNYXglzQlQtTjiX8Ty9ueGECIQCM
|
||||||
|
GD3UbQKiA0gf5plBA24I4wFVKxxa4wXbW/7SfP6XmQ==
|
||||||
|
-----END RSA PRIVATE KEY-----
|
@ -70,37 +70,26 @@ groups() ->
|
|||||||
%%
|
%%
|
||||||
%% starting/stopping
|
%% starting/stopping
|
||||||
%%
|
%%
|
||||||
-spec init_per_suite(config()) ->
|
-spec init_per_suite(config()) -> config().
|
||||||
config().
|
|
||||||
init_per_suite(Config) ->
|
init_per_suite(C) ->
|
||||||
%% TODO remove this after cut off wapi
|
wapi_ct_helper:init_suite(?MODULE, C).
|
||||||
ok = application:set_env(wapi, transport, thrift),
|
|
||||||
ct_helper:makeup_cfg([
|
-spec end_per_suite(config()) -> _.
|
||||||
ct_helper:test_case_name(init),
|
|
||||||
ct_payment_system:setup(#{
|
|
||||||
optional_apps => [
|
|
||||||
bender_client,
|
|
||||||
wapi,
|
|
||||||
wapi_woody_client
|
|
||||||
]
|
|
||||||
})
|
|
||||||
], Config).
|
|
||||||
|
|
||||||
-spec end_per_suite(config()) ->
|
|
||||||
_.
|
|
||||||
end_per_suite(C) ->
|
end_per_suite(C) ->
|
||||||
%% TODO remove this after cut off wapi
|
_ = wapi_ct_helper:stop_mocked_service_sup(?config(suite_test_sup, C)),
|
||||||
ok = application:unset_env(wapi, transport),
|
_ = [application:stop(App) || App <- ?config(apps, C)],
|
||||||
ok = ct_payment_system:shutdown(C).
|
ok.
|
||||||
|
|
||||||
-spec init_per_group(group_name(), config()) ->
|
-spec init_per_group(group_name(), config()) ->
|
||||||
config().
|
config().
|
||||||
init_per_group(Group, Config) when Group =:= base ->
|
init_per_group(Group, Config) when Group =:= base ->
|
||||||
ok = ff_context:save(ff_context:create(#{
|
ok = wapi_context:save(wapi_context:create(#{
|
||||||
party_client => party_client:create_client(),
|
party_client => party_client:create_client(),
|
||||||
woody_context => woody_context:new(<<"init_per_group/", (atom_to_binary(Group, utf8))/binary>>)
|
woody_context => woody_context:new(<<"init_per_group/", (atom_to_binary(Group, utf8))/binary>>)
|
||||||
})),
|
})),
|
||||||
Party = create_party(Config),
|
Party = genlib:bsuuid(),
|
||||||
{ok, Token} = wapi_ct_helper:issue_token(Party, [{[party], write}], unlimited, ?DOMAIN),
|
{ok, Token} = wapi_ct_helper:issue_token(Party, [{[party], write}], unlimited, ?DOMAIN),
|
||||||
Config1 = [{party, Party} | Config],
|
Config1 = [{party, Party} | Config],
|
||||||
[{context, wapi_ct_helper:get_context(Token)} | Config1];
|
[{context, wapi_ct_helper:get_context(Token)} | Config1];
|
||||||
@ -115,14 +104,14 @@ end_per_group(_Group, _C) ->
|
|||||||
-spec init_per_testcase(test_case_name(), config()) ->
|
-spec init_per_testcase(test_case_name(), config()) ->
|
||||||
config().
|
config().
|
||||||
init_per_testcase(Name, C) ->
|
init_per_testcase(Name, C) ->
|
||||||
C1 = ct_helper:makeup_cfg([ct_helper:test_case_name(Name), ct_helper:woody_ctx()], C),
|
C1 = wapi_ct_helper:makeup_cfg([wapi_ct_helper:test_case_name(Name), wapi_ct_helper:woody_ctx()], C),
|
||||||
ok = ct_helper:set_context(C1),
|
ok = wapi_context:save(C1),
|
||||||
[{test_sup, wapi_ct_helper:start_mocked_service_sup(?MODULE)} | C1].
|
[{test_sup, wapi_ct_helper:start_mocked_service_sup(?MODULE)} | C1].
|
||||||
|
|
||||||
-spec end_per_testcase(test_case_name(), config()) ->
|
-spec end_per_testcase(test_case_name(), config()) ->
|
||||||
config().
|
config().
|
||||||
end_per_testcase(_Name, C) ->
|
end_per_testcase(_Name, C) ->
|
||||||
ok = ct_helper:unset_context(),
|
ok = wapi_context:cleanup(),
|
||||||
wapi_ct_helper:stop_mocked_service_sup(?config(test_sup, C)),
|
wapi_ct_helper:stop_mocked_service_sup(?config(test_sup, C)),
|
||||||
ok.
|
ok.
|
||||||
|
|
||||||
@ -148,7 +137,7 @@ create_report_ok_test(C) ->
|
|||||||
<<"toTime">> => ?TIMESTAMP
|
<<"toTime">> => ?TIMESTAMP
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
ct_helper:cfg(context, C)
|
wapi_ct_helper:cfg(context, C)
|
||||||
).
|
).
|
||||||
|
|
||||||
-spec get_report_ok_test(config()) ->
|
-spec get_report_ok_test(config()) ->
|
||||||
@ -167,7 +156,7 @@ get_report_ok_test(C) ->
|
|||||||
<<"reportID">> => ?INTEGER
|
<<"reportID">> => ?INTEGER
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
ct_helper:cfg(context, C)
|
wapi_ct_helper:cfg(context, C)
|
||||||
).
|
).
|
||||||
|
|
||||||
-spec get_reports_ok_test(config()) ->
|
-spec get_reports_ok_test(config()) ->
|
||||||
@ -193,7 +182,7 @@ get_reports_ok_test(C) ->
|
|||||||
<<"type">> => <<"withdrawalRegistry">>
|
<<"type">> => <<"withdrawalRegistry">>
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
ct_helper:cfg(context, C)
|
wapi_ct_helper:cfg(context, C)
|
||||||
).
|
).
|
||||||
|
|
||||||
-spec reports_with_wrong_identity_ok_test(config()) ->
|
-spec reports_with_wrong_identity_ok_test(config()) ->
|
||||||
@ -217,7 +206,7 @@ reports_with_wrong_identity_ok_test(C) ->
|
|||||||
<<"toTime">> => ?TIMESTAMP
|
<<"toTime">> => ?TIMESTAMP
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
ct_helper:cfg(context, C)
|
wapi_ct_helper:cfg(context, C)
|
||||||
),
|
),
|
||||||
?emptyresp(400) = call_api(
|
?emptyresp(400) = call_api(
|
||||||
fun swag_client_wallet_reports_api:get_report/3,
|
fun swag_client_wallet_reports_api:get_report/3,
|
||||||
@ -227,7 +216,7 @@ reports_with_wrong_identity_ok_test(C) ->
|
|||||||
<<"reportID">> => ?INTEGER
|
<<"reportID">> => ?INTEGER
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
ct_helper:cfg(context, C)
|
wapi_ct_helper:cfg(context, C)
|
||||||
),
|
),
|
||||||
?emptyresp(400) = call_api(
|
?emptyresp(400) = call_api(
|
||||||
fun swag_client_wallet_reports_api:get_reports/3,
|
fun swag_client_wallet_reports_api:get_reports/3,
|
||||||
@ -241,7 +230,7 @@ reports_with_wrong_identity_ok_test(C) ->
|
|||||||
<<"type">> => <<"withdrawalRegistry">>
|
<<"type">> => <<"withdrawalRegistry">>
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
ct_helper:cfg(context, C)
|
wapi_ct_helper:cfg(context, C)
|
||||||
).
|
).
|
||||||
|
|
||||||
-spec download_file_ok_test(config()) ->
|
-spec download_file_ok_test(config()) ->
|
||||||
@ -260,7 +249,7 @@ download_file_ok_test(C) ->
|
|||||||
<<"expiresAt">> => ?TIMESTAMP
|
<<"expiresAt">> => ?TIMESTAMP
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
ct_helper:cfg(context, C)
|
wapi_ct_helper:cfg(context, C)
|
||||||
).
|
).
|
||||||
|
|
||||||
%%
|
%%
|
||||||
@ -272,11 +261,6 @@ call_api(F, Params, Context) ->
|
|||||||
Response = F(Url, PreparedParams, Opts),
|
Response = F(Url, PreparedParams, Opts),
|
||||||
wapi_client_lib:handle_response(Response).
|
wapi_client_lib:handle_response(Response).
|
||||||
|
|
||||||
create_party(_C) ->
|
|
||||||
ID = genlib:bsuuid(),
|
|
||||||
_ = ff_party:create(ID),
|
|
||||||
ID.
|
|
||||||
|
|
||||||
create_identity(C) ->
|
create_identity(C) ->
|
||||||
PartyID = ?config(party, C),
|
PartyID = ?config(party, C),
|
||||||
Params = #{
|
Params = #{
|
||||||
@ -291,6 +275,6 @@ create_context(PartyID, C) ->
|
|||||||
|
|
||||||
create_woody_ctx(C) ->
|
create_woody_ctx(C) ->
|
||||||
#{
|
#{
|
||||||
woody_context => ct_helper:get_woody_ctx(C)
|
woody_context => wapi_ct_helper:get_woody_ctx(C)
|
||||||
}.
|
}.
|
||||||
|
|
||||||
|
7
apps/wapi/test/wapi_report_tests_SUITE_data/jwk.json
Normal file
7
apps/wapi/test/wapi_report_tests_SUITE_data/jwk.json
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
{
|
||||||
|
"use": "enc",
|
||||||
|
"kty": "oct",
|
||||||
|
"kid": "1",
|
||||||
|
"alg": "dir",
|
||||||
|
"k": "M3VKOExvQVdhWUtXekduVGt1eDdrUmtwTTNBSko1a2M"
|
||||||
|
}
|
9
apps/wapi/test/wapi_report_tests_SUITE_data/private.pem
Normal file
9
apps/wapi/test/wapi_report_tests_SUITE_data/private.pem
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
-----BEGIN RSA PRIVATE KEY-----
|
||||||
|
MIIBOwIBAAJBAK9fx7qOJT7Aoseu7KKgaLagBh3wvDzg7F/ZMtGbPFikJnnvRWvF
|
||||||
|
B5oEGbMPblvtF0/fjqfu+eqjP3Z1tUSn7TkCAwEAAQJABUY5KIgr4JZEjwLYxQ9T
|
||||||
|
9uIbLP1Xe/E7yqoqmBk2GGhSrPY0OeRkYnUVLcP96UPQhF63iuG8VF6uZ7oAPsq+
|
||||||
|
gQIhANZy3jSCzPjXYHRU1kRqQzpt2S+OqoEiqQ6YG1HrC/VxAiEA0Vq6JlQK2tOX
|
||||||
|
37SS00dK0Qog4Qi8dN73GliFQNP18EkCIQC4epSA48zkfJMzQBAbRraSuxDNApPX
|
||||||
|
BzQbo+pMrEDbYQIgY4AncQgIkLB4Qk5kah48JNYXglzQlQtTjiX8Ty9ueGECIQCM
|
||||||
|
GD3UbQKiA0gf5plBA24I4wFVKxxa4wXbW/7SfP6XmQ==
|
||||||
|
-----END RSA PRIVATE KEY-----
|
@ -87,42 +87,27 @@ groups() ->
|
|||||||
%%
|
%%
|
||||||
%% starting/stopping
|
%% starting/stopping
|
||||||
%%
|
%%
|
||||||
-spec init_per_suite(config()) ->
|
-spec init_per_suite(config()) -> config().
|
||||||
config().
|
|
||||||
init_per_suite(Config) ->
|
init_per_suite(C) ->
|
||||||
%% TODO remove this after cut off wapi
|
wapi_ct_helper:init_suite(?MODULE, C).
|
||||||
ok = application:set_env(wapi, transport, thrift),
|
|
||||||
ct_helper:makeup_cfg([
|
-spec end_per_suite(config()) -> _.
|
||||||
ct_helper:test_case_name(init),
|
|
||||||
ct_payment_system:setup(#{
|
|
||||||
optional_apps => [
|
|
||||||
bender_client,
|
|
||||||
wapi_woody_client,
|
|
||||||
wapi
|
|
||||||
]
|
|
||||||
})
|
|
||||||
], Config).
|
|
||||||
|
|
||||||
-spec end_per_suite(config()) ->
|
|
||||||
_.
|
|
||||||
end_per_suite(C) ->
|
end_per_suite(C) ->
|
||||||
%% TODO remove this after cut off wapi
|
_ = wapi_ct_helper:stop_mocked_service_sup(?config(suite_test_sup, C)),
|
||||||
ok = application:unset_env(wapi, transport),
|
_ = [application:stop(App) || App <- ?config(apps, C)],
|
||||||
ok = ct_payment_system:shutdown(C).
|
ok.
|
||||||
|
|
||||||
-spec init_per_group(group_name(), config()) ->
|
-spec init_per_group(group_name(), config()) ->
|
||||||
config().
|
config().
|
||||||
init_per_group(Group, Config) when Group =:= base ->
|
init_per_group(Group, Config) when Group =:= base ->
|
||||||
ok = ff_context:save(ff_context:create(#{
|
ok = wapi_context:save(wapi_context:create(#{
|
||||||
party_client => party_client:create_client(),
|
party_client => party_client:create_client(),
|
||||||
woody_context => woody_context:new(<<"init_per_group/", (atom_to_binary(Group, utf8))/binary>>)
|
woody_context => woody_context:new(<<"init_per_group/", (atom_to_binary(Group, utf8))/binary>>)
|
||||||
})),
|
})),
|
||||||
Party = create_party(Config),
|
Party = genlib:bsuuid(),
|
||||||
BasePermissions = [
|
{ok, Token} = wapi_ct_helper:issue_token(Party, [{[party], write}], unlimited, ?DOMAIN),
|
||||||
{[party], read},
|
|
||||||
{[party], write}
|
|
||||||
],
|
|
||||||
{ok, Token} = wapi_ct_helper:issue_token(Party, BasePermissions, {deadline, 10}, ?DOMAIN),
|
|
||||||
Config1 = [{party, Party} | Config],
|
Config1 = [{party, Party} | Config],
|
||||||
[{context, wapi_ct_helper:get_context(Token)} | Config1];
|
[{context, wapi_ct_helper:get_context(Token)} | Config1];
|
||||||
init_per_group(_, Config) ->
|
init_per_group(_, Config) ->
|
||||||
@ -136,14 +121,14 @@ end_per_group(_Group, _C) ->
|
|||||||
-spec init_per_testcase(test_case_name(), config()) ->
|
-spec init_per_testcase(test_case_name(), config()) ->
|
||||||
config().
|
config().
|
||||||
init_per_testcase(Name, C) ->
|
init_per_testcase(Name, C) ->
|
||||||
C1 = ct_helper:makeup_cfg([ct_helper:test_case_name(Name), ct_helper:woody_ctx()], C),
|
C1 = wapi_ct_helper:makeup_cfg([wapi_ct_helper:test_case_name(Name), wapi_ct_helper:woody_ctx()], C),
|
||||||
ok = ct_helper:set_context(C1),
|
ok = wapi_context:save(C1),
|
||||||
[{test_sup, wapi_ct_helper:start_mocked_service_sup(?MODULE)} | C1].
|
[{test_sup, wapi_ct_helper:start_mocked_service_sup(?MODULE)} | C1].
|
||||||
|
|
||||||
-spec end_per_testcase(test_case_name(), config()) ->
|
-spec end_per_testcase(test_case_name(), config()) ->
|
||||||
config().
|
config().
|
||||||
end_per_testcase(_Name, C) ->
|
end_per_testcase(_Name, C) ->
|
||||||
ok = ct_helper:unset_context(),
|
ok = wapi_context:cleanup(),
|
||||||
wapi_ct_helper:stop_mocked_service_sup(?config(test_sup, C)),
|
wapi_ct_helper:stop_mocked_service_sup(?config(test_sup, C)),
|
||||||
ok.
|
ok.
|
||||||
|
|
||||||
@ -162,7 +147,7 @@ list_wallets(C) ->
|
|||||||
<<"limit">> => <<"123">>
|
<<"limit">> => <<"123">>
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
ct_helper:cfg(context, C)
|
wapi_ct_helper:cfg(context, C)
|
||||||
).
|
).
|
||||||
|
|
||||||
-spec list_wallets_invalid_error(config()) ->
|
-spec list_wallets_invalid_error(config()) ->
|
||||||
@ -194,7 +179,7 @@ list_withdrawals(C) ->
|
|||||||
<<"limit">> => <<"123">>
|
<<"limit">> => <<"123">>
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
ct_helper:cfg(context, C)
|
wapi_ct_helper:cfg(context, C)
|
||||||
).
|
).
|
||||||
|
|
||||||
-spec list_withdrawals_invalid_error(config()) ->
|
-spec list_withdrawals_invalid_error(config()) ->
|
||||||
@ -226,7 +211,7 @@ list_deposits(C) ->
|
|||||||
<<"limit">> => <<"123">>
|
<<"limit">> => <<"123">>
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
ct_helper:cfg(context, C)
|
wapi_ct_helper:cfg(context, C)
|
||||||
).
|
).
|
||||||
|
|
||||||
-spec list_deposits_invalid_error(config()) ->
|
-spec list_deposits_invalid_error(config()) ->
|
||||||
@ -258,7 +243,7 @@ list_destinations(C) ->
|
|||||||
<<"limit">> => <<"123">>
|
<<"limit">> => <<"123">>
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
ct_helper:cfg(context, C)
|
wapi_ct_helper:cfg(context, C)
|
||||||
).
|
).
|
||||||
|
|
||||||
-spec list_destinations_invalid_error(config()) ->
|
-spec list_destinations_invalid_error(config()) ->
|
||||||
@ -290,7 +275,7 @@ list_identities(C) ->
|
|||||||
<<"limit">> => <<"123">>
|
<<"limit">> => <<"123">>
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
ct_helper:cfg(context, C)
|
wapi_ct_helper:cfg(context, C)
|
||||||
).
|
).
|
||||||
|
|
||||||
-spec list_identities_invalid_error(config()) ->
|
-spec list_identities_invalid_error(config()) ->
|
||||||
@ -328,7 +313,7 @@ check_error(Error, MockFunc, SwagFunc, C) ->
|
|||||||
<<"limit">> => <<"123">>
|
<<"limit">> => <<"123">>
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
ct_helper:cfg(context, C)
|
wapi_ct_helper:cfg(context, C)
|
||||||
).
|
).
|
||||||
|
|
||||||
-spec call_api(function(), map(), wapi_client_lib:context()) ->
|
-spec call_api(function(), map(), wapi_client_lib:context()) ->
|
||||||
@ -337,8 +322,3 @@ call_api(F, Params, Context) ->
|
|||||||
{Url, PreparedParams, Opts} = wapi_client_lib:make_request(Context, Params),
|
{Url, PreparedParams, Opts} = wapi_client_lib:make_request(Context, Params),
|
||||||
Response = F(Url, PreparedParams, Opts),
|
Response = F(Url, PreparedParams, Opts),
|
||||||
wapi_client_lib:handle_response(Response).
|
wapi_client_lib:handle_response(Response).
|
||||||
|
|
||||||
create_party(_C) ->
|
|
||||||
ID = genlib:bsuuid(),
|
|
||||||
_ = ff_party:create(ID),
|
|
||||||
ID.
|
|
||||||
|
7
apps/wapi/test/wapi_stat_tests_SUITE_data/jwk.json
Normal file
7
apps/wapi/test/wapi_stat_tests_SUITE_data/jwk.json
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
{
|
||||||
|
"use": "enc",
|
||||||
|
"kty": "oct",
|
||||||
|
"kid": "1",
|
||||||
|
"alg": "dir",
|
||||||
|
"k": "M3VKOExvQVdhWUtXekduVGt1eDdrUmtwTTNBSko1a2M"
|
||||||
|
}
|
9
apps/wapi/test/wapi_stat_tests_SUITE_data/private.pem
Normal file
9
apps/wapi/test/wapi_stat_tests_SUITE_data/private.pem
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
-----BEGIN RSA PRIVATE KEY-----
|
||||||
|
MIIBOwIBAAJBAK9fx7qOJT7Aoseu7KKgaLagBh3wvDzg7F/ZMtGbPFikJnnvRWvF
|
||||||
|
B5oEGbMPblvtF0/fjqfu+eqjP3Z1tUSn7TkCAwEAAQJABUY5KIgr4JZEjwLYxQ9T
|
||||||
|
9uIbLP1Xe/E7yqoqmBk2GGhSrPY0OeRkYnUVLcP96UPQhF63iuG8VF6uZ7oAPsq+
|
||||||
|
gQIhANZy3jSCzPjXYHRU1kRqQzpt2S+OqoEiqQ6YG1HrC/VxAiEA0Vq6JlQK2tOX
|
||||||
|
37SS00dK0Qog4Qi8dN73GliFQNP18EkCIQC4epSA48zkfJMzQBAbRraSuxDNApPX
|
||||||
|
BzQbo+pMrEDbYQIgY4AncQgIkLB4Qk5kah48JNYXglzQlQtTjiX8Ty9ueGECIQCM
|
||||||
|
GD3UbQKiA0gf5plBA24I4wFVKxxa4wXbW/7SfP6XmQ==
|
||||||
|
-----END RSA PRIVATE KEY-----
|
@ -59,6 +59,29 @@
|
|||||||
context = ?DEFAULT_CONTEXT(PartyID)
|
context = ?DEFAULT_CONTEXT(PartyID)
|
||||||
}).
|
}).
|
||||||
|
|
||||||
|
-define(WITHDRAWAL_QUOTE, #wthd_Quote{
|
||||||
|
cash_from = ?CASH,
|
||||||
|
cash_to = ?CASH,
|
||||||
|
created_at = ?TIMESTAMP,
|
||||||
|
expires_on = ?TIMESTAMP,
|
||||||
|
operation_timestamp = ?TIMESTAMP,
|
||||||
|
domain_revision = 123,
|
||||||
|
party_revision = 123,
|
||||||
|
route = #wthd_Route{
|
||||||
|
provider_id = 123,
|
||||||
|
terminal_id = 123
|
||||||
|
},
|
||||||
|
quote_data = {str, ?STRING}
|
||||||
|
}).
|
||||||
|
|
||||||
|
-define(WITHDRAWAL_EVENT(Change), #wthd_Event{
|
||||||
|
change = Change,
|
||||||
|
occured_at = ?TIMESTAMP,
|
||||||
|
event_id = ?INTEGER
|
||||||
|
}).
|
||||||
|
|
||||||
|
-define(WITHDRAWAL_STATUS_CHANGE, {status_changed, #wthd_StatusChange{status = {pending, #wthd_status_Pending{}}}}).
|
||||||
|
|
||||||
-define(BLOCKING, unblocked).
|
-define(BLOCKING, unblocked).
|
||||||
|
|
||||||
-define(ACCOUNT, #account_Account{
|
-define(ACCOUNT, #account_Account{
|
||||||
@ -205,7 +228,7 @@
|
|||||||
id = ?STRING,
|
id = ?STRING,
|
||||||
name = ?STRING,
|
name = ?STRING,
|
||||||
created_at = ?TIMESTAMP,
|
created_at = ?TIMESTAMP,
|
||||||
provider = ?STRING,
|
provider = ?INTEGER,
|
||||||
identity_class = ?STRING,
|
identity_class = ?STRING,
|
||||||
identity_level = ?STRING,
|
identity_level = ?STRING,
|
||||||
effective_challenge = ?STRING,
|
effective_challenge = ?STRING,
|
||||||
|
@ -68,42 +68,27 @@ groups() ->
|
|||||||
%%
|
%%
|
||||||
%% starting/stopping
|
%% starting/stopping
|
||||||
%%
|
%%
|
||||||
-spec init_per_suite(config()) ->
|
-spec init_per_suite(config()) -> config().
|
||||||
config().
|
|
||||||
init_per_suite(Config) ->
|
init_per_suite(C) ->
|
||||||
%% TODO remove this after cut off wapi
|
wapi_ct_helper:init_suite(?MODULE, C).
|
||||||
ok = application:set_env(wapi, transport, thrift),
|
|
||||||
ct_helper:makeup_cfg([
|
-spec end_per_suite(config()) -> _.
|
||||||
ct_helper:test_case_name(init),
|
|
||||||
ct_payment_system:setup(#{
|
|
||||||
optional_apps => [
|
|
||||||
bender_client,
|
|
||||||
wapi_woody_client,
|
|
||||||
wapi
|
|
||||||
]
|
|
||||||
})
|
|
||||||
], Config).
|
|
||||||
|
|
||||||
-spec end_per_suite(config()) ->
|
|
||||||
_.
|
|
||||||
end_per_suite(C) ->
|
end_per_suite(C) ->
|
||||||
%% TODO remove this after cut off wapi
|
_ = wapi_ct_helper:stop_mocked_service_sup(?config(suite_test_sup, C)),
|
||||||
ok = application:unset_env(wapi, transport),
|
_ = [application:stop(App) || App <- ?config(apps, C)],
|
||||||
ok = ct_payment_system:shutdown(C).
|
ok.
|
||||||
|
|
||||||
-spec init_per_group(group_name(), config()) ->
|
-spec init_per_group(group_name(), config()) ->
|
||||||
config().
|
config().
|
||||||
init_per_group(Group, Config) when Group =:= base ->
|
init_per_group(Group, Config) when Group =:= base ->
|
||||||
ok = ff_context:save(ff_context:create(#{
|
ok = wapi_context:save(wapi_context:create(#{
|
||||||
party_client => party_client:create_client(),
|
party_client => party_client:create_client(),
|
||||||
woody_context => woody_context:new(<<"init_per_group/", (atom_to_binary(Group, utf8))/binary>>)
|
woody_context => woody_context:new(<<"init_per_group/", (atom_to_binary(Group, utf8))/binary>>)
|
||||||
})),
|
})),
|
||||||
Party = create_party(Config),
|
Party = genlib:bsuuid(),
|
||||||
BasePermissions = [
|
{ok, Token} = wapi_ct_helper:issue_token(Party, [{[party], write}], unlimited, ?DOMAIN),
|
||||||
{[party], read},
|
|
||||||
{[party], write}
|
|
||||||
],
|
|
||||||
{ok, Token} = wapi_ct_helper:issue_token(Party, BasePermissions, {deadline, 10}, ?DOMAIN),
|
|
||||||
Config1 = [{party, Party} | Config],
|
Config1 = [{party, Party} | Config],
|
||||||
[{context, wapi_ct_helper:get_context(Token)} | Config1];
|
[{context, wapi_ct_helper:get_context(Token)} | Config1];
|
||||||
init_per_group(_, Config) ->
|
init_per_group(_, Config) ->
|
||||||
@ -117,14 +102,14 @@ end_per_group(_Group, _C) ->
|
|||||||
-spec init_per_testcase(test_case_name(), config()) ->
|
-spec init_per_testcase(test_case_name(), config()) ->
|
||||||
config().
|
config().
|
||||||
init_per_testcase(Name, C) ->
|
init_per_testcase(Name, C) ->
|
||||||
C1 = ct_helper:makeup_cfg([ct_helper:test_case_name(Name), ct_helper:woody_ctx()], C),
|
C1 = wapi_ct_helper:makeup_cfg([wapi_ct_helper:test_case_name(Name), wapi_ct_helper:woody_ctx()], C),
|
||||||
ok = ct_helper:set_context(C1),
|
ok = wapi_context:save(C1),
|
||||||
[{test_sup, wapi_ct_helper:start_mocked_service_sup(?MODULE)} | C1].
|
[{test_sup, wapi_ct_helper:start_mocked_service_sup(?MODULE)} | C1].
|
||||||
|
|
||||||
-spec end_per_testcase(test_case_name(), config()) ->
|
-spec end_per_testcase(test_case_name(), config()) ->
|
||||||
config().
|
config().
|
||||||
end_per_testcase(_Name, C) ->
|
end_per_testcase(_Name, C) ->
|
||||||
ok = ct_helper:unset_context(),
|
ok = wapi_context:cleanup(),
|
||||||
wapi_ct_helper:stop_mocked_service_sup(?config(test_sup, C)),
|
wapi_ct_helper:stop_mocked_service_sup(?config(test_sup, C)),
|
||||||
ok.
|
ok.
|
||||||
|
|
||||||
@ -135,6 +120,7 @@ end_per_testcase(_Name, C) ->
|
|||||||
create(C) ->
|
create(C) ->
|
||||||
PartyID = ?config(party, C),
|
PartyID = ?config(party, C),
|
||||||
wapi_ct_helper:mock_services([
|
wapi_ct_helper:mock_services([
|
||||||
|
{bender_thrift, fun('GenerateID', _) -> {ok, ?GENERATE_ID_RESULT} end},
|
||||||
{fistful_identity, fun('GetContext', _) -> {ok, ?DEFAULT_CONTEXT(PartyID)} end},
|
{fistful_identity, fun('GetContext', _) -> {ok, ?DEFAULT_CONTEXT(PartyID)} end},
|
||||||
{fistful_wallet, fun('Create', _) -> {ok, ?WALLET(PartyID)} end}
|
{fistful_wallet, fun('Create', _) -> {ok, ?WALLET(PartyID)} end}
|
||||||
], C),
|
], C),
|
||||||
@ -150,7 +136,7 @@ create(C) ->
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
ct_helper:cfg(context, C)
|
wapi_ct_helper:cfg(context, C)
|
||||||
).
|
).
|
||||||
|
|
||||||
-spec get(config()) ->
|
-spec get(config()) ->
|
||||||
@ -167,7 +153,7 @@ get(C) ->
|
|||||||
<<"walletID">> => ?STRING
|
<<"walletID">> => ?STRING
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
ct_helper:cfg(context, C)
|
wapi_ct_helper:cfg(context, C)
|
||||||
).
|
).
|
||||||
|
|
||||||
-spec get_by_external_id(config()) ->
|
-spec get_by_external_id(config()) ->
|
||||||
@ -185,7 +171,7 @@ get_by_external_id(C) ->
|
|||||||
<<"externalID">> => ?STRING
|
<<"externalID">> => ?STRING
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
ct_helper:cfg(context, C)
|
wapi_ct_helper:cfg(context, C)
|
||||||
).
|
).
|
||||||
|
|
||||||
-spec get_account(config()) ->
|
-spec get_account(config()) ->
|
||||||
@ -207,7 +193,7 @@ get_account(C) ->
|
|||||||
<<"walletID">> => ?STRING
|
<<"walletID">> => ?STRING
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
ct_helper:cfg(context, C)
|
wapi_ct_helper:cfg(context, C)
|
||||||
).
|
).
|
||||||
|
|
||||||
%%
|
%%
|
||||||
@ -218,8 +204,3 @@ call_api(F, Params, Context) ->
|
|||||||
{Url, PreparedParams, Opts} = wapi_client_lib:make_request(Context, Params),
|
{Url, PreparedParams, Opts} = wapi_client_lib:make_request(Context, Params),
|
||||||
Response = F(Url, PreparedParams, Opts),
|
Response = F(Url, PreparedParams, Opts),
|
||||||
wapi_client_lib:handle_response(Response).
|
wapi_client_lib:handle_response(Response).
|
||||||
|
|
||||||
create_party(_C) ->
|
|
||||||
ID = genlib:bsuuid(),
|
|
||||||
_ = ff_party:create(ID),
|
|
||||||
ID.
|
|
||||||
|
7
apps/wapi/test/wapi_wallet_tests_SUITE_data/jwk.json
Normal file
7
apps/wapi/test/wapi_wallet_tests_SUITE_data/jwk.json
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
{
|
||||||
|
"use": "enc",
|
||||||
|
"kty": "oct",
|
||||||
|
"kid": "1",
|
||||||
|
"alg": "dir",
|
||||||
|
"k": "M3VKOExvQVdhWUtXekduVGt1eDdrUmtwTTNBSko1a2M"
|
||||||
|
}
|
9
apps/wapi/test/wapi_wallet_tests_SUITE_data/private.pem
Normal file
9
apps/wapi/test/wapi_wallet_tests_SUITE_data/private.pem
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
-----BEGIN RSA PRIVATE KEY-----
|
||||||
|
MIIBOwIBAAJBAK9fx7qOJT7Aoseu7KKgaLagBh3wvDzg7F/ZMtGbPFikJnnvRWvF
|
||||||
|
B5oEGbMPblvtF0/fjqfu+eqjP3Z1tUSn7TkCAwEAAQJABUY5KIgr4JZEjwLYxQ9T
|
||||||
|
9uIbLP1Xe/E7yqoqmBk2GGhSrPY0OeRkYnUVLcP96UPQhF63iuG8VF6uZ7oAPsq+
|
||||||
|
gQIhANZy3jSCzPjXYHRU1kRqQzpt2S+OqoEiqQ6YG1HrC/VxAiEA0Vq6JlQK2tOX
|
||||||
|
37SS00dK0Qog4Qi8dN73GliFQNP18EkCIQC4epSA48zkfJMzQBAbRraSuxDNApPX
|
||||||
|
BzQbo+pMrEDbYQIgY4AncQgIkLB4Qk5kah48JNYXglzQlQtTjiX8Ty9ueGECIQCM
|
||||||
|
GD3UbQKiA0gf5plBA24I4wFVKxxa4wXbW/7SfP6XmQ==
|
||||||
|
-----END RSA PRIVATE KEY-----
|
@ -69,37 +69,26 @@ groups() ->
|
|||||||
%%
|
%%
|
||||||
%% starting/stopping
|
%% starting/stopping
|
||||||
%%
|
%%
|
||||||
-spec init_per_suite(config()) ->
|
-spec init_per_suite(config()) -> config().
|
||||||
config().
|
|
||||||
init_per_suite(Config) ->
|
init_per_suite(C) ->
|
||||||
%% TODO remove this after cut off wapi
|
wapi_ct_helper:init_suite(?MODULE, C).
|
||||||
ok = application:set_env(wapi, transport, thrift),
|
|
||||||
ct_helper:makeup_cfg([
|
-spec end_per_suite(config()) -> _.
|
||||||
ct_helper:test_case_name(init),
|
|
||||||
ct_payment_system:setup(#{
|
|
||||||
optional_apps => [
|
|
||||||
bender_client,
|
|
||||||
wapi_woody_client,
|
|
||||||
wapi
|
|
||||||
]
|
|
||||||
})
|
|
||||||
], Config).
|
|
||||||
|
|
||||||
-spec end_per_suite(config()) ->
|
|
||||||
_.
|
|
||||||
end_per_suite(C) ->
|
end_per_suite(C) ->
|
||||||
%% TODO remove this after cut off wapi
|
_ = wapi_ct_helper:stop_mocked_service_sup(?config(suite_test_sup, C)),
|
||||||
ok = application:unset_env(wapi, transport),
|
_ = [application:stop(App) || App <- ?config(apps, C)],
|
||||||
ok = ct_payment_system:shutdown(C).
|
ok.
|
||||||
|
|
||||||
-spec init_per_group(group_name(), config()) ->
|
-spec init_per_group(group_name(), config()) ->
|
||||||
config().
|
config().
|
||||||
init_per_group(Group, Config) when Group =:= base ->
|
init_per_group(Group, Config) when Group =:= base ->
|
||||||
ok = ff_context:save(ff_context:create(#{
|
ok = wapi_context:save(wapi_context:create(#{
|
||||||
party_client => party_client:create_client(),
|
party_client => party_client:create_client(),
|
||||||
woody_context => woody_context:new(<<"init_per_group/", (atom_to_binary(Group, utf8))/binary>>)
|
woody_context => woody_context:new(<<"init_per_group/", (atom_to_binary(Group, utf8))/binary>>)
|
||||||
})),
|
})),
|
||||||
Party = create_party(Config),
|
Party = genlib:bsuuid(),
|
||||||
{ok, Token} = wapi_ct_helper:issue_token(Party, [{[party], write}], unlimited, ?DOMAIN),
|
{ok, Token} = wapi_ct_helper:issue_token(Party, [{[party], write}], unlimited, ?DOMAIN),
|
||||||
Config1 = [{party, Party} | Config],
|
Config1 = [{party, Party} | Config],
|
||||||
[{context, wapi_ct_helper:get_context(Token)} | Config1];
|
[{context, wapi_ct_helper:get_context(Token)} | Config1];
|
||||||
@ -114,14 +103,14 @@ end_per_group(_Group, _C) ->
|
|||||||
-spec init_per_testcase(test_case_name(), config()) ->
|
-spec init_per_testcase(test_case_name(), config()) ->
|
||||||
config().
|
config().
|
||||||
init_per_testcase(Name, C) ->
|
init_per_testcase(Name, C) ->
|
||||||
C1 = ct_helper:makeup_cfg([ct_helper:test_case_name(Name), ct_helper:woody_ctx()], C),
|
C1 = wapi_ct_helper:makeup_cfg([wapi_ct_helper:test_case_name(Name), wapi_ct_helper:woody_ctx()], C),
|
||||||
ok = ct_helper:set_context(C1),
|
ok = wapi_context:save(C1),
|
||||||
[{test_sup, wapi_ct_helper:start_mocked_service_sup(?MODULE)} | C1].
|
[{test_sup, wapi_ct_helper:start_mocked_service_sup(?MODULE)} | C1].
|
||||||
|
|
||||||
-spec end_per_testcase(test_case_name(), config()) ->
|
-spec end_per_testcase(test_case_name(), config()) ->
|
||||||
config().
|
config().
|
||||||
end_per_testcase(_Name, C) ->
|
end_per_testcase(_Name, C) ->
|
||||||
ok = ct_helper:unset_context(),
|
ok = wapi_context:cleanup(),
|
||||||
wapi_ct_helper:stop_mocked_service_sup(?config(test_sup, C)),
|
wapi_ct_helper:stop_mocked_service_sup(?config(test_sup, C)),
|
||||||
ok.
|
ok.
|
||||||
|
|
||||||
@ -147,7 +136,7 @@ create_webhook_ok_test(C) ->
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
ct_helper:cfg(context, C)
|
wapi_ct_helper:cfg(context, C)
|
||||||
).
|
).
|
||||||
|
|
||||||
-spec get_webhooks_ok_test(config()) ->
|
-spec get_webhooks_ok_test(config()) ->
|
||||||
@ -165,7 +154,7 @@ get_webhooks_ok_test(C) ->
|
|||||||
<<"identityID">> => IdentityID
|
<<"identityID">> => IdentityID
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
ct_helper:cfg(context, C)
|
wapi_ct_helper:cfg(context, C)
|
||||||
).
|
).
|
||||||
|
|
||||||
-spec get_webhook_ok_test(config()) ->
|
-spec get_webhook_ok_test(config()) ->
|
||||||
@ -186,7 +175,7 @@ get_webhook_ok_test(C) ->
|
|||||||
<<"identityID">> => IdentityID
|
<<"identityID">> => IdentityID
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
ct_helper:cfg(context, C)
|
wapi_ct_helper:cfg(context, C)
|
||||||
).
|
).
|
||||||
|
|
||||||
-spec delete_webhook_ok_test(config()) ->
|
-spec delete_webhook_ok_test(config()) ->
|
||||||
@ -207,7 +196,7 @@ delete_webhook_ok_test(C) ->
|
|||||||
<<"identityID">> => IdentityID
|
<<"identityID">> => IdentityID
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
ct_helper:cfg(context, C)
|
wapi_ct_helper:cfg(context, C)
|
||||||
).
|
).
|
||||||
|
|
||||||
%%
|
%%
|
||||||
@ -219,11 +208,6 @@ call_api(F, Params, Context) ->
|
|||||||
Response = F(Url, PreparedParams, Opts),
|
Response = F(Url, PreparedParams, Opts),
|
||||||
wapi_client_lib:handle_response(Response).
|
wapi_client_lib:handle_response(Response).
|
||||||
|
|
||||||
create_party(_C) ->
|
|
||||||
ID = genlib:bsuuid(),
|
|
||||||
_ = ff_party:create(ID),
|
|
||||||
ID.
|
|
||||||
|
|
||||||
create_identity(C) ->
|
create_identity(C) ->
|
||||||
PartyID = ?config(party, C),
|
PartyID = ?config(party, C),
|
||||||
Params = #{
|
Params = #{
|
||||||
@ -238,5 +222,5 @@ create_context(PartyID, C) ->
|
|||||||
|
|
||||||
create_woody_ctx(C) ->
|
create_woody_ctx(C) ->
|
||||||
#{
|
#{
|
||||||
woody_context => ct_helper:get_woody_ctx(C)
|
woody_context => wapi_ct_helper:get_woody_ctx(C)
|
||||||
}.
|
}.
|
||||||
|
7
apps/wapi/test/wapi_webhook_tests_SUITE_data/jwk.json
Normal file
7
apps/wapi/test/wapi_webhook_tests_SUITE_data/jwk.json
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
{
|
||||||
|
"use": "enc",
|
||||||
|
"kty": "oct",
|
||||||
|
"kid": "1",
|
||||||
|
"alg": "dir",
|
||||||
|
"k": "M3VKOExvQVdhWUtXekduVGt1eDdrUmtwTTNBSko1a2M"
|
||||||
|
}
|
9
apps/wapi/test/wapi_webhook_tests_SUITE_data/private.pem
Normal file
9
apps/wapi/test/wapi_webhook_tests_SUITE_data/private.pem
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
-----BEGIN RSA PRIVATE KEY-----
|
||||||
|
MIIBOwIBAAJBAK9fx7qOJT7Aoseu7KKgaLagBh3wvDzg7F/ZMtGbPFikJnnvRWvF
|
||||||
|
B5oEGbMPblvtF0/fjqfu+eqjP3Z1tUSn7TkCAwEAAQJABUY5KIgr4JZEjwLYxQ9T
|
||||||
|
9uIbLP1Xe/E7yqoqmBk2GGhSrPY0OeRkYnUVLcP96UPQhF63iuG8VF6uZ7oAPsq+
|
||||||
|
gQIhANZy3jSCzPjXYHRU1kRqQzpt2S+OqoEiqQ6YG1HrC/VxAiEA0Vq6JlQK2tOX
|
||||||
|
37SS00dK0Qog4Qi8dN73GliFQNP18EkCIQC4epSA48zkfJMzQBAbRraSuxDNApPX
|
||||||
|
BzQbo+pMrEDbYQIgY4AncQgIkLB4Qk5kah48JNYXglzQlQtTjiX8Ty9ueGECIQCM
|
||||||
|
GD3UbQKiA0gf5plBA24I4wFVKxxa4wXbW/7SfP6XmQ==
|
||||||
|
-----END RSA PRIVATE KEY-----
|
@ -23,7 +23,10 @@
|
|||||||
-export([
|
-export([
|
||||||
create/1,
|
create/1,
|
||||||
get/1,
|
get/1,
|
||||||
get_by_external_id/1
|
get_by_external_id/1,
|
||||||
|
create_quote/1,
|
||||||
|
get_events/1,
|
||||||
|
get_event/1
|
||||||
]).
|
]).
|
||||||
|
|
||||||
% common-api is used since it is the domain used in production RN
|
% common-api is used since it is the domain used in production RN
|
||||||
@ -58,7 +61,10 @@ groups() ->
|
|||||||
[
|
[
|
||||||
create,
|
create,
|
||||||
get,
|
get,
|
||||||
get_by_external_id
|
get_by_external_id,
|
||||||
|
create_quote,
|
||||||
|
get_events,
|
||||||
|
get_event
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
].
|
].
|
||||||
@ -66,42 +72,27 @@ groups() ->
|
|||||||
%%
|
%%
|
||||||
%% starting/stopping
|
%% starting/stopping
|
||||||
%%
|
%%
|
||||||
-spec init_per_suite(config()) ->
|
-spec init_per_suite(config()) -> config().
|
||||||
config().
|
|
||||||
init_per_suite(Config) ->
|
init_per_suite(C) ->
|
||||||
%% TODO remove this after cut off wapi
|
wapi_ct_helper:init_suite(?MODULE, C).
|
||||||
ok = application:set_env(wapi, transport, thrift),
|
|
||||||
ct_helper:makeup_cfg([
|
-spec end_per_suite(config()) -> _.
|
||||||
ct_helper:test_case_name(init),
|
|
||||||
ct_payment_system:setup(#{
|
|
||||||
optional_apps => [
|
|
||||||
bender_client,
|
|
||||||
wapi_woody_client,
|
|
||||||
wapi
|
|
||||||
]
|
|
||||||
})
|
|
||||||
], Config).
|
|
||||||
|
|
||||||
-spec end_per_suite(config()) ->
|
|
||||||
_.
|
|
||||||
end_per_suite(C) ->
|
end_per_suite(C) ->
|
||||||
%% TODO remove this after cut off wapi
|
_ = wapi_ct_helper:stop_mocked_service_sup(?config(suite_test_sup, C)),
|
||||||
ok = application:unset_env(wapi, transport),
|
_ = [application:stop(App) || App <- ?config(apps, C)],
|
||||||
ok = ct_payment_system:shutdown(C).
|
ok.
|
||||||
|
|
||||||
-spec init_per_group(group_name(), config()) ->
|
-spec init_per_group(group_name(), config()) ->
|
||||||
config().
|
config().
|
||||||
init_per_group(Group, Config) when Group =:= base ->
|
init_per_group(Group, Config) when Group =:= base ->
|
||||||
ok = ff_context:save(ff_context:create(#{
|
ok = wapi_context:save(wapi_context:create(#{
|
||||||
party_client => party_client:create_client(),
|
party_client => party_client:create_client(),
|
||||||
woody_context => woody_context:new(<<"init_per_group/", (atom_to_binary(Group, utf8))/binary>>)
|
woody_context => woody_context:new(<<"init_per_group/", (atom_to_binary(Group, utf8))/binary>>)
|
||||||
})),
|
})),
|
||||||
Party = create_party(Config),
|
Party = genlib:bsuuid(),
|
||||||
BasePermissions = [
|
{ok, Token} = wapi_ct_helper:issue_token(Party, [{[party], write}], unlimited, ?DOMAIN),
|
||||||
{[party], read},
|
|
||||||
{[party], write}
|
|
||||||
],
|
|
||||||
{ok, Token} = wapi_ct_helper:issue_token(Party, BasePermissions, {deadline, 10}, ?DOMAIN),
|
|
||||||
Config1 = [{party, Party} | Config],
|
Config1 = [{party, Party} | Config],
|
||||||
[{context, wapi_ct_helper:get_context(Token)} | Config1];
|
[{context, wapi_ct_helper:get_context(Token)} | Config1];
|
||||||
init_per_group(_, Config) ->
|
init_per_group(_, Config) ->
|
||||||
@ -115,14 +106,14 @@ end_per_group(_Group, _C) ->
|
|||||||
-spec init_per_testcase(test_case_name(), config()) ->
|
-spec init_per_testcase(test_case_name(), config()) ->
|
||||||
config().
|
config().
|
||||||
init_per_testcase(Name, C) ->
|
init_per_testcase(Name, C) ->
|
||||||
C1 = ct_helper:makeup_cfg([ct_helper:test_case_name(Name), ct_helper:woody_ctx()], C),
|
C1 = wapi_ct_helper:makeup_cfg([wapi_ct_helper:test_case_name(Name), wapi_ct_helper:woody_ctx()], C),
|
||||||
ok = ct_helper:set_context(C1),
|
ok = wapi_context:save(C1),
|
||||||
[{test_sup, wapi_ct_helper:start_mocked_service_sup(?MODULE)} | C1].
|
[{test_sup, wapi_ct_helper:start_mocked_service_sup(?MODULE)} | C1].
|
||||||
|
|
||||||
-spec end_per_testcase(test_case_name(), config()) ->
|
-spec end_per_testcase(test_case_name(), config()) ->
|
||||||
config().
|
config().
|
||||||
end_per_testcase(_Name, C) ->
|
end_per_testcase(_Name, C) ->
|
||||||
ok = ct_helper:unset_context(),
|
ok = wapi_context:cleanup(),
|
||||||
wapi_ct_helper:stop_mocked_service_sup(?config(test_sup, C)),
|
wapi_ct_helper:stop_mocked_service_sup(?config(test_sup, C)),
|
||||||
ok.
|
ok.
|
||||||
|
|
||||||
@ -133,6 +124,7 @@ end_per_testcase(_Name, C) ->
|
|||||||
create(C) ->
|
create(C) ->
|
||||||
PartyID = ?config(party, C),
|
PartyID = ?config(party, C),
|
||||||
wapi_ct_helper:mock_services([
|
wapi_ct_helper:mock_services([
|
||||||
|
{bender_thrift, fun('GenerateID', _) -> {ok, ?GENERATE_ID_RESULT} end},
|
||||||
{fistful_wallet, fun('GetContext', _) -> {ok, ?DEFAULT_CONTEXT(PartyID)} end},
|
{fistful_wallet, fun('GetContext', _) -> {ok, ?DEFAULT_CONTEXT(PartyID)} end},
|
||||||
{fistful_destination, fun('GetContext', _) -> {ok, ?DEFAULT_CONTEXT(PartyID)} end},
|
{fistful_destination, fun('GetContext', _) -> {ok, ?DEFAULT_CONTEXT(PartyID)} end},
|
||||||
{fistful_withdrawal, fun('Create', _) -> {ok, ?WITHDRAWAL(PartyID)} end}
|
{fistful_withdrawal, fun('Create', _) -> {ok, ?WITHDRAWAL(PartyID)} end}
|
||||||
@ -148,7 +140,7 @@ create(C) ->
|
|||||||
<<"currency">> => <<"RUB">>
|
<<"currency">> => <<"RUB">>
|
||||||
}
|
}
|
||||||
})},
|
})},
|
||||||
ct_helper:cfg(context, C)
|
wapi_ct_helper:cfg(context, C)
|
||||||
).
|
).
|
||||||
|
|
||||||
-spec get(config()) ->
|
-spec get(config()) ->
|
||||||
@ -165,7 +157,7 @@ get(C) ->
|
|||||||
<<"withdrawalID">> => ?STRING
|
<<"withdrawalID">> => ?STRING
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
ct_helper:cfg(context, C)
|
wapi_ct_helper:cfg(context, C)
|
||||||
).
|
).
|
||||||
|
|
||||||
-spec get_by_external_id(config()) ->
|
-spec get_by_external_id(config()) ->
|
||||||
@ -183,7 +175,82 @@ get_by_external_id(C) ->
|
|||||||
<<"externalID">> => ?STRING
|
<<"externalID">> => ?STRING
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
ct_helper:cfg(context, C)
|
wapi_ct_helper:cfg(context, C)
|
||||||
|
).
|
||||||
|
|
||||||
|
-spec create_quote(config()) ->
|
||||||
|
_.
|
||||||
|
create_quote(C) ->
|
||||||
|
PartyID = ?config(party, C),
|
||||||
|
wapi_ct_helper:mock_services([
|
||||||
|
{fistful_wallet, fun('GetContext', _) -> {ok, ?DEFAULT_CONTEXT(PartyID)} end},
|
||||||
|
{fistful_destination, fun('GetContext', _) -> {ok, ?DEFAULT_CONTEXT(PartyID)} end},
|
||||||
|
{fistful_withdrawal, fun('GetQuote', _) -> {ok, ?WITHDRAWAL_QUOTE} end}
|
||||||
|
], C),
|
||||||
|
{ok, _} = call_api(
|
||||||
|
fun swag_client_wallet_withdrawals_api:create_quote/3,
|
||||||
|
#{
|
||||||
|
body => genlib_map:compact(#{
|
||||||
|
<<"walletID">> => ?STRING,
|
||||||
|
<<"destinationID">> => ?STRING,
|
||||||
|
<<"currencyFrom">> => <<"RUB">>,
|
||||||
|
<<"currencyTo">> => <<"USD">>,
|
||||||
|
<<"cash">> => #{
|
||||||
|
<<"amount">> => 100,
|
||||||
|
<<"currency">> => <<"RUB">>
|
||||||
|
}
|
||||||
|
})},
|
||||||
|
wapi_ct_helper:cfg(context, C)
|
||||||
|
).
|
||||||
|
|
||||||
|
-spec get_events(config()) ->
|
||||||
|
_.
|
||||||
|
get_events(C) ->
|
||||||
|
PartyID = ?config(party, C),
|
||||||
|
wapi_ct_helper:mock_services([
|
||||||
|
{fistful_withdrawal,
|
||||||
|
fun
|
||||||
|
('GetContext', _) -> {ok, ?DEFAULT_CONTEXT(PartyID)};
|
||||||
|
('GetEvents', [_, #'EventRange'{limit = 0}]) -> {ok, []};
|
||||||
|
('GetEvents', _) -> {ok, [?WITHDRAWAL_EVENT(?WITHDRAWAL_STATUS_CHANGE)]}
|
||||||
|
end
|
||||||
|
}
|
||||||
|
], C),
|
||||||
|
{ok, _} = call_api(
|
||||||
|
fun swag_client_wallet_withdrawals_api:poll_withdrawal_events/3,
|
||||||
|
#{
|
||||||
|
binding => #{
|
||||||
|
<<"withdrawalID">> => ?STRING
|
||||||
|
},
|
||||||
|
qs_val => #{
|
||||||
|
<<"limit">> => 10
|
||||||
|
}
|
||||||
|
},
|
||||||
|
wapi_ct_helper:cfg(context, C)
|
||||||
|
).
|
||||||
|
|
||||||
|
-spec get_event(config()) ->
|
||||||
|
_.
|
||||||
|
get_event(C) ->
|
||||||
|
PartyID = ?config(party, C),
|
||||||
|
wapi_ct_helper:mock_services([
|
||||||
|
{fistful_withdrawal,
|
||||||
|
fun
|
||||||
|
('GetContext', _) -> {ok, ?DEFAULT_CONTEXT(PartyID)};
|
||||||
|
('GetEvents', [_, #'EventRange'{limit = 0}]) -> {ok, []};
|
||||||
|
('GetEvents', _) -> {ok, [?WITHDRAWAL_EVENT(?WITHDRAWAL_STATUS_CHANGE)]}
|
||||||
|
end
|
||||||
|
}
|
||||||
|
], C),
|
||||||
|
{ok, _} = call_api(
|
||||||
|
fun swag_client_wallet_withdrawals_api:get_withdrawal_events/3,
|
||||||
|
#{
|
||||||
|
binding => #{
|
||||||
|
<<"withdrawalID">> => ?STRING,
|
||||||
|
<<"eventID">> => ?INTEGER
|
||||||
|
}
|
||||||
|
},
|
||||||
|
wapi_ct_helper:cfg(context, C)
|
||||||
).
|
).
|
||||||
|
|
||||||
%%
|
%%
|
||||||
@ -194,8 +261,3 @@ call_api(F, Params, Context) ->
|
|||||||
{Url, PreparedParams, Opts} = wapi_client_lib:make_request(Context, Params),
|
{Url, PreparedParams, Opts} = wapi_client_lib:make_request(Context, Params),
|
||||||
Response = F(Url, PreparedParams, Opts),
|
Response = F(Url, PreparedParams, Opts),
|
||||||
wapi_client_lib:handle_response(Response).
|
wapi_client_lib:handle_response(Response).
|
||||||
|
|
||||||
create_party(_C) ->
|
|
||||||
ID = genlib:bsuuid(),
|
|
||||||
_ = ff_party:create(ID),
|
|
||||||
ID.
|
|
||||||
|
7
apps/wapi/test/wapi_withdrawal_tests_SUITE_data/jwk.json
Normal file
7
apps/wapi/test/wapi_withdrawal_tests_SUITE_data/jwk.json
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
{
|
||||||
|
"use": "enc",
|
||||||
|
"kty": "oct",
|
||||||
|
"kid": "1",
|
||||||
|
"alg": "dir",
|
||||||
|
"k": "M3VKOExvQVdhWUtXekduVGt1eDdrUmtwTTNBSko1a2M"
|
||||||
|
}
|
@ -0,0 +1,9 @@
|
|||||||
|
-----BEGIN RSA PRIVATE KEY-----
|
||||||
|
MIIBOwIBAAJBAK9fx7qOJT7Aoseu7KKgaLagBh3wvDzg7F/ZMtGbPFikJnnvRWvF
|
||||||
|
B5oEGbMPblvtF0/fjqfu+eqjP3Z1tUSn7TkCAwEAAQJABUY5KIgr4JZEjwLYxQ9T
|
||||||
|
9uIbLP1Xe/E7yqoqmBk2GGhSrPY0OeRkYnUVLcP96UPQhF63iuG8VF6uZ7oAPsq+
|
||||||
|
gQIhANZy3jSCzPjXYHRU1kRqQzpt2S+OqoEiqQ6YG1HrC/VxAiEA0Vq6JlQK2tOX
|
||||||
|
37SS00dK0Qog4Qi8dN73GliFQNP18EkCIQC4epSA48zkfJMzQBAbRraSuxDNApPX
|
||||||
|
BzQbo+pMrEDbYQIgY4AncQgIkLB4Qk5kah48JNYXglzQlQtTjiX8Ty9ueGECIQCM
|
||||||
|
GD3UbQKiA0gf5plBA24I4wFVKxxa4wXbW/7SfP6XmQ==
|
||||||
|
-----END RSA PRIVATE KEY-----
|
141
apps/wapi_core/src/wapi_context.erl
Normal file
141
apps/wapi_core/src/wapi_context.erl
Normal file
@ -0,0 +1,141 @@
|
|||||||
|
-module(wapi_context).
|
||||||
|
|
||||||
|
-export([create/0]).
|
||||||
|
-export([create/1]).
|
||||||
|
-export([save/1]).
|
||||||
|
-export([load/0]).
|
||||||
|
-export([cleanup/0]).
|
||||||
|
|
||||||
|
-export([get_woody_context/1]).
|
||||||
|
-export([set_woody_context/2]).
|
||||||
|
-export([get_user_identity/1]).
|
||||||
|
-export([set_user_identity/2]).
|
||||||
|
-export([get_party_client_context/1]).
|
||||||
|
-export([set_party_client_context/2]).
|
||||||
|
-export([get_party_client/1]).
|
||||||
|
-export([set_party_client/2]).
|
||||||
|
|
||||||
|
-opaque context() :: #{
|
||||||
|
woody_context := woody_context(),
|
||||||
|
party_client_context := party_client_context(),
|
||||||
|
party_client => party_client(),
|
||||||
|
user_identity => user_identity()
|
||||||
|
}.
|
||||||
|
-type options() :: #{
|
||||||
|
party_client => party_client(),
|
||||||
|
user_identity => user_identity(),
|
||||||
|
woody_context => woody_context(),
|
||||||
|
party_client_context => party_client_context()
|
||||||
|
}.
|
||||||
|
|
||||||
|
-export_type([context/0]).
|
||||||
|
-export_type([options/0]).
|
||||||
|
|
||||||
|
%% Internal types
|
||||||
|
|
||||||
|
-type user_identity() :: woody_user_identity:user_identity().
|
||||||
|
-type woody_context() :: woody_context:ctx().
|
||||||
|
-type party_client() :: party_client:client().
|
||||||
|
-type party_client_context() :: party_client:context().
|
||||||
|
|
||||||
|
-define(REGISTRY_KEY, {p, l, {?MODULE, stored_context}}).
|
||||||
|
|
||||||
|
%% API
|
||||||
|
|
||||||
|
-spec create() -> context().
|
||||||
|
create() ->
|
||||||
|
create(#{}).
|
||||||
|
|
||||||
|
-spec create(options()) -> context().
|
||||||
|
create(Options0) ->
|
||||||
|
Options1 = ensure_woody_context_exists(Options0),
|
||||||
|
ensure_party_context_exists(Options1).
|
||||||
|
|
||||||
|
-spec save(context()) -> ok.
|
||||||
|
save(Context) ->
|
||||||
|
true = try gproc:reg(?REGISTRY_KEY, Context)
|
||||||
|
catch
|
||||||
|
error:badarg ->
|
||||||
|
gproc:set_value(?REGISTRY_KEY, Context)
|
||||||
|
end,
|
||||||
|
ok.
|
||||||
|
|
||||||
|
-spec load() -> context() | no_return().
|
||||||
|
load() ->
|
||||||
|
gproc:get_value(?REGISTRY_KEY).
|
||||||
|
|
||||||
|
-spec cleanup() -> ok.
|
||||||
|
cleanup() ->
|
||||||
|
true = gproc:unreg(?REGISTRY_KEY),
|
||||||
|
ok.
|
||||||
|
|
||||||
|
-spec get_woody_context(context()) -> woody_context().
|
||||||
|
get_woody_context(Context) ->
|
||||||
|
#{woody_context := WoodyContext} = ensure_woody_user_info_set(Context),
|
||||||
|
WoodyContext.
|
||||||
|
|
||||||
|
-spec set_woody_context(woody_context(), context()) -> context().
|
||||||
|
set_woody_context(WoodyContext, #{party_client_context := PartyContext0} = Context) ->
|
||||||
|
PartyContext1 = party_client_context:set_woody_context(WoodyContext, PartyContext0),
|
||||||
|
Context#{
|
||||||
|
woody_context => WoodyContext,
|
||||||
|
party_client_context => PartyContext1
|
||||||
|
}.
|
||||||
|
|
||||||
|
-spec get_party_client(context()) -> party_client().
|
||||||
|
get_party_client(#{party_client := PartyClient}) ->
|
||||||
|
PartyClient;
|
||||||
|
get_party_client(Context) ->
|
||||||
|
error(no_party_client, [Context]).
|
||||||
|
|
||||||
|
-spec set_party_client(party_client(), context()) -> context().
|
||||||
|
set_party_client(PartyClient, Context) ->
|
||||||
|
Context#{party_client => PartyClient}.
|
||||||
|
|
||||||
|
-spec get_party_client_context(context()) -> party_client_context().
|
||||||
|
get_party_client_context(Context) ->
|
||||||
|
#{party_client_context := PartyContext} = ensure_party_user_info_set(Context),
|
||||||
|
PartyContext.
|
||||||
|
|
||||||
|
-spec set_party_client_context(party_client_context(), context()) -> context().
|
||||||
|
set_party_client_context(PartyContext, Context) ->
|
||||||
|
Context#{party_client_context := PartyContext}.
|
||||||
|
|
||||||
|
-spec get_user_identity(context()) -> user_identity() | no_return().
|
||||||
|
get_user_identity(#{user_identity := Identity}) ->
|
||||||
|
Identity;
|
||||||
|
get_user_identity(Context) ->
|
||||||
|
WoodyContext = get_woody_context(Context),
|
||||||
|
woody_user_identity:get(WoodyContext).
|
||||||
|
|
||||||
|
-spec set_user_identity(user_identity(), context()) -> context().
|
||||||
|
set_user_identity(Identity, Context) ->
|
||||||
|
Context#{user_identity => Identity}.
|
||||||
|
|
||||||
|
%% Internal functions
|
||||||
|
|
||||||
|
-spec ensure_woody_context_exists(options()) -> options().
|
||||||
|
ensure_woody_context_exists(#{woody_context := _WoodyContext} = Options) ->
|
||||||
|
Options;
|
||||||
|
ensure_woody_context_exists(Options) ->
|
||||||
|
Options#{woody_context => woody_context:new()}.
|
||||||
|
|
||||||
|
-spec ensure_party_context_exists(options()) -> options().
|
||||||
|
ensure_party_context_exists(#{party_client_context := _PartyContext} = Options) ->
|
||||||
|
Options;
|
||||||
|
ensure_party_context_exists(#{woody_context := WoodyContext} = Options) ->
|
||||||
|
Options#{party_client_context => party_client:create_context(#{woody_context => WoodyContext})}.
|
||||||
|
|
||||||
|
-spec ensure_woody_user_info_set(context()) -> context().
|
||||||
|
ensure_woody_user_info_set(#{user_identity := Identity, woody_context := WoodyContext} = Context) ->
|
||||||
|
NewWoodyContext = woody_user_identity:put(Identity, WoodyContext),
|
||||||
|
Context#{woody_context := NewWoodyContext};
|
||||||
|
ensure_woody_user_info_set(Context) ->
|
||||||
|
Context.
|
||||||
|
|
||||||
|
-spec ensure_party_user_info_set(context()) -> context().
|
||||||
|
ensure_party_user_info_set(#{user_identity := Identity, party_client_context := PartyContext} = Context) ->
|
||||||
|
NewPartyContext = party_client_context:set_user_info(Identity, PartyContext),
|
||||||
|
Context#{party_client_context := NewPartyContext};
|
||||||
|
ensure_party_user_info_set(Context) ->
|
||||||
|
Context.
|
19
apps/wapi_core/src/wapi_core.app.src
Normal file
19
apps/wapi_core/src/wapi_core.app.src
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
{application, wapi_core, [
|
||||||
|
{description,
|
||||||
|
"Core types and facilities"
|
||||||
|
},
|
||||||
|
{vsn, "1"},
|
||||||
|
{registered, []},
|
||||||
|
{applications, [
|
||||||
|
kernel,
|
||||||
|
stdlib,
|
||||||
|
genlib
|
||||||
|
]},
|
||||||
|
{env, []},
|
||||||
|
{modules, []},
|
||||||
|
{maintainers, [
|
||||||
|
"Andrey Mayorov <a.mayorov@rbkmoney.com>"
|
||||||
|
]},
|
||||||
|
{licenses, []},
|
||||||
|
{links, ["https://github.com/rbkmoney/wapi-v2"]}
|
||||||
|
]}.
|
41
apps/wapi_core/src/wapi_failure.erl
Normal file
41
apps/wapi_core/src/wapi_failure.erl
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
%%%
|
||||||
|
%%% Errors
|
||||||
|
|
||||||
|
-module(wapi_failure).
|
||||||
|
|
||||||
|
-type code() :: binary().
|
||||||
|
-type reason() :: binary().
|
||||||
|
|
||||||
|
-type failure() :: #{
|
||||||
|
code := code(),
|
||||||
|
reason => reason(),
|
||||||
|
sub => sub_failure()
|
||||||
|
}.
|
||||||
|
|
||||||
|
-type sub_failure() :: #{
|
||||||
|
code := code(),
|
||||||
|
sub => sub_failure()
|
||||||
|
}.
|
||||||
|
|
||||||
|
-export_type([code/0]).
|
||||||
|
-export_type([reason/0]).
|
||||||
|
-export_type([failure/0]).
|
||||||
|
-export_type([sub_failure/0]).
|
||||||
|
|
||||||
|
-export([code/1]).
|
||||||
|
-export([reason/1]).
|
||||||
|
-export([sub_failure/1]).
|
||||||
|
|
||||||
|
%% API
|
||||||
|
|
||||||
|
-spec code(failure() | sub_failure()) -> code().
|
||||||
|
code(#{code := Code}) ->
|
||||||
|
Code.
|
||||||
|
|
||||||
|
-spec reason(failure()) -> reason() | undefined.
|
||||||
|
reason(Failure) ->
|
||||||
|
maps:get(reason, Failure, undefined).
|
||||||
|
|
||||||
|
-spec sub_failure(failure() | sub_failure()) -> sub_failure() | undefined.
|
||||||
|
sub_failure(Failure) ->
|
||||||
|
maps:get(sub, Failure, undefined).
|
20
apps/wapi_core/src/wapi_id.erl
Normal file
20
apps/wapi_core/src/wapi_id.erl
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
%%
|
||||||
|
%% Identificators-related utils
|
||||||
|
|
||||||
|
-module(wapi_id).
|
||||||
|
|
||||||
|
-export([generate_snowflake_id/0]).
|
||||||
|
|
||||||
|
%% Types
|
||||||
|
|
||||||
|
-type binary_id() :: binary().
|
||||||
|
|
||||||
|
-export_type([binary_id/0]).
|
||||||
|
|
||||||
|
%% API
|
||||||
|
|
||||||
|
-spec generate_snowflake_id() ->
|
||||||
|
binary_id().
|
||||||
|
generate_snowflake_id() ->
|
||||||
|
<<ID:64>> = snowflake:new(),
|
||||||
|
genlib_format:format_int_base(ID, 62).
|
119
apps/wapi_core/src/wapi_pipeline.erl
Normal file
119
apps/wapi_core/src/wapi_pipeline.erl
Normal file
@ -0,0 +1,119 @@
|
|||||||
|
%%%
|
||||||
|
%%% Pipeline
|
||||||
|
%%%
|
||||||
|
%%% TODO
|
||||||
|
%%% - A simple `ok` as a possible result only make everything more complex
|
||||||
|
%%%
|
||||||
|
|
||||||
|
-module(wapi_pipeline).
|
||||||
|
|
||||||
|
-export([do/1]).
|
||||||
|
-export([do/2]).
|
||||||
|
-export([unwrap/1]).
|
||||||
|
-export([unwrap/2]).
|
||||||
|
-export([expect/2]).
|
||||||
|
-export([flip/1]).
|
||||||
|
-export([valid/2]).
|
||||||
|
|
||||||
|
-export([with/3]).
|
||||||
|
|
||||||
|
%%
|
||||||
|
|
||||||
|
-type thrown(_E) ::
|
||||||
|
no_return().
|
||||||
|
|
||||||
|
-type result(T, E) ::
|
||||||
|
{ok, T} | {error, E}.
|
||||||
|
|
||||||
|
-spec do(fun(() -> ok | T | thrown(E))) ->
|
||||||
|
ok | result(T, E).
|
||||||
|
|
||||||
|
do(Fun) ->
|
||||||
|
try Fun() of
|
||||||
|
ok ->
|
||||||
|
ok;
|
||||||
|
R ->
|
||||||
|
{ok, R}
|
||||||
|
catch
|
||||||
|
Thrown -> {error, Thrown}
|
||||||
|
end.
|
||||||
|
|
||||||
|
-spec do(Tag, fun(() -> ok | T | thrown(E))) ->
|
||||||
|
ok | result(T, {Tag, E}).
|
||||||
|
|
||||||
|
do(Tag, Fun) ->
|
||||||
|
do(fun () -> unwrap(Tag, do(Fun)) end).
|
||||||
|
|
||||||
|
-spec unwrap
|
||||||
|
(ok) -> ok;
|
||||||
|
({ok, V}) -> V;
|
||||||
|
({error, E}) -> thrown(E).
|
||||||
|
|
||||||
|
unwrap(ok) ->
|
||||||
|
ok;
|
||||||
|
unwrap({ok, V}) ->
|
||||||
|
V;
|
||||||
|
unwrap({error, E}) ->
|
||||||
|
throw(E).
|
||||||
|
|
||||||
|
-spec expect
|
||||||
|
(_E, ok) -> ok;
|
||||||
|
(_E, {ok, V}) -> V;
|
||||||
|
( E, {error, _}) -> thrown(E).
|
||||||
|
|
||||||
|
expect(_, ok) ->
|
||||||
|
ok;
|
||||||
|
expect(_, {ok, V}) ->
|
||||||
|
V;
|
||||||
|
expect(E, {error, _}) ->
|
||||||
|
throw(E).
|
||||||
|
|
||||||
|
-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}).
|
||||||
|
|
||||||
|
unwrap(_, ok) ->
|
||||||
|
ok;
|
||||||
|
unwrap(_, {ok, V}) ->
|
||||||
|
V;
|
||||||
|
unwrap(Tag, {error, E}) ->
|
||||||
|
throw({Tag, E}).
|
||||||
|
|
||||||
|
-spec valid(T, T) ->
|
||||||
|
ok | {error, T}.
|
||||||
|
|
||||||
|
valid(V, V) ->
|
||||||
|
ok;
|
||||||
|
valid(_, V) ->
|
||||||
|
{error, V}.
|
||||||
|
|
||||||
|
%% TODO
|
||||||
|
%% - Too complex
|
||||||
|
%% - Not the right place
|
||||||
|
|
||||||
|
-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 :: _.
|
||||||
|
|
||||||
|
with(Model, St, F) ->
|
||||||
|
case F(maps:get(Model, St, undefined)) of
|
||||||
|
{ok, Events0} when is_list(Events0) ->
|
||||||
|
Events1 = [{Model, Ev} || Ev <- Events0],
|
||||||
|
{ok, Events1};
|
||||||
|
{error, Reason} ->
|
||||||
|
{error, {Model, Reason}}
|
||||||
|
end.
|
71
apps/wapi_core/src/wapi_thrift_utils.erl
Normal file
71
apps/wapi_core/src/wapi_thrift_utils.erl
Normal file
@ -0,0 +1,71 @@
|
|||||||
|
-module(wapi_thrift_utils).
|
||||||
|
|
||||||
|
-export([serialize/2]).
|
||||||
|
-export([deserialize/2]).
|
||||||
|
|
||||||
|
-spec serialize(thrift_type(), term()) -> binary().
|
||||||
|
|
||||||
|
-type thrift_type() ::
|
||||||
|
thrift_base_type() |
|
||||||
|
thrift_collection_type() |
|
||||||
|
thrift_enum_type() |
|
||||||
|
thrift_struct_type().
|
||||||
|
|
||||||
|
-type thrift_base_type() ::
|
||||||
|
bool |
|
||||||
|
double |
|
||||||
|
i8 |
|
||||||
|
i16 |
|
||||||
|
i32 |
|
||||||
|
i64 |
|
||||||
|
string.
|
||||||
|
|
||||||
|
-type thrift_collection_type() ::
|
||||||
|
{list, thrift_type()} |
|
||||||
|
{set, thrift_type()} |
|
||||||
|
{map, thrift_type(), thrift_type()}.
|
||||||
|
|
||||||
|
-type thrift_enum_type() ::
|
||||||
|
{enum, thrift_type_ref()}.
|
||||||
|
|
||||||
|
-type thrift_struct_type() ::
|
||||||
|
{struct, thrift_struct_flavor(), thrift_type_ref() | thrift_struct_def()}.
|
||||||
|
|
||||||
|
-type thrift_struct_flavor() :: struct | union | exception.
|
||||||
|
|
||||||
|
-type thrift_type_ref() :: {module(), Name :: atom()}.
|
||||||
|
|
||||||
|
-type thrift_struct_def() :: list({
|
||||||
|
Tag :: pos_integer(),
|
||||||
|
Requireness :: required | optional | undefined,
|
||||||
|
Type :: thrift_struct_type(),
|
||||||
|
Name :: atom(),
|
||||||
|
Default :: any()
|
||||||
|
}).
|
||||||
|
|
||||||
|
serialize(Type, Data) ->
|
||||||
|
{ok, Trans} = thrift_membuffer_transport:new(),
|
||||||
|
{ok, Proto} = new_protocol(Trans),
|
||||||
|
case thrift_protocol:write(Proto, {Type, Data}) of
|
||||||
|
{NewProto, ok} ->
|
||||||
|
{_, {ok, Result}} = thrift_protocol:close_transport(NewProto),
|
||||||
|
Result;
|
||||||
|
{_NewProto, {error, Reason}} ->
|
||||||
|
erlang:error({thrift, {protocol, Reason}})
|
||||||
|
end.
|
||||||
|
|
||||||
|
-spec deserialize(thrift_type(), binary()) ->
|
||||||
|
term().
|
||||||
|
|
||||||
|
deserialize(Type, Data) ->
|
||||||
|
{ok, Trans} = thrift_membuffer_transport:new(Data),
|
||||||
|
{ok, Proto} = new_protocol(Trans),
|
||||||
|
case thrift_protocol:read(Proto, Type) of
|
||||||
|
{_NewProto, {ok, Result}} ->
|
||||||
|
Result;
|
||||||
|
{_NewProto, {error, Reason}} ->
|
||||||
|
erlang:error({thrift, {protocol, Reason}})
|
||||||
|
end.
|
||||||
|
|
||||||
|
new_protocol(Trans) ->
|
||||||
|
thrift_binary_protocol:new(Trans, [{strict_read, true}, {strict_write, true}]).
|
84
apps/wapi_core/src/wapi_time.erl
Normal file
84
apps/wapi_core/src/wapi_time.erl
Normal file
@ -0,0 +1,84 @@
|
|||||||
|
%%%
|
||||||
|
%%% A matter of time.
|
||||||
|
|
||||||
|
-module(wapi_time).
|
||||||
|
|
||||||
|
-export([now/0]).
|
||||||
|
-export([to_rfc3339/1]).
|
||||||
|
-export([from_rfc3339/1]).
|
||||||
|
-export([add_interval/2]).
|
||||||
|
|
||||||
|
-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 datetime_interval() :: {date(), time()}.
|
||||||
|
|
||||||
|
%% API
|
||||||
|
|
||||||
|
-spec now() -> timestamp_ms().
|
||||||
|
now() ->
|
||||||
|
erlang:system_time(millisecond).
|
||||||
|
|
||||||
|
-spec to_rfc3339(timestamp_ms()) -> binary().
|
||||||
|
to_rfc3339(Timestamp) ->
|
||||||
|
genlib_rfc3339:format_relaxed(Timestamp, millisecond).
|
||||||
|
|
||||||
|
-spec from_rfc3339(binary()) -> timestamp_ms().
|
||||||
|
from_rfc3339(BTimestamp) ->
|
||||||
|
genlib_rfc3339:parse(BTimestamp, millisecond).
|
||||||
|
|
||||||
|
-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.
|
||||||
|
|
||||||
|
|
||||||
|
%% 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))).
|
||||||
|
|
||||||
|
-spec add_second_interval_test() -> _.
|
||||||
|
add_second_interval_test() ->
|
||||||
|
Timestamp = wapi_time:now(),
|
||||||
|
NewTimestamp = add_interval(Timestamp, {{0, 0, 0}, {0, 0, 1}}),
|
||||||
|
?assertEqual(Timestamp + 1000, NewTimestamp).
|
||||||
|
|
||||||
|
-spec add_minute_interval_test() -> _.
|
||||||
|
add_minute_interval_test() ->
|
||||||
|
Timestamp = wapi_time:now(),
|
||||||
|
NewTimestamp = add_interval(Timestamp, {{0, 0, 0}, {0, 1, 0}}),
|
||||||
|
?assertEqual(Timestamp + 60*1000, NewTimestamp).
|
||||||
|
|
||||||
|
-spec add_hour_interval_test() -> _.
|
||||||
|
add_hour_interval_test() ->
|
||||||
|
Timestamp = wapi_time:now(),
|
||||||
|
NewTimestamp = add_interval(Timestamp, {{0, 0, 0}, {1, 0, 0}}),
|
||||||
|
?assertEqual(Timestamp + 60*60*1000, NewTimestamp).
|
||||||
|
|
||||||
|
-spec add_day_interval_test() -> _.
|
||||||
|
add_day_interval_test() ->
|
||||||
|
Timestamp = wapi_time:now(),
|
||||||
|
NewTimestamp = add_interval(Timestamp, {{0, 0, 1}, {0, 0, 0}}),
|
||||||
|
?assertEqual(Timestamp + 24*60*60*1000, NewTimestamp).
|
||||||
|
|
||||||
|
-endif.
|
Loading…
Reference in New Issue
Block a user