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,
|
||||
swag_server_wallet,
|
||||
swag_client_payres,
|
||||
party_client,
|
||||
scoper,
|
||||
jose,
|
||||
jsx,
|
||||
@ -29,7 +30,6 @@
|
||||
snowflake,
|
||||
woody_user_identity,
|
||||
payproc_errors,
|
||||
ff_server,
|
||||
uac
|
||||
]},
|
||||
{env, []}
|
||||
|
@ -97,7 +97,7 @@ get_context_from_state(withdrawal, #wthd_WithdrawalState{context = Context}) ->
|
||||
Context.
|
||||
|
||||
get_owner(ContextThrift) ->
|
||||
Context = ff_codec:unmarshal(context, ContextThrift),
|
||||
Context = wapi_codec:unmarshal(context, ContextThrift),
|
||||
wapi_backend_utils:get_from_ctx(<<"owner">>, Context).
|
||||
|
||||
is_resource_owner(Owner, HandlerCtx) ->
|
||||
|
@ -6,8 +6,18 @@
|
||||
-define(BENDER_DOMAIN, <<"wapi">>).
|
||||
|
||||
%% Context
|
||||
-type md() :: ff_entity_context:md().
|
||||
-type context() :: ff_entity_context:context().
|
||||
-type context() :: #{namespace() => md()}.
|
||||
-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 id() :: binary().
|
||||
-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,
|
||||
exp_date => {Month, Year}
|
||||
}}},
|
||||
{ok, ff_codec:marshal(resource, CostructedResource)};
|
||||
{ok, wapi_codec:marshal(resource, CostructedResource)};
|
||||
{error, {decryption_failed, _} = Error} ->
|
||||
logger:warning("Resource token decryption failed: ~p", [Error]),
|
||||
{error, invalid_resource_token}
|
||||
@ -133,7 +133,7 @@ when Type =:= <<"CryptoWalletDestinationResource">> ->
|
||||
id => CryptoWalletID,
|
||||
currency => marshal_crypto_currency_data(Resource)
|
||||
})}},
|
||||
{ok, ff_codec:marshal(resource, CostructedResource)}.
|
||||
{ok, wapi_codec:marshal(resource, CostructedResource)}.
|
||||
|
||||
service_call(Params, Context) ->
|
||||
wapi_handler_utils:service_call(Params, Context).
|
||||
@ -167,13 +167,13 @@ marshal(resource, #{
|
||||
bin => maps:get(<<"bin">>, BankCard),
|
||||
masked_pan => maps:get(<<"lastDigits">>, BankCard)
|
||||
}}},
|
||||
ff_codec:marshal(resource, Resource);
|
||||
wapi_codec:marshal(resource, Resource);
|
||||
|
||||
marshal(context, Context) ->
|
||||
ff_codec:marshal(context, Context);
|
||||
wapi_codec:marshal(context, Context);
|
||||
|
||||
marshal(T, V) ->
|
||||
ff_codec:marshal(T, V).
|
||||
wapi_codec:marshal(T, V).
|
||||
|
||||
maybe_marshal(_, undefined) ->
|
||||
undefined;
|
||||
@ -243,10 +243,10 @@ unmarshal(resource, {crypto_wallet, #'ResourceCryptoWallet'{crypto_wallet = #'Cr
|
||||
});
|
||||
|
||||
unmarshal(context, Context) ->
|
||||
ff_codec:unmarshal(context, Context);
|
||||
wapi_codec:unmarshal(context, Context);
|
||||
|
||||
unmarshal(T, V) ->
|
||||
ff_codec:unmarshal(T, V).
|
||||
wapi_codec:unmarshal(T, V).
|
||||
|
||||
maybe_unmarshal(_, undefined) ->
|
||||
undefined;
|
||||
|
@ -69,10 +69,10 @@ process_request(Tag, OperationID, Req, SwagContext, Opts, WoodyContext) ->
|
||||
_ = logger:info("Processing request ~p", [OperationID]),
|
||||
try
|
||||
%% 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),
|
||||
Handler = get_handler(Tag, genlib_app:env(?APP, transport)),
|
||||
Handler = get_handler(Tag),
|
||||
case wapi_auth:authorize_operation(OperationID, Req, Context) of
|
||||
ok ->
|
||||
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}} ->
|
||||
process_woody_error(Source, Class, Details)
|
||||
after
|
||||
ff_context:cleanup()
|
||||
wapi_context:cleanup()
|
||||
end.
|
||||
|
||||
-spec throw_result(request_result()) ->
|
||||
@ -95,9 +95,8 @@ process_request(Tag, OperationID, Req, SwagContext, Opts, WoodyContext) ->
|
||||
throw_result(Res) ->
|
||||
erlang:throw({?request_result, Res}).
|
||||
|
||||
get_handler(wallet, thrift) -> wapi_wallet_thrift_handler;
|
||||
get_handler(wallet, _) -> wapi_wallet_handler;
|
||||
get_handler(payres, _) -> wapi_payres_handler.
|
||||
get_handler(wallet) -> wapi_wallet_handler;
|
||||
get_handler(payres) -> wapi_payres_handler.
|
||||
|
||||
-spec create_woody_context(tag(), req_data(), wapi_auth:context(), opts()) ->
|
||||
woody_context:ctx().
|
||||
@ -142,11 +141,11 @@ process_woody_error(_Source, resource_unavailable, _Details) ->
|
||||
process_woody_error(_Source, result_unknown, _Details) ->
|
||||
wapi_handler_utils:reply_error(504).
|
||||
|
||||
-spec create_ff_context(woody_context:ctx(), opts()) ->
|
||||
ff_context:context().
|
||||
create_ff_context(WoodyContext, Opts) ->
|
||||
-spec create_wapi_context(woody_context:ctx(), opts()) ->
|
||||
wapi_context:context().
|
||||
create_wapi_context(WoodyContext, Opts) ->
|
||||
ContextOptions = #{
|
||||
woody_context => WoodyContext,
|
||||
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) ->
|
||||
ff_codec:marshal(context, Ctx);
|
||||
wapi_codec:marshal(context, Ctx);
|
||||
|
||||
marshal(proof_type, <<"RUSDomesticPassport">>) ->
|
||||
rus_domestic_passport;
|
||||
@ -363,7 +363,7 @@ marshal(proof_type, <<"RUSRetireeInsuranceCertificate">>) ->
|
||||
rus_retiree_insurance_cert;
|
||||
|
||||
marshal(T, V) ->
|
||||
ff_codec:marshal(T, V).
|
||||
wapi_codec:marshal(T, V).
|
||||
|
||||
%%
|
||||
|
||||
@ -463,7 +463,7 @@ unmarshal(blocking, blocked) ->
|
||||
true;
|
||||
|
||||
unmarshal(T, V) ->
|
||||
ff_codec:unmarshal(T, V).
|
||||
wapi_codec:unmarshal(T, V).
|
||||
|
||||
maybe_unmarshal(_, 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,
|
||||
<<"name">> => Response#fistfulstat_StatIdentity.name,
|
||||
<<"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,
|
||||
<<"level">> => Response#fistfulstat_StatIdentity.identity_level,
|
||||
<<"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({usdt, _}) -> <<"USDT">>;
|
||||
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().
|
||||
get_unique_id() ->
|
||||
ff_id:generate_snowflake_id().
|
||||
wapi_id:generate_snowflake_id().
|
||||
%%
|
||||
|
||||
-ifdef(TEST).
|
||||
@ -273,25 +273,25 @@ get_path_test() ->
|
||||
|
||||
-spec mask_test() -> _.
|
||||
mask_test() ->
|
||||
?assertEqual(<<"Жур">>, mask(leading, 0, $*, <<"Жур">>)),
|
||||
?assertEqual(<<"*ур">>, mask(leading, 1, $*, <<"Жур">>)),
|
||||
?assertEqual(<<"**р">>, mask(leading, 2, $*, <<"Жур">>)),
|
||||
?assertEqual(<<"***">>, mask(leading, 3, $*, <<"Жур">>)),
|
||||
?assertEqual(<<"Жур">>, mask(trailing, 0, $*, <<"Жур">>)),
|
||||
?assertEqual(<<"Жу*">>, mask(trailing, 1, $*, <<"Жур">>)),
|
||||
?assertEqual(<<"Ж**">>, mask(trailing, 2, $*, <<"Жур">>)),
|
||||
?assertEqual(<<"***">>, mask(trailing, 3, $*, <<"Жур">>)).
|
||||
?assertEqual(<<"Хуй">>, mask(leading, 0, $*, <<"Хуй">>)),
|
||||
?assertEqual(<<"*уй">>, mask(leading, 1, $*, <<"Хуй">>)),
|
||||
?assertEqual(<<"**й">>, mask(leading, 2, $*, <<"Хуй">>)),
|
||||
?assertEqual(<<"***">>, mask(leading, 3, $*, <<"Хуй">>)),
|
||||
?assertEqual(<<"Хуй">>, mask(trailing, 0, $*, <<"Хуй">>)),
|
||||
?assertEqual(<<"Ху*">>, mask(trailing, 1, $*, <<"Хуй">>)),
|
||||
?assertEqual(<<"Х**">>, mask(trailing, 2, $*, <<"Хуй">>)),
|
||||
?assertEqual(<<"***">>, mask(trailing, 3, $*, <<"Хуй">>)).
|
||||
|
||||
-spec mask_and_keep_test() -> _.
|
||||
mask_and_keep_test() ->
|
||||
?assertEqual(<<"***">>, mask_and_keep(leading, 0, $*, <<"Жур">>)),
|
||||
?assertEqual(<<"Ж**">>, mask_and_keep(leading, 1, $*, <<"Жур">>)),
|
||||
?assertEqual(<<"Жу*">>, mask_and_keep(leading, 2, $*, <<"Жур">>)),
|
||||
?assertEqual(<<"Жур">>, mask_and_keep(leading, 3, $*, <<"Жур">>)),
|
||||
?assertEqual(<<"***">>, mask_and_keep(trailing, 0, $*, <<"Жур">>)),
|
||||
?assertEqual(<<"**р">>, mask_and_keep(trailing, 1, $*, <<"Жур">>)),
|
||||
?assertEqual(<<"*ур">>, mask_and_keep(trailing, 2, $*, <<"Жур">>)),
|
||||
?assertEqual(<<"Жур">>, mask_and_keep(trailing, 3, $*, <<"Жур">>)).
|
||||
?assertEqual(<<"***">>, mask_and_keep(leading, 0, $*, <<"Хуй">>)),
|
||||
?assertEqual(<<"Х**">>, mask_and_keep(leading, 1, $*, <<"Хуй">>)),
|
||||
?assertEqual(<<"Ху*">>, mask_and_keep(leading, 2, $*, <<"Хуй">>)),
|
||||
?assertEqual(<<"Хуй">>, mask_and_keep(leading, 3, $*, <<"Хуй">>)),
|
||||
?assertEqual(<<"***">>, mask_and_keep(trailing, 0, $*, <<"Хуй">>)),
|
||||
?assertEqual(<<"**й">>, mask_and_keep(trailing, 1, $*, <<"Хуй">>)),
|
||||
?assertEqual(<<"*уй">>, mask_and_keep(trailing, 2, $*, <<"Хуй">>)),
|
||||
?assertEqual(<<"Хуй">>, mask_and_keep(trailing, 3, $*, <<"Хуй">>)).
|
||||
|
||||
-spec parse_deadline_test() -> _.
|
||||
parse_deadline_test() ->
|
||||
|
@ -113,10 +113,10 @@ marshal(body, #{
|
||||
};
|
||||
|
||||
marshal(context, Ctx) ->
|
||||
ff_codec:marshal(context, Ctx);
|
||||
wapi_codec:marshal(context, Ctx);
|
||||
|
||||
marshal(T, V) ->
|
||||
ff_codec:marshal(T, V).
|
||||
wapi_codec:marshal(T, V).
|
||||
|
||||
unmarshal(transfer, #w2w_transfer_W2WTransferState{
|
||||
id = ID,
|
||||
@ -157,7 +157,7 @@ unmarshal(transfer_status, {failed, #w2w_status_Failed{failure = Failure}}) ->
|
||||
};
|
||||
|
||||
unmarshal(T, V) ->
|
||||
ff_codec:unmarshal(T, V).
|
||||
wapi_codec:unmarshal(T, V).
|
||||
|
||||
maybe_unmarshal(_T, undefined) ->
|
||||
undefined;
|
||||
|
@ -141,10 +141,10 @@ marshal(account_params, {IdentityID, CurrencyID}) ->
|
||||
};
|
||||
|
||||
marshal(context, Ctx) ->
|
||||
ff_codec:marshal(context, Ctx);
|
||||
wapi_codec:marshal(context, Ctx);
|
||||
|
||||
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) ->
|
||||
ff_codec:unmarshal(context, Ctx);
|
||||
wapi_codec:unmarshal(context, Ctx);
|
||||
|
||||
unmarshal(T, V) ->
|
||||
ff_codec:unmarshal(T, V).
|
||||
wapi_codec:unmarshal(T, V).
|
||||
|
||||
maybe_unmarshal(_, undefined) ->
|
||||
undefined;
|
||||
|
@ -25,13 +25,13 @@
|
||||
-spec authorize_api_key(operation_id(), api_key(), handler_opts()) ->
|
||||
false | {true, wapi_auth:context()}.
|
||||
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
|
||||
{ok, Context0} ->
|
||||
Context = wapi_auth:create_wapi_context(Context0),
|
||||
{true, Context};
|
||||
{error, Error} ->
|
||||
_ = logger:info("API Key authorization failed for ~p due to ~p", [OperationID, Error]),
|
||||
_ = logger:info("API Key authorization failed: ~p", [Error]),
|
||||
false
|
||||
end.
|
||||
|
||||
@ -40,50 +40,8 @@ authorize_api_key(OperationID, ApiKey, _Opts) ->
|
||||
handle_request(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()) ->
|
||||
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
|
||||
process_request('ListIdentities', Params, Context, _Opts) ->
|
||||
@ -101,32 +59,26 @@ process_request('ListIdentities', Params, Context, _Opts) ->
|
||||
})
|
||||
end;
|
||||
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);
|
||||
{error, {identity, notfound}} -> wapi_handler_utils:reply_ok(404);
|
||||
{error, {identity, unauthorized}} -> wapi_handler_utils:reply_ok(404)
|
||||
end;
|
||||
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}} ->
|
||||
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}} ->
|
||||
wapi_handler_utils:reply_ok(422, wapi_handler_utils:get_error_msg(<<"No such provider">>));
|
||||
{error, {identity_class, notfound}} ->
|
||||
wapi_handler_utils:reply_ok(422, wapi_handler_utils:get_error_msg(<<"No such identity class">>));
|
||||
{error, {external_id_conflict, ID, ExternalID}} ->
|
||||
wapi_handler_utils:logic_error(external_id_conflict, {ID, ExternalID});
|
||||
{error, {email, notfound}} ->
|
||||
wapi_handler_utils:reply_error(400, #{
|
||||
<<"errorType">> => <<"NotFound">>,
|
||||
<<"name">> => <<"email">>,
|
||||
<<"description">> => <<"No email in JWT">>
|
||||
})
|
||||
{error, inaccessible} ->
|
||||
wapi_handler_utils:reply_ok(422, wapi_handler_utils:get_error_msg(<<"Identity inaccessible">>));
|
||||
{error, {external_id_conflict, ID}} ->
|
||||
wapi_handler_utils:reply_error(409, #{<<"id">> => ID})
|
||||
end;
|
||||
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);
|
||||
{error, {identity, notfound}} -> wapi_handler_utils:reply_ok(404);
|
||||
{error, {identity, unauthorized}} -> wapi_handler_utils:reply_ok(404)
|
||||
@ -135,7 +87,7 @@ process_request('StartIdentityChallenge', #{
|
||||
'identityID' := IdentityId,
|
||||
'IdentityChallenge' := Params
|
||||
}, 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}} ->
|
||||
wapi_handler_utils:reply_ok(202, Challenge, get_location('GetIdentityChallenge', [ChallengeId], Opts));
|
||||
{error, {identity, notfound}} ->
|
||||
@ -144,7 +96,9 @@ process_request('StartIdentityChallenge', #{
|
||||
wapi_handler_utils:reply_ok(404);
|
||||
{error, {challenge, conflict}} ->
|
||||
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);
|
||||
{error, {challenge, {class, notfound}}} ->
|
||||
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">>));
|
||||
{error, {challenge, {proof, insufficient}}} ->
|
||||
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:get_error_msg(<<"Illegal identification type for current identity level">>)
|
||||
)
|
||||
@ -162,20 +116,20 @@ process_request('GetIdentityChallenge', #{
|
||||
'identityID' := IdentityId,
|
||||
'challengeID' := ChallengeId
|
||||
}, 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);
|
||||
{error, {identity, notfound}} -> wapi_handler_utils:reply_ok(404);
|
||||
{error, {identity, unauthorized}} -> wapi_handler_utils:reply_ok(404);
|
||||
{error, {challenge, notfound}} -> wapi_handler_utils:reply_ok(404)
|
||||
end;
|
||||
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);
|
||||
{error, {identity, notfound}} -> wapi_handler_utils:reply_ok(404);
|
||||
{error, {identity, unauthorized}} -> wapi_handler_utils:reply_ok(404)
|
||||
end;
|
||||
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);
|
||||
{error, {identity, notfound}} -> 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;
|
||||
|
||||
%% Wallets
|
||||
|
||||
process_request('ListWallets', Params, Context, _Opts) ->
|
||||
case wapi_wallet_ff_backend:list_wallets(Params, Context) of
|
||||
{ok, {200, _, List}} -> wapi_handler_utils:reply_ok(200, List);
|
||||
{error, {Code, _, Error}} -> wapi_handler_utils:reply_error(Code, Error)
|
||||
case wapi_stat_backend:list_wallets(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('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);
|
||||
{error, {wallet, notfound}} -> wapi_handler_utils:reply_ok(404);
|
||||
{error, {wallet, unauthorized}} -> wapi_handler_utils:reply_ok(404)
|
||||
end;
|
||||
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);
|
||||
{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;
|
||||
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}} ->
|
||||
wapi_handler_utils:reply_ok(201, Wallet, get_location('GetWallet', [WalletId], Opts));
|
||||
{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">>));
|
||||
{error, {currency, notfound}} ->
|
||||
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">>));
|
||||
{error, {external_id_conflict, ID, ExternalID}} ->
|
||||
wapi_handler_utils:logic_error(external_id_conflict, {ID, ExternalID});
|
||||
{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">>))
|
||||
{error, {external_id_conflict, ID}} ->
|
||||
wapi_handler_utils:reply_error(409, #{<<"id">> => ID})
|
||||
end;
|
||||
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);
|
||||
{error, {wallet, notfound}} -> wapi_handler_utils:reply_ok(404);
|
||||
{error, {wallet, unauthorized}} -> wapi_handler_utils:reply_ok(404)
|
||||
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) ->
|
||||
case wapi_stat_backend:list_destinations(Params, Context) of
|
||||
{ok, Result} -> wapi_handler_utils:reply_ok(200, Result);
|
||||
@ -265,13 +204,13 @@ process_request('ListDestinations', Params, Context, _Opts) ->
|
||||
})
|
||||
end;
|
||||
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);
|
||||
{error, {destination, notfound}} -> wapi_handler_utils:reply_ok(404);
|
||||
{error, {destination, unauthorized}} -> wapi_handler_utils:reply_ok(404)
|
||||
end;
|
||||
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} ->
|
||||
wapi_handler_utils:reply_ok(200, Destination);
|
||||
{error, {external_id, {unknown_external_id, ExternalID}}} ->
|
||||
@ -282,7 +221,7 @@ process_request('GetDestinationByExternalID', #{'externalID' := ExternalID}, Con
|
||||
wapi_handler_utils:reply_ok(404)
|
||||
end;
|
||||
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}} ->
|
||||
wapi_handler_utils:reply_ok(201, Destination, get_location('GetDestination', [DestinationId], Opts));
|
||||
{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">>));
|
||||
{error, {currency, notfound}} ->
|
||||
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">>));
|
||||
{error, {external_id_conflict, ID, ExternalID}} ->
|
||||
{error, {external_id_conflict, {ID, ExternalID}}} ->
|
||||
wapi_handler_utils:logic_error(external_id_conflict, {ID, ExternalID});
|
||||
{error, invalid} ->
|
||||
wapi_handler_utils:reply_ok(422, wapi_handler_utils:get_error_msg(<<"Invalid currency">>));
|
||||
{error, {invalid_resource_token, Type}} ->
|
||||
{error, invalid_resource_token} ->
|
||||
wapi_handler_utils:reply_error(400, #{
|
||||
<<"errorType">> => <<"InvalidResourceToken">>,
|
||||
<<"name">> => Type,
|
||||
<<"name">> => <<"BankCardDestinationResource">>,
|
||||
<<"description">> => <<"Specified resource token is invalid">>
|
||||
})
|
||||
end;
|
||||
@ -308,9 +245,9 @@ process_request('IssueDestinationGrant', #{
|
||||
'destinationID' := DestinationId,
|
||||
'DestinationGrantRequest' := #{<<"validUntil">> := Expiration}
|
||||
}, Context, _Opts) ->
|
||||
case wapi_wallet_ff_backend:get_destination(DestinationId, Context) of
|
||||
case wapi_destination_backend:get(DestinationId, Context) of
|
||||
{ok, _} ->
|
||||
case wapi_backend_utils:issue_grant_token({destinations, DestinationId}, Expiration, Context) of
|
||||
case issue_grant_token({destinations, DestinationId}, Expiration, Context) of
|
||||
{ok, Token} ->
|
||||
wapi_handler_utils:reply_ok(201, #{
|
||||
<<"token">> => Token,
|
||||
@ -326,40 +263,59 @@ process_request('IssueDestinationGrant', #{
|
||||
{error, {destination, unauthorized}} ->
|
||||
wapi_handler_utils:reply_ok(404)
|
||||
end;
|
||||
process_request('CreateWithdrawal', #{'WithdrawalParameters' := Params}, Context, Opts) ->
|
||||
case wapi_wallet_ff_backend:create_withdrawal(Params, Context) of
|
||||
{ok, Withdrawal = #{<<"id">> := WithdrawalId}} ->
|
||||
wapi_handler_utils:reply_ok(202, Withdrawal, get_location('GetWithdrawal', [WithdrawalId], Opts));
|
||||
{error, {source, notfound}} ->
|
||||
wapi_handler_utils:reply_ok(422, wapi_handler_utils:get_error_msg(<<"No such wallet">>));
|
||||
|
||||
%% Withdrawals
|
||||
|
||||
process_request('CreateQuote', Params, Context, _Opts) ->
|
||||
case wapi_withdrawal_backend:create_quote(Params, Context) of
|
||||
{ok, Quote} ->
|
||||
wapi_handler_utils:reply_ok(202, Quote);
|
||||
{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, {destination, unauthorized}} ->
|
||||
wapi_handler_utils:reply_ok(422, wapi_handler_utils:get_error_msg(<<"Destination unauthorized">>));
|
||||
{error, {provider, notfound}} ->
|
||||
wapi_handler_utils:reply_ok(422, wapi_handler_utils:get_error_msg(<<"No such provider">>));
|
||||
{error, {external_id_conflict, ID, ExternalID}} ->
|
||||
{error, {wallet, notfound}} ->
|
||||
wapi_handler_utils:reply_ok(422, wapi_handler_utils:get_error_msg(<<"No such wallet">>));
|
||||
{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});
|
||||
{error, {wallet, notfound}} ->
|
||||
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">>));
|
||||
{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, _}} ->
|
||||
wapi_handler_utils:reply_ok(422,
|
||||
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: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: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">>)
|
||||
);
|
||||
{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;
|
||||
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} ->
|
||||
wapi_handler_utils:reply_ok(200, Withdrawal);
|
||||
{error, {withdrawal, {unknown_withdrawal, WithdrawalId}}} ->
|
||||
{error, {withdrawal, notfound}} ->
|
||||
wapi_handler_utils:reply_ok(404);
|
||||
{error, {withdrawal, unauthorized}} ->
|
||||
wapi_handler_utils:reply_ok(404)
|
||||
end;
|
||||
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} ->
|
||||
wapi_handler_utils:reply_ok(200, Withdrawal);
|
||||
{error, {external_id, {unknown_external_id, ExternalID}}} ->
|
||||
wapi_handler_utils:reply_ok(404);
|
||||
{error, {withdrawal, {unknown_withdrawal, _WithdrawalId}}} ->
|
||||
{error, {withdrawal, notfound}} ->
|
||||
wapi_handler_utils:reply_ok(404);
|
||||
{error, {withdrawal, unauthorized}} ->
|
||||
wapi_handler_utils:reply_ok(404)
|
||||
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) ->
|
||||
case wapi_wallet_ff_backend:get_withdrawal_events(Params, Context) of
|
||||
case wapi_withdrawal_backend:get_events(Params, Context) of
|
||||
{ok, Events} ->
|
||||
wapi_handler_utils:reply_ok(200, Events);
|
||||
{error, {withdrawal, {unknown_withdrawal, _WithdrawalId}}} ->
|
||||
{error, {withdrawal, notfound}} ->
|
||||
wapi_handler_utils:reply_ok(404);
|
||||
{error, {withdrawal, unauthorized}} ->
|
||||
wapi_handler_utils:reply_ok(404)
|
||||
@ -430,457 +402,62 @@ process_request('GetWithdrawalEvents', #{
|
||||
'withdrawalID' := WithdrawalId,
|
||||
'eventID' := EventId
|
||||
}, 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} ->
|
||||
wapi_handler_utils:reply_ok(200, Event);
|
||||
{error, {withdrawal, {unknown_withdrawal, WithdrawalId}}} ->
|
||||
{error, {withdrawal, notfound}} ->
|
||||
wapi_handler_utils:reply_ok(404);
|
||||
{error, {withdrawal, unauthorized}} ->
|
||||
wapi_handler_utils:reply_ok(404);
|
||||
{error, {event, notfound}} ->
|
||||
wapi_handler_utils:reply_ok(404)
|
||||
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
|
||||
|
||||
process_request('ListDeposits', Params, Context, _Opts) ->
|
||||
case wapi_wallet_ff_backend:list_deposits(Params, Context) of
|
||||
{ok, {200, _, List}} -> wapi_handler_utils:reply_ok(200, List);
|
||||
{error, {Code, _, Error}} -> wapi_handler_utils:reply_error(Code, Error)
|
||||
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}} ->
|
||||
case wapi_stat_backend:list_deposits(Params, Context) of
|
||||
{ok, List} -> wapi_handler_utils:reply_ok(200, List);
|
||||
{error, {invalid, Errors}} ->
|
||||
wapi_handler_utils:reply_error(400, #{
|
||||
<<"errorType">> => <<"InvalidResourceToken">>,
|
||||
<<"name">> => Type,
|
||||
<<"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
|
||||
<<"errorType">> => <<"NoMatch">>,
|
||||
<<"description">> => Errors
|
||||
});
|
||||
{error, {p2p_template, unauthorized}} ->
|
||||
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}} ->
|
||||
{error, {bad_token, Reason}} ->
|
||||
wapi_handler_utils:reply_error(400, #{
|
||||
<<"errorType">> => <<"InvalidResourceToken">>,
|
||||
<<"name">> => Type,
|
||||
<<"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">>
|
||||
<<"errorType">> => <<"InvalidToken">>,
|
||||
<<"description">> => Reason
|
||||
})
|
||||
end;
|
||||
|
||||
%% W2W
|
||||
|
||||
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} ->
|
||||
wapi_handler_utils:reply_ok(202, W2WTransfer);
|
||||
{error, {wallet_from, notfound}} ->
|
||||
wapi_handler_utils:reply_ok(422,
|
||||
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}} ->
|
||||
wapi_handler_utils:reply_ok(422,
|
||||
wapi_handler_utils:get_error_msg(<<"No such wallet receiver">>));
|
||||
{error, {wallet_from, {inaccessible, _}}} ->
|
||||
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}}}} ->
|
||||
{error, not_allowed_currency} ->
|
||||
wapi_handler_utils:reply_ok(422,
|
||||
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: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;
|
||||
|
||||
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} ->
|
||||
wapi_handler_utils:reply_ok(200, W2WTransfer);
|
||||
{error, {w2w_transfer, unauthorized}} ->
|
||||
@ -895,13 +472,20 @@ get_location(OperationId, Params, Opts) ->
|
||||
#{path := PathSpec} = swag_server_wallet_router:get_operation(OperationId),
|
||||
wapi_handler_utils:get_location(PathSpec, Params, Opts).
|
||||
|
||||
-spec not_implemented() -> no_return().
|
||||
not_implemented() ->
|
||||
wapi_handler_utils:throw_not_implemented().
|
||||
issue_grant_token(TokenSpec, Expiration, Context) ->
|
||||
case get_expiration_deadline(Expiration) of
|
||||
{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_default_url_lifetime() ->
|
||||
Now = erlang:system_time(second),
|
||||
Lifetime = application:get_env(wapi, file_storage_url_lifetime, ?DEFAULT_URL_LIFETIME),
|
||||
genlib_rfc3339:format(Now + Lifetime, second).
|
||||
get_expiration_deadline(Expiration) ->
|
||||
{DateTime, MilliSec} = woody_deadline:from_binary(wapi_utils:to_universal_time(Expiration)),
|
||||
Deadline = genlib_time:daytime_to_unixtime(DateTime) + MilliSec div 1000,
|
||||
case genlib_time:unow() - Deadline < 0 of
|
||||
true ->
|
||||
{ok, Deadline};
|
||||
false ->
|
||||
{error, expired}
|
||||
end.
|
||||
|
@ -1,6 +1,13 @@
|
||||
-module(wapi_withdrawal_backend).
|
||||
|
||||
-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 handler_context() :: wapi_handler:context().
|
||||
@ -16,20 +23,34 @@
|
||||
{quote_invalid_wallet, _} |
|
||||
{quote, {invalid_body, _}} |
|
||||
{quote, {invalid_destination, _}} |
|
||||
{quote, token_expired} |
|
||||
{forbidden_currency, _} |
|
||||
{forbidden_amount, _} |
|
||||
{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}}.
|
||||
|
||||
-export([create/2]).
|
||||
-export([get/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").
|
||||
|
||||
%% 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()) ->
|
||||
{ok, response_data()} | {error, create_error()}.
|
||||
@ -76,9 +97,7 @@ create(Params, Context, HandlerContext) ->
|
||||
}} ->
|
||||
{error, {identity_providers_mismatch, {WalletProvider, DestinationProvider}}};
|
||||
{exception, #wthd_NoDestinationResourceInfo{}} ->
|
||||
{error, {destination_resource, {bin_data, not_found}}};
|
||||
{exception, Details} ->
|
||||
{error, Details}
|
||||
{error, {destination_resource, {bin_data, not_found}}}
|
||||
end.
|
||||
|
||||
-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}}}
|
||||
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
|
||||
%%
|
||||
|
||||
service_call(Params, Context) ->
|
||||
wapi_handler_utils:service_call(Params, Context).
|
||||
create_quote_token(Quote, WalletID, DestinationID, PartyID) ->
|
||||
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
|
||||
|
||||
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) ->
|
||||
do(fun() ->
|
||||
Params1 = unwrap(try_decode_quote_token(Params0)),
|
||||
@ -137,7 +306,7 @@ check_withdrawal_params(Params0, HandlerContext) ->
|
||||
try_decode_quote_token(Params = #{<<"quoteToken">> := QuoteToken}) ->
|
||||
do(fun() ->
|
||||
{_, _, 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">> => #{
|
||||
quote => Quote,
|
||||
wallet_id => WalletID,
|
||||
@ -239,12 +408,12 @@ maybe_check_quote_token(Params = #{<<"quoteToken">> := #{
|
||||
wallet_id := WalletID,
|
||||
destination_id := DestinationID,
|
||||
party_id := PartyID
|
||||
}}, Context) ->
|
||||
}}, HandlerContext) ->
|
||||
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(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}
|
||||
end);
|
||||
maybe_check_quote_token(Params, _Context) ->
|
||||
@ -267,9 +436,6 @@ check_quote_withdrawal(DestinationID, DestinationID) ->
|
||||
check_quote_withdrawal(_, DestinationID) ->
|
||||
{error, {quote, {invalid_destination, DestinationID}}}.
|
||||
|
||||
marshal_quote_body(Body) ->
|
||||
{genlib:to_int(maps:get(<<"amount">>, Body)), maps:get(<<"currency">>, Body)}.
|
||||
|
||||
%% Marshaling
|
||||
|
||||
marshal(withdrawal_params, Params = #{
|
||||
@ -286,34 +452,61 @@ marshal(withdrawal_params, Params = #{
|
||||
wallet_id = marshal(id, WalletID),
|
||||
destination_id = marshal(id, DestinationID),
|
||||
body = marshal_body(Body),
|
||||
quote = marshal_quote(Quote),
|
||||
quote = Quote,
|
||||
external_id = maybe_marshal(id, ExternalID),
|
||||
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) ->
|
||||
ff_codec:marshal(context, Context);
|
||||
wapi_codec:marshal(context, Context);
|
||||
|
||||
marshal(T, V) ->
|
||||
ff_codec:marshal(T, V).
|
||||
wapi_codec:marshal(T, V).
|
||||
|
||||
maybe_marshal(_, undefined) ->
|
||||
undefined;
|
||||
maybe_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) ->
|
||||
#'Cash'{
|
||||
amount = genlib:to_int(maps:get(<<"amount">>, Body)),
|
||||
currency = #'CurrencyRef'{
|
||||
symbolic_code = maps:get(<<"currency">>, Body)
|
||||
}
|
||||
currency = marshal_currency_ref(maps:get(<<"currency">>, Body))
|
||||
}.
|
||||
|
||||
marshal_quote(undefined) ->
|
||||
undefined;
|
||||
marshal_quote(Quote) ->
|
||||
ff_withdrawal_codec:marshal(quote, Quote).
|
||||
marshal_currency_ref(Currency) ->
|
||||
#'CurrencyRef'{
|
||||
symbolic_code = Currency
|
||||
}.
|
||||
|
||||
unmarshal({list, Type}, List) ->
|
||||
lists:map(fun(V) -> unmarshal(Type, V) end, List);
|
||||
|
||||
unmarshal(withdrawal, #wthd_WithdrawalState{
|
||||
id = ID,
|
||||
@ -336,8 +529,31 @@ unmarshal(withdrawal, #wthd_WithdrawalState{
|
||||
<<"metadata">> => UnmarshaledMetadata
|
||||
}, 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) ->
|
||||
ff_codec:unmarshal(T, V).
|
||||
wapi_codec:unmarshal(T, V).
|
||||
|
||||
maybe_unmarshal(_, undefined) ->
|
||||
undefined;
|
||||
|
@ -1,5 +1,7 @@
|
||||
-module(wapi_withdrawal_quote).
|
||||
|
||||
-include_lib("fistful_proto/include/ff_proto_withdrawal_thrift.hrl").
|
||||
|
||||
-export([create_token_payload/4]).
|
||||
-export([decode_token_payload/1]).
|
||||
|
||||
@ -17,23 +19,26 @@
|
||||
-type wallet_id() :: binary().
|
||||
-type destination_id() :: binary() | undefined.
|
||||
-type party_id() :: binary().
|
||||
-type quote() :: ff_withdrawal:quote().
|
||||
-type quote() :: ff_proto_withdrawal_thrift:'Quote'().
|
||||
|
||||
%% API
|
||||
|
||||
-spec create_token_payload(quote(), wallet_id(), destination_id(), party_id()) ->
|
||||
token_payload().
|
||||
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(#{
|
||||
<<"version">> => 2,
|
||||
<<"walletID">> => WalletID,
|
||||
<<"destinationID">> => DestinationID,
|
||||
<<"partyID">> => PartyID,
|
||||
<<"quote">> => encode_quote(Quote)
|
||||
<<"quote">> => Quote
|
||||
}).
|
||||
|
||||
-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) ->
|
||||
#{
|
||||
<<"version">> := 2,
|
||||
@ -43,42 +48,9 @@ decode_token_payload(#{<<"version">> := 2} = Payload) ->
|
||||
} = Payload,
|
||||
Quote = decode_quote(EncodedQuote),
|
||||
DestinationID = maps:get(<<"destinationID">>, Payload, undefined),
|
||||
{ok, Quote, WalletID, DestinationID, PartyID};
|
||||
decode_token_payload(#{<<"version">> := 1} = Payload) ->
|
||||
#{
|
||||
<<"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}.
|
||||
{ok, {Quote, WalletID, DestinationID, PartyID}};
|
||||
decode_token_payload(#{<<"version">> := 1} = _Payload) ->
|
||||
{error, token_expired}.
|
||||
|
||||
%% Internals
|
||||
|
||||
@ -86,7 +58,7 @@ decode_token_payload(#{<<"version">> := 1} = Payload) ->
|
||||
token_payload().
|
||||
encode_quote(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).
|
||||
|
||||
-spec decode_quote(token_payload()) ->
|
||||
@ -94,15 +66,7 @@ encode_quote(Quote) ->
|
||||
decode_quote(Encoded) ->
|
||||
Type = {struct, struct, {ff_proto_withdrawal_thrift, 'Quote'}},
|
||||
Bin = base64:decode(Encoded),
|
||||
Thrift = ff_proto_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}.
|
||||
|
||||
wapi_thrift_utils:deserialize(Type, Bin).
|
||||
|
||||
-ifdef(TEST).
|
||||
-include_lib("eunit/include/eunit.hrl").
|
||||
@ -130,9 +94,10 @@ payload_symmetry_test() ->
|
||||
domain_revision => 1,
|
||||
party_revision => 2
|
||||
},
|
||||
Payload = create_token_payload(Quote, WalletID, DestinationID, PartyID),
|
||||
{ok, Decoded, WalletID, DestinationID, PartyID} = decode_token_payload(Payload),
|
||||
?assertEqual(Quote, Decoded).
|
||||
ThriftQuote = ff_withdrawal_codec:marshal(quote, Quote),
|
||||
Payload = create_token_payload(ThriftQuote, WalletID, DestinationID, PartyID),
|
||||
{ok, {Decoded, WalletID, DestinationID, PartyID}} = decode_token_payload(Payload),
|
||||
?assertEqual(ThriftQuote, Decoded).
|
||||
|
||||
-spec payload_v2_decoding_test() -> _.
|
||||
payload_v2_decoding_test() ->
|
||||
@ -156,6 +121,7 @@ payload_v2_decoding_test() ->
|
||||
domain_revision => 1,
|
||||
party_revision => 2
|
||||
},
|
||||
ExpectedThriftQuote = ff_withdrawal_codec:marshal(quote, ExpectedQuote),
|
||||
Payload = #{
|
||||
<<"version">> => 2,
|
||||
<<"walletID">> => WalletID,
|
||||
@ -171,7 +137,7 @@ payload_v2_decoding_test() ->
|
||||
>>
|
||||
},
|
||||
?assertEqual(
|
||||
{ok, ExpectedQuote, WalletID, DestinationID, PartyID},
|
||||
{ok, {ExpectedThriftQuote, WalletID, DestinationID, PartyID}},
|
||||
decode_token_payload(Payload)
|
||||
).
|
||||
|
||||
@ -180,18 +146,6 @@ payload_v1_decoding_test() ->
|
||||
PartyID = <<"party">>,
|
||||
WalletID = <<"wallet">>,
|
||||
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 = #{
|
||||
<<"version">> => 1,
|
||||
<<"walletID">> => WalletID,
|
||||
@ -213,7 +167,7 @@ payload_v1_decoding_test() ->
|
||||
}
|
||||
},
|
||||
?assertEqual(
|
||||
{ok, ExpectedQuote, WalletID, DestinationID, PartyID},
|
||||
{error, token_expired},
|
||||
decode_token_payload(Payload)
|
||||
).
|
||||
|
||||
|
@ -4,10 +4,17 @@
|
||||
-include_lib("wapi_wallet_dummy_data.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([start_app/1]).
|
||||
-export([start_app/2]).
|
||||
-export([start_wapi/1]).
|
||||
-export([issue_token/4]).
|
||||
-export([get_context/1]).
|
||||
-export([get_keysource/2]).
|
||||
@ -25,58 +32,120 @@
|
||||
-define(DOMAIN, <<"wallet-api">>).
|
||||
|
||||
%%
|
||||
-type config() :: [{atom(), any()}].
|
||||
-type config() :: [{atom(), any()}].
|
||||
-type test_case_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).
|
||||
|
||||
-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()) ->
|
||||
config().
|
||||
init_suite(Module, Config) ->
|
||||
SupPid = start_mocked_service_sup(Module),
|
||||
Apps1 =
|
||||
start_app(scoper) ++
|
||||
start_app(woody),
|
||||
ServiceURLs = mock_services_([
|
||||
{
|
||||
'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].
|
||||
start_app(woody) ++
|
||||
start_app({wapi, Config}),
|
||||
[{apps, lists:reverse(Apps1)}, {suite_test_sup, SupPid} | Config].
|
||||
|
||||
-spec start_app(app_name()) ->
|
||||
[app_name()].
|
||||
|
||||
start_app(scoper = AppName) ->
|
||||
start_app(AppName, []);
|
||||
start_app_with(AppName, [
|
||||
{storage, scoper_storage_logger}
|
||||
]);
|
||||
|
||||
start_app(woody = AppName) ->
|
||||
start_app(AppName, [
|
||||
start_app_with(AppName, [
|
||||
{acceptors_pool_size, 4}
|
||||
]);
|
||||
|
||||
start_app(wapi_woody_client = AppName) ->
|
||||
start_app(AppName, [
|
||||
{service_urls, #{
|
||||
cds_storage => "http://cds:8022/v2/storage",
|
||||
identdoc_storage => "http://cds:8022/v1/identity_document_storage",
|
||||
fistful_stat => "http://fistful-magista:8022/stat"
|
||||
start_app({wapi = AppName, Config}) ->
|
||||
JwkPath = get_keysource("jwk.json", Config),
|
||||
start_app_with(AppName, [
|
||||
{ip, "::"},
|
||||
{port, 8080},
|
||||
{realm, <<"external">>},
|
||||
{public_endpoint, <<"localhost:8080">>},
|
||||
{access_conf, #{
|
||||
jwt => #{
|
||||
keyset => #{
|
||||
wapi => {pem_file, get_keysource("private.pem", Config)}
|
||||
}
|
||||
}
|
||||
}},
|
||||
{service_retries, #{
|
||||
fistful_stat => #{
|
||||
'GetWallets' => {linear, 3, 1000},
|
||||
'_' => finish
|
||||
{signee, wapi},
|
||||
{lechiffre_opts, #{
|
||||
encryption_key_path => JwkPath,
|
||||
decryption_key_paths => [JwkPath]
|
||||
}},
|
||||
{swagger_handler_opts, #{
|
||||
validation_opts => #{
|
||||
custom_validator => wapi_swagger_validator
|
||||
}
|
||||
}}
|
||||
]);
|
||||
|
||||
start_app(AppName) ->
|
||||
genlib_app:start_application(AppName).
|
||||
[genlib_app:start_application(AppName)].
|
||||
|
||||
-spec start_app(app_name(), list()) ->
|
||||
[app_name()].
|
||||
@ -84,23 +153,25 @@ start_app(AppName) ->
|
||||
start_app(AppName, Env) ->
|
||||
genlib_app:start_application_with(AppName, Env).
|
||||
|
||||
-spec start_wapi(config()) ->
|
||||
[app_name()].
|
||||
start_wapi(Config) ->
|
||||
start_app(wapi, [
|
||||
{ip, ?WAPI_IP},
|
||||
{port, ?WAPI_PORT},
|
||||
{realm, <<"external">>},
|
||||
{public_endpoint, <<"localhost:8080">>},
|
||||
{access_conf, #{
|
||||
jwt => #{
|
||||
keyset => #{
|
||||
wapi => {pem_file, get_keysource("keys/local/private.pem", Config)}
|
||||
}
|
||||
}
|
||||
}},
|
||||
{signee, ?SIGNEE}
|
||||
]).
|
||||
-spec start_app_with(app_name(), app_env()) -> [app_name()].
|
||||
|
||||
start_app_with(AppName, Env) ->
|
||||
_ = application:load(AppName),
|
||||
_ = set_app_env(AppName, Env),
|
||||
case application:ensure_all_started(AppName) of
|
||||
{ok, Apps} ->
|
||||
Apps;
|
||||
{error, Reason} ->
|
||||
exit({start_app_failed, AppName, Reason})
|
||||
end.
|
||||
|
||||
set_app_env(AppName, Env) ->
|
||||
lists:foreach(
|
||||
fun ({K, V}) ->
|
||||
ok = application:set_env(AppName, K, V)
|
||||
end,
|
||||
Env
|
||||
).
|
||||
|
||||
-spec get_keysource(_, config()) ->
|
||||
_.
|
||||
|
@ -51,14 +51,14 @@ init([]) ->
|
||||
[test_case_name()].
|
||||
all() ->
|
||||
[
|
||||
{group, default}
|
||||
{group, base}
|
||||
].
|
||||
|
||||
-spec groups() ->
|
||||
[{group_name(), list(), [test_case_name()]}].
|
||||
groups() ->
|
||||
[
|
||||
{default, [], [
|
||||
{base, [], [
|
||||
bank_card_resource_test,
|
||||
bitcoin_resource_test,
|
||||
litecoin_resource_test,
|
||||
@ -73,38 +73,27 @@ groups() ->
|
||||
%%
|
||||
%% starting/stopping
|
||||
%%
|
||||
-spec init_per_suite(config()) ->
|
||||
config().
|
||||
init_per_suite(Config0) ->
|
||||
%% TODO remove this after cut off wapi
|
||||
ok = application:set_env(wapi, transport, thrift),
|
||||
ct_helper:makeup_cfg([
|
||||
ct_helper:test_case_name(init),
|
||||
ct_payment_system:setup(#{
|
||||
optional_apps => [
|
||||
bender_client,
|
||||
wapi_woody_client,
|
||||
wapi
|
||||
]
|
||||
})
|
||||
], Config0).
|
||||
-spec init_per_suite(config()) -> config().
|
||||
|
||||
init_per_suite(C) ->
|
||||
wapi_ct_helper:init_suite(?MODULE, C).
|
||||
|
||||
-spec end_per_suite(config()) -> _.
|
||||
|
||||
-spec end_per_suite(config()) ->
|
||||
_.
|
||||
end_per_suite(C) ->
|
||||
%% TODO remove this after cut off wapi
|
||||
ok = application:unset_env(wapi, transport),
|
||||
ok = ct_payment_system:shutdown(C).
|
||||
_ = wapi_ct_helper:stop_mocked_service_sup(?config(suite_test_sup, C)),
|
||||
_ = [application:stop(App) || App <- ?config(apps, C)],
|
||||
ok.
|
||||
|
||||
-spec init_per_group(group_name(), config()) ->
|
||||
config().
|
||||
init_per_group(default = Group, Config) ->
|
||||
ok = ff_context:save(ff_context:create(#{
|
||||
init_per_group(Group, Config) when Group =:= base ->
|
||||
ok = wapi_context:save(wapi_context:create(#{
|
||||
party_client => party_client:create_client(),
|
||||
woody_context => woody_context:new(<<"init_per_group/", (atom_to_binary(Group, utf8))/binary>>)
|
||||
})),
|
||||
Party = create_party(Config),
|
||||
{ok, Token} = wapi_ct_helper:issue_token(Party, [{[party], write}], {deadline, 10}, ?DOMAIN),
|
||||
Party = genlib:bsuuid(),
|
||||
{ok, Token} = wapi_ct_helper:issue_token(Party, [{[party], write}], unlimited, ?DOMAIN),
|
||||
Config1 = [{party, Party} | Config],
|
||||
[{context, wapi_ct_helper:get_context(Token)} | Config1];
|
||||
init_per_group(_, Config) ->
|
||||
@ -118,14 +107,14 @@ end_per_group(_Group, _C) ->
|
||||
-spec init_per_testcase(test_case_name(), config()) ->
|
||||
config().
|
||||
init_per_testcase(Name, C) ->
|
||||
C1 = ct_helper:makeup_cfg([ct_helper:test_case_name(Name), ct_helper:woody_ctx()], C),
|
||||
ok = ct_helper:set_context(C1),
|
||||
C1 = wapi_ct_helper:makeup_cfg([wapi_ct_helper:test_case_name(Name), wapi_ct_helper:woody_ctx()], C),
|
||||
ok = wapi_context:save(C1),
|
||||
[{test_sup, wapi_ct_helper:start_mocked_service_sup(?MODULE)} | C1].
|
||||
|
||||
-spec end_per_testcase(test_case_name(), config()) ->
|
||||
config().
|
||||
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)),
|
||||
ok.
|
||||
|
||||
@ -205,12 +194,18 @@ zcash_resource_test(C) ->
|
||||
%%
|
||||
|
||||
do_destination_lifecycle(ResourceType, C) ->
|
||||
PartyID = ?config(party, C),
|
||||
PartyID = wapi_ct_helper:cfg(party, C),
|
||||
Identity = generate_identity(PartyID),
|
||||
Resource = generate_resource(ResourceType),
|
||||
Context = generate_context(PartyID),
|
||||
Destination = generate_destination(Identity#idnt_IdentityState.id, Resource, Context),
|
||||
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_destination,
|
||||
fun
|
||||
@ -224,7 +219,7 @@ do_destination_lifecycle(ResourceType, C) ->
|
||||
#{
|
||||
body => build_destination_spec(Destination)
|
||||
},
|
||||
ct_helper:cfg(context, C)
|
||||
wapi_ct_helper:cfg(context, C)
|
||||
),
|
||||
{ok, GetResult} = call_api(
|
||||
fun swag_client_wallet_withdrawals_api:get_destination/3,
|
||||
@ -233,7 +228,7 @@ do_destination_lifecycle(ResourceType, C) ->
|
||||
<<"destinationID">> => ?STRING
|
||||
}
|
||||
},
|
||||
ct_helper:cfg(context, C)
|
||||
wapi_ct_helper:cfg(context, C)
|
||||
),
|
||||
?assertEqual(CreateResult, GetResult),
|
||||
{ok, GetByIDResult} = call_api(
|
||||
@ -243,7 +238,7 @@ do_destination_lifecycle(ResourceType, C) ->
|
||||
<<"externalID">> => Destination#dst_DestinationState.external_id
|
||||
}
|
||||
},
|
||||
ct_helper:cfg(context, C)
|
||||
wapi_ct_helper:cfg(context, C)
|
||||
),
|
||||
?assertEqual(GetResult, GetByIDResult),
|
||||
?assertEqual(Destination#dst_DestinationState.id, maps:get(<<"id">>, CreateResult)),
|
||||
@ -266,11 +261,6 @@ call_api(F, Params, Context) ->
|
||||
Response = F(Url, PreparedParams, Opts),
|
||||
wapi_client_lib:handle_response(Response).
|
||||
|
||||
create_party(_C) ->
|
||||
ID = genlib:bsuuid(),
|
||||
_ = ff_party:create(ID),
|
||||
ID.
|
||||
|
||||
build_destination_spec(D) ->
|
||||
#{
|
||||
<<"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
|
||||
%%
|
||||
-spec init_per_suite(config()) ->
|
||||
config().
|
||||
init_per_suite(Config) ->
|
||||
%% TODO remove this after cut off wapi
|
||||
ok = application:set_env(wapi, transport, thrift),
|
||||
ct_helper:makeup_cfg([
|
||||
ct_helper:test_case_name(init),
|
||||
ct_payment_system:setup(#{
|
||||
optional_apps => [
|
||||
bender_client,
|
||||
wapi,
|
||||
wapi_woody_client
|
||||
]
|
||||
})
|
||||
], Config).
|
||||
-spec init_per_suite(config()) -> config().
|
||||
|
||||
init_per_suite(C) ->
|
||||
wapi_ct_helper:init_suite(?MODULE, C).
|
||||
|
||||
-spec end_per_suite(config()) -> _.
|
||||
|
||||
-spec end_per_suite(config()) ->
|
||||
_.
|
||||
end_per_suite(C) ->
|
||||
%% TODO remove this after cut off wapi
|
||||
ok = application:unset_env(wapi, transport),
|
||||
ok = ct_payment_system:shutdown(C).
|
||||
_ = wapi_ct_helper:stop_mocked_service_sup(?config(suite_test_sup, C)),
|
||||
_ = [application:stop(App) || App <- ?config(apps, C)],
|
||||
ok.
|
||||
|
||||
-spec init_per_group(group_name(), config()) ->
|
||||
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(),
|
||||
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),
|
||||
Config1 = [{party, Party} | Config],
|
||||
[{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()) ->
|
||||
config().
|
||||
init_per_testcase(Name, C) ->
|
||||
C1 = ct_helper:makeup_cfg([ct_helper:test_case_name(Name), ct_helper:woody_ctx()], C),
|
||||
ok = ct_helper:set_context(C1),
|
||||
C1 = wapi_ct_helper:makeup_cfg([wapi_ct_helper:test_case_name(Name), wapi_ct_helper:woody_ctx()], C),
|
||||
ok = wapi_context:save(C1),
|
||||
[{test_sup, wapi_ct_helper:start_mocked_service_sup(?MODULE)} | C1].
|
||||
|
||||
-spec end_per_testcase(test_case_name(), config()) ->
|
||||
config().
|
||||
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)),
|
||||
ok.
|
||||
|
||||
@ -137,6 +126,7 @@ end_per_testcase(_Name, C) ->
|
||||
create_identity(C) ->
|
||||
PartyID = ?config(party, C),
|
||||
wapi_ct_helper:mock_services([
|
||||
{bender_thrift, fun('GenerateID', _) -> {ok, ?GENERATE_ID_RESULT} end},
|
||||
{fistful_identity, fun('Create', _) -> {ok, ?IDENTITY(PartyID)} end}
|
||||
], C),
|
||||
{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()) ->
|
||||
@ -168,7 +158,7 @@ get_identity(C) ->
|
||||
<<"identityID">> => ?STRING
|
||||
}
|
||||
},
|
||||
ct_helper:cfg(context, C)
|
||||
wapi_ct_helper:cfg(context, C)
|
||||
).
|
||||
|
||||
-spec create_identity_challenge(config()) ->
|
||||
@ -180,6 +170,7 @@ create_identity_challenge(C) ->
|
||||
('GetContext', _) -> {ok, ?DEFAULT_CONTEXT(PartyID)};
|
||||
('StartChallenge', _) -> {ok, ?IDENTITY_CHALLENGE(?IDENTITY_CHALLENGE_STATUS_COMPLETED)}
|
||||
end},
|
||||
{bender_thrift, fun('GenerateID', _) -> {ok, ?GENERATE_ID_RESULT} end},
|
||||
{identdoc_storage, fun('Get', _) -> {ok, ?IDENT_DOC} end}
|
||||
], C),
|
||||
{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()) ->
|
||||
@ -228,7 +219,7 @@ get_identity_challenge(C) ->
|
||||
<<"challengeID">> => ?STRING
|
||||
}
|
||||
},
|
||||
ct_helper:cfg(context, C)
|
||||
wapi_ct_helper:cfg(context, C)
|
||||
).
|
||||
|
||||
-spec list_identity_challenges(config()) ->
|
||||
@ -252,7 +243,7 @@ list_identity_challenges(C) ->
|
||||
<<"status">> => <<"Completed">>
|
||||
}
|
||||
},
|
||||
ct_helper:cfg(context, C)
|
||||
wapi_ct_helper:cfg(context, C)
|
||||
).
|
||||
|
||||
-spec get_identity_challenge_event(config()) ->
|
||||
@ -274,7 +265,7 @@ get_identity_challenge_event(C) ->
|
||||
<<"eventID">> => ?INTEGER
|
||||
}
|
||||
},
|
||||
ct_helper:cfg(context, C)
|
||||
wapi_ct_helper:cfg(context, C)
|
||||
).
|
||||
|
||||
-spec poll_identity_challenge_events(config()) ->
|
||||
@ -299,7 +290,7 @@ poll_identity_challenge_events(C) ->
|
||||
<<"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),
|
||||
Response = F(Url, PreparedParams, Opts),
|
||||
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
|
||||
%%
|
||||
-spec init_per_suite(config()) ->
|
||||
config().
|
||||
init_per_suite(Config) ->
|
||||
%% TODO remove this after cut off wapi
|
||||
ok = application:set_env(wapi, transport, thrift),
|
||||
ct_helper:makeup_cfg([
|
||||
ct_helper:test_case_name(init),
|
||||
ct_payment_system:setup(#{
|
||||
optional_apps => [
|
||||
bender_client,
|
||||
wapi,
|
||||
wapi_woody_client
|
||||
]
|
||||
})
|
||||
], Config).
|
||||
-spec init_per_suite(config()) -> config().
|
||||
|
||||
init_per_suite(C) ->
|
||||
wapi_ct_helper:init_suite(?MODULE, C).
|
||||
|
||||
-spec end_per_suite(config()) -> _.
|
||||
|
||||
-spec end_per_suite(config()) ->
|
||||
_.
|
||||
end_per_suite(C) ->
|
||||
%% TODO remove this after cut off wapi
|
||||
ok = application:unset_env(wapi, transport),
|
||||
ok = ct_payment_system:shutdown(C).
|
||||
_ = wapi_ct_helper:stop_mocked_service_sup(?config(suite_test_sup, C)),
|
||||
_ = [application:stop(App) || App <- ?config(apps, C)],
|
||||
ok.
|
||||
|
||||
-spec init_per_group(group_name(), config()) ->
|
||||
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(),
|
||||
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),
|
||||
Config1 = [{party, Party} | Config],
|
||||
[{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()) ->
|
||||
config().
|
||||
init_per_testcase(Name, C) ->
|
||||
C1 = ct_helper:makeup_cfg([ct_helper:test_case_name(Name), ct_helper:woody_ctx()], C),
|
||||
ok = ct_helper:set_context(C1),
|
||||
C1 = wapi_ct_helper:makeup_cfg([wapi_ct_helper:test_case_name(Name), wapi_ct_helper:woody_ctx()], C),
|
||||
ok = wapi_context:save(C1),
|
||||
[{test_sup, wapi_ct_helper:start_mocked_service_sup(?MODULE)} | C1].
|
||||
|
||||
-spec end_per_testcase(test_case_name(), config()) ->
|
||||
config().
|
||||
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)),
|
||||
ok.
|
||||
|
||||
@ -148,7 +137,7 @@ create_report_ok_test(C) ->
|
||||
<<"toTime">> => ?TIMESTAMP
|
||||
}
|
||||
},
|
||||
ct_helper:cfg(context, C)
|
||||
wapi_ct_helper:cfg(context, C)
|
||||
).
|
||||
|
||||
-spec get_report_ok_test(config()) ->
|
||||
@ -167,7 +156,7 @@ get_report_ok_test(C) ->
|
||||
<<"reportID">> => ?INTEGER
|
||||
}
|
||||
},
|
||||
ct_helper:cfg(context, C)
|
||||
wapi_ct_helper:cfg(context, C)
|
||||
).
|
||||
|
||||
-spec get_reports_ok_test(config()) ->
|
||||
@ -193,7 +182,7 @@ get_reports_ok_test(C) ->
|
||||
<<"type">> => <<"withdrawalRegistry">>
|
||||
}
|
||||
},
|
||||
ct_helper:cfg(context, C)
|
||||
wapi_ct_helper:cfg(context, C)
|
||||
).
|
||||
|
||||
-spec reports_with_wrong_identity_ok_test(config()) ->
|
||||
@ -217,7 +206,7 @@ reports_with_wrong_identity_ok_test(C) ->
|
||||
<<"toTime">> => ?TIMESTAMP
|
||||
}
|
||||
},
|
||||
ct_helper:cfg(context, C)
|
||||
wapi_ct_helper:cfg(context, C)
|
||||
),
|
||||
?emptyresp(400) = call_api(
|
||||
fun swag_client_wallet_reports_api:get_report/3,
|
||||
@ -227,7 +216,7 @@ reports_with_wrong_identity_ok_test(C) ->
|
||||
<<"reportID">> => ?INTEGER
|
||||
}
|
||||
},
|
||||
ct_helper:cfg(context, C)
|
||||
wapi_ct_helper:cfg(context, C)
|
||||
),
|
||||
?emptyresp(400) = call_api(
|
||||
fun swag_client_wallet_reports_api:get_reports/3,
|
||||
@ -241,7 +230,7 @@ reports_with_wrong_identity_ok_test(C) ->
|
||||
<<"type">> => <<"withdrawalRegistry">>
|
||||
}
|
||||
},
|
||||
ct_helper:cfg(context, C)
|
||||
wapi_ct_helper:cfg(context, C)
|
||||
).
|
||||
|
||||
-spec download_file_ok_test(config()) ->
|
||||
@ -260,7 +249,7 @@ download_file_ok_test(C) ->
|
||||
<<"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),
|
||||
wapi_client_lib:handle_response(Response).
|
||||
|
||||
create_party(_C) ->
|
||||
ID = genlib:bsuuid(),
|
||||
_ = ff_party:create(ID),
|
||||
ID.
|
||||
|
||||
create_identity(C) ->
|
||||
PartyID = ?config(party, C),
|
||||
Params = #{
|
||||
@ -291,6 +275,6 @@ create_context(PartyID, 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
|
||||
%%
|
||||
-spec init_per_suite(config()) ->
|
||||
config().
|
||||
init_per_suite(Config) ->
|
||||
%% TODO remove this after cut off wapi
|
||||
ok = application:set_env(wapi, transport, thrift),
|
||||
ct_helper:makeup_cfg([
|
||||
ct_helper:test_case_name(init),
|
||||
ct_payment_system:setup(#{
|
||||
optional_apps => [
|
||||
bender_client,
|
||||
wapi_woody_client,
|
||||
wapi
|
||||
]
|
||||
})
|
||||
], Config).
|
||||
-spec init_per_suite(config()) -> config().
|
||||
|
||||
init_per_suite(C) ->
|
||||
wapi_ct_helper:init_suite(?MODULE, C).
|
||||
|
||||
-spec end_per_suite(config()) -> _.
|
||||
|
||||
-spec end_per_suite(config()) ->
|
||||
_.
|
||||
end_per_suite(C) ->
|
||||
%% TODO remove this after cut off wapi
|
||||
ok = application:unset_env(wapi, transport),
|
||||
ok = ct_payment_system:shutdown(C).
|
||||
_ = wapi_ct_helper:stop_mocked_service_sup(?config(suite_test_sup, C)),
|
||||
_ = [application:stop(App) || App <- ?config(apps, C)],
|
||||
ok.
|
||||
|
||||
-spec init_per_group(group_name(), config()) ->
|
||||
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(),
|
||||
woody_context => woody_context:new(<<"init_per_group/", (atom_to_binary(Group, utf8))/binary>>)
|
||||
})),
|
||||
Party = create_party(Config),
|
||||
BasePermissions = [
|
||||
{[party], read},
|
||||
{[party], write}
|
||||
],
|
||||
{ok, Token} = wapi_ct_helper:issue_token(Party, BasePermissions, {deadline, 10}, ?DOMAIN),
|
||||
Party = genlib:bsuuid(),
|
||||
{ok, Token} = wapi_ct_helper:issue_token(Party, [{[party], write}], unlimited, ?DOMAIN),
|
||||
Config1 = [{party, Party} | Config],
|
||||
[{context, wapi_ct_helper:get_context(Token)} | Config1];
|
||||
init_per_group(_, Config) ->
|
||||
@ -136,14 +121,14 @@ end_per_group(_Group, _C) ->
|
||||
-spec init_per_testcase(test_case_name(), config()) ->
|
||||
config().
|
||||
init_per_testcase(Name, C) ->
|
||||
C1 = ct_helper:makeup_cfg([ct_helper:test_case_name(Name), ct_helper:woody_ctx()], C),
|
||||
ok = ct_helper:set_context(C1),
|
||||
C1 = wapi_ct_helper:makeup_cfg([wapi_ct_helper:test_case_name(Name), wapi_ct_helper:woody_ctx()], C),
|
||||
ok = wapi_context:save(C1),
|
||||
[{test_sup, wapi_ct_helper:start_mocked_service_sup(?MODULE)} | C1].
|
||||
|
||||
-spec end_per_testcase(test_case_name(), config()) ->
|
||||
config().
|
||||
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)),
|
||||
ok.
|
||||
|
||||
@ -162,7 +147,7 @@ list_wallets(C) ->
|
||||
<<"limit">> => <<"123">>
|
||||
}
|
||||
},
|
||||
ct_helper:cfg(context, C)
|
||||
wapi_ct_helper:cfg(context, C)
|
||||
).
|
||||
|
||||
-spec list_wallets_invalid_error(config()) ->
|
||||
@ -194,7 +179,7 @@ list_withdrawals(C) ->
|
||||
<<"limit">> => <<"123">>
|
||||
}
|
||||
},
|
||||
ct_helper:cfg(context, C)
|
||||
wapi_ct_helper:cfg(context, C)
|
||||
).
|
||||
|
||||
-spec list_withdrawals_invalid_error(config()) ->
|
||||
@ -226,7 +211,7 @@ list_deposits(C) ->
|
||||
<<"limit">> => <<"123">>
|
||||
}
|
||||
},
|
||||
ct_helper:cfg(context, C)
|
||||
wapi_ct_helper:cfg(context, C)
|
||||
).
|
||||
|
||||
-spec list_deposits_invalid_error(config()) ->
|
||||
@ -258,7 +243,7 @@ list_destinations(C) ->
|
||||
<<"limit">> => <<"123">>
|
||||
}
|
||||
},
|
||||
ct_helper:cfg(context, C)
|
||||
wapi_ct_helper:cfg(context, C)
|
||||
).
|
||||
|
||||
-spec list_destinations_invalid_error(config()) ->
|
||||
@ -290,7 +275,7 @@ list_identities(C) ->
|
||||
<<"limit">> => <<"123">>
|
||||
}
|
||||
},
|
||||
ct_helper:cfg(context, C)
|
||||
wapi_ct_helper:cfg(context, C)
|
||||
).
|
||||
|
||||
-spec list_identities_invalid_error(config()) ->
|
||||
@ -328,7 +313,7 @@ check_error(Error, MockFunc, SwagFunc, C) ->
|
||||
<<"limit">> => <<"123">>
|
||||
}
|
||||
},
|
||||
ct_helper:cfg(context, C)
|
||||
wapi_ct_helper:cfg(context, C)
|
||||
).
|
||||
|
||||
-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),
|
||||
Response = F(Url, PreparedParams, Opts),
|
||||
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)
|
||||
}).
|
||||
|
||||
-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(ACCOUNT, #account_Account{
|
||||
@ -205,7 +228,7 @@
|
||||
id = ?STRING,
|
||||
name = ?STRING,
|
||||
created_at = ?TIMESTAMP,
|
||||
provider = ?STRING,
|
||||
provider = ?INTEGER,
|
||||
identity_class = ?STRING,
|
||||
identity_level = ?STRING,
|
||||
effective_challenge = ?STRING,
|
||||
|
@ -68,42 +68,27 @@ groups() ->
|
||||
%%
|
||||
%% starting/stopping
|
||||
%%
|
||||
-spec init_per_suite(config()) ->
|
||||
config().
|
||||
init_per_suite(Config) ->
|
||||
%% TODO remove this after cut off wapi
|
||||
ok = application:set_env(wapi, transport, thrift),
|
||||
ct_helper:makeup_cfg([
|
||||
ct_helper:test_case_name(init),
|
||||
ct_payment_system:setup(#{
|
||||
optional_apps => [
|
||||
bender_client,
|
||||
wapi_woody_client,
|
||||
wapi
|
||||
]
|
||||
})
|
||||
], Config).
|
||||
-spec init_per_suite(config()) -> config().
|
||||
|
||||
init_per_suite(C) ->
|
||||
wapi_ct_helper:init_suite(?MODULE, C).
|
||||
|
||||
-spec end_per_suite(config()) -> _.
|
||||
|
||||
-spec end_per_suite(config()) ->
|
||||
_.
|
||||
end_per_suite(C) ->
|
||||
%% TODO remove this after cut off wapi
|
||||
ok = application:unset_env(wapi, transport),
|
||||
ok = ct_payment_system:shutdown(C).
|
||||
_ = wapi_ct_helper:stop_mocked_service_sup(?config(suite_test_sup, C)),
|
||||
_ = [application:stop(App) || App <- ?config(apps, C)],
|
||||
ok.
|
||||
|
||||
-spec init_per_group(group_name(), config()) ->
|
||||
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(),
|
||||
woody_context => woody_context:new(<<"init_per_group/", (atom_to_binary(Group, utf8))/binary>>)
|
||||
})),
|
||||
Party = create_party(Config),
|
||||
BasePermissions = [
|
||||
{[party], read},
|
||||
{[party], write}
|
||||
],
|
||||
{ok, Token} = wapi_ct_helper:issue_token(Party, BasePermissions, {deadline, 10}, ?DOMAIN),
|
||||
Party = genlib:bsuuid(),
|
||||
{ok, Token} = wapi_ct_helper:issue_token(Party, [{[party], write}], unlimited, ?DOMAIN),
|
||||
Config1 = [{party, Party} | Config],
|
||||
[{context, wapi_ct_helper:get_context(Token)} | Config1];
|
||||
init_per_group(_, Config) ->
|
||||
@ -117,14 +102,14 @@ end_per_group(_Group, _C) ->
|
||||
-spec init_per_testcase(test_case_name(), config()) ->
|
||||
config().
|
||||
init_per_testcase(Name, C) ->
|
||||
C1 = ct_helper:makeup_cfg([ct_helper:test_case_name(Name), ct_helper:woody_ctx()], C),
|
||||
ok = ct_helper:set_context(C1),
|
||||
C1 = wapi_ct_helper:makeup_cfg([wapi_ct_helper:test_case_name(Name), wapi_ct_helper:woody_ctx()], C),
|
||||
ok = wapi_context:save(C1),
|
||||
[{test_sup, wapi_ct_helper:start_mocked_service_sup(?MODULE)} | C1].
|
||||
|
||||
-spec end_per_testcase(test_case_name(), config()) ->
|
||||
config().
|
||||
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)),
|
||||
ok.
|
||||
|
||||
@ -135,6 +120,7 @@ end_per_testcase(_Name, C) ->
|
||||
create(C) ->
|
||||
PartyID = ?config(party, C),
|
||||
wapi_ct_helper:mock_services([
|
||||
{bender_thrift, fun('GenerateID', _) -> {ok, ?GENERATE_ID_RESULT} end},
|
||||
{fistful_identity, fun('GetContext', _) -> {ok, ?DEFAULT_CONTEXT(PartyID)} end},
|
||||
{fistful_wallet, fun('Create', _) -> {ok, ?WALLET(PartyID)} end}
|
||||
], C),
|
||||
@ -150,7 +136,7 @@ create(C) ->
|
||||
}
|
||||
}
|
||||
},
|
||||
ct_helper:cfg(context, C)
|
||||
wapi_ct_helper:cfg(context, C)
|
||||
).
|
||||
|
||||
-spec get(config()) ->
|
||||
@ -167,7 +153,7 @@ get(C) ->
|
||||
<<"walletID">> => ?STRING
|
||||
}
|
||||
},
|
||||
ct_helper:cfg(context, C)
|
||||
wapi_ct_helper:cfg(context, C)
|
||||
).
|
||||
|
||||
-spec get_by_external_id(config()) ->
|
||||
@ -185,7 +171,7 @@ get_by_external_id(C) ->
|
||||
<<"externalID">> => ?STRING
|
||||
}
|
||||
},
|
||||
ct_helper:cfg(context, C)
|
||||
wapi_ct_helper:cfg(context, C)
|
||||
).
|
||||
|
||||
-spec get_account(config()) ->
|
||||
@ -207,7 +193,7 @@ get_account(C) ->
|
||||
<<"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),
|
||||
Response = F(Url, PreparedParams, Opts),
|
||||
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
|
||||
%%
|
||||
-spec init_per_suite(config()) ->
|
||||
config().
|
||||
init_per_suite(Config) ->
|
||||
%% TODO remove this after cut off wapi
|
||||
ok = application:set_env(wapi, transport, thrift),
|
||||
ct_helper:makeup_cfg([
|
||||
ct_helper:test_case_name(init),
|
||||
ct_payment_system:setup(#{
|
||||
optional_apps => [
|
||||
bender_client,
|
||||
wapi_woody_client,
|
||||
wapi
|
||||
]
|
||||
})
|
||||
], Config).
|
||||
-spec init_per_suite(config()) -> config().
|
||||
|
||||
init_per_suite(C) ->
|
||||
wapi_ct_helper:init_suite(?MODULE, C).
|
||||
|
||||
-spec end_per_suite(config()) -> _.
|
||||
|
||||
-spec end_per_suite(config()) ->
|
||||
_.
|
||||
end_per_suite(C) ->
|
||||
%% TODO remove this after cut off wapi
|
||||
ok = application:unset_env(wapi, transport),
|
||||
ok = ct_payment_system:shutdown(C).
|
||||
_ = wapi_ct_helper:stop_mocked_service_sup(?config(suite_test_sup, C)),
|
||||
_ = [application:stop(App) || App <- ?config(apps, C)],
|
||||
ok.
|
||||
|
||||
-spec init_per_group(group_name(), config()) ->
|
||||
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(),
|
||||
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),
|
||||
Config1 = [{party, Party} | Config],
|
||||
[{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()) ->
|
||||
config().
|
||||
init_per_testcase(Name, C) ->
|
||||
C1 = ct_helper:makeup_cfg([ct_helper:test_case_name(Name), ct_helper:woody_ctx()], C),
|
||||
ok = ct_helper:set_context(C1),
|
||||
C1 = wapi_ct_helper:makeup_cfg([wapi_ct_helper:test_case_name(Name), wapi_ct_helper:woody_ctx()], C),
|
||||
ok = wapi_context:save(C1),
|
||||
[{test_sup, wapi_ct_helper:start_mocked_service_sup(?MODULE)} | C1].
|
||||
|
||||
-spec end_per_testcase(test_case_name(), config()) ->
|
||||
config().
|
||||
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)),
|
||||
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()) ->
|
||||
@ -165,7 +154,7 @@ get_webhooks_ok_test(C) ->
|
||||
<<"identityID">> => IdentityID
|
||||
}
|
||||
},
|
||||
ct_helper:cfg(context, C)
|
||||
wapi_ct_helper:cfg(context, C)
|
||||
).
|
||||
|
||||
-spec get_webhook_ok_test(config()) ->
|
||||
@ -186,7 +175,7 @@ get_webhook_ok_test(C) ->
|
||||
<<"identityID">> => IdentityID
|
||||
}
|
||||
},
|
||||
ct_helper:cfg(context, C)
|
||||
wapi_ct_helper:cfg(context, C)
|
||||
).
|
||||
|
||||
-spec delete_webhook_ok_test(config()) ->
|
||||
@ -207,7 +196,7 @@ delete_webhook_ok_test(C) ->
|
||||
<<"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),
|
||||
wapi_client_lib:handle_response(Response).
|
||||
|
||||
create_party(_C) ->
|
||||
ID = genlib:bsuuid(),
|
||||
_ = ff_party:create(ID),
|
||||
ID.
|
||||
|
||||
create_identity(C) ->
|
||||
PartyID = ?config(party, C),
|
||||
Params = #{
|
||||
@ -238,5 +222,5 @@ create_context(PartyID, 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([
|
||||
create/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
|
||||
@ -58,7 +61,10 @@ groups() ->
|
||||
[
|
||||
create,
|
||||
get,
|
||||
get_by_external_id
|
||||
get_by_external_id,
|
||||
create_quote,
|
||||
get_events,
|
||||
get_event
|
||||
]
|
||||
}
|
||||
].
|
||||
@ -66,42 +72,27 @@ groups() ->
|
||||
%%
|
||||
%% starting/stopping
|
||||
%%
|
||||
-spec init_per_suite(config()) ->
|
||||
config().
|
||||
init_per_suite(Config) ->
|
||||
%% TODO remove this after cut off wapi
|
||||
ok = application:set_env(wapi, transport, thrift),
|
||||
ct_helper:makeup_cfg([
|
||||
ct_helper:test_case_name(init),
|
||||
ct_payment_system:setup(#{
|
||||
optional_apps => [
|
||||
bender_client,
|
||||
wapi_woody_client,
|
||||
wapi
|
||||
]
|
||||
})
|
||||
], Config).
|
||||
-spec init_per_suite(config()) -> config().
|
||||
|
||||
init_per_suite(C) ->
|
||||
wapi_ct_helper:init_suite(?MODULE, C).
|
||||
|
||||
-spec end_per_suite(config()) -> _.
|
||||
|
||||
-spec end_per_suite(config()) ->
|
||||
_.
|
||||
end_per_suite(C) ->
|
||||
%% TODO remove this after cut off wapi
|
||||
ok = application:unset_env(wapi, transport),
|
||||
ok = ct_payment_system:shutdown(C).
|
||||
_ = wapi_ct_helper:stop_mocked_service_sup(?config(suite_test_sup, C)),
|
||||
_ = [application:stop(App) || App <- ?config(apps, C)],
|
||||
ok.
|
||||
|
||||
-spec init_per_group(group_name(), config()) ->
|
||||
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(),
|
||||
woody_context => woody_context:new(<<"init_per_group/", (atom_to_binary(Group, utf8))/binary>>)
|
||||
})),
|
||||
Party = create_party(Config),
|
||||
BasePermissions = [
|
||||
{[party], read},
|
||||
{[party], write}
|
||||
],
|
||||
{ok, Token} = wapi_ct_helper:issue_token(Party, BasePermissions, {deadline, 10}, ?DOMAIN),
|
||||
Party = genlib:bsuuid(),
|
||||
{ok, Token} = wapi_ct_helper:issue_token(Party, [{[party], write}], unlimited, ?DOMAIN),
|
||||
Config1 = [{party, Party} | Config],
|
||||
[{context, wapi_ct_helper:get_context(Token)} | Config1];
|
||||
init_per_group(_, Config) ->
|
||||
@ -115,14 +106,14 @@ end_per_group(_Group, _C) ->
|
||||
-spec init_per_testcase(test_case_name(), config()) ->
|
||||
config().
|
||||
init_per_testcase(Name, C) ->
|
||||
C1 = ct_helper:makeup_cfg([ct_helper:test_case_name(Name), ct_helper:woody_ctx()], C),
|
||||
ok = ct_helper:set_context(C1),
|
||||
C1 = wapi_ct_helper:makeup_cfg([wapi_ct_helper:test_case_name(Name), wapi_ct_helper:woody_ctx()], C),
|
||||
ok = wapi_context:save(C1),
|
||||
[{test_sup, wapi_ct_helper:start_mocked_service_sup(?MODULE)} | C1].
|
||||
|
||||
-spec end_per_testcase(test_case_name(), config()) ->
|
||||
config().
|
||||
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)),
|
||||
ok.
|
||||
|
||||
@ -133,6 +124,7 @@ end_per_testcase(_Name, C) ->
|
||||
create(C) ->
|
||||
PartyID = ?config(party, C),
|
||||
wapi_ct_helper:mock_services([
|
||||
{bender_thrift, fun('GenerateID', _) -> {ok, ?GENERATE_ID_RESULT} end},
|
||||
{fistful_wallet, fun('GetContext', _) -> {ok, ?DEFAULT_CONTEXT(PartyID)} end},
|
||||
{fistful_destination, fun('GetContext', _) -> {ok, ?DEFAULT_CONTEXT(PartyID)} end},
|
||||
{fistful_withdrawal, fun('Create', _) -> {ok, ?WITHDRAWAL(PartyID)} end}
|
||||
@ -148,7 +140,7 @@ create(C) ->
|
||||
<<"currency">> => <<"RUB">>
|
||||
}
|
||||
})},
|
||||
ct_helper:cfg(context, C)
|
||||
wapi_ct_helper:cfg(context, C)
|
||||
).
|
||||
|
||||
-spec get(config()) ->
|
||||
@ -165,7 +157,7 @@ get(C) ->
|
||||
<<"withdrawalID">> => ?STRING
|
||||
}
|
||||
},
|
||||
ct_helper:cfg(context, C)
|
||||
wapi_ct_helper:cfg(context, C)
|
||||
).
|
||||
|
||||
-spec get_by_external_id(config()) ->
|
||||
@ -183,7 +175,82 @@ get_by_external_id(C) ->
|
||||
<<"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),
|
||||
Response = F(Url, PreparedParams, Opts),
|
||||
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