TD-329: Add withdrawal context support (#12)

* Bump to valitydev/machinery-erlang@62c3243
* Bump to valitydev/machinegun-proto@347c5c4
* Drop top-level `damsel` dependency
* Drop `get_rate` testcase as meaningless
* Drop testcases which became duplicate
* Support new context type @ config
* Delegate context accessors to specific modules
* Support rollbacks @ valitydev/limiter-proto@6158184
This commit is contained in:
Andrew Mayorov 2022-07-06 18:32:32 +03:00 committed by GitHub
parent 2dff7b518d
commit 618a751712
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
26 changed files with 819 additions and 1004 deletions

4
.env
View File

@ -1,4 +1,4 @@
SERVICE_NAME=limiter SERVICE_NAME=limiter
OTP_VERSION=24.2.0 OTP_VERSION=24.3.4
REBAR_VERSION=3.18 REBAR_VERSION=3.18
THRIFT_VERSION=0.14.2.2 THRIFT_VERSION=0.14.2.3

View File

@ -161,7 +161,7 @@ call_accounter(Function, Args, LimitContext) ->
WoodyContext = lim_context:woody_context(LimitContext), WoodyContext = lim_context:woody_context(LimitContext),
lim_client_woody:call(accounter, Function, Args, WoodyContext). lim_client_woody:call(accounter, Function, Args, WoodyContext).
convert_exception(#'InvalidRequest'{errors = Errors}) -> convert_exception(#base_InvalidRequest{errors = Errors}) ->
Errors; Errors;
convert_exception(#accounter_InvalidPostingParams{wrong_postings = Errors}) -> convert_exception(#accounter_InvalidPostingParams{wrong_postings = Errors}) ->
maps:fold(fun(_, Error, Acc) -> [Error | Acc] end, [], Errors). maps:fold(fun(_, Error, Acc) -> [Error | Acc] end, [], Errors).

View File

@ -8,7 +8,7 @@
currency := currency() currency := currency()
}. }.
-type currency() :: lim_domain_thrift:'CurrencySymbolicCode'(). -type currency() :: dmsl_domain_thrift:'CurrencySymbolicCode'().
-type config() :: lim_config_machine:config(). -type config() :: lim_config_machine:config().
-type body_type() :: full | partial. -type body_type() :: full | partial.
@ -24,47 +24,18 @@ get(BodyType, Config, LimitContext) ->
do(fun() -> do(fun() ->
ContextType = lim_config_machine:context_type(Config), ContextType = lim_config_machine:context_type(Config),
{ok, Operation} = lim_context:get_operation(ContextType, LimitContext), {ok, Operation} = lim_context:get_operation(ContextType, LimitContext),
Body = unwrap(get_body_for_operation(BodyType, Operation, Config, LimitContext)), Body = unwrap(get_body_for_operation(BodyType, ContextType, LimitContext)),
apply_op_behaviour(Body, Config, LimitContext) apply_op_behaviour(Operation, Body, Config)
end). end).
-spec get_body_for_operation(body_type(), lim_context:context_operation(), config(), lim_context:t()) -> -spec get_body_for_operation(body_type(), lim_context:context_type(), lim_context:t()) ->
{ok, cash()} | {error, notfound}. {ok, cash()} | {error, notfound}.
get_body_for_operation(full, invoice = Operation, Config, LimitContext) -> get_body_for_operation(full, ContextType, LimitContext) ->
ContextType = lim_config_machine:context_type(Config), lim_context:get_value(ContextType, cost, LimitContext);
lim_context:get_from_context(ContextType, cost, Operation, LimitContext); get_body_for_operation(partial, ContextType, LimitContext) ->
get_body_for_operation(full, invoice_adjustment, Config, LimitContext) -> lim_context:get_value(ContextType, capture_cost, LimitContext).
ContextType = lim_config_machine:context_type(Config),
lim_context:get_from_context(ContextType, cost, invoice, LimitContext);
get_body_for_operation(full, invoice_payment = Operation, Config, LimitContext) ->
ContextType = lim_config_machine:context_type(Config),
lim_context:get_from_context(ContextType, cost, Operation, LimitContext);
get_body_for_operation(full, invoice_payment_adjustment, Config, LimitContext) ->
ContextType = lim_config_machine:context_type(Config),
lim_context:get_from_context(ContextType, cost, invoice_payment, LimitContext);
get_body_for_operation(full, invoice_payment_refund, Config, LimitContext) ->
ContextType = lim_config_machine:context_type(Config),
lim_context:get_from_context(ContextType, cost, invoice_payment, LimitContext);
get_body_for_operation(full, invoice_payment_chargeback = Operation, Config, LimitContext) ->
ContextType = lim_config_machine:context_type(Config),
lim_context:get_from_context(ContextType, body, Operation, LimitContext);
get_body_for_operation(partial, invoice, _Config, _LimitContext) ->
{error, notfound};
get_body_for_operation(partial, invoice_adjustment, _Config, _LimitContext) ->
{error, notfound};
get_body_for_operation(partial, invoice_payment = Operation, Config, LimitContext) ->
ContextType = lim_config_machine:context_type(Config),
lim_context:get_from_context(ContextType, capture_cost, Operation, LimitContext);
get_body_for_operation(partial, invoice_payment_adjustment, _Config, _LimitContext) ->
{error, notfound};
get_body_for_operation(partial, invoice_payment_refund = Operation, Config, LimitContext) ->
ContextType = lim_config_machine:context_type(Config),
lim_context:get_from_context(ContextType, cost, Operation, LimitContext);
get_body_for_operation(partial, invoice_payment_chargeback, _Config, _LimitContext) ->
{error, notfound}.
apply_op_behaviour(Body, #{op_behaviour := ComputationConfig}, LimitContext) -> apply_op_behaviour(Operation, Body, #{op_behaviour := ComputationConfig}) ->
{ok, Operation} = lim_context:get_operation(payment_processing, LimitContext),
case maps:get(Operation, ComputationConfig, undefined) of case maps:get(Operation, ComputationConfig, undefined) of
addition -> addition ->
Body; Body;
@ -73,7 +44,7 @@ apply_op_behaviour(Body, #{op_behaviour := ComputationConfig}, LimitContext) ->
undefined -> undefined ->
Body Body
end; end;
apply_op_behaviour(Body, _Config, _LimitContext) -> apply_op_behaviour(_Operation, Body, _Config) ->
Body. Body.
invert_body(Cash = #{amount := Amount}) -> invert_body(Cash = #{amount := Amount}) ->

View File

@ -1,6 +1,7 @@
-module(lim_config_codec). -module(lim_config_codec).
-include_lib("limiter_proto/include/lim_limiter_config_thrift.hrl"). -include_lib("limiter_proto/include/limproto_config_thrift.hrl").
-include_lib("limiter_proto/include/limproto_timerange_thrift.hrl").
-export([marshal/2]). -export([marshal/2]).
-export([unmarshal/2]). -export([unmarshal/2]).
@ -35,7 +36,7 @@ maybe_apply(Value, Fun, _Default) ->
-spec marshal(type_name(), decoded_value()) -> encoded_value(). -spec marshal(type_name(), decoded_value()) -> encoded_value().
marshal(timestamped_change, {ev, Timestamp, Change}) -> marshal(timestamped_change, {ev, Timestamp, Change}) ->
#limiter_config_TimestampedChange{ #config_TimestampedChange{
change = marshal_change(Change), change = marshal_change(Change),
occured_at = marshal_timestamp(Timestamp) occured_at = marshal_timestamp(Timestamp)
}. }.
@ -53,11 +54,11 @@ marshal_timestamp({DateTime, USec}) ->
genlib_rfc3339:format_relaxed(TimeinUnit, Unit). genlib_rfc3339:format_relaxed(TimeinUnit, Unit).
marshal_change({created, Config}) -> marshal_change({created, Config}) ->
{created, #limiter_config_CreatedChange{limit_config = marshal_config(Config)}}. {created, #config_CreatedChange{limit_config = marshal_config(Config)}}.
-spec marshal_config(decoded_value()) -> encoded_value(). -spec marshal_config(decoded_value()) -> encoded_value().
marshal_config(Config) -> marshal_config(Config) ->
#limiter_config_LimitConfig{ #config_LimitConfig{
id = lim_config_machine:id(Config), id = lim_config_machine:id(Config),
processor_type = lim_config_machine:processor_type(Config), processor_type = lim_config_machine:processor_type(Config),
description = lim_config_machine:description(Config), description = lim_config_machine:description(Config),
@ -73,62 +74,64 @@ marshal_config(Config) ->
marshal_op_behaviour(OpBehaviour) -> marshal_op_behaviour(OpBehaviour) ->
PaymentRefund = maps:get(invoice_payment_refund, OpBehaviour, undefined), PaymentRefund = maps:get(invoice_payment_refund, OpBehaviour, undefined),
#limiter_config_OperationLimitBehaviour{ #config_OperationLimitBehaviour{
invoice_payment_refund = maybe_apply(PaymentRefund, fun marshal_behaviour/1) invoice_payment_refund = maybe_apply(PaymentRefund, fun marshal_behaviour/1)
}. }.
marshal_behaviour(subtraction) -> marshal_behaviour(subtraction) ->
{subtraction, #limiter_config_Subtraction{}}; {subtraction, #config_Subtraction{}};
marshal_behaviour(addition) -> marshal_behaviour(addition) ->
{addition, #limiter_config_Addition{}}. {addition, #config_Addition{}}.
marshal_time_range_type({calendar, CalendarType}) -> marshal_time_range_type({calendar, CalendarType}) ->
{calendar, marshal_calendar_time_range_type(CalendarType)}; {calendar, marshal_calendar_time_range_type(CalendarType)};
marshal_time_range_type({interval, Amount}) -> marshal_time_range_type({interval, Amount}) ->
{interval, #limiter_time_range_TimeRangeTypeInterval{amount = Amount}}. {interval, #timerange_TimeRangeTypeInterval{amount = Amount}}.
marshal_calendar_time_range_type(day) -> marshal_calendar_time_range_type(day) ->
{day, #limiter_time_range_TimeRangeTypeCalendarDay{}}; {day, #timerange_TimeRangeTypeCalendarDay{}};
marshal_calendar_time_range_type(week) -> marshal_calendar_time_range_type(week) ->
{week, #limiter_time_range_TimeRangeTypeCalendarWeek{}}; {week, #timerange_TimeRangeTypeCalendarWeek{}};
marshal_calendar_time_range_type(month) -> marshal_calendar_time_range_type(month) ->
{month, #limiter_time_range_TimeRangeTypeCalendarMonth{}}; {month, #timerange_TimeRangeTypeCalendarMonth{}};
marshal_calendar_time_range_type(year) -> marshal_calendar_time_range_type(year) ->
{year, #limiter_time_range_TimeRangeTypeCalendarYear{}}. {year, #timerange_TimeRangeTypeCalendarYear{}}.
marshal_context_type(payment_processing) -> marshal_context_type(payment_processing) ->
{payment_processing, #limiter_config_LimitContextTypePaymentProcessing{}}. {payment_processing, #config_LimitContextTypePaymentProcessing{}};
marshal_context_type(withdrawal_processing) ->
{withdrawal_processing, #config_LimitContextTypeWithdrawalProcessing{}}.
marshal_type({turnover, Metric}) -> marshal_type({turnover, Metric}) ->
{turnover, #limiter_config_LimitTypeTurnover{ {turnover, #config_LimitTypeTurnover{
metric = marshal_turnover_metric(Metric) metric = marshal_turnover_metric(Metric)
}}. }}.
marshal_turnover_metric(number) -> marshal_turnover_metric(number) ->
{number, #limiter_config_LimitTurnoverNumber{}}; {number, #config_LimitTurnoverNumber{}};
marshal_turnover_metric({amount, Currency}) -> marshal_turnover_metric({amount, Currency}) ->
{amount, #limiter_config_LimitTurnoverAmount{currency = Currency}}. {amount, #config_LimitTurnoverAmount{currency = Currency}}.
marshal_scope(Types) -> marshal_scope(Types) ->
{multi, ordsets:from_list(lists:map(fun marshal_scope_type/1, ordsets:to_list(Types)))}. {multi, ordsets:from_list(lists:map(fun marshal_scope_type/1, ordsets:to_list(Types)))}.
marshal_scope_type(party) -> marshal_scope_type(party) ->
{party, #limiter_config_LimitScopeEmptyDetails{}}; {party, #config_LimitScopeEmptyDetails{}};
marshal_scope_type(shop) -> marshal_scope_type(shop) ->
{shop, #limiter_config_LimitScopeEmptyDetails{}}; {shop, #config_LimitScopeEmptyDetails{}};
marshal_scope_type(wallet) -> marshal_scope_type(wallet) ->
{wallet, #limiter_config_LimitScopeEmptyDetails{}}; {wallet, #config_LimitScopeEmptyDetails{}};
marshal_scope_type(identity) -> marshal_scope_type(identity) ->
{identity, #limiter_config_LimitScopeEmptyDetails{}}; {identity, #config_LimitScopeEmptyDetails{}};
marshal_scope_type(payment_tool) -> marshal_scope_type(payment_tool) ->
{payment_tool, #limiter_config_LimitScopeEmptyDetails{}}. {payment_tool, #config_LimitScopeEmptyDetails{}}.
%% %%
-spec unmarshal(type_name(), encoded_value()) -> decoded_value(). -spec unmarshal(type_name(), encoded_value()) -> decoded_value().
unmarshal(timestamped_change, TimestampedChange) -> unmarshal(timestamped_change, TimestampedChange) ->
Timestamp = unmarshal_timestamp(TimestampedChange#limiter_config_TimestampedChange.occured_at), Timestamp = unmarshal_timestamp(TimestampedChange#config_TimestampedChange.occured_at),
Change = unmarshal_change(TimestampedChange#limiter_config_TimestampedChange.change), Change = unmarshal_change(TimestampedChange#config_TimestampedChange.change),
{ev, Timestamp, Change}. {ev, Timestamp, Change}.
unmarshal_timestamp(Timestamp) when is_binary(Timestamp) -> unmarshal_timestamp(Timestamp) when is_binary(Timestamp) ->
@ -148,7 +151,7 @@ unmarshal_timestamp(Timestamp) when is_binary(Timestamp) ->
end. end.
-spec unmarshal_params(encoded_value()) -> decoded_value(). -spec unmarshal_params(encoded_value()) -> decoded_value().
unmarshal_params(#limiter_config_LimitConfigParams{ unmarshal_params(#config_LimitConfigParams{
id = ID, id = ID,
description = Description, description = Description,
started_at = StartedAt, started_at = StartedAt,
@ -171,10 +174,10 @@ unmarshal_params(#limiter_config_LimitConfigParams{
op_behaviour => maybe_apply(OpBehaviour, fun unmarshal_op_behaviour/1) op_behaviour => maybe_apply(OpBehaviour, fun unmarshal_op_behaviour/1)
}). }).
unmarshal_change({created, #limiter_config_CreatedChange{limit_config = Config}}) -> unmarshal_change({created, #config_CreatedChange{limit_config = Config}}) ->
{created, unmarshal_config(Config)}. {created, unmarshal_config(Config)}.
unmarshal_config(#limiter_config_LimitConfig{ unmarshal_config(#config_LimitConfig{
id = ID, id = ID,
processor_type = ProcessorType, processor_type = ProcessorType,
description = Description, description = Description,
@ -217,25 +220,25 @@ derive_type(undefined, {cash, Currency}) ->
-spec unmarshal_op_behaviour(encoded_value()) -> decoded_value(). -spec unmarshal_op_behaviour(encoded_value()) -> decoded_value().
unmarshal_op_behaviour(OpBehaviour) -> unmarshal_op_behaviour(OpBehaviour) ->
#limiter_config_OperationLimitBehaviour{ #config_OperationLimitBehaviour{
invoice_payment_refund = Refund invoice_payment_refund = Refund
} = OpBehaviour, } = OpBehaviour,
genlib_map:compact(#{ genlib_map:compact(#{
invoice_payment_refund => maybe_apply(Refund, fun unmarshal_behaviour/1) invoice_payment_refund => maybe_apply(Refund, fun unmarshal_behaviour/1)
}). }).
unmarshal_behaviour({subtraction, #limiter_config_Subtraction{}}) -> unmarshal_behaviour({subtraction, #config_Subtraction{}}) ->
subtraction; subtraction;
unmarshal_behaviour({addition, #limiter_config_Addition{}}) -> unmarshal_behaviour({addition, #config_Addition{}}) ->
addition. addition.
-spec unmarshal_body_type_deprecated(encoded_value()) -> decoded_value(). -spec unmarshal_body_type_deprecated(encoded_value()) -> decoded_value().
unmarshal_body_type_deprecated({cash, #limiter_config_LimitBodyTypeCash{currency = Currency}}) -> unmarshal_body_type_deprecated({cash, #config_LimitBodyTypeCash{currency = Currency}}) ->
{cash, Currency}. {cash, Currency}.
unmarshal_time_range_type({calendar, CalendarType}) -> unmarshal_time_range_type({calendar, CalendarType}) ->
{calendar, unmarshal_calendar_time_range_type(CalendarType)}; {calendar, unmarshal_calendar_time_range_type(CalendarType)};
unmarshal_time_range_type({interval, #limiter_time_range_TimeRangeTypeInterval{amount = Amount}}) -> unmarshal_time_range_type({interval, #timerange_TimeRangeTypeInterval{amount = Amount}}) ->
{interval, Amount}. {interval, Amount}.
unmarshal_calendar_time_range_type({day, _}) -> unmarshal_calendar_time_range_type({day, _}) ->
@ -247,15 +250,17 @@ unmarshal_calendar_time_range_type({month, _}) ->
unmarshal_calendar_time_range_type({year, _}) -> unmarshal_calendar_time_range_type({year, _}) ->
year. year.
unmarshal_context_type({payment_processing, #limiter_config_LimitContextTypePaymentProcessing{}}) -> unmarshal_context_type({payment_processing, #config_LimitContextTypePaymentProcessing{}}) ->
payment_processing. payment_processing;
unmarshal_context_type({withdrawal_processing, #config_LimitContextTypeWithdrawalProcessing{}}) ->
withdrawal_processing.
unmarshal_type({turnover, #limiter_config_LimitTypeTurnover{metric = Metric}}) -> unmarshal_type({turnover, #config_LimitTypeTurnover{metric = Metric}}) ->
{turnover, maybe_apply(Metric, fun unmarshal_turnover_metric/1, number)}. {turnover, maybe_apply(Metric, fun unmarshal_turnover_metric/1, number)}.
unmarshal_turnover_metric({number, _}) -> unmarshal_turnover_metric({number, _}) ->
number; number;
unmarshal_turnover_metric({amount, #limiter_config_LimitTurnoverAmount{currency = Currency}}) -> unmarshal_turnover_metric({amount, #config_LimitTurnoverAmount{currency = Currency}}) ->
{amount, Currency}. {amount, Currency}.
unmarshal_scope({single, Type}) -> unmarshal_scope({single, Type}) ->
@ -302,15 +307,15 @@ marshal_unmarshal_created_test() ->
-spec unmarshal_created_w_deprecated_body_type_test_() -> [_TestGen]. -spec unmarshal_created_w_deprecated_body_type_test_() -> [_TestGen].
unmarshal_created_w_deprecated_body_type_test_() -> unmarshal_created_w_deprecated_body_type_test_() ->
Now = lim_time:now(), Now = lim_time:now(),
Config = #limiter_config_LimitConfig{ Config = #config_LimitConfig{
id = <<"ID">>, id = <<"ID">>,
processor_type = <<"TurnoverProcessor">>, processor_type = <<"TurnoverProcessor">>,
created_at = lim_time:to_rfc3339(Now), created_at = lim_time:to_rfc3339(Now),
started_at = <<"2000-01-01T00:00:00Z">>, started_at = <<"2000-01-01T00:00:00Z">>,
shard_size = 42, shard_size = 42,
time_range_type = {calendar, {day, #limiter_time_range_TimeRangeTypeCalendarDay{}}}, time_range_type = {calendar, {day, #timerange_TimeRangeTypeCalendarDay{}}},
context_type = {payment_processing, #limiter_config_LimitContextTypePaymentProcessing{}}, context_type = {payment_processing, #config_LimitContextTypePaymentProcessing{}},
body_type_deprecated = {cash, #limiter_config_LimitBodyTypeCash{currency = <<"☭☭☭"/utf8>>}} body_type_deprecated = {cash, #config_LimitBodyTypeCash{currency = <<"☭☭☭"/utf8>>}}
}, },
[ [
?_assertMatch( ?_assertMatch(
@ -320,8 +325,8 @@ unmarshal_created_w_deprecated_body_type_test_() ->
type := {turnover, {amount, <<"☭☭☭"/utf8>>}} type := {turnover, {amount, <<"☭☭☭"/utf8>>}}
}}, }},
unmarshal_change( unmarshal_change(
{created, #limiter_config_CreatedChange{ {created, #config_CreatedChange{
limit_config = Config#limiter_config_LimitConfig{ limit_config = Config#config_LimitConfig{
type = undefined type = undefined
} }
}} }}
@ -334,9 +339,9 @@ unmarshal_created_w_deprecated_body_type_test_() ->
type := {turnover, {amount, <<"☭☭☭"/utf8>>}} type := {turnover, {amount, <<"☭☭☭"/utf8>>}}
}}, }},
unmarshal_change( unmarshal_change(
{created, #limiter_config_CreatedChange{ {created, #config_CreatedChange{
limit_config = Config#limiter_config_LimitConfig{ limit_config = Config#config_LimitConfig{
type = {turnover, #limiter_config_LimitTypeTurnover{}} type = {turnover, #config_LimitTypeTurnover{}}
} }
}} }}
) )

View File

@ -1,6 +1,6 @@
-module(lim_config_machine). -module(lim_config_machine).
-include_lib("limiter_proto/include/lim_limiter_thrift.hrl"). -include_lib("limiter_proto/include/limproto_limiter_thrift.hrl").
%% Accessors %% Accessors
@ -79,10 +79,10 @@
-type op_behaviour() :: #{operation_type() := addition | subtraction}. -type op_behaviour() :: #{operation_type() := addition | subtraction}.
-type operation_type() :: invoice_payment_refund. -type operation_type() :: invoice_payment_refund.
-type lim_id() :: lim_limiter_thrift:'LimitID'(). -type lim_id() :: limproto_limiter_thrift:'LimitID'().
-type lim_change() :: lim_limiter_thrift:'LimitChange'(). -type lim_change() :: limproto_limiter_thrift:'LimitChange'().
-type limit() :: lim_limiter_thrift:'Limit'(). -type limit() :: limproto_limiter_thrift:'Limit'().
-type timestamp() :: lim_base_thrift:'Timestamp'(). -type timestamp() :: dmsl_base_thrift:'Timestamp'().
-export_type([config/0]). -export_type([config/0]).
-export_type([limit_type/0]). -export_type([limit_type/0]).
@ -522,14 +522,14 @@ mk_shard_id(Prefix, Units, ShardSize) ->
-spec mk_scope_prefix(config(), lim_context()) -> prefix(). -spec mk_scope_prefix(config(), lim_context()) -> prefix().
mk_scope_prefix(Config, LimitContext) -> mk_scope_prefix(Config, LimitContext) ->
mk_scope_prefix_impl(scope(Config), LimitContext). mk_scope_prefix_impl(scope(Config), context_type(Config), LimitContext).
-spec mk_scope_prefix_impl(limit_scope(), lim_context()) -> prefix(). -spec mk_scope_prefix_impl(limit_scope(), context_type(), lim_context()) -> prefix().
mk_scope_prefix_impl(Scope, LimitContext) -> mk_scope_prefix_impl(Scope, ContextType, LimitContext) ->
Bits = enumerate_context_bits(Scope), Bits = enumerate_context_bits(Scope),
ordsets:fold( ordsets:fold(
fun(Bit, Acc) -> fun(Bit, Acc) ->
{ok, Value} = extract_context_bit(Bit, LimitContext), {ok, Value} = extract_context_bit(Bit, ContextType, LimitContext),
append_prefix(Value, Acc) append_prefix(Value, Acc)
end, end,
<<>>, <<>>,
@ -541,7 +541,7 @@ append_prefix(Fragment, Acc) ->
<<Acc/binary, "/", Fragment/binary>>. <<Acc/binary, "/", Fragment/binary>>.
-type context_bit() :: -type context_bit() ::
{from, lim_context:context_type(), _Name :: atom(), lim_context:context_operation()} {from, _ValueName :: atom()}
| {order, integer(), context_bit()}. | {order, integer(), context_bit()}.
-spec enumerate_context_bits(limit_scope()) -> ordsets:ordset(context_bit()). -spec enumerate_context_bits(limit_scope()) -> ordsets:ordset(context_bit()).
@ -550,7 +550,7 @@ enumerate_context_bits(Types) ->
append_context_bits(party, Bits) -> append_context_bits(party, Bits) ->
ordsets:add_element( ordsets:add_element(
{order, 1, {from, payment_processing, owner_id, invoice}}, {order, 1, {from, owner_id}},
Bits Bits
); );
append_context_bits(shop, Bits) -> append_context_bits(shop, Bits) ->
@ -558,31 +558,33 @@ append_context_bits(shop, Bits) ->
% NOTE % NOTE
% Shop scope implies party scope. % Shop scope implies party scope.
% Also we need to preserve order between party / shop to ensure backwards compatibility. % Also we need to preserve order between party / shop to ensure backwards compatibility.
{order, 1, {from, payment_processing, owner_id, invoice}}, {order, 1, {from, owner_id}},
{order, 2, {from, payment_processing, shop_id, invoice}} {order, 2, {from, shop_id}}
]); ]);
append_context_bits(payment_tool, Bits) -> append_context_bits(payment_tool, Bits) ->
ordsets:add_element( ordsets:add_element(
{from, payment_processing, payer, invoice_payment}, {from, payment_tool},
Bits Bits
). ).
-spec extract_context_bit(context_bit(), lim_context()) -> {ok, binary()}. -spec extract_context_bit(context_bit(), context_type(), lim_context()) -> {ok, binary()}.
extract_context_bit({order, _, Bit}, LimitContext) -> extract_context_bit({order, _, Bit}, ContextType, LimitContext) ->
extract_context_bit(Bit, LimitContext); extract_context_bit(Bit, ContextType, LimitContext);
extract_context_bit({from, payment_processing, payer, Op}, LimitContext) -> extract_context_bit({from, payment_tool}, ContextType, LimitContext) ->
{ok, {_, PayerData}} = lim_context:get_from_context(payment_processing, payer, Op, LimitContext), {ok, PaymentTool} = lim_context:get_value(ContextType, payment_tool, LimitContext),
#{payment_tool := {PaymentToolType, PaymentToolData}} = PayerData, case PaymentTool of
case PaymentToolType of {bank_card, #{token := Token, exp_date := {Month, Year}}} ->
bank_card -> {ok, mk_scope_component([Token, Month, Year])};
Token = maps:get(token, PaymentToolData), {bank_card, #{token := Token, exp_date := undefined}} ->
ExpData = maps:get(exp_date, PaymentToolData, <<>>), {ok, mk_scope_component([Token, <<"undefined">>])};
{ok, <<Token/binary, "/", ExpData/binary>>}; {digital_wallet, #{id := ID, service := Service}} ->
_ -> {ok, mk_scope_component([<<"DW">>, Service, ID])}
{error, {unsupported_payment_tool_type, PaymentToolType}}
end; end;
extract_context_bit({from, ContextType, ValueName, Op}, LimitContext) -> extract_context_bit({from, ValueName}, ContextType, LimitContext) ->
lim_context:get_from_context(ContextType, ValueName, Op, LimitContext). lim_context:get_value(ContextType, ValueName, LimitContext).
mk_scope_component(Fragments) ->
lim_string:join($/, Fragments).
%%% Machinery callbacks %%% Machinery callbacks
@ -640,6 +642,9 @@ apply_event_({created, Config}, undefined) ->
-ifdef(TEST). -ifdef(TEST).
-include_lib("eunit/include/eunit.hrl"). -include_lib("eunit/include/eunit.hrl").
-include_lib("limiter_proto/include/limproto_context_payproc_thrift.hrl").
-include_lib("damsel/include/dmsl_domain_thrift.hrl").
-spec test() -> _. -spec test() -> _.
-spec check_sign_prefix_test() -> _. -spec check_sign_prefix_test() -> _.
@ -782,32 +787,42 @@ check_calculate_year_shard_id_test() ->
?assertEqual(<<"past/year/1">>, calculate_calendar_shard_id(year, <<"2000-01-01T00:00:00Z">>, StartedAt2, 1)), ?assertEqual(<<"past/year/1">>, calculate_calendar_shard_id(year, <<"2000-01-01T00:00:00Z">>, StartedAt2, 1)),
?assertEqual(<<"future/year/0">>, calculate_calendar_shard_id(year, <<"2001-01-01T00:00:00Z">>, StartedAt2, 1)). ?assertEqual(<<"future/year/0">>, calculate_calendar_shard_id(year, <<"2001-01-01T00:00:00Z">>, StartedAt2, 1)).
-define(LIMIT_CONTEXT, #{ -define(PAYPROC_CTX_INVOICE(Invoice), #limiter_LimitContext{
context => #{ payment_processing = #context_payproc_Context{
payment_processing => #{ op = {invoice, #context_payproc_OperationInvoice{}},
op => invoice, invoice = #context_payproc_Invoice{
invoice => #{ invoice = Invoice
owner_id => <<"OWNER">>,
shop_id => <<"SHLOP">>
}
} }
} }
}). }).
-define(INVOICE(OwnerID, ShopID), #domain_Invoice{
id = <<"ID">>,
owner_id = OwnerID,
shop_id = ShopID,
created_at = <<"2000-02-02T12:12:12Z">>,
status = {unpaid, #domain_InvoiceUnpaid{}},
details = #domain_InvoiceDetails{product = <<>>},
due = <<"2222-02-02T12:12:12Z">>,
cost = #domain_Cash{amount = 42, currency = #domain_CurrencyRef{symbolic_code = <<"CNY">>}}
}).
-spec global_scope_empty_prefix_test() -> _. -spec global_scope_empty_prefix_test() -> _.
global_scope_empty_prefix_test() -> global_scope_empty_prefix_test() ->
?assertEqual(<<>>, mk_scope_prefix_impl(ordsets:new(), ?LIMIT_CONTEXT)). Context = #{context => ?PAYPROC_CTX_INVOICE(?INVOICE(<<"OWNER">>, <<"SHOP">>))},
?assertEqual(<<>>, mk_scope_prefix_impl(ordsets:new(), payment_processing, Context)).
-spec preserve_scope_prefix_order_test_() -> [_TestGen]. -spec preserve_scope_prefix_order_test_() -> [_TestGen].
preserve_scope_prefix_order_test_() -> preserve_scope_prefix_order_test_() ->
Context = #{context => ?PAYPROC_CTX_INVOICE(?INVOICE(<<"OWNER">>, <<"SHLOP">>))},
[ [
?_assertEqual( ?_assertEqual(
<<"/OWNER/SHLOP">>, <<"/OWNER/SHLOP">>,
mk_scope_prefix_impl(ordsets:from_list([shop, party]), ?LIMIT_CONTEXT) mk_scope_prefix_impl(ordsets:from_list([shop, party]), payment_processing, Context)
), ),
?_assertEqual( ?_assertEqual(
<<"/OWNER/SHLOP">>, <<"/OWNER/SHLOP">>,
mk_scope_prefix_impl(ordsets:from_list([shop]), ?LIMIT_CONTEXT) mk_scope_prefix_impl(ordsets:from_list([shop]), payment_processing, Context)
) )
]. ].

View File

@ -70,13 +70,13 @@ unmarshal(T, V, C) when
-spec marshal_event(machinery_mg_schema:version(), event(), context()) -> {machinery_msgpack:t(), context()}. -spec marshal_event(machinery_mg_schema:version(), event(), context()) -> {machinery_msgpack:t(), context()}.
marshal_event(1, TimestampedChange, Context) -> marshal_event(1, TimestampedChange, Context) ->
ThriftChange = lim_config_codec:marshal(timestamped_change, TimestampedChange), ThriftChange = lim_config_codec:marshal(timestamped_change, TimestampedChange),
Type = {struct, struct, {lim_limiter_config_thrift, 'TimestampedChange'}}, Type = {struct, struct, {limproto_config_thrift, 'TimestampedChange'}},
{{bin, lim_proto_utils:serialize(Type, ThriftChange)}, Context}. {{bin, lim_proto_utils:serialize(Type, ThriftChange)}, Context}.
-spec unmarshal_event(machinery_mg_schema:version(), machinery_msgpack:t(), context()) -> {event(), context()}. -spec unmarshal_event(machinery_mg_schema:version(), machinery_msgpack:t(), context()) -> {event(), context()}.
unmarshal_event(1, EncodedChange, Context) -> unmarshal_event(1, EncodedChange, Context) ->
{bin, EncodedThriftChange} = EncodedChange, {bin, EncodedThriftChange} = EncodedChange,
Type = {struct, struct, {lim_limiter_config_thrift, 'TimestampedChange'}}, Type = {struct, struct, {limproto_config_thrift, 'TimestampedChange'}},
ThriftChange = lim_proto_utils:deserialize(Type, EncodedThriftChange), ThriftChange = lim_proto_utils:deserialize(Type, EncodedThriftChange),
{lim_config_codec:unmarshal(timestamped_change, ThriftChange), Context}. {lim_config_codec:unmarshal(timestamped_change, ThriftChange), Context}.
@ -104,7 +104,7 @@ marshal_unmarshal_created_test() ->
scope => ordsets:from_list([party]), scope => ordsets:from_list([party]),
description => <<"description">> description => <<"description">>
}}, }},
Context = #{machine_ref => ID, machine_ns => config}, Context = #{machine_id => ID, machine_ns => config},
Event = {ev, lim_time:machinery_now(), Created}, Event = {ev, lim_time:machinery_now(), Created},
{Marshaled, _} = marshal_event(1, Event, Context), {Marshaled, _} = marshal_event(1, Event, Context),
{Unmarshaled, _} = unmarshal_event(1, Marshaled, Context), {Unmarshaled, _} = unmarshal_event(1, Marshaled, Context),

View File

@ -1,6 +1,7 @@
-module(lim_configurator). -module(lim_configurator).
-include_lib("limiter_proto/include/lim_configurator_thrift.hrl"). -include_lib("limiter_proto/include/limproto_configurator_thrift.hrl").
-include_lib("damsel/include/dmsl_base_thrift.hrl").
%% Woody handler %% Woody handler
@ -23,7 +24,7 @@ handle_function(Fn, Args, WoodyCtx, Opts) ->
-spec handle_function_(woody:func(), woody:args(), lim_context:t(), woody:options()) -> {ok, woody:result()}. -spec handle_function_(woody:func(), woody:args(), lim_context:t(), woody:options()) -> {ok, woody:result()}.
handle_function_( handle_function_(
'CreateLegacy', 'CreateLegacy',
{#limiter_configurator_LimitCreateParams{ {#configurator_LimitCreateParams{
id = ID, id = ID,
name = Name, name = Name,
description = Description, description = Description,
@ -51,7 +52,7 @@ handle_function_(
{error, {name, notfound}} -> {error, {name, notfound}} ->
woody_error:raise( woody_error:raise(
business, business,
#limiter_configurator_LimitConfigNameNotFound{} #configurator_LimitConfigNameNotFound{}
) )
end; end;
handle_function_('Create', {Params}, LimitContext, _Opts) -> handle_function_('Create', {Params}, LimitContext, _Opts) ->
@ -70,7 +71,7 @@ handle_function_('Get', {LimitID}, LimitContext, _Opts) ->
{ok, LimitConfig} -> {ok, LimitConfig} ->
{ok, lim_config_codec:marshal_config(LimitConfig)}; {ok, lim_config_codec:marshal_config(LimitConfig)};
{error, notfound} -> {error, notfound} ->
woody_error:raise(business, #limiter_configurator_LimitConfigNotFound{}) woody_error:raise(business, #configurator_LimitConfigNotFound{})
end. end.
map_type({turnover, _}) -> map_type({turnover, _}) ->
@ -78,7 +79,7 @@ map_type({turnover, _}) ->
map_type(_) -> map_type(_) ->
woody_error:raise( woody_error:raise(
business, business,
#'InvalidRequest'{errors = [<<"Config type not found.">>]} #base_InvalidRequest{errors = [<<"Config type not found.">>]}
). ).
mk_limit_config(<<"ShopDayTurnover">>) -> mk_limit_config(<<"ShopDayTurnover">>) ->

View File

@ -1,12 +1,11 @@
-module(lim_context). -module(lim_context).
-include_lib("limiter_proto/include/lim_limiter_context_thrift.hrl"). -include_lib("limiter_proto/include/limproto_limiter_thrift.hrl").
-export([create/1]). -export([create/1]).
-export([woody_context/1]). -export([woody_context/1]).
-export([get_operation/2]). -export([get_operation/2]).
-export([get_from_context/3]). -export([get_value/3]).
-export([get_from_context/4]).
-export([set_context/2]). -export([set_context/2]).
-export([set_clock/2]). -export([set_clock/2]).
@ -14,107 +13,26 @@
-export([clock/1]). -export([clock/1]).
-type woody_context() :: woody_context:ctx(). -type woody_context() :: woody_context:ctx().
-type timestamp() :: binary(). -type limit_context() :: limproto_limiter_thrift:'LimitContext'().
-type thrift_context() :: lim_limiter_thrift:'LimitContext'(). -type clock() :: limproto_limiter_thrift:'Clock'().
-type clock() :: lim_limiter_thrift:'Clock'().
-type id() :: binary().
-type token() :: binary().
-type exp_date() :: binary().
-type cash() :: lim_body:cash().
-type t() :: #{ -type t() :: #{
woody_context => woody_context(), woody_context => woody_context(),
context => context(), context => limit_context(),
clock => clock() clock => clock()
}. }.
-type context_type() :: payment_processing. -type context_type() :: payment_processing | withdrawal_processing.
-type context_operation() :: payment_processing_operation(). -type context_inner() :: lim_payproc_context:context() | lim_wthdproc_context:context().
-type context_operation() :: lim_payproc_context:operation() | lim_wthdproc_context:operation().
-type context() :: #{
payment_processing => payment_processing_context()
}.
-type payment_processing_context() :: #{
op := payment_processing_operation(),
invoice => payment_processing_invoice()
}.
-type payment_processing_operation() ::
invoice
| invoice_adjustment
| invoice_payment
| invoice_payment_adjustment
| invoice_payment_refund
| invoice_payment_chargeback.
-type payment_processing_invoice() :: #{
id => id(),
owner_id => id(),
shop_id => id(),
cost => cash(),
created_at => timestamp(),
adjustment => payment_processing_adjustment(),
payment => payment_processing_payment()
}.
-type payment_processing_adjustment() :: #{
id => id()
}.
-type payment_processing_payment() :: #{
id => id(),
owner_id => id(),
shop_id => id(),
cost => cash(),
capture_cost => cash(),
created_at => timestamp(),
flow => instant | hold,
payer => payment_processing_payer(),
adjustment => payment_processing_payment_adjustment(),
refund => payment_processing_payment_refund(),
chargeback => payment_processing_payment_chargeback()
}.
-type payment_processing_payer() ::
{payment_resource, payment_processing_payer_data()}
| {customer, payment_processing_payer_data()}
| {recurrent, payment_processing_payer_data()}.
-type payment_processing_payer_data() :: #{
payment_tool => payment_processing_payment_tool()
}.
-type payment_processing_payment_tool() :: {bank_card, payment_processing_bank_card()}.
-type payment_processing_bank_card() :: #{
token => token(),
exp_date => exp_date()
}.
-type payment_processing_payment_adjustment() :: #{
id => id(),
created_at => timestamp()
}.
-type payment_processing_payment_refund() :: #{
id => id(),
cost => cash(),
created_at => timestamp()
}.
-type payment_processing_payment_chargeback() :: #{
id => id(),
levy => cash(),
body => cash(),
created_at => timestamp()
}.
-export_type([t/0]). -export_type([t/0]).
-export_type([context/0]).
-export_type([context_type/0]). -export_type([context_type/0]).
-export_type([context_operation/0]). -export_type([context_operation/0]).
-callback get_operation(context_inner()) -> {ok, context_operation()} | {error, notfound}.
-callback get_value(_Name :: atom(), context_inner()) -> {ok, term()} | {error, notfound | {unsupported, _}}.
-spec create(woody_context()) -> t(). -spec create(woody_context()) -> t().
create(WoodyContext) -> create(WoodyContext) ->
#{woody_context => WoodyContext}. #{woody_context => WoodyContext}.
@ -129,83 +47,33 @@ clock(#{clock := Clock}) ->
clock(_) -> clock(_) ->
{error, notfound}. {error, notfound}.
-spec set_context(thrift_context(), t()) -> t(). -spec set_context(limit_context(), t()) -> t().
set_context(Context, LimContext) -> set_context(Context, LimContext) ->
LimContext#{context => unmarshal_context(Context)}. LimContext#{context => Context}.
-spec set_clock(clock(), t()) -> t(). -spec set_clock(clock(), t()) -> t().
set_clock(Clock, LimContext) -> set_clock(Clock, LimContext) ->
LimContext#{clock => Clock}. LimContext#{clock => Clock}.
-spec get_operation(context_type(), t()) -> {ok, atom()} | {error, notfound}. -spec get_operation(context_type(), t()) ->
get_operation(Type, #{context := Context}) -> {ok, context_operation()} | {error, notfound}.
case maps:get(Type, Context, undefined) of get_operation(Type, Context) ->
undefined -> {Mod, OperationContext} = get_operation_context(Type, Context),
{error, notfound}; Mod:get_operation(OperationContext).
#{op := Operation} ->
{ok, Operation}
end.
-spec get_from_context(context_type(), atom(), t()) -> {ok, term()} | {error, notfound}. -spec get_value(context_type(), atom(), t()) ->
get_from_context(payment_processing, ValueName, LimContext = #{context := Context}) -> {ok, term()} | {error, notfound | {unsupported, _}}.
case maps:get(payment_processing, Context, undefined) of get_value(Type, ValueName, Context) ->
undefined -> {Mod, OperationContext} = get_operation_context(Type, Context),
{error, notfound}; Mod:get_value(ValueName, OperationContext).
#{op := Operation} ->
get_from_context(payment_processing, ValueName, Operation, LimContext)
end;
get_from_context(_, _ValueName, _LimContext) ->
{error, notfound}.
-spec get_from_context(context_type(), atom(), context_operation(), t()) -> {ok, term()} | {error, notfound}. get_operation_context(
get_from_context(payment_processing, ValueName, Op, #{context := #{payment_processing := Context}}) -> payment_processing,
case get_payment_processing_operation_context(Op, Context) of #{context := #limiter_LimitContext{payment_processing = PayprocContext}}
{ok, OperationContext} ->
case maps:get(ValueName, OperationContext, undefined) of
undefined ->
{error, notfound};
Value ->
{ok, Value}
end;
Error ->
Error
end;
get_from_context(_, _ValueName, _Op, _LimContext) ->
{error, notfound}.
get_payment_processing_operation_context(invoice, #{invoice := Invoice}) ->
{ok, Invoice};
get_payment_processing_operation_context(invoice_adjustment, #{invoice := #{adjustment := Adjustment}}) ->
{ok, Adjustment};
get_payment_processing_operation_context(invoice_payment, #{invoice := #{payment := Payment}}) ->
{ok, Payment};
get_payment_processing_operation_context(
invoice_payment_adjustment,
#{invoice := #{payment := #{adjustment := Adjustment}}}
) -> ) ->
{ok, Adjustment}; {lim_payproc_context, PayprocContext};
get_payment_processing_operation_context( get_operation_context(
invoice_payment_refund, withdrawal_processing,
#{invoice := #{payment := #{refund := Refund}}} #{context := #limiter_LimitContext{withdrawal_processing = WithdrawalContext}}
) -> ) ->
{ok, Refund}; {lim_wthdproc_context, WithdrawalContext}.
get_payment_processing_operation_context(
invoice_payment_chargeback,
#{invoice := #{payment := #{chargeback := Chargeback}}}
) ->
{ok, Chargeback};
get_payment_processing_operation_context(_, _) ->
{error, notfound}.
%%
unmarshal_context(#limiter_context_LimitContext{limiter_payment_processing = PaymentProcessing}) when
PaymentProcessing =/= undefined
->
#{payment_processing => lim_limiter_context:unmarshal(PaymentProcessing)};
unmarshal_context(#limiter_context_LimitContext{payment_processing = PaymentProcessing}) when
PaymentProcessing =/= undefined
->
#{payment_processing => lim_payproc_context:unmarshal(PaymentProcessing)};
unmarshal_context(_) ->
#{}.

View File

@ -1,6 +1,8 @@
-module(lim_handler). -module(lim_handler).
-include_lib("limiter_proto/include/lim_limiter_thrift.hrl"). -include_lib("limiter_proto/include/limproto_limiter_thrift.hrl").
-include_lib("limiter_proto/include/limproto_base_thrift.hrl").
-include_lib("damsel/include/dmsl_base_thrift.hrl").
%% Woody handler %% Woody handler
@ -84,7 +86,7 @@ handle_get_error(Error) ->
-spec handle_hold_error(_) -> no_return(). -spec handle_hold_error(_) -> no_return().
handle_hold_error({_, {invalid_request, Errors}}) -> handle_hold_error({_, {invalid_request, Errors}}) ->
woody_error:raise(business, #'InvalidRequest'{errors = Errors}); woody_error:raise(business, #base_InvalidRequest{errors = Errors});
handle_hold_error(Error) -> handle_hold_error(Error) ->
handle_default_error(Error). handle_default_error(Error).
@ -92,13 +94,13 @@ handle_hold_error(Error) ->
handle_commit_error({_, {forbidden_operation_amount, Error}}) -> handle_commit_error({_, {forbidden_operation_amount, Error}}) ->
handle_forbidden_operation_amount_error(Error); handle_forbidden_operation_amount_error(Error);
handle_commit_error({_, {invalid_request, Errors}}) -> handle_commit_error({_, {invalid_request, Errors}}) ->
woody_error:raise(business, #'InvalidRequest'{errors = Errors}); woody_error:raise(business, #base_InvalidRequest{errors = Errors});
handle_commit_error(Error) -> handle_commit_error(Error) ->
handle_default_error(Error). handle_default_error(Error).
-spec handle_rollback_error(_) -> no_return(). -spec handle_rollback_error(_) -> no_return().
handle_rollback_error({_, {invalid_request, Errors}}) -> handle_rollback_error({_, {invalid_request, Errors}}) ->
woody_error:raise(business, #'InvalidRequest'{errors = Errors}); woody_error:raise(business, #base_InvalidRequest{errors = Errors});
handle_rollback_error(Error) -> handle_rollback_error(Error) ->
handle_default_error(Error). handle_default_error(Error).
@ -122,7 +124,7 @@ handle_forbidden_operation_amount_error(#{
positive -> positive ->
woody_error:raise(business, #limiter_ForbiddenOperationAmount{ woody_error:raise(business, #limiter_ForbiddenOperationAmount{
amount = Partial, amount = Partial,
allowed_range = #limiter_base_AmountRange{ allowed_range = #base_AmountRange{
upper = {inclusive, Full}, upper = {inclusive, Full},
lower = {inclusive, 0} lower = {inclusive, 0}
} }
@ -130,7 +132,7 @@ handle_forbidden_operation_amount_error(#{
negative -> negative ->
woody_error:raise(business, #limiter_ForbiddenOperationAmount{ woody_error:raise(business, #limiter_ForbiddenOperationAmount{
amount = Partial, amount = Partial,
allowed_range = #limiter_base_AmountRange{ allowed_range = #base_AmountRange{
upper = {inclusive, 0}, upper = {inclusive, 0},
lower = {inclusive, Full} lower = {inclusive, Full}
} }

View File

@ -1,129 +0,0 @@
-module(lim_limiter_context).
-include_lib("limiter_proto/include/lim_limiter_context_thrift.hrl").
-export([unmarshal/1]).
-type thrift_context() :: lim_limiter_context_thrift:'ContextPaymentProcessing'().
%%
-spec unmarshal(thrift_context()) -> lim_context:context().
unmarshal(#limiter_context_ContextPaymentProcessing{
op = {Operation, _},
invoice = Invoice
}) ->
genlib_map:compact(#{
op => Operation,
invoice => maybe_unmarshal(Invoice, fun unmarshal_payment_processing_invoice/1)
}).
unmarshal_payment_processing_invoice(#limiter_context_Invoice{
id = ID,
owner_id = OwnerID,
shop_id = ShopID,
cost = Cost,
created_at = CreatedAt,
effective_payment = Payment,
effective_adjustment = Adjustment
}) ->
genlib_map:compact(#{
id => maybe_unmarshal(ID, fun unmarshal_string/1),
owner_id => maybe_unmarshal(OwnerID, fun unmarshal_string/1),
shop_id => maybe_unmarshal(ShopID, fun unmarshal_string/1),
cost => maybe_unmarshal(Cost, fun unmarshal_cash/1),
created_at => maybe_unmarshal(CreatedAt, fun unmarshal_string/1),
adjustment => maybe_unmarshal(
Adjustment,
fun unmarshal_payment_processing_invoice_adjustment/1
),
payment => maybe_unmarshal(Payment, fun unmarshal_payment_processing_invoice_payment/1)
}).
unmarshal_payment_processing_invoice_adjustment(#limiter_context_InvoiceAdjustment{id = ID}) ->
genlib_map:compact(#{
id => maybe_unmarshal(ID, fun unmarshal_string/1)
}).
unmarshal_payment_processing_invoice_payment(#limiter_context_InvoicePayment{
id = ID,
owner_id = OwnerID,
shop_id = ShopID,
cost = Cost,
capture_cost = CaptureCost,
created_at = CreatedAt,
flow = Flow,
payer = Payer,
effective_adjustment = Adjustment,
effective_refund = EffectiveRefund,
effective_chargeback = EffectiveChargeback
}) ->
genlib_map:compact(#{
id => maybe_unmarshal(ID, fun unmarshal_string/1),
owner_id => maybe_unmarshal(OwnerID, fun unmarshal_string/1),
shop_id => maybe_unmarshal(ShopID, fun unmarshal_string/1),
cost => maybe_unmarshal(Cost, fun unmarshal_cash/1),
capture_cost => maybe_unmarshal(CaptureCost, fun unmarshal_cash/1),
created_at => maybe_unmarshal(CreatedAt, fun unmarshal_string/1),
flow => maybe_unmarshal(Flow, fun unmarshal_payment_processing_invoice_payment_flow/1),
payer => maybe_unmarshal(Payer, fun unmarshal_payment_processing_invoice_payment_payer/1),
adjustment => maybe_unmarshal(
Adjustment,
fun unmarshal_payment_processing_invoice_payment_adjustment/1
),
refund => maybe_unmarshal(EffectiveRefund, fun unmarshal_payment_processing_invoice_payment_refund/1),
chargeback => maybe_unmarshal(
EffectiveChargeback,
fun unmarshal_payment_processing_invoice_payment_chargeback/1
)
}).
unmarshal_payment_processing_invoice_payment_flow({Flow, _}) ->
Flow.
unmarshal_payment_processing_invoice_payment_payer({Payer, _}) ->
{Payer, #{}}.
unmarshal_payment_processing_invoice_payment_adjustment(#limiter_context_InvoicePaymentAdjustment{
id = ID,
created_at = CreatedAt
}) ->
genlib_map:compact(#{
id => maybe_unmarshal(ID, fun unmarshal_string/1),
created_at => maybe_unmarshal(CreatedAt, fun unmarshal_string/1)
}).
unmarshal_payment_processing_invoice_payment_refund(#limiter_context_InvoicePaymentRefund{
id = ID,
cost = Cost,
created_at = CreatedAt
}) ->
genlib_map:compact(#{
id => maybe_unmarshal(ID, fun unmarshal_string/1),
cost => maybe_unmarshal(Cost, fun unmarshal_cash/1),
created_at => maybe_unmarshal(CreatedAt, fun unmarshal_string/1)
}).
unmarshal_payment_processing_invoice_payment_chargeback(#limiter_context_InvoicePaymentChargeback{
id = ID,
levy = Levy,
body = Body,
created_at = CreatedAt
}) ->
genlib_map:compact(#{
id => maybe_unmarshal(ID, fun unmarshal_string/1),
levy => maybe_unmarshal(Levy, fun unmarshal_cash/1),
body => maybe_unmarshal(Body, fun unmarshal_cash/1),
created_at => maybe_unmarshal(CreatedAt, fun unmarshal_string/1)
}).
unmarshal_cash(#domain_Cash{amount = Amount, currency = #domain_CurrencyRef{symbolic_code = Currency}}) ->
#{amount => Amount, currency => Currency}.
unmarshal_string(Value) ->
Value.
maybe_unmarshal(undefined, _) ->
undefined;
maybe_unmarshal(Value, UnmarshalFun) ->
UnmarshalFun(Value).

View File

@ -1,213 +1,247 @@
-module(lim_payproc_context). -module(lim_payproc_context).
-include_lib("limiter_proto/include/lim_limiter_payproc_context_thrift.hrl"). -include_lib("limiter_proto/include/limproto_context_payproc_thrift.hrl").
-include_lib("damsel/include/dmsl_domain_thrift.hrl").
-export([unmarshal/1]). -behaviour(lim_context).
-export([get_operation/1]).
-export([get_value/2]).
-type thrift_context() :: lim_limiter_payproc_context_thrift:'Context'(). -type context() :: limproto_context_payproc_thrift:'Context'().
-type operation() ::
invoice
| invoice_adjustment
| invoice_payment
| invoice_payment_adjustment
| invoice_payment_refund
| invoice_payment_chargeback.
-export_type([operation/0]).
-export_type([context/0]).
%% %%
-spec unmarshal(thrift_context()) -> lim_context:context(). -spec get_operation(context()) -> {ok, operation()} | {error, notfound}.
unmarshal(#limiter_context_payproc_Context{ get_operation(#context_payproc_Context{op = {Operation, _}}) ->
op = {Operation, _}, {ok, Operation};
invoice = Invoice get_operation(#context_payproc_Context{op = undefined}) ->
}) -> {error, notfound}.
genlib_map:compact(#{
op => Operation,
invoice => maybe_unmarshal(Invoice, fun unmarshal_payment_processing_invoice/1)
}).
unmarshal_payment_processing_invoice(#limiter_context_payproc_Invoice{ -spec get_value(atom(), context()) -> {ok, term()} | {error, notfound | {unsupported, _}}.
invoice = #domain_Invoice{ get_value(ValueName, Context) ->
id = ID, case get_operation(Context) of
owner_id = OwnerID, {ok, Operation} ->
shop_id = ShopID, get_value(ValueName, Operation, Context);
cost = Cost, {error, _} = Error ->
created_at = CreatedAt Error
}, end.
payment = Payment,
adjustment = Adjustment
}) ->
genlib_map:compact(#{
id => maybe_unmarshal(ID, fun unmarshal_string/1),
owner_id => maybe_unmarshal(OwnerID, fun unmarshal_string/1),
shop_id => maybe_unmarshal(ShopID, fun unmarshal_string/1),
cost => maybe_unmarshal(Cost, fun unmarshal_cash/1),
created_at => maybe_unmarshal(CreatedAt, fun unmarshal_string/1),
adjustment => maybe_unmarshal(
Adjustment,
fun unmarshal_payment_processing_invoice_adjustment/1
),
payment => maybe_unmarshal(Payment, fun unmarshal_payment_processing_invoice_payment/1)
}).
unmarshal_payment_processing_invoice_adjustment(#domain_InvoiceAdjustment{id = ID}) -> get_value(owner_id, _Operation, Context) ->
#{id => unmarshal_string(ID)}. get_owner_id(Context);
get_value(shop_id, _Operation, Context) ->
get_shop_id(Context);
get_value(created_at, Operation, Context) ->
get_created_at(Operation, Context);
get_value(cost, Operation, Context) ->
get_cost(Operation, Context);
get_value(capture_cost, Operation, Context) ->
get_capture_cost(Operation, Context);
get_value(payment_tool, Operation, Context) ->
get_payment_tool(Operation, Context);
get_value(ValueName, _Operation, _Context) ->
{error, {unsupported, ValueName}}.
unmarshal_payment_processing_invoice_payment(#limiter_context_payproc_InvoicePayment{ %%
payment = #domain_InvoicePayment{
id = ID,
owner_id = OwnerID,
shop_id = ShopID,
cost = Cost,
created_at = CreatedAt,
flow = Flow,
status = Status,
payer = Payer
},
adjustment = Adjustment,
refund = EffectiveRefund,
chargeback = EffectiveChargeback
}) ->
genlib_map:compact(#{
id => maybe_unmarshal(ID, fun unmarshal_string/1),
owner_id => maybe_unmarshal(OwnerID, fun unmarshal_string/1),
shop_id => maybe_unmarshal(ShopID, fun unmarshal_string/1),
cost => maybe_unmarshal(Cost, fun unmarshal_cash/1),
capture_cost => get_capture_cost_from_status(Status),
created_at => maybe_unmarshal(CreatedAt, fun unmarshal_string/1),
flow => maybe_unmarshal(Flow, fun unmarshal_payment_processing_invoice_payment_flow/1),
payer => maybe_unmarshal(Payer, fun unmarshal_payment_processing_invoice_payment_payer/1),
adjustment => maybe_unmarshal(
Adjustment,
fun unmarshal_payment_processing_invoice_payment_adjustment/1
),
refund => maybe_unmarshal(EffectiveRefund, fun unmarshal_payment_processing_invoice_payment_refund/1),
chargeback => maybe_unmarshal(
EffectiveChargeback,
fun unmarshal_payment_processing_invoice_payment_chargeback/1
)
}).
get_capture_cost_from_status({captured, #domain_InvoicePaymentCaptured{cost = Cost}}) -> -define(INVOICE(V), #context_payproc_Context{
maybe_unmarshal(Cost, fun unmarshal_cash/1); invoice = #context_payproc_Invoice{
get_capture_cost_from_status(_) -> invoice = V = #domain_Invoice{}
undefined. }
}).
unmarshal_payment_processing_invoice_payment_flow({Flow, _}) -> -define(INVOICE_ADJUSTMENT(V), #context_payproc_Context{
Flow. invoice = #context_payproc_Invoice{
adjustment = V = #domain_InvoiceAdjustment{}
}
}).
unmarshal_payment_processing_invoice_payment_payer({Payer, Data}) -> -define(INVOICE_PAYMENT(V), #context_payproc_Context{
{Payer, unmarshal_payment_processing_invoice_payment_payer_data(Data)}. invoice = #context_payproc_Invoice{
payment = #context_payproc_InvoicePayment{payment = V = #domain_InvoicePayment{}}
}
}).
unmarshal_payment_processing_invoice_payment_payer_data(#domain_PaymentResourcePayer{ -define(INVOICE_PAYMENT_ADJUSTMENT(V), #context_payproc_Context{
resource = #domain_DisposablePaymentResource{payment_tool = PaymentTool} invoice = #context_payproc_Invoice{
}) -> payment = #context_payproc_InvoicePayment{adjustment = V = #domain_InvoicePaymentAdjustment{}}
genlib_map:compact(#{ }
payment_tool => maybe_unmarshal(PaymentTool, fun unmarshal_payment_processing_payment_tool/1) }).
});
unmarshal_payment_processing_invoice_payment_payer_data(#domain_CustomerPayer{payment_tool = PaymentTool}) ->
genlib_map:compact(#{
payment_tool => maybe_unmarshal(PaymentTool, fun unmarshal_payment_processing_payment_tool/1)
});
unmarshal_payment_processing_invoice_payment_payer_data(#domain_RecurrentPayer{payment_tool = PaymentTool}) ->
genlib_map:compact(#{
payment_tool => maybe_unmarshal(PaymentTool, fun unmarshal_payment_processing_payment_tool/1)
}).
unmarshal_payment_processing_payment_tool({bank_card, #domain_BankCard{token = Token, exp_date = ExpDate}}) -> -define(INVOICE_PAYMENT_REFUND(V), #context_payproc_Context{
{bank_card, #{ invoice = #context_payproc_Invoice{
token => Token, payment = #context_payproc_InvoicePayment{refund = V = #domain_InvoicePaymentRefund{}}
exp_date => maybe_unmarshal(ExpDate, fun unmarshal_payment_processing_bank_card_exp_data/1) }
}}; }).
unmarshal_payment_processing_payment_tool(_) ->
undefined.
unmarshal_payment_processing_bank_card_exp_data(#domain_BankCardExpDate{month = Month, year = Year}) -> -define(INVOICE_PAYMENT_CHARGEBACK(V), #context_payproc_Context{
BinaryMonth = integer_to_binary(Month), invoice = #context_payproc_Invoice{
BinaryYear = integer_to_binary(Year), payment = #context_payproc_InvoicePayment{chargeback = V = #domain_InvoicePaymentChargeback{}}
<<BinaryMonth/binary, "/", BinaryYear/binary>>. }
}).
unmarshal_payment_processing_invoice_payment_adjustment(#domain_InvoicePaymentAdjustment{ get_owner_id(?INVOICE(Invoice)) ->
id = ID, {ok, Invoice#domain_Invoice.owner_id};
created_at = CreatedAt get_owner_id(_) ->
}) -> {error, notfound}.
genlib_map:compact(#{
id => maybe_unmarshal(ID, fun unmarshal_string/1),
created_at => maybe_unmarshal(CreatedAt, fun unmarshal_string/1)
}).
unmarshal_payment_processing_invoice_payment_refund(#domain_InvoicePaymentRefund{ get_shop_id(?INVOICE(Invoice)) ->
id = ID, {ok, Invoice#domain_Invoice.shop_id};
cash = Cost, get_shop_id(_) ->
created_at = CreatedAt {error, notfound}.
}) ->
genlib_map:compact(#{
id => maybe_unmarshal(ID, fun unmarshal_string/1),
cost => maybe_unmarshal(Cost, fun unmarshal_cash/1),
created_at => maybe_unmarshal(CreatedAt, fun unmarshal_string/1)
}).
unmarshal_payment_processing_invoice_payment_chargeback(#domain_InvoicePaymentChargeback{ get_created_at(invoice, ?INVOICE(Invoice)) ->
id = ID, {ok, Invoice#domain_Invoice.created_at};
levy = Levy, get_created_at(invoice_adjustment, ?INVOICE_ADJUSTMENT(Adjustment)) ->
body = Body, {ok, Adjustment#domain_InvoiceAdjustment.created_at};
created_at = CreatedAt get_created_at(invoice_payment, ?INVOICE_PAYMENT(Payment)) ->
}) -> {ok, Payment#domain_InvoicePayment.created_at};
genlib_map:compact(#{ get_created_at(invoice_payment_adjustment, ?INVOICE_PAYMENT_ADJUSTMENT(Adjustment)) ->
id => maybe_unmarshal(ID, fun unmarshal_string/1), {ok, Adjustment#domain_InvoicePaymentAdjustment.created_at};
levy => maybe_unmarshal(Levy, fun unmarshal_cash/1), get_created_at(invoice_payment_refund, ?INVOICE_PAYMENT_REFUND(Refund)) ->
body => maybe_unmarshal(Body, fun unmarshal_cash/1), {ok, Refund#domain_InvoicePaymentRefund.created_at};
created_at => maybe_unmarshal(CreatedAt, fun unmarshal_string/1) get_created_at(invoice_payment_chargeback, ?INVOICE_PAYMENT_CHARGEBACK(Chargeback)) ->
}). {ok, Chargeback#domain_InvoicePaymentChargeback.created_at};
get_created_at(_, _CtxInvoice) ->
{error, notfound}.
unmarshal_cash(#domain_Cash{amount = Amount, currency = #domain_CurrencyRef{symbolic_code = Currency}}) -> get_cost(invoice, ?INVOICE(Invoice)) ->
#{amount => Amount, currency => Currency}. lim_payproc_utils:cash(Invoice#domain_Invoice.cost);
get_cost(invoice_payment, ?INVOICE_PAYMENT(Payment)) ->
lim_payproc_utils:cash(Payment#domain_InvoicePayment.cost);
get_cost(invoice_payment_refund, ?INVOICE_PAYMENT_REFUND(Refund)) ->
lim_payproc_utils:cash(Refund#domain_InvoicePaymentRefund.cash);
get_cost(invoice_payment_chargeback, ?INVOICE_PAYMENT_CHARGEBACK(Chargeback)) ->
lim_payproc_utils:cash(Chargeback#domain_InvoicePaymentChargeback.body);
get_cost(_, _CtxInvoice) ->
{error, notfound}.
unmarshal_string(Value) -> get_capture_cost(invoice_payment, ?INVOICE_PAYMENT(Payment)) ->
Value. get_capture_cost(Payment#domain_InvoicePayment.status);
get_capture_cost(_, _CtxInvoice) ->
{error, notfound}.
maybe_unmarshal(undefined, _) -> get_capture_cost({captured, #domain_InvoicePaymentCaptured{cost = Cost}}) when Cost /= undefined ->
undefined; lim_payproc_utils:cash(Cost);
maybe_unmarshal(Value, UnmarshalFun) -> get_capture_cost({_Status, _}) ->
UnmarshalFun(Value). {error, notfound}.
get_payment_tool(Operation, ?INVOICE_PAYMENT(Payment)) when
Operation == invoice_payment;
Operation == invoice_payment_adjustment;
Operation == invoice_payment_refund;
Operation == invoice_payment_chargeback
->
{_Type, Payer} = Payment#domain_InvoicePayment.payer,
get_payer_payment_tool(Payer);
get_payment_tool(_, _CtxInvoice) ->
{error, notfound}.
get_payer_payment_tool(#domain_PaymentResourcePayer{resource = #domain_DisposablePaymentResource{payment_tool = PT}}) ->
lim_payproc_utils:payment_tool(PT);
get_payer_payment_tool(#domain_CustomerPayer{payment_tool = PT}) ->
lim_payproc_utils:payment_tool(PT);
get_payer_payment_tool(#domain_RecurrentPayer{payment_tool = PT}) ->
lim_payproc_utils:payment_tool(PT).
-ifdef(TEST). -ifdef(TEST).
-include_lib("eunit/include/eunit.hrl"). -include_lib("eunit/include/eunit.hrl").
-spec test() -> _. -spec test() -> _.
-spec marshal_unmarshal_created_test() -> _. -define(PAYMENT_W_PAYER(Payer), #domain_InvoicePayment{
marshal_unmarshal_created_test() -> id = <<"ID">>,
ExpDate = #domain_BankCardExpDate{month = 2, year = 2022}, created_at = <<"2000-02-02T12:12:12Z">>,
status = {pending, #domain_InvoicePaymentPending{}},
cost = #domain_Cash{
amount = 42,
currency = #domain_CurrencyRef{symbolic_code = <<"CNY">>}
},
domain_revision = 42,
flow = {instant, #domain_InvoicePaymentFlowInstant{}},
payer = Payer
}).
-define(CONTEXT_PAYMENT(Payment), #context_payproc_Context{
op = {invoice_payment, #context_payproc_OperationInvoicePayment{}},
invoice = #context_payproc_Invoice{
payment = #context_payproc_InvoicePayment{
payment = Payment
}
}
}).
-spec get_payment_tool_test_() -> [_TestGen].
get_payment_tool_test_() ->
PaymentTool = PaymentTool =
{bank_card, #domain_BankCard{ {bank_card, #domain_BankCard{
token = <<"Token">>, token = <<"Token">>,
bin = <<"bin">>, bin = <<"654321">>,
exp_date = ExpDate, exp_date = #domain_BankCardExpDate{month = 2, year = 2022},
last_digits = <<"1234">> last_digits = <<"1234">>
}}, }},
Data0 = #domain_PaymentResourcePayer{ PaymentResourcePayer =
resource = #domain_DisposablePaymentResource{payment_tool = PaymentTool}, {payment_resource, #domain_PaymentResourcePayer{
contact_info = #domain_ContactInfo{} resource = #domain_DisposablePaymentResource{payment_tool = PaymentTool},
}, contact_info = #domain_ContactInfo{}
#{payment_tool := _} = unmarshal_payment_processing_invoice_payment_payer_data(Data0), }},
Data1 = #domain_CustomerPayer{ CustomerPayer =
customer_id = <<"customer_id">>, {customer, #domain_CustomerPayer{
customer_binding_id = <<"customer_binding_id">>, customer_id = <<"customer_id">>,
rec_payment_tool_id = <<"rec_payment_tool_id">>, customer_binding_id = <<"customer_binding_id">>,
payment_tool = PaymentTool, rec_payment_tool_id = <<"rec_payment_tool_id">>,
contact_info = #domain_ContactInfo{} payment_tool = PaymentTool,
}, contact_info = #domain_ContactInfo{}
#{payment_tool := _} = unmarshal_payment_processing_invoice_payment_payer_data(Data1), }},
Data2 = #domain_RecurrentPayer{ RecurrentPayer =
payment_tool = PaymentTool, {recurrent, #domain_RecurrentPayer{
recurrent_parent = #domain_RecurrentParentPayment{ payment_tool = PaymentTool,
invoice_id = <<"invoice_id">>, recurrent_parent = #domain_RecurrentParentPayment{
payment_id = <<"payment_id">> invoice_id = <<"invoice_id">>,
}, payment_id = <<"payment_id">>
contact_info = #domain_ContactInfo{} },
}, contact_info = #domain_ContactInfo{}
#{payment_tool := _} = unmarshal_payment_processing_invoice_payment_payer_data(Data2), }},
Data3 = #domain_RecurrentPayer{ ExpectedValue = {bank_card, #{token => <<"Token">>, exp_date => {2, 2022}}},
payment_tool = {payment_terminal, #domain_PaymentTerminal{}}, [
recurrent_parent = #domain_RecurrentParentPayment{ ?_assertEqual(
invoice_id = <<"invoice_id">>, {ok, ExpectedValue},
payment_id = <<"payment_id">> get_value(payment_tool, ?CONTEXT_PAYMENT(?PAYMENT_W_PAYER(PaymentResourcePayer)))
}, ),
contact_info = #domain_ContactInfo{} ?_assertEqual(
}, {ok, ExpectedValue},
#{} = unmarshal_payment_processing_invoice_payment_payer_data(Data3). get_value(payment_tool, ?CONTEXT_PAYMENT(?PAYMENT_W_PAYER(CustomerPayer)))
),
?_assertEqual(
{ok, ExpectedValue},
get_value(payment_tool, ?CONTEXT_PAYMENT(?PAYMENT_W_PAYER(RecurrentPayer)))
)
].
-spec get_payment_tool_unsupported_test_() -> _TestGen.
get_payment_tool_unsupported_test_() ->
Payer =
{recurrent, #domain_RecurrentPayer{
payment_tool = {payment_terminal, #domain_PaymentTerminal{}},
recurrent_parent = #domain_RecurrentParentPayment{
invoice_id = <<"invoice_id">>,
payment_id = <<"payment_id">>
},
contact_info = #domain_ContactInfo{}
}},
?_assertEqual(
{error, {unsupported, {payment_tool, payment_terminal}}},
get_value(payment_tool, ?CONTEXT_PAYMENT(?PAYMENT_W_PAYER(Payer)))
).
-endif. -endif.

View File

@ -0,0 +1,48 @@
-module(lim_payproc_utils).
-include_lib("damsel/include/dmsl_domain_thrift.hrl").
-export([cash/1]).
-export([payment_tool/1]).
-type cash() ::
lim_body:cash().
-type payment_tool() ::
{bank_card, #{
token := binary(),
exp_date := {1..12, 2000..9999} | undefined
}}
| {digital_wallet, #{
id := binary(),
service := binary()
}}.
%%
-spec cash(dmsl_domain_thrift:'Cash'()) ->
{ok, cash()}.
cash(#domain_Cash{amount = Amount, currency = #domain_CurrencyRef{symbolic_code = Currency}}) ->
{ok, #{amount => Amount, currency => Currency}}.
-spec payment_tool(dmsl_domain_thrift:'PaymentTool'()) ->
{ok, payment_tool()} | {error, {unsupported, _}}.
payment_tool({bank_card, BC}) ->
{ok,
{bank_card, #{
token => BC#domain_BankCard.token,
exp_date => get_bank_card_expdate(BC#domain_BankCard.exp_date)
}}};
payment_tool({digital_wallet, DW}) ->
{ok,
{digital_wallet, #{
id => DW#domain_DigitalWallet.id,
service => DW#domain_DigitalWallet.payment_service#domain_PaymentServiceRef.id
}}};
payment_tool({Type, _}) ->
{error, {unsupported, {payment_tool, Type}}}.
get_bank_card_expdate(#domain_BankCardExpDate{month = Month, year = Year}) ->
{Month, Year};
get_bank_card_expdate(undefined) ->
undefined.

View File

@ -1,6 +1,7 @@
-module(lim_range_codec). -module(lim_range_codec).
-include_lib("limiter_proto/include/lim_limiter_range_thrift.hrl"). -include_lib("limiter_proto/include/limproto_range_thrift.hrl").
-include_lib("limiter_proto/include/limproto_timerange_thrift.hrl").
-export([marshal/2]). -export([marshal/2]).
-export([unmarshal/2]). -export([unmarshal/2]).
@ -20,14 +21,14 @@
-spec marshal(type_name(), decoded_value()) -> encoded_value(). -spec marshal(type_name(), decoded_value()) -> encoded_value().
marshal(timestamped_change, {ev, Timestamp, Change}) -> marshal(timestamped_change, {ev, Timestamp, Change}) ->
#limiter_range_TimestampedChange{ #range_TimestampedChange{
change = marshal(change, Change), change = marshal(change, Change),
occured_at = marshal(timestamp, Timestamp) occured_at = marshal(timestamp, Timestamp)
}; };
marshal(change, {created, Range}) -> marshal(change, {created, Range}) ->
{created, #limiter_range_CreatedChange{limit_range = marshal(range, Range)}}; {created, #range_CreatedChange{limit_range = marshal(range, Range)}};
marshal(change, {time_range_created, TimeRange}) -> marshal(change, {time_range_created, TimeRange}) ->
{time_range_created, #limiter_range_TimeRangeCreatedChange{time_range = marshal(time_range, TimeRange)}}; {time_range_created, #range_TimeRangeCreatedChange{time_range = marshal(time_range, TimeRange)}};
marshal( marshal(
range, range,
Range = #{ Range = #{
@ -36,7 +37,7 @@ marshal(
created_at := CreatedAt created_at := CreatedAt
} }
) -> ) ->
#limiter_range_LimitRange{ #range_LimitRange{
id = ID, id = ID,
type = marshal(time_range_type, Type), type = marshal(time_range_type, Type),
created_at = CreatedAt, created_at = CreatedAt,
@ -48,7 +49,7 @@ marshal(time_range, #{
account_id_from := AccountIDFrom, account_id_from := AccountIDFrom,
account_id_to := AccountIDTo account_id_to := AccountIDTo
}) -> }) ->
#limiter_time_range_TimeRange{ #timerange_TimeRange{
upper = Upper, upper = Upper,
lower = Lower, lower = Lower,
account_id_from = AccountIDFrom, account_id_from = AccountIDFrom,
@ -57,15 +58,15 @@ marshal(time_range, #{
marshal(time_range_type, {calendar, SubType}) -> marshal(time_range_type, {calendar, SubType}) ->
{calendar, marshal(time_range_sub_type, SubType)}; {calendar, marshal(time_range_sub_type, SubType)};
marshal(time_range_type, {interval, Interval}) -> marshal(time_range_type, {interval, Interval}) ->
{interval, #limiter_time_range_TimeRangeTypeInterval{amount = Interval}}; {interval, #timerange_TimeRangeTypeInterval{amount = Interval}};
marshal(time_range_sub_type, year) -> marshal(time_range_sub_type, year) ->
{year, #limiter_time_range_TimeRangeTypeCalendarYear{}}; {year, #timerange_TimeRangeTypeCalendarYear{}};
marshal(time_range_sub_type, month) -> marshal(time_range_sub_type, month) ->
{month, #limiter_time_range_TimeRangeTypeCalendarMonth{}}; {month, #timerange_TimeRangeTypeCalendarMonth{}};
marshal(time_range_sub_type, week) -> marshal(time_range_sub_type, week) ->
{week, #limiter_time_range_TimeRangeTypeCalendarWeek{}}; {week, #timerange_TimeRangeTypeCalendarWeek{}};
marshal(time_range_sub_type, day) -> marshal(time_range_sub_type, day) ->
{day, #limiter_time_range_TimeRangeTypeCalendarDay{}}; {day, #timerange_TimeRangeTypeCalendarDay{}};
marshal(timestamp, {DateTime, USec}) -> marshal(timestamp, {DateTime, USec}) ->
DateTimeinSeconds = genlib_time:daytime_to_unixtime(DateTime), DateTimeinSeconds = genlib_time:daytime_to_unixtime(DateTime),
{TimeinUnit, Unit} = {TimeinUnit, Unit} =
@ -80,14 +81,14 @@ marshal(timestamp, {DateTime, USec}) ->
-spec unmarshal(type_name(), encoded_value()) -> decoded_value(). -spec unmarshal(type_name(), encoded_value()) -> decoded_value().
unmarshal(timestamped_change, TimestampedChange) -> unmarshal(timestamped_change, TimestampedChange) ->
Timestamp = unmarshal(timestamp, TimestampedChange#limiter_range_TimestampedChange.occured_at), Timestamp = unmarshal(timestamp, TimestampedChange#range_TimestampedChange.occured_at),
Change = unmarshal(change, TimestampedChange#limiter_range_TimestampedChange.change), Change = unmarshal(change, TimestampedChange#range_TimestampedChange.change),
{ev, Timestamp, Change}; {ev, Timestamp, Change};
unmarshal(change, {created, #limiter_range_CreatedChange{limit_range = Range}}) -> unmarshal(change, {created, #range_CreatedChange{limit_range = Range}}) ->
{created, unmarshal(range, Range)}; {created, unmarshal(range, Range)};
unmarshal(change, {time_range_created, #limiter_range_TimeRangeCreatedChange{time_range = Range}}) -> unmarshal(change, {time_range_created, #range_TimeRangeCreatedChange{time_range = Range}}) ->
{time_range_created, unmarshal(time_range, Range)}; {time_range_created, unmarshal(time_range, Range)};
unmarshal(range, #limiter_range_LimitRange{ unmarshal(range, #range_LimitRange{
id = ID, id = ID,
type = Type, type = Type,
created_at = CreatedAt, created_at = CreatedAt,
@ -99,7 +100,7 @@ unmarshal(range, #limiter_range_LimitRange{
created_at => CreatedAt, created_at => CreatedAt,
currency => Currency currency => Currency
}); });
unmarshal(time_range, #limiter_time_range_TimeRange{ unmarshal(time_range, #timerange_TimeRange{
upper = Upper, upper = Upper,
lower = Lower, lower = Lower,
account_id_from = AccountIDFrom, account_id_from = AccountIDFrom,
@ -113,7 +114,7 @@ unmarshal(time_range, #limiter_time_range_TimeRange{
}; };
unmarshal(time_range_type, {calendar, SubType}) -> unmarshal(time_range_type, {calendar, SubType}) ->
{calendar, unmarshal(time_range_sub_type, SubType)}; {calendar, unmarshal(time_range_sub_type, SubType)};
unmarshal(time_range_type, {interval, #limiter_time_range_TimeRangeTypeInterval{amount = Interval}}) -> unmarshal(time_range_type, {interval, #timerange_TimeRangeTypeInterval{amount = Interval}}) ->
{interval, Interval}; {interval, Interval};
unmarshal(time_range_sub_type, {year, _}) -> unmarshal(time_range_sub_type, {year, _}) ->
year; year;

View File

@ -70,13 +70,13 @@ unmarshal(T, V, C) when
-spec marshal_event(machinery_mg_schema:version(), event(), context()) -> {machinery_msgpack:t(), context()}. -spec marshal_event(machinery_mg_schema:version(), event(), context()) -> {machinery_msgpack:t(), context()}.
marshal_event(1, TimestampedChange, Context) -> marshal_event(1, TimestampedChange, Context) ->
ThriftChange = lim_range_codec:marshal(timestamped_change, TimestampedChange), ThriftChange = lim_range_codec:marshal(timestamped_change, TimestampedChange),
Type = {struct, struct, {lim_limiter_range_thrift, 'TimestampedChange'}}, Type = {struct, struct, {limproto_range_thrift, 'TimestampedChange'}},
{{bin, lim_proto_utils:serialize(Type, ThriftChange)}, Context}. {{bin, lim_proto_utils:serialize(Type, ThriftChange)}, Context}.
-spec unmarshal_event(machinery_mg_schema:version(), machinery_msgpack:t(), context()) -> {event(), context()}. -spec unmarshal_event(machinery_mg_schema:version(), machinery_msgpack:t(), context()) -> {event(), context()}.
unmarshal_event(1, EncodedChange, Context) -> unmarshal_event(1, EncodedChange, Context) ->
{bin, EncodedThriftChange} = EncodedChange, {bin, EncodedThriftChange} = EncodedChange,
Type = {struct, struct, {lim_limiter_range_thrift, 'TimestampedChange'}}, Type = {struct, struct, {limproto_range_thrift, 'TimestampedChange'}},
ThriftChange = lim_proto_utils:deserialize(Type, EncodedThriftChange), ThriftChange = lim_proto_utils:deserialize(Type, EncodedThriftChange),
{lim_range_codec:unmarshal(timestamped_change, ThriftChange), Context}. {lim_range_codec:unmarshal(timestamped_change, ThriftChange), Context}.
@ -98,7 +98,7 @@ marshal_unmarshal_created_test() ->
created_at => <<"2000-01-01T00:00:00Z">>, created_at => <<"2000-01-01T00:00:00Z">>,
currency => <<"USD">> currency => <<"USD">>
}}, }},
Context = #{machine_ref => ID, machine_ns => limrange}, Context = #{machine_id => ID, machine_ns => limrange},
Event = {ev, lim_time:machinery_now(), Created}, Event = {ev, lim_time:machinery_now(), Created},
{Marshaled, _} = marshal_event(1, Event, Context), {Marshaled, _} = marshal_event(1, Event, Context),
{Unmarshaled, _} = unmarshal_event(1, Marshaled, Context), {Unmarshaled, _} = unmarshal_event(1, Marshaled, Context),
@ -113,7 +113,7 @@ marshal_unmarshal_time_range_created_test() ->
upper => <<"2000-01-01T00:00:00Z">>, upper => <<"2000-01-01T00:00:00Z">>,
lower => <<"2000-01-01T00:00:00Z">> lower => <<"2000-01-01T00:00:00Z">>
}}, }},
Context = #{machine_ref => <<"id">>, machine_ns => limrange}, Context = #{machine_id => <<"id">>, machine_ns => limrange},
Event = {ev, lim_time:machinery_now(), TimeRangeCreated}, Event = {ev, lim_time:machinery_now(), TimeRangeCreated},
{Marshaled, _} = marshal_event(1, Event, Context), {Marshaled, _} = marshal_event(1, Event, Context),
{Unmarshaled, _} = unmarshal_event(1, Marshaled, Context), {Unmarshaled, _} = unmarshal_event(1, Marshaled, Context),

View File

@ -20,7 +20,7 @@
| {error, conversion_error()}. | {error, conversion_error()}.
convert(#{amount := Amount, currency := Currency}, DestinationCurrency, Config, LimitContext) -> convert(#{amount := Amount, currency := Currency}, DestinationCurrency, Config, LimitContext) ->
ContextType = lim_config_machine:context_type(Config), ContextType = lim_config_machine:context_type(Config),
{ok, Timestamp} = lim_context:get_from_context(ContextType, created_at, LimitContext), {ok, Timestamp} = lim_context:get_value(ContextType, created_at, LimitContext),
Request = #rate_ConversionRequest{ Request = #rate_ConversionRequest{
source = Currency, source = Currency,
destination = DestinationCurrency, destination = DestinationCurrency,

View File

@ -3,7 +3,7 @@
-export([compute/4]). -export([compute/4]).
-type amount() :: lim_body:amount(). -type amount() :: lim_body:amount().
-type currency() :: lim_domain_thrift:'CurrencySymbolicCode'(). -type currency() :: dmsl_domain_thrift:'CurrencySymbolicCode'().
-type stage() :: hold | commit. -type stage() :: hold | commit.
-type t() :: number | {amount, currency()}. -type t() :: number | {amount, currency()}.
@ -39,7 +39,7 @@ get_commit_body(Config, LimitContext) ->
case lim_body:get(partial, Config, LimitContext) of case lim_body:get(partial, Config, LimitContext) of
{ok, Body} -> {ok, Body} ->
Body; Body;
{error, notfound} -> {error, _} ->
get_body(Config, LimitContext) get_body(Config, LimitContext)
end. end.

View File

@ -1,6 +1,6 @@
-module(lim_turnover_processor). -module(lim_turnover_processor).
-include_lib("limiter_proto/include/lim_limiter_thrift.hrl"). -include_lib("limiter_proto/include/limproto_limiter_thrift.hrl").
-behaviour(lim_config_machine). -behaviour(lim_config_machine).
@ -108,13 +108,13 @@ rollback(LimitChange = #limiter_LimitChange{id = LimitID}, Config, LimitContext)
end). end).
compute_limit_time_range_location(LimitID, Config, LimitContext) -> compute_limit_time_range_location(LimitID, Config, LimitContext) ->
{ok, Timestamp} = lim_context:get_from_context(payment_processing, created_at, LimitContext), Timestamp = get_timestamp(Config, LimitContext),
LimitRangeID = construct_range_id(LimitID, Timestamp, Config, LimitContext), LimitRangeID = construct_range_id(LimitID, Timestamp, Config, LimitContext),
TimeRange = lim_config_machine:calculate_time_range(Timestamp, Config), TimeRange = lim_config_machine:calculate_time_range(Timestamp, Config),
{LimitRangeID, TimeRange}. {LimitRangeID, TimeRange}.
ensure_limit_time_range(LimitID, Config, LimitContext) -> ensure_limit_time_range(LimitID, Config, LimitContext) ->
{ok, Timestamp} = lim_context:get_from_context(payment_processing, created_at, LimitContext), Timestamp = get_timestamp(Config, LimitContext),
{LimitRangeID, TimeRange} = compute_limit_time_range_location(LimitID, Config, LimitContext), {LimitRangeID, TimeRange} = compute_limit_time_range_location(LimitID, Config, LimitContext),
CreateParams = genlib_map:compact(#{ CreateParams = genlib_map:compact(#{
id => LimitRangeID, id => LimitRangeID,
@ -124,6 +124,11 @@ ensure_limit_time_range(LimitID, Config, LimitContext) ->
}), }),
unwrap(lim_range_machine:ensure_exists(CreateParams, TimeRange, LimitContext)). unwrap(lim_range_machine:ensure_exists(CreateParams, TimeRange, LimitContext)).
get_timestamp(Config, LimitContext) ->
ContextType = lim_config_machine:context_type(Config),
{ok, Timestamp} = lim_context:get_value(ContextType, created_at, LimitContext),
Timestamp.
construct_plan_id(#limiter_LimitChange{change_id = ChangeID}) -> construct_plan_id(#limiter_LimitChange{change_id = ChangeID}) ->
% DISCUSS % DISCUSS
ChangeID. ChangeID.

View File

@ -0,0 +1,73 @@
-module(lim_wthdproc_context).
-include_lib("limiter_proto/include/limproto_context_withdrawal_thrift.hrl").
-include_lib("damsel/include/dmsl_wthd_domain_thrift.hrl").
-behaviour(lim_context).
-export([get_operation/1]).
-export([get_value/2]).
-type context() :: limproto_context_withdrawal_thrift:'Context'().
-type operation() ::
withdrawal.
-export_type([operation/0]).
-export_type([context/0]).
%%
-spec get_operation(context()) -> {ok, operation()} | {error, notfound}.
get_operation(#context_withdrawal_Context{op = {Operation, _}}) ->
{ok, Operation};
get_operation(#context_withdrawal_Context{op = undefined}) ->
{error, notfound}.
-spec get_value(atom(), context()) -> {ok, term()} | {error, notfound | {unsupported, _}}.
get_value(ValueName, Context) ->
case get_operation(Context) of
{ok, Operation} ->
get_value(ValueName, Operation, Context);
{error, _} = Error ->
Error
end.
get_value(owner_id, _Operation, Context) ->
get_owner_id(Context);
get_value(created_at, Operation, Context) ->
get_created_at(Operation, Context);
get_value(cost, Operation, Context) ->
get_cost(Operation, Context);
get_value(payment_tool, Operation, Context) ->
get_payment_tool(Operation, Context);
get_value(ValueName, _Operation, _Context) ->
{error, {unsupported, ValueName}}.
-define(WITHDRAWAL(V), #context_withdrawal_Context{
withdrawal = #context_withdrawal_Withdrawal{
withdrawal = V = #wthd_domain_Withdrawal{}
}
}).
get_owner_id(?WITHDRAWAL(Wthd)) ->
Identity = Wthd#wthd_domain_Withdrawal.sender,
{ok, Identity#wthd_domain_Identity.owner_id};
get_owner_id(_CtxWithdrawal) ->
{error, notfound}.
get_created_at(withdrawal, ?WITHDRAWAL(Wthd)) ->
{ok, Wthd#wthd_domain_Withdrawal.created_at};
get_created_at(_, _CtxWithdrawal) ->
{error, notfound}.
get_cost(withdrawal, ?WITHDRAWAL(Wthd)) ->
Body = Wthd#wthd_domain_Withdrawal.body,
lim_payproc_utils:cash(Body);
get_cost(_, _CtxWithdrawal) ->
{error, notfound}.
get_payment_tool(withdrawal, ?WITHDRAWAL(Wthd)) ->
Destination = Wthd#wthd_domain_Withdrawal.destination,
lim_payproc_utils:payment_tool(Destination);
get_payment_tool(_, _CtxWithdrawal) ->
{error, notfound}.

View File

@ -5,7 +5,6 @@
{applications, [ {applications, [
kernel, kernel,
stdlib, stdlib,
damsel,
limiter_proto, limiter_proto,
xrates_proto, xrates_proto,
machinery, machinery,

View File

@ -91,11 +91,11 @@ get_handler_specs(ServiceOpts) ->
[ [
{ {
maps:get(path, LimiterService, <<"/v1/limiter">>), maps:get(path, LimiterService, <<"/v1/limiter">>),
{{lim_limiter_thrift, 'Limiter'}, lim_handler} {{limproto_limiter_thrift, 'Limiter'}, lim_handler}
}, },
{ {
maps:get(path, ConfiguratorService, <<"/v1/configurator">>), maps:get(path, ConfiguratorService, <<"/v1/configurator">>),
{{lim_configurator_thrift, 'Configurator'}, lim_configurator} {{limproto_configurator_thrift, 'Configurator'}, lim_configurator}
} }
]. ].

View File

@ -1,11 +1,12 @@
-module(lim_client). -module(lim_client).
-include_lib("limiter_proto/include/lim_limiter_thrift.hrl"). -include_lib("limiter_proto/include/limproto_limiter_thrift.hrl").
-export([new/0]). -export([new/0]).
-export([get/3]). -export([get/3]).
-export([hold/3]). -export([hold/3]).
-export([commit/3]). -export([commit/3]).
-export([rollback/3]).
-export([legacy_create_config/2]). -export([legacy_create_config/2]).
-export([create_config/2]). -export([create_config/2]).
@ -13,12 +14,12 @@
-type client() :: woody_context:ctx(). -type client() :: woody_context:ctx().
-type limit_id() :: lim_limiter_thrift:'LimitID'(). -type limit_id() :: limproto_limiter_thrift:'LimitID'().
-type limit_change() :: lim_limiter_thrift:'LimitChange'(). -type limit_change() :: limproto_limiter_thrift:'LimitChange'().
-type limit_context() :: lim_limiter_thrift:'LimitContext'(). -type limit_context() :: limproto_limiter_thrift:'LimitContext'().
-type clock() :: lim_limiter_thrift:'Clock'(). -type clock() :: limproto_limiter_thrift:'Clock'().
-type legacy_create_params() :: lim_configurator_thrift:'LimitCreateParams'(). -type legacy_create_params() :: limproto_configurator_thrift:'LimitCreateParams'().
-type limit_config_params() :: lim_limiter_config_thrift:'LimitConfigParams'(). -type limit_config_params() :: limproto_config_thrift:'LimitConfigParams'().
%%% API %%% API
@ -38,6 +39,10 @@ hold(LimitChange, Context, Client) ->
commit(LimitChange, Context, Client) -> commit(LimitChange, Context, Client) ->
call('Commit', {LimitChange, clock(), Context}, Client). call('Commit', {LimitChange, clock(), Context}, Client).
-spec rollback(limit_change(), limit_context(), client()) -> woody:result() | no_return().
rollback(LimitChange, Context, Client) ->
call('Rollback', {LimitChange, clock(), Context}, Client).
%% %%
-spec legacy_create_config(legacy_create_params(), client()) -> woody:result() | no_return(). -spec legacy_create_config(legacy_create_params(), client()) -> woody:result() | no_return().
@ -56,7 +61,7 @@ get_config(LimitConfigID, Client) ->
-spec call(atom(), tuple(), client()) -> woody:result() | no_return(). -spec call(atom(), tuple(), client()) -> woody:result() | no_return().
call(Function, Args, Client) -> call(Function, Args, Client) ->
Call = {{lim_limiter_thrift, 'Limiter'}, Function, Args}, Call = {{limproto_limiter_thrift, 'Limiter'}, Function, Args},
Opts = #{ Opts = #{
url => <<"http://limiter:8022/v1/limiter">>, url => <<"http://limiter:8022/v1/limiter">>,
event_handler => scoper_woody_event_handler, event_handler => scoper_woody_event_handler,
@ -68,7 +73,7 @@ call(Function, Args, Client) ->
-spec call_configurator(atom(), tuple(), client()) -> woody:result() | no_return(). -spec call_configurator(atom(), tuple(), client()) -> woody:result() | no_return().
call_configurator(Function, Args, Client) -> call_configurator(Function, Args, Client) ->
Call = {{lim_configurator_thrift, 'Configurator'}, Function, Args}, Call = {{limproto_configurator_thrift, 'Configurator'}, Function, Args},
Opts = #{ Opts = #{
url => <<"http://limiter:8022/v1/configurator">>, url => <<"http://limiter:8022/v1/configurator">>,
event_handler => scoper_woody_event_handler, event_handler => scoper_woody_event_handler,

View File

@ -2,7 +2,7 @@
-include_lib("common_test/include/ct.hrl"). -include_lib("common_test/include/ct.hrl").
-include_lib("stdlib/include/assert.hrl"). -include_lib("stdlib/include/assert.hrl").
-include_lib("limiter_proto/include/lim_configurator_thrift.hrl"). -include_lib("limiter_proto/include/limproto_configurator_thrift.hrl").
-include("lim_ct_helper.hrl"). -include("lim_ct_helper.hrl").
-export([all/0]). -export([all/0]).
@ -17,6 +17,7 @@
-export([create_config/1]). -export([create_config/1]).
-export([create_config_single_scope/1]). -export([create_config_single_scope/1]).
-export([get_config/1]). -export([get_config/1]).
-export([get_inexistent_config/1]).
-type group_name() :: atom(). -type group_name() :: atom().
-type test_case_name() :: atom(). -type test_case_name() :: atom().
@ -36,7 +37,8 @@ groups() ->
legacy_create_config, legacy_create_config,
create_config, create_config,
create_config_single_scope, create_config_single_scope,
get_config get_config,
get_inexistent_config
]} ]}
]. ].
@ -81,14 +83,14 @@ legacy_create_config(C) ->
Client = lim_client:new(), Client = lim_client:new(),
ID = ?config(limit_id, C), ID = ?config(limit_id, C),
Description = genlib:unique(), Description = genlib:unique(),
Params = #limiter_configurator_LimitCreateParams{ Params = #configurator_LimitCreateParams{
id = ID, id = ID,
name = <<"GlobalMonthTurnover">>, name = <<"GlobalMonthTurnover">>,
description = Description, description = Description,
started_at = <<"2000-01-01T00:00:00Z">> started_at = <<"2000-01-01T00:00:00Z">>
}, },
?assertMatch( ?assertMatch(
{ok, #limiter_config_LimitConfig{ {ok, #config_LimitConfig{
id = ID, id = ID,
description = Description description = Description
}}, }},
@ -100,7 +102,7 @@ create_config(C) ->
Client = lim_client:new(), Client = lim_client:new(),
ID = ?config(limit_id, C), ID = ?config(limit_id, C),
Description = genlib:unique(), Description = genlib:unique(),
Params = #limiter_config_LimitConfigParams{ Params = #config_LimitConfigParams{
id = ?config(limit_id, C), id = ?config(limit_id, C),
description = Description, description = Description,
started_at = <<"2000-01-01T00:00:00Z">>, started_at = <<"2000-01-01T00:00:00Z">>,
@ -115,7 +117,7 @@ create_config(C) ->
context_type = ?ctx_type_payproc() context_type = ?ctx_type_payproc()
}, },
?assertMatch( ?assertMatch(
{ok, #limiter_config_LimitConfig{ {ok, #config_LimitConfig{
id = ID, id = ID,
description = Description description = Description
}}, }},
@ -125,7 +127,7 @@ create_config(C) ->
-spec create_config_single_scope(config()) -> _. -spec create_config_single_scope(config()) -> _.
create_config_single_scope(C) -> create_config_single_scope(C) ->
Client = lim_client:new(), Client = lim_client:new(),
Params = #limiter_config_LimitConfigParams{ Params = #config_LimitConfigParams{
id = ?config(limit_id, C), id = ?config(limit_id, C),
started_at = <<"2000-01-01T00:00:00Z">>, started_at = <<"2000-01-01T00:00:00Z">>,
time_range_type = ?time_range_week(), time_range_type = ?time_range_week(),
@ -135,7 +137,7 @@ create_config_single_scope(C) ->
context_type = ?ctx_type_payproc(), context_type = ?ctx_type_payproc(),
op_behaviour = ?op_behaviour() op_behaviour = ?op_behaviour()
}, },
{ok, #limiter_config_LimitConfig{ {ok, #config_LimitConfig{
scope = Scope scope = Scope
}} = lim_client:create_config(Params, Client), }} = lim_client:create_config(Params, Client),
?assertEqual(?scope([?scope_party()]), Scope). ?assertEqual(?scope([?scope_party()]), Scope).
@ -144,13 +146,20 @@ create_config_single_scope(C) ->
get_config(C) -> get_config(C) ->
ID = ?config(limit_id, C), ID = ?config(limit_id, C),
#{client := Client} = prepare_environment(ID, <<"GlobalMonthTurnover">>, C), #{client := Client} = prepare_environment(ID, <<"GlobalMonthTurnover">>, C),
{ok, #limiter_config_LimitConfig{id = ID}} = lim_client:get_config(ID, Client). {ok, #config_LimitConfig{id = ID}} = lim_client:get_config(ID, Client).
-spec get_inexistent_config(config()) -> _.
get_inexistent_config(_C) ->
?assertEqual(
{exception, #configurator_LimitConfigNotFound{}},
lim_client:get_config(<<"NOSUCHCONFIG">>, lim_client:new())
).
%% %%
prepare_environment(ID, LimitName, _C) -> prepare_environment(ID, LimitName, _C) ->
Client = lim_client:new(), Client = lim_client:new(),
Params = #limiter_configurator_LimitCreateParams{ Params = #configurator_LimitCreateParams{
id = ID, id = ID,
name = LimitName, name = LimitName,
description = <<"description">>, description = <<"description">>,

View File

@ -1,7 +1,13 @@
-ifndef(__limiter_ct_helper__). -ifndef(__limiter_ct_helper__).
-define(__limiter_ct_helper__, 42). -define(__limiter_ct_helper__, 42).
-include_lib("limiter_proto/include/lim_configurator_thrift.hrl"). -include_lib("limiter_proto/include/limproto_config_thrift.hrl").
-include_lib("limiter_proto/include/limproto_timerange_thrift.hrl").
-include_lib("limiter_proto/include/limproto_limiter_thrift.hrl").
-include_lib("limiter_proto/include/limproto_context_payproc_thrift.hrl").
-include_lib("limiter_proto/include/limproto_context_withdrawal_thrift.hrl").
-include_lib("damsel/include/dmsl_domain_thrift.hrl").
-include_lib("damsel/include/dmsl_wthd_domain_thrift.hrl").
-define(currency, <<"RUB">>). -define(currency, <<"RUB">>).
-define(string, <<"STRING">>). -define(string, <<"STRING">>).
@ -16,126 +22,83 @@
-define(scope(Types), {multi, ordsets:from_list(Types)}). -define(scope(Types), {multi, ordsets:from_list(Types)}).
-define(global(), ?scope([])). -define(global(), ?scope([])).
-define(scope_party(), {party, #limiter_config_LimitScopeEmptyDetails{}}). -define(scope_party(), {party, #config_LimitScopeEmptyDetails{}}).
-define(scope_shop(), {shop, #limiter_config_LimitScopeEmptyDetails{}}). -define(scope_shop(), {shop, #config_LimitScopeEmptyDetails{}}).
-define(scope_payment_tool(), {payment_tool, #limiter_config_LimitScopeEmptyDetails{}}). -define(scope_payment_tool(), {payment_tool, #config_LimitScopeEmptyDetails{}}).
-define(lim_type_turnover(), ?lim_type_turnover(?turnover_metric_number())). -define(lim_type_turnover(), ?lim_type_turnover(?turnover_metric_number())).
-define(lim_type_turnover(Metric), -define(lim_type_turnover(Metric),
{turnover, #limiter_config_LimitTypeTurnover{metric = Metric}} {turnover, #config_LimitTypeTurnover{metric = Metric}}
). ).
-define(turnover_metric_number(), {number, #limiter_config_LimitTurnoverNumber{}}). -define(turnover_metric_number(), {number, #config_LimitTurnoverNumber{}}).
-define(turnover_metric_amount(), ?turnover_metric_amount(?currency)). -define(turnover_metric_amount(), ?turnover_metric_amount(?currency)).
-define(turnover_metric_amount(Currency), -define(turnover_metric_amount(Currency),
{amount, #limiter_config_LimitTurnoverAmount{currency = Currency}} {amount, #config_LimitTurnoverAmount{currency = Currency}}
). ).
-define(time_range_day(), -define(time_range_day(),
{calendar, {day, #limiter_time_range_TimeRangeTypeCalendarDay{}}} {calendar, {day, #timerange_TimeRangeTypeCalendarDay{}}}
). ).
-define(time_range_week(), -define(time_range_week(),
{calendar, {week, #limiter_time_range_TimeRangeTypeCalendarWeek{}}} {calendar, {week, #timerange_TimeRangeTypeCalendarWeek{}}}
). ).
-define(time_range_month(), -define(time_range_month(),
{calendar, {month, #limiter_time_range_TimeRangeTypeCalendarMonth{}}} {calendar, {month, #timerange_TimeRangeTypeCalendarMonth{}}}
). ).
-define(time_range_year(), -define(time_range_year(),
{calendar, {year, #limiter_time_range_TimeRangeTypeCalendarYear{}}} {calendar, {year, #timerange_TimeRangeTypeCalendarYear{}}}
). ).
-define(op_behaviour(), ?op_behaviour(?op_addition())). -define(op_behaviour(), ?op_behaviour(?op_addition())).
-define(op_behaviour(Refund), #limiter_config_OperationLimitBehaviour{ -define(op_behaviour(Refund), #config_OperationLimitBehaviour{
invoice_payment_refund = Refund invoice_payment_refund = Refund
}). }).
-define(op_addition(), {addition, #limiter_config_Addition{}}). -define(op_addition(), {addition, #config_Addition{}}).
-define(op_subtraction(), {subtraction, #limiter_config_Subtraction{}}). -define(op_subtraction(), {subtraction, #config_Subtraction{}}).
-define(ctx_type_payproc(), -define(ctx_type_payproc(),
{payment_processing, #limiter_config_LimitContextTypePaymentProcessing{}} {payment_processing, #config_LimitContextTypePaymentProcessing{}}
). ).
-define(op_invoice(), {invoice, #limiter_context_PaymentProcessingOperationInvoice{}}). -define(ctx_type_wthdproc(),
-define(op_invoice_payment(), {invoice_payment, #limiter_context_PaymentProcessingOperationInvoicePayment{}}). {withdrawal_processing, #config_LimitContextTypeWithdrawalProcessing{}}
).
-define(ctx_invoice(Cost), #limiter_context_LimitContext{
limiter_payment_processing = #limiter_context_ContextPaymentProcessing{
op = ?op_invoice(),
invoice = #limiter_context_Invoice{
created_at = <<"2000-01-01T00:00:00Z">>,
cost = Cost
}
}
}).
-define(ctx_invoice_payment(Cost, CaptureCost), ?ctx_invoice_payment(undefined, undefined, Cost, CaptureCost)).
-define(ctx_invoice_payment(OwnerID, ShopID, Cost, CaptureCost), #limiter_context_LimitContext{
limiter_payment_processing = #limiter_context_ContextPaymentProcessing{
op = ?op_invoice_payment(),
invoice = #limiter_context_Invoice{
owner_id = OwnerID,
shop_id = ShopID,
effective_payment = #limiter_context_InvoicePayment{
created_at = <<"2000-01-01T00:00:00Z">>,
cost = Cost,
capture_cost = CaptureCost,
flow = {hold, #limiter_context_InvoicePaymentFlowHold{}},
payer = {payment_resource, #limiter_context_PaymentResourcePayer{}},
effective_adjustment = #limiter_context_InvoicePaymentAdjustment{},
effective_chargeback = #limiter_context_InvoicePaymentChargeback{}
},
effective_adjustment = #limiter_context_InvoiceAdjustment{}
}
}
}).
-define(ctx_invoice_payment(Payment), #limiter_context_LimitContext{
limiter_payment_processing = #limiter_context_ContextPaymentProcessing{
op = ?op_invoice_payment(),
invoice = #limiter_context_Invoice{
effective_payment = Payment
}
}
}).
-define(ctx_invoice_payment_refund(OwnerID, ShopID, Cost, CaptureCost, RefundCost), #limiter_context_LimitContext{
limiter_payment_processing = #limiter_context_ContextPaymentProcessing{
op = {invoice_payment_refund, #limiter_context_PaymentProcessingOperationInvoicePaymentRefund{}},
invoice = #limiter_context_Invoice{
owner_id = OwnerID,
shop_id = ShopID,
effective_payment = #limiter_context_InvoicePayment{
created_at = <<"2000-01-01T00:00:00Z">>,
cost = Cost,
capture_cost = CaptureCost,
effective_refund = #limiter_context_InvoicePaymentRefund{
cost = RefundCost,
created_at = <<"2000-01-01T00:00:00Z">>
}
}
}
}
}).
%% Payproc %% Payproc
-define(payproc_op_invoice, {invoice, #limiter_context_payproc_OperationInvoice{}}). -define(op_invoice, {invoice, #context_payproc_OperationInvoice{}}).
-define(payproc_op_invoice_payment, {invoice_payment, #limiter_context_payproc_OperationInvoicePayment{}}). -define(op_payment, {invoice_payment, #context_payproc_OperationInvoicePayment{}}).
-define(op_refund, {invoice_payment_refund, #context_payproc_OperationInvoicePaymentRefund{}}).
-define(payproc_bank_card(), -define(bank_card(), ?bank_card(?string, 2, 2022)).
?payproc_bank_card(?string, 2, 2022)
-define(bank_card(Token),
{bank_card, #domain_BankCard{
token = Token,
bin = ?string,
last_digits = ?string
}}
). ).
-define(payproc_bank_card(Token, Month, Year), #domain_BankCard{ -define(bank_card(Token, Month, Year),
token = Token, {bank_card, #domain_BankCard{
bin = ?string, token = Token,
last_digits = ?string, bin = ?string,
exp_date = #domain_BankCardExpDate{month = Month, year = Year} last_digits = ?string,
}). exp_date = #domain_BankCardExpDate{month = Month, year = Year}
}}
).
-define(payproc_invoice(OwnerID, ShopID, Cost), #domain_Invoice{ -define(digital_wallet(ID, Service),
{digital_wallet, #domain_DigitalWallet{
id = ID,
payment_service = #domain_PaymentServiceRef{id = Service}
}}
).
-define(invoice(OwnerID, ShopID, Cost), #domain_Invoice{
id = ?string, id = ?string,
owner_id = OwnerID, owner_id = OwnerID,
shop_id = ShopID, shop_id = ShopID,
@ -146,13 +109,17 @@
cost = Cost cost = Cost
}). }).
-define(payproc_invoice_payment(Cost, CaptureCost), -define(invoice_payment(Cost, CaptureCost),
?payproc_invoice_payment(Cost, CaptureCost, {bank_card, ?payproc_bank_card()}) ?invoice_payment(Cost, CaptureCost, ?bank_card())
). ).
-define(payproc_invoice_payment(Cost, CaptureCost, PaymentTool), #domain_InvoicePayment{ -define(invoice_payment(Cost, CaptureCost, PaymentTool),
?invoice_payment(Cost, CaptureCost, PaymentTool, ?timestamp)
).
-define(invoice_payment(Cost, CaptureCost, PaymentTool, CreatedAt), #domain_InvoicePayment{
id = ?string, id = ?string,
created_at = ?timestamp, created_at = CreatedAt,
status = {captured, #domain_InvoicePaymentCaptured{cost = CaptureCost}}, status = {captured, #domain_InvoicePaymentCaptured{cost = CaptureCost}},
cost = Cost, cost = Cost,
domain_revision = 1, domain_revision = 1,
@ -166,62 +133,87 @@
}} }}
}). }).
-define(payproc_ctx_invoice(Cost), #limiter_context_LimitContext{ -define(payproc_ctx_invoice(Cost), #limiter_LimitContext{
payment_processing = #limiter_context_payproc_Context{ payment_processing = #context_payproc_Context{
op = ?payproc_op_invoice, op = ?op_invoice,
invoice = #limiter_context_payproc_Invoice{ invoice = #context_payproc_Invoice{
invoice = ?payproc_invoice(?string, ?string, Cost) invoice = ?invoice(?string, ?string, Cost)
} }
} }
}). }).
-define(payproc_ctx_invoice_payment(Cost, CaptureCost), -define(payproc_ctx_payment(Cost, CaptureCost),
?payproc_ctx_invoice_payment(?string, ?string, Cost, CaptureCost) ?payproc_ctx_payment(?string, ?string, Cost, CaptureCost)
). ).
-define(payproc_ctx_invoice_payment(OwnerID, ShopID, Cost, CaptureCost), #limiter_context_LimitContext{ -define(payproc_ctx_payment(OwnerID, ShopID, Cost, CaptureCost), #limiter_LimitContext{
payment_processing = #limiter_context_payproc_Context{ payment_processing = #context_payproc_Context{
op = ?payproc_op_invoice_payment, op = ?op_payment,
invoice = #limiter_context_payproc_Invoice{ invoice = #context_payproc_Invoice{
invoice = ?payproc_invoice(OwnerID, ShopID, Cost), invoice = ?invoice(OwnerID, ShopID, Cost),
payment = #limiter_context_payproc_InvoicePayment{ payment = #context_payproc_InvoicePayment{
payment = ?payproc_invoice_payment(Cost, CaptureCost) payment = ?invoice_payment(Cost, CaptureCost)
} }
} }
} }
}). }).
-define(payproc_ctx_invoice_payment(Payment), #limiter_context_LimitContext{ -define(payproc_ctx_payment(Payment), #limiter_LimitContext{
payment_processing = #limiter_context_payproc_Context{ payment_processing = #context_payproc_Context{
op = ?payproc_op_invoice_payment, op = ?op_payment,
invoice = #limiter_context_payproc_Invoice{ invoice = #context_payproc_Invoice{
invoice = ?payproc_invoice(?string, ?string, ?cash(10)), invoice = ?invoice(?string, ?string, ?cash(10)),
payment = #limiter_context_payproc_InvoicePayment{ payment = #context_payproc_InvoicePayment{
payment = Payment payment = Payment
} }
} }
} }
}). }).
-define(payproc_ctx_invoice_payment_refund(OwnerID, ShopID, Cost, CaptureCost, RefundCost), -define(payproc_ctx_refund(OwnerID, ShopID, Cost, CaptureCost, RefundCost), #limiter_LimitContext{
#limiter_context_LimitContext{ payment_processing = #context_payproc_Context{
payment_processing = #limiter_context_payproc_Context{ op = ?op_refund,
op = {invoice_payment_refund, #limiter_context_payproc_OperationInvoicePaymentRefund{}}, invoice = #context_payproc_Invoice{
invoice = #limiter_context_payproc_Invoice{ invoice = ?invoice(OwnerID, ShopID, Cost),
invoice = ?payproc_invoice(OwnerID, ShopID, Cost), payment = #context_payproc_InvoicePayment{
payment = #limiter_context_payproc_InvoicePayment{ payment = ?invoice_payment(Cost, CaptureCost),
payment = ?payproc_invoice_payment(Cost, CaptureCost), refund = #domain_InvoicePaymentRefund{
refund = #domain_InvoicePaymentRefund{ id = ?string,
id = ?string, status = {succeeded, #domain_InvoicePaymentRefundSucceeded{}},
status = {succeeded, #domain_InvoicePaymentRefundSucceeded{}}, created_at = ?timestamp,
created_at = ?timestamp, domain_revision = 1,
domain_revision = 1, cash = RefundCost
cash = RefundCost
}
} }
} }
} }
} }
). }).
%% Wthdproc
-define(identity(OwnerID), #wthd_domain_Identity{
id = OwnerID,
owner_id = OwnerID
}).
-define(withdrawal(Body), ?withdrawal(Body, ?bank_card(), ?string)).
-define(withdrawal(Body, Destination, OwnerID), #wthd_domain_Withdrawal{
body = Body,
created_at = ?timestamp,
destination = Destination,
sender = ?identity(OwnerID)
}).
-define(op_withdrawal, {withdrawal, #context_withdrawal_OperationWithdrawal{}}).
-define(wthdproc_ctx_withdrawal(Cost), #limiter_LimitContext{
withdrawal_processing = #context_withdrawal_Context{
op = ?op_withdrawal,
withdrawal = #context_withdrawal_Withdrawal{
withdrawal = ?withdrawal(Cost)
}
}
}).
-endif. -endif.

View File

@ -2,10 +2,8 @@
-include_lib("stdlib/include/assert.hrl"). -include_lib("stdlib/include/assert.hrl").
-include_lib("common_test/include/ct.hrl"). -include_lib("common_test/include/ct.hrl").
-include_lib("lim_ct_helper.hrl"). -include_lib("damsel/include/dmsl_base_thrift.hrl").
-include("lim_ct_helper.hrl").
-include_lib("limiter_proto/include/lim_configurator_thrift.hrl").
-include_lib("xrates_proto/include/xrates_rate_thrift.hrl").
-export([all/0]). -export([all/0]).
@ -18,17 +16,18 @@
-export([commit_with_default_exchange/1]). -export([commit_with_default_exchange/1]).
-export([partial_commit_with_exchange/1]). -export([partial_commit_with_exchange/1]).
-export([commit_with_exchange/1]). -export([commit_with_exchange/1]).
-export([get_rate/1]).
-export([get_limit_ok/1]). -export([get_limit_ok/1]).
-export([get_limit_notfound/1]). -export([get_limit_notfound/1]).
-export([hold_ok/1]). -export([hold_ok/1]).
-export([commit_ok/1]). -export([commit_ok/1]).
-export([rollback_ok/1]). -export([rollback_ok/1]).
-export([partial_zero_commit_rollbacks/1]).
-export([refund_ok/1]). -export([refund_ok/1]).
-export([get_config_ok/1]). -export([get_config_ok/1]).
-export([commit_inexistent_hold_fails/1]). -export([commit_inexistent_hold_fails/1]).
-export([partial_commit_inexistent_hold_fails/1]). -export([partial_commit_inexistent_hold_fails/1]).
-export([commit_multirange_limit_ok/1]). -export([commit_multirange_limit_ok/1]).
-export([commit_with_payment_tool_scope_ok/1]).
-export([commit_processes_idempotently/1]). -export([commit_processes_idempotently/1]).
-export([full_commit_processes_idempotently/1]). -export([full_commit_processes_idempotently/1]).
@ -40,23 +39,16 @@
-export([commit_refund_keep_number_unchanged/1]). -export([commit_refund_keep_number_unchanged/1]).
-export([partial_commit_number_counts_as_single_op/1]). -export([partial_commit_number_counts_as_single_op/1]).
-export([payproc_hold_ok/1]).
-export([payproc_commit_ok/1]).
-export([payproc_rollback_ok/1]).
-export([payproc_refund_ok/1]).
-export([payproc_commit_with_payment_tool_scope_ok/1]).
-type group_name() :: atom(). -type group_name() :: atom().
-type test_case_name() :: atom(). -type test_case_name() :: atom().
-define(RATE_SOURCE_ID, <<"dummy_source_id">>).
%% tests descriptions %% tests descriptions
-spec all() -> [{group, group_name()}]. -spec all() -> [{group, group_name()}].
all() -> all() ->
[ [
{group, default}, {group, default},
{group, withdrawals},
{group, cashless}, {group, cashless},
{group, idempotency} {group, idempotency}
]. ].
@ -68,22 +60,24 @@ groups() ->
commit_with_default_exchange, commit_with_default_exchange,
partial_commit_with_exchange, partial_commit_with_exchange,
commit_with_exchange, commit_with_exchange,
get_rate,
get_limit_ok, get_limit_ok,
get_limit_notfound, get_limit_notfound,
hold_ok, hold_ok,
commit_ok, commit_ok,
rollback_ok, rollback_ok,
partial_zero_commit_rollbacks,
get_config_ok, get_config_ok,
refund_ok, refund_ok,
commit_inexistent_hold_fails, commit_inexistent_hold_fails,
partial_commit_inexistent_hold_fails, partial_commit_inexistent_hold_fails,
commit_multirange_limit_ok, commit_multirange_limit_ok,
payproc_hold_ok, commit_with_payment_tool_scope_ok
payproc_commit_ok, ]},
payproc_rollback_ok, {withdrawals, [parallel], [
payproc_refund_ok, get_limit_ok,
payproc_commit_with_payment_tool_scope_ok hold_ok,
commit_ok,
rollback_ok
]}, ]},
{cashless, [parallel], [ {cashless, [parallel], [
commit_number_ok, commit_number_ok,
@ -155,18 +149,8 @@ commit_with_default_exchange(C) ->
Rational = #base_Rational{p = 1000000, q = 100}, Rational = #base_Rational{p = 1000000, q = 100},
_ = mock_exchange(Rational, C), _ = mock_exchange(Rational, C),
ID = configure_limit(?time_range_month(), ?global(), C), ID = configure_limit(?time_range_month(), ?global(), C),
Context = #limiter_context_LimitContext{ Cost = ?cash(10000, <<"SOME_CURRENCY">>),
limiter_payment_processing = #limiter_context_ContextPaymentProcessing{ Context = ?payproc_ctx_invoice(Cost),
op = {invoice, #limiter_context_PaymentProcessingOperationInvoice{}},
invoice = #limiter_context_Invoice{
created_at = <<"2000-01-01T00:00:00Z">>,
cost = #domain_Cash{
amount = 10000,
currency = #domain_CurrencyRef{symbolic_code = <<"SOME_CURRENCY">>}
}
}
}
},
{ok, {vector, _}} = hold_and_commit(?LIMIT_CHANGE(ID), Context, ?config(client, C)), {ok, {vector, _}} = hold_and_commit(?LIMIT_CHANGE(ID), Context, ?config(client, C)),
{ok, #limiter_Limit{amount = 10000}} = lim_client:get(ID, Context, ?config(client, C)). {ok, #limiter_Limit{amount = 10000}} = lim_client:get(ID, Context, ?config(client, C)).
@ -175,24 +159,9 @@ partial_commit_with_exchange(C) ->
Rational = #base_Rational{p = 800000, q = 100}, Rational = #base_Rational{p = 800000, q = 100},
_ = mock_exchange(Rational, C), _ = mock_exchange(Rational, C),
ID = configure_limit(?time_range_month(), ?global(), C), ID = configure_limit(?time_range_month(), ?global(), C),
Context = #limiter_context_LimitContext{ Cost = ?cash(1000, <<"USD">>),
limiter_payment_processing = #limiter_context_ContextPaymentProcessing{ CaptureCost = ?cash(800, <<"USD">>),
op = {invoice_payment, #limiter_context_PaymentProcessingOperationInvoicePayment{}}, Context = ?payproc_ctx_payment(Cost, CaptureCost),
invoice = #limiter_context_Invoice{
effective_payment = #limiter_context_InvoicePayment{
created_at = <<"2000-01-01T00:00:00Z">>,
cost = #domain_Cash{
amount = 10000,
currency = #domain_CurrencyRef{symbolic_code = <<"USD">>}
},
capture_cost = #domain_Cash{
amount = 8000,
currency = #domain_CurrencyRef{symbolic_code = <<"USD">>}
}
}
}
}
},
{ok, {vector, _}} = hold_and_commit(?LIMIT_CHANGE(ID), Context, ?config(client, C)), {ok, {vector, _}} = hold_and_commit(?LIMIT_CHANGE(ID), Context, ?config(client, C)),
{ok, #limiter_Limit{amount = 8400}} = lim_client:get(ID, Context, ?config(client, C)). {ok, #limiter_Limit{amount = 8400}} = lim_client:get(ID, Context, ?config(client, C)).
@ -201,43 +170,19 @@ commit_with_exchange(C) ->
Rational = #base_Rational{p = 1000000, q = 100}, Rational = #base_Rational{p = 1000000, q = 100},
_ = mock_exchange(Rational, C), _ = mock_exchange(Rational, C),
ID = configure_limit(?time_range_month(), ?global(), C), ID = configure_limit(?time_range_month(), ?global(), C),
Context = #limiter_context_LimitContext{ Cost = ?cash(10000, <<"USD">>),
limiter_payment_processing = #limiter_context_ContextPaymentProcessing{ Context = ?payproc_ctx_invoice(Cost),
op = {invoice, #limiter_context_PaymentProcessingOperationInvoice{}},
invoice = #limiter_context_Invoice{
created_at = <<"2000-01-01T00:00:00Z">>,
cost = #domain_Cash{
amount = 10000,
currency = #domain_CurrencyRef{symbolic_code = <<"USD">>}
}
}
}
},
{ok, {vector, _}} = hold_and_commit(?LIMIT_CHANGE(ID), Context, ?config(client, C)), {ok, {vector, _}} = hold_and_commit(?LIMIT_CHANGE(ID), Context, ?config(client, C)),
{ok, #limiter_Limit{amount = 10500}} = lim_client:get(ID, Context, ?config(client, C)). {ok, #limiter_Limit{amount = 10500}} = lim_client:get(ID, Context, ?config(client, C)).
-spec get_rate(config()) -> _.
get_rate(C) ->
Rational = #base_Rational{p = 10, q = 10},
_ = mock_exchange(Rational, C),
Request = #rate_ConversionRequest{
source = <<"RUB">>,
destination = <<"USD">>,
amount = 100,
datetime = <<"Timestamp">>
},
WoodyContext = woody_context:new(),
{ok, Rational} = lim_client_woody:call(
xrates,
'GetConvertedAmount',
{?RATE_SOURCE_ID, Request},
WoodyContext
).
-spec get_limit_ok(config()) -> _. -spec get_limit_ok(config()) -> _.
get_limit_ok(C) -> get_limit_ok(C) ->
ID = configure_limit(?time_range_month(), ?global(), C), ID = configure_limit(?time_range_month(), ?global(), C),
Context = ?ctx_invoice(_Cost = undefined), Context =
case get_group_name(C) of
default -> ?payproc_ctx_invoice(?cash(0));
withdrawals -> ?wthdproc_ctx_withdrawal(?cash(0))
end,
?assertMatch( ?assertMatch(
{ok, #limiter_Limit{amount = 0}}, {ok, #limiter_Limit{amount = 0}},
lim_client:get(ID, Context, ?config(client, C)) lim_client:get(ID, Context, ?config(client, C))
@ -245,7 +190,7 @@ get_limit_ok(C) ->
-spec get_limit_notfound(config()) -> _. -spec get_limit_notfound(config()) -> _.
get_limit_notfound(C) -> get_limit_notfound(C) ->
Context = ?ctx_invoice(_Cost = undefined), Context = ?payproc_ctx_invoice(?cash(0)),
?assertEqual( ?assertEqual(
{exception, #limiter_LimitNotFound{}}, {exception, #limiter_LimitNotFound{}},
lim_client:get(<<"NOSUCHLIMITID">>, Context, ?config(client, C)) lim_client:get(<<"NOSUCHLIMITID">>, Context, ?config(client, C))
@ -254,36 +199,50 @@ get_limit_notfound(C) ->
-spec hold_ok(config()) -> _. -spec hold_ok(config()) -> _.
hold_ok(C) -> hold_ok(C) ->
ID = configure_limit(?time_range_month(), ?global(), C), ID = configure_limit(?time_range_month(), ?global(), C),
Context = ?ctx_invoice(?cash(10)), Context =
case get_group_name(C) of
default -> ?payproc_ctx_invoice(?cash(10));
withdrawals -> ?wthdproc_ctx_withdrawal(?cash(10))
end,
{ok, {vector, #limiter_VectorClock{}}} = lim_client:hold(?LIMIT_CHANGE(ID), Context, ?config(client, C)), {ok, {vector, #limiter_VectorClock{}}} = lim_client:hold(?LIMIT_CHANGE(ID), Context, ?config(client, C)),
{ok, #limiter_Limit{}} = lim_client:get(ID, Context, ?config(client, C)). {ok, #limiter_Limit{}} = lim_client:get(ID, Context, ?config(client, C)).
-spec commit_ok(config()) -> _. -spec commit_ok(config()) -> _.
commit_ok(C) -> commit_ok(C) ->
ID = configure_limit(?time_range_month(), ?global(), C), ID = configure_limit(?time_range_month(), ?global(), C),
Context = #limiter_context_LimitContext{ Context =
limiter_payment_processing = #limiter_context_ContextPaymentProcessing{ case get_group_name(C) of
op = {invoice, #limiter_context_PaymentProcessingOperationInvoice{}}, default -> ?payproc_ctx_invoice(?cash(10, <<"RUB">>));
invoice = #limiter_context_Invoice{ withdrawals -> ?wthdproc_ctx_withdrawal(?cash(10, <<"RUB">>))
created_at = <<"2000-01-01T00:00:00Z">>, end,
cost = #domain_Cash{
amount = 10,
currency = #domain_CurrencyRef{symbolic_code = <<"RUB">>}
}
}
}
},
{ok, {vector, _}} = hold_and_commit(?LIMIT_CHANGE(ID), Context, ?config(client, C)), {ok, {vector, _}} = hold_and_commit(?LIMIT_CHANGE(ID), Context, ?config(client, C)),
{ok, #limiter_Limit{}} = lim_client:get(ID, Context, ?config(client, C)). {ok, #limiter_Limit{}} = lim_client:get(ID, Context, ?config(client, C)).
-spec rollback_ok(config()) -> _. -spec rollback_ok(config()) -> _.
rollback_ok(C) -> rollback_ok(C) ->
ID = configure_limit(?time_range_week(), ?global(), C), ID = configure_limit(?time_range_week(), ?global(), C),
Context0 = ?ctx_invoice_payment(?cash(10), ?cash(10)), Context =
Context1 = ?ctx_invoice_payment(?cash(10), ?cash(0)), case get_group_name(C) of
default -> ?payproc_ctx_invoice(?cash(10, <<"RUB">>));
withdrawals -> ?wthdproc_ctx_withdrawal(?cash(10, <<"RUB">>))
end,
Change = ?LIMIT_CHANGE(ID),
{ok, {vector, _}} = lim_client:hold(Change, Context, ?config(client, C)),
{ok, {vector, _}} = lim_client:rollback(Change, Context, ?config(client, C)).
-spec partial_zero_commit_rollbacks(config()) -> _.
partial_zero_commit_rollbacks(C) ->
ID = configure_limit(?time_range_week(), ?global(), C),
Context0 = ?payproc_ctx_payment(?cash(10), ?cash(10)),
Context1 = ?payproc_ctx_payment(?cash(10), ?cash(0)),
Change = ?LIMIT_CHANGE(ID), Change = ?LIMIT_CHANGE(ID),
{ok, {vector, _}} = lim_client:hold(Change, Context0, ?config(client, C)), {ok, {vector, _}} = lim_client:hold(Change, Context0, ?config(client, C)),
{ok, {vector, _}} = lim_client:commit(Change, Context1, ?config(client, C)). {ok, {vector, _}} = lim_client:commit(Change, Context1, ?config(client, C)),
% NOTE
% Successful rollback here means that partial commit with zero is handled exactly
% like rollback, thus subsequent rollback succeeds idempotently. This is a backwards
% compatibility measure.
{ok, {vector, _}} = lim_client:rollback(Change, Context0, ?config(client, C)).
-spec refund_ok(config()) -> _. -spec refund_ok(config()) -> _.
refund_ok(C) -> refund_ok(C) ->
@ -291,8 +250,8 @@ refund_ok(C) ->
OwnerID = <<"WWWcool Ltd">>, OwnerID = <<"WWWcool Ltd">>,
ShopID = <<"shop">>, ShopID = <<"shop">>,
ID = configure_limit(?time_range_day(), ?scope([?scope_party(), ?scope_shop()]), C), ID = configure_limit(?time_range_day(), ?scope([?scope_party(), ?scope_shop()]), C),
Context0 = ?ctx_invoice_payment(OwnerID, ShopID, ?cash(15), ?cash(15)), Context0 = ?payproc_ctx_payment(OwnerID, ShopID, ?cash(15), ?cash(15)),
RefundContext1 = ?ctx_invoice_payment_refund(OwnerID, ShopID, ?cash(10), ?cash(10), ?cash(10)), RefundContext1 = ?payproc_ctx_refund(OwnerID, ShopID, ?cash(10), ?cash(10), ?cash(10)),
{ok, {vector, _}} = hold_and_commit(?LIMIT_CHANGE(ID, <<"Payment">>), Context0, Client), {ok, {vector, _}} = hold_and_commit(?LIMIT_CHANGE(ID, <<"Payment">>), Context0, Client),
{ok, {vector, _}} = hold_and_commit(?LIMIT_CHANGE(ID, <<"Refund">>), RefundContext1, Client), {ok, {vector, _}} = hold_and_commit(?LIMIT_CHANGE(ID, <<"Refund">>), RefundContext1, Client),
{ok, #limiter_Limit{} = Limit2} = lim_client:get(ID, RefundContext1, Client), {ok, #limiter_Limit{} = Limit2} = lim_client:get(ID, RefundContext1, Client),
@ -301,33 +260,33 @@ refund_ok(C) ->
-spec get_config_ok(config()) -> _. -spec get_config_ok(config()) -> _.
get_config_ok(C) -> get_config_ok(C) ->
ID = configure_limit(?time_range_week(), ?global(), C), ID = configure_limit(?time_range_week(), ?global(), C),
{ok, #limiter_config_LimitConfig{}} = lim_client:get_config(ID, ?config(client, C)). {ok, #config_LimitConfig{}} = lim_client:get_config(ID, ?config(client, C)).
-spec commit_inexistent_hold_fails(config()) -> _. -spec commit_inexistent_hold_fails(config()) -> _.
commit_inexistent_hold_fails(C) -> commit_inexistent_hold_fails(C) ->
ID = configure_limit(?time_range_week(), ?global(), C), ID = configure_limit(?time_range_week(), ?global(), C),
Context = ?ctx_invoice_payment(?cash(42), undefined), Context = ?payproc_ctx_payment(?cash(42), undefined),
% NOTE % NOTE
% We do not expect `LimitChangeNotFound` here because we no longer reconcile with accounter % We do not expect `LimitChangeNotFound` here because we no longer reconcile with accounter
% before requesting him to hold / commit. % before requesting him to hold / commit.
{exception, #'InvalidRequest'{}} = {exception, #base_InvalidRequest{}} =
lim_client:commit(?LIMIT_CHANGE(ID), Context, ?config(client, C)). lim_client:commit(?LIMIT_CHANGE(ID), Context, ?config(client, C)).
-spec partial_commit_inexistent_hold_fails(config()) -> _. -spec partial_commit_inexistent_hold_fails(config()) -> _.
partial_commit_inexistent_hold_fails(C) -> partial_commit_inexistent_hold_fails(C) ->
ID = configure_limit(?time_range_week(), ?global(), C), ID = configure_limit(?time_range_week(), ?global(), C),
Context = ?ctx_invoice_payment(?cash(42), ?cash(21)), Context = ?payproc_ctx_payment(?cash(42), ?cash(21)),
% NOTE % NOTE
% We do not expect `LimitChangeNotFound` here because we no longer reconcile with accounter % We do not expect `LimitChangeNotFound` here because we no longer reconcile with accounter
% before requesting him to hold / commit. % before requesting him to hold / commit.
{exception, #'InvalidRequest'{}} = {exception, #base_InvalidRequest{}} =
lim_client:commit(?LIMIT_CHANGE(ID), Context, ?config(client, C)). lim_client:commit(?LIMIT_CHANGE(ID), Context, ?config(client, C)).
-spec commit_multirange_limit_ok(config()) -> _. -spec commit_multirange_limit_ok(config()) -> _.
commit_multirange_limit_ok(C) -> commit_multirange_limit_ok(C) ->
ID = ?config(id, C), ID = ?config(id, C),
Client = ?config(client, C), Client = ?config(client, C),
Params = #limiter_config_LimitConfigParams{ Params = #config_LimitConfigParams{
id = ID, id = ID,
started_at = <<"2000-01-01T00:00:00Z">>, started_at = <<"2000-01-01T00:00:00Z">>,
shard_size = 12, shard_size = 12,
@ -335,30 +294,52 @@ commit_multirange_limit_ok(C) ->
context_type = ?ctx_type_payproc(), context_type = ?ctx_type_payproc(),
type = ?lim_type_turnover(?turnover_metric_amount(<<"RUB">>)), type = ?lim_type_turnover(?turnover_metric_amount(<<"RUB">>)),
scope = ?scope([]), scope = ?scope([]),
op_behaviour = #limiter_config_OperationLimitBehaviour{} op_behaviour = #config_OperationLimitBehaviour{}
}, },
{ok, _LimitConfig} = lim_client:create_config(Params, Client), {ok, _LimitConfig} = lim_client:create_config(Params, Client),
% NOTE % NOTE
% Expecting those 3 changes will be accounted in the same limit range machine. % Expecting those 3 changes will be accounted in the same limit range machine.
% We have no way to verify it here though. % We have no way to verify it here though.
PaymentJan = #limiter_context_InvoicePayment{ PaymentJan = ?invoice_payment(?cash(42), ?cash(42), ?bank_card(), <<"2020-01-01T00:00:00Z">>),
created_at = <<"2020-01-01T00:00:00Z">>, {ok, _} = hold_and_commit(?LIMIT_CHANGE(ID, 1), ?payproc_ctx_payment(PaymentJan), Client),
cost = ?cash(42) PaymentFeb = ?invoice_payment(?cash(43), ?cash(43), ?bank_card(), <<"2020-02-01T00:00:00Z">>),
}, {ok, _} = hold_and_commit(?LIMIT_CHANGE(ID, 2), ?payproc_ctx_payment(PaymentFeb), Client),
{ok, _} = hold_and_commit(?LIMIT_CHANGE(ID, 1), ?ctx_invoice_payment(PaymentJan), Client), PaymentApr = ?invoice_payment(?cash(44), ?cash(44), ?bank_card(), <<"2020-04-01T00:00:00Z">>),
PaymentFeb = #limiter_context_InvoicePayment{ {ok, _} = hold_and_commit(?LIMIT_CHANGE(ID, 3), ?payproc_ctx_payment(PaymentApr), Client),
created_at = <<"2020-02-01T00:00:00Z">>, {ok, #limiter_Limit{amount = 42}} = lim_client:get(ID, ?payproc_ctx_payment(PaymentJan), Client),
cost = ?cash(43) {ok, #limiter_Limit{amount = 43}} = lim_client:get(ID, ?payproc_ctx_payment(PaymentFeb), Client),
}, {ok, #limiter_Limit{amount = 44}} = lim_client:get(ID, ?payproc_ctx_payment(PaymentApr), Client).
{ok, _} = hold_and_commit(?LIMIT_CHANGE(ID, 2), ?ctx_invoice_payment(PaymentFeb), Client),
PaymentApr = #limiter_context_InvoicePayment{ -spec commit_with_payment_tool_scope_ok(config()) -> _.
created_at = <<"2020-04-01T00:00:00Z">>, commit_with_payment_tool_scope_ok(C) ->
cost = ?cash(44) Client = ?config(client, C),
}, ID = configure_limit(?time_range_week(), ?scope([?scope_payment_tool()]), ?turnover_metric_number(), C),
{ok, _} = hold_and_commit(?LIMIT_CHANGE(ID, 3), ?ctx_invoice_payment(PaymentApr), Client), Context1 = ?payproc_ctx_payment(
{ok, #limiter_Limit{amount = 42}} = lim_client:get(ID, ?ctx_invoice_payment(PaymentJan), Client), ?invoice_payment(?cash(10), ?cash(10), ?bank_card(<<"Token">>, 2, 2022))
{ok, #limiter_Limit{amount = 43}} = lim_client:get(ID, ?ctx_invoice_payment(PaymentFeb), Client), ),
{ok, #limiter_Limit{amount = 44}} = lim_client:get(ID, ?ctx_invoice_payment(PaymentApr), Client). Context2 = ?payproc_ctx_payment(
?invoice_payment(?cash(10), ?cash(10), ?bank_card(<<"OtherToken">>, 2, 2022))
),
Context3 = ?payproc_ctx_payment(
?invoice_payment(?cash(10), ?cash(10), ?bank_card(?string, 3, 2022))
),
Context4 = ?payproc_ctx_payment(
?invoice_payment(?cash(10), ?cash(10), ?bank_card(?string))
),
Context5 = ?payproc_ctx_payment(
?invoice_payment(?cash(10), ?cash(10), ?digital_wallet(<<"ID42">>, <<"Pepal">>))
),
{ok, LimitState0} = lim_client:get(ID, Context1, Client),
_ = hold_and_commit(?LIMIT_CHANGE(ID, 1), Context1, Client),
_ = hold_and_commit(?LIMIT_CHANGE(ID, 2), Context2, Client),
_ = hold_and_commit(?LIMIT_CHANGE(ID, 3), Context3, Client),
_ = hold_and_commit(?LIMIT_CHANGE(ID, 4), Context4, Client),
_ = hold_and_commit(?LIMIT_CHANGE(ID, 5), Context5, Client),
{ok, LimitState1} = lim_client:get(ID, Context1, Client),
?assertEqual(
LimitState1#limiter_Limit.amount,
LimitState0#limiter_Limit.amount + 1
).
%% %%
@ -366,7 +347,7 @@ commit_multirange_limit_ok(C) ->
commit_processes_idempotently(C) -> commit_processes_idempotently(C) ->
Client = ?config(client, C), Client = ?config(client, C),
ID = configure_limit(?time_range_week(), ?global(), C), ID = configure_limit(?time_range_week(), ?global(), C),
Context = ?ctx_invoice_payment(?cash(42), undefined), Context = ?payproc_ctx_payment(?cash(42), undefined),
Change = ?LIMIT_CHANGE(ID), Change = ?LIMIT_CHANGE(ID),
{ok, _} = lim_client:hold(Change, Context, Client), {ok, _} = lim_client:hold(Change, Context, Client),
{ok, _} = lim_client:hold(Change, Context, Client), {ok, _} = lim_client:hold(Change, Context, Client),
@ -380,7 +361,7 @@ full_commit_processes_idempotently(C) ->
Client = ?config(client, C), Client = ?config(client, C),
ID = configure_limit(?time_range_week(), ?global(), C), ID = configure_limit(?time_range_week(), ?global(), C),
Cost = ?cash(42), Cost = ?cash(42),
Context = ?ctx_invoice_payment(Cost, Cost), Context = ?payproc_ctx_payment(Cost, Cost),
Change = ?LIMIT_CHANGE(ID), Change = ?LIMIT_CHANGE(ID),
{ok, _} = lim_client:hold(Change, Context, Client), {ok, _} = lim_client:hold(Change, Context, Client),
{ok, _} = lim_client:hold(Change, Context, Client), {ok, _} = lim_client:hold(Change, Context, Client),
@ -393,7 +374,7 @@ full_commit_processes_idempotently(C) ->
partial_commit_processes_idempotently(C) -> partial_commit_processes_idempotently(C) ->
Client = ?config(client, C), Client = ?config(client, C),
ID = configure_limit(?time_range_week(), ?global(), C), ID = configure_limit(?time_range_week(), ?global(), C),
Context = ?ctx_invoice_payment(?cash(42), ?cash(40)), Context = ?payproc_ctx_payment(?cash(42), ?cash(40)),
Change = ?LIMIT_CHANGE(ID), Change = ?LIMIT_CHANGE(ID),
{ok, _} = lim_client:hold(Change, Context, Client), {ok, _} = lim_client:hold(Change, Context, Client),
{ok, _} = lim_client:hold(Change, Context, Client), {ok, _} = lim_client:hold(Change, Context, Client),
@ -406,7 +387,7 @@ partial_commit_processes_idempotently(C) ->
rollback_processes_idempotently(C) -> rollback_processes_idempotently(C) ->
Client = ?config(client, C), Client = ?config(client, C),
ID = configure_limit(?time_range_week(), ?global(), C), ID = configure_limit(?time_range_week(), ?global(), C),
Context = ?ctx_invoice_payment(?cash(42), ?cash(0)), Context = ?payproc_ctx_payment(?cash(42), ?cash(0)),
Change = ?LIMIT_CHANGE(ID), Change = ?LIMIT_CHANGE(ID),
{ok, _} = lim_client:hold(Change, Context, Client), {ok, _} = lim_client:hold(Change, Context, Client),
{ok, _} = lim_client:hold(Change, Context, Client), {ok, _} = lim_client:hold(Change, Context, Client),
@ -421,7 +402,7 @@ rollback_processes_idempotently(C) ->
commit_number_ok(C) -> commit_number_ok(C) ->
Client = ?config(client, C), Client = ?config(client, C),
ID = configure_limit(?time_range_week(), ?global(), ?turnover_metric_number(), C), ID = configure_limit(?time_range_week(), ?global(), ?turnover_metric_number(), C),
Context = ?ctx_invoice_payment(?cash(10), ?cash(10)), Context = ?payproc_ctx_payment(?cash(10), ?cash(10)),
{ok, LimitState0} = lim_client:get(ID, Context, Client), {ok, LimitState0} = lim_client:get(ID, Context, Client),
_ = hold_and_commit(?LIMIT_CHANGE(ID), Context, Client), _ = hold_and_commit(?LIMIT_CHANGE(ID), Context, Client),
{ok, LimitState1} = lim_client:get(ID, Context, Client), {ok, LimitState1} = lim_client:get(ID, Context, Client),
@ -434,8 +415,8 @@ commit_number_ok(C) ->
rollback_number_ok(C) -> rollback_number_ok(C) ->
Client = ?config(client, C), Client = ?config(client, C),
ID = configure_limit(?time_range_week(), ?global(), ?turnover_metric_number(), C), ID = configure_limit(?time_range_week(), ?global(), ?turnover_metric_number(), C),
Context = ?ctx_invoice_payment(?cash(10), ?cash(10)), Context = ?payproc_ctx_payment(?cash(10), ?cash(10)),
ContextRollback = ?ctx_invoice_payment(?cash(10), ?cash(0)), ContextRollback = ?payproc_ctx_payment(?cash(10), ?cash(0)),
{ok, LimitState0} = lim_client:get(ID, Context, Client), {ok, LimitState0} = lim_client:get(ID, Context, Client),
_ = hold_and_commit(?LIMIT_CHANGE(ID), Context, ContextRollback, Client), _ = hold_and_commit(?LIMIT_CHANGE(ID), Context, ContextRollback, Client),
{ok, LimitState1} = lim_client:get(ID, Context, Client), {ok, LimitState1} = lim_client:get(ID, Context, Client),
@ -451,8 +432,8 @@ commit_refund_keep_number_unchanged(C) ->
Cost = ?cash(10), Cost = ?cash(10),
CaptureCost = ?cash(8), CaptureCost = ?cash(8),
RefundCost = ?cash(5), RefundCost = ?cash(5),
PaymentContext = ?ctx_invoice_payment(<<"OWNER">>, <<"SHOP">>, Cost, CaptureCost), PaymentContext = ?payproc_ctx_payment(<<"OWNER">>, <<"SHOP">>, Cost, CaptureCost),
RefundContext = ?ctx_invoice_payment_refund(<<"OWNER">>, <<"SHOP">>, Cost, CaptureCost, RefundCost), RefundContext = ?payproc_ctx_refund(<<"OWNER">>, <<"SHOP">>, Cost, CaptureCost, RefundCost),
{ok, LimitState0} = lim_client:get(ID, PaymentContext, Client), {ok, LimitState0} = lim_client:get(ID, PaymentContext, Client),
_ = hold_and_commit(?LIMIT_CHANGE(ID, 1), PaymentContext, Client), _ = hold_and_commit(?LIMIT_CHANGE(ID, 1), PaymentContext, Client),
_ = hold_and_commit(?LIMIT_CHANGE(ID, 2), RefundContext, Client), _ = hold_and_commit(?LIMIT_CHANGE(ID, 2), RefundContext, Client),
@ -467,8 +448,8 @@ commit_refund_keep_number_unchanged(C) ->
partial_commit_number_counts_as_single_op(C) -> partial_commit_number_counts_as_single_op(C) ->
Client = ?config(client, C), Client = ?config(client, C),
ID = configure_limit(?time_range_week(), ?global(), ?turnover_metric_number(), C), ID = configure_limit(?time_range_week(), ?global(), ?turnover_metric_number(), C),
Context = ?ctx_invoice_payment(?cash(10), ?cash(10)), Context = ?payproc_ctx_payment(?cash(10), ?cash(10)),
ContextPartial = ?ctx_invoice_payment(?cash(10), ?cash(5)), ContextPartial = ?payproc_ctx_payment(?cash(10), ?cash(5)),
{ok, LimitState0} = lim_client:get(ID, Context, Client), {ok, LimitState0} = lim_client:get(ID, Context, Client),
_ = hold_and_commit(?LIMIT_CHANGE(ID), Context, ContextPartial, Client), _ = hold_and_commit(?LIMIT_CHANGE(ID), Context, ContextPartial, Client),
{ok, LimitState1} = lim_client:get(ID, Context, Client), {ok, LimitState1} = lim_client:get(ID, Context, Client),
@ -479,79 +460,6 @@ partial_commit_number_counts_as_single_op(C) ->
%% %%
-spec payproc_hold_ok(config()) -> _.
payproc_hold_ok(C) ->
ID = configure_limit(?time_range_month(), ?global(), C),
Context = ?payproc_ctx_invoice(?cash(10)),
{ok, {vector, #limiter_VectorClock{}}} = lim_client:hold(?LIMIT_CHANGE(ID), Context, ?config(client, C)),
{ok, #limiter_Limit{}} = lim_client:get(ID, Context, ?config(client, C)).
-spec payproc_commit_ok(config()) -> _.
payproc_commit_ok(C) ->
ID = configure_limit(?time_range_year(), ?global(), C),
Context = ?payproc_ctx_invoice(?cash(10)),
{ok, {vector, _}} = hold_and_commit(?LIMIT_CHANGE(ID), Context, ?config(client, C)),
{ok, #limiter_Limit{}} = lim_client:get(ID, Context, ?config(client, C)).
-spec payproc_rollback_ok(config()) -> _.
payproc_rollback_ok(C) ->
ID = configure_limit(?time_range_week(), ?global(), C),
Context0 = ?payproc_ctx_invoice_payment(?cash(10), ?cash(10)),
Context1 = ?payproc_ctx_invoice_payment(?cash(10), ?cash(0)),
Change = ?LIMIT_CHANGE(ID),
{ok, {vector, _}} = lim_client:hold(Change, Context0, ?config(client, C)),
{ok, {vector, _}} = lim_client:commit(Change, Context1, ?config(client, C)).
-spec payproc_refund_ok(config()) -> _.
payproc_refund_ok(C) ->
Client = ?config(client, C),
OwnerID = <<"WWWcool Ltd">>,
ShopID = <<"shop">>,
ID = configure_limit(?time_range_day(), ?scope([?scope_party(), ?scope_shop()]), C),
Context0 = ?payproc_ctx_invoice_payment(OwnerID, ShopID, ?cash(15), ?cash(15)),
RefundContext1 = ?payproc_ctx_invoice_payment_refund(OwnerID, ShopID, ?cash(10), ?cash(10), ?cash(10)),
{ok, {vector, _}} = hold_and_commit(?LIMIT_CHANGE(ID, <<"Payment">>), Context0, Client),
{ok, {vector, _}} = hold_and_commit(?LIMIT_CHANGE(ID, <<"Refund">>), RefundContext1, Client),
{ok, #limiter_Limit{} = Limit2} = lim_client:get(ID, RefundContext1, Client),
?assertEqual(Limit2#limiter_Limit.amount, 5).
-spec payproc_commit_with_payment_tool_scope_ok(config()) -> _.
payproc_commit_with_payment_tool_scope_ok(C) ->
Client = ?config(client, C),
ID = configure_limit(?time_range_week(), ?scope([?scope_payment_tool()]), ?turnover_metric_number(), C),
Context0 = ?payproc_ctx_invoice_payment(
?payproc_invoice_payment(
?cash(10),
?cash(10),
{bank_card, ?payproc_bank_card()}
)
),
Context1 = ?payproc_ctx_invoice_payment(
?payproc_invoice_payment(
?cash(10),
?cash(10),
{bank_card, ?payproc_bank_card(<<"OtherToken">>, 2, 2022)}
)
),
Context2 = ?payproc_ctx_invoice_payment(
?payproc_invoice_payment(
?cash(10),
?cash(10),
{bank_card, ?payproc_bank_card(?string, 3, 2022)}
)
),
{ok, LimitState0} = lim_client:get(ID, Context0, Client),
_ = hold_and_commit(?LIMIT_CHANGE(ID, 1), Context0, Client),
_ = hold_and_commit(?LIMIT_CHANGE(ID, 2), Context1, Client),
_ = hold_and_commit(?LIMIT_CHANGE(ID, 3), Context2, Client),
{ok, LimitState1} = lim_client:get(ID, Context0, Client),
?assertEqual(
LimitState1#limiter_Limit.amount,
LimitState0#limiter_Limit.amount + 1
).
%%
gen_change_id(LimitID, ChangeID) -> gen_change_id(LimitID, ChangeID) ->
genlib:format("~s/~p", [LimitID, ChangeID]). genlib:format("~s/~p", [LimitID, ChangeID]).
@ -570,14 +478,19 @@ configure_limit(TimeRange, Scope, C) ->
configure_limit(TimeRange, Scope, Metric, C) -> configure_limit(TimeRange, Scope, Metric, C) ->
ID = ?config(id, C), ID = ?config(id, C),
Params = #limiter_config_LimitConfigParams{ ContextType =
case get_group_name(C) of
withdrawals -> ?ctx_type_wthdproc();
_Default -> ?ctx_type_payproc()
end,
Params = #config_LimitConfigParams{
id = ID, id = ID,
started_at = <<"2000-01-01T00:00:00Z">>, started_at = <<"2000-01-01T00:00:00Z">>,
time_range_type = TimeRange, time_range_type = TimeRange,
shard_size = 1, shard_size = 1,
type = ?lim_type_turnover(Metric), type = ?lim_type_turnover(Metric),
scope = Scope, scope = Scope,
context_type = ?ctx_type_payproc(), context_type = ContextType,
op_behaviour = ?op_behaviour(?op_subtraction()) op_behaviour = ?op_behaviour(?op_subtraction())
}, },
{ok, _LimitConfig} = lim_client:create_config(Params, ?config(client, C)), {ok, _LimitConfig} = lim_client:create_config(Params, ?config(client, C)),
@ -585,3 +498,7 @@ configure_limit(TimeRange, Scope, Metric, C) ->
gen_unique_id(Prefix) -> gen_unique_id(Prefix) ->
genlib:format("~s/~B", [Prefix, lim_time:now()]). genlib:format("~s/~B", [Prefix, lim_time:now()]).
get_group_name(C) ->
GroupProps = ?config(tc_group_properties, C),
proplists:get_value(name, GroupProps).

View File

@ -26,7 +26,6 @@
%% Common project dependencies. %% Common project dependencies.
{deps, [ {deps, [
{damsel, {git, "https://github.com/valitydev/damsel.git", {branch, "master"}}},
{limiter_proto, {git, "https://github.com/valitydev/limiter-proto.git", {branch, "master"}}}, {limiter_proto, {git, "https://github.com/valitydev/limiter-proto.git", {branch, "master"}}},
{xrates_proto, {git, "https://github.com/valitydev/xrates-proto.git", {branch, "master"}}}, {xrates_proto, {git, "https://github.com/valitydev/xrates-proto.git", {branch, "master"}}},
{machinery, {git, "https://github.com/valitydev/machinery-erlang.git", {branch, "master"}}}, {machinery, {git, "https://github.com/valitydev/machinery-erlang.git", {branch, "master"}}},

View File

@ -9,8 +9,8 @@
{<<"cowlib">>,{pkg,<<"cowlib">>,<<"2.11.0">>},2}, {<<"cowlib">>,{pkg,<<"cowlib">>,<<"2.11.0">>},2},
{<<"damsel">>, {<<"damsel">>,
{git,"https://github.com/valitydev/damsel.git", {git,"https://github.com/valitydev/damsel.git",
{ref,"1d60b20f2136938c43902dd38a80ae70f03e4a14"}}, {ref,"33c5665571042440ccec109735481d8c13704ec2"}},
0}, 1},
{<<"erl_health">>, {<<"erl_health">>,
{git,"https://github.com/valitydev/erlang-health.git", {git,"https://github.com/valitydev/erlang-health.git",
{ref,"5958e2f35cd4d09f40685762b82b82f89b4d9333"}}, {ref,"5958e2f35cd4d09f40685762b82b82f89b4d9333"}},
@ -25,16 +25,16 @@
{<<"jsx">>,{pkg,<<"jsx">>,<<"3.1.0">>},1}, {<<"jsx">>,{pkg,<<"jsx">>,<<"3.1.0">>},1},
{<<"limiter_proto">>, {<<"limiter_proto">>,
{git,"https://github.com/valitydev/limiter-proto.git", {git,"https://github.com/valitydev/limiter-proto.git",
{ref,"ac9705389211682263c0a983ae76f663d4857ec9"}}, {ref,"61581846b4d41de3a9e561c79f558e74450ab950"}},
0}, 0},
{<<"machinery">>, {<<"machinery">>,
{git,"https://github.com/valitydev/machinery-erlang.git", {git,"https://github.com/valitydev/machinery-erlang.git",
{ref,"ff4cfefb616250f6905c25e79f74a7a30eb1aae5"}}, {ref,"62c32434c80a462956ad9d50f9bce47836580d77"}},
0}, 0},
{<<"metrics">>,{pkg,<<"metrics">>,<<"1.0.1">>},2}, {<<"metrics">>,{pkg,<<"metrics">>,<<"1.0.1">>},2},
{<<"mg_proto">>, {<<"mg_proto">>,
{git,"https://github.com/valitydev/machinegun-proto.git", {git,"https://github.com/valitydev/machinegun-proto.git",
{ref,"b43d6fd0939ee4029ec8873dbd16f3c5fbe4a95c"}}, {ref,"347c5c44c8dcca24a50e0509c0df5401f863e790"}},
1}, 1},
{<<"mimerl">>,{pkg,<<"mimerl">>,<<"1.2.0">>},2}, {<<"mimerl">>,{pkg,<<"mimerl">>,<<"1.2.0">>},2},
{<<"parse_trans">>,{pkg,<<"parse_trans">>,<<"3.3.1">>},2}, {<<"parse_trans">>,{pkg,<<"parse_trans">>,<<"3.3.1">>},2},