mirror of
https://github.com/valitydev/hellgate.git
synced 2024-11-06 02:45:20 +00:00
ED-212: refactor routing (#594)
This commit is contained in:
parent
c5f4735cfc
commit
0587356c41
@ -208,7 +208,6 @@
|
|||||||
-type shop() :: dmsl_domain_thrift:'Shop'().
|
-type shop() :: dmsl_domain_thrift:'Shop'().
|
||||||
-type payment_tool() :: dmsl_domain_thrift:'PaymentTool'().
|
-type payment_tool() :: dmsl_domain_thrift:'PaymentTool'().
|
||||||
-type recurrent_paytool_service_terms() :: dmsl_domain_thrift:'RecurrentPaytoolsServiceTerms'().
|
-type recurrent_paytool_service_terms() :: dmsl_domain_thrift:'RecurrentPaytoolsServiceTerms'().
|
||||||
|
|
||||||
-type session_status() :: active | suspended | finished.
|
-type session_status() :: active | suspended | finished.
|
||||||
|
|
||||||
-type session() :: #{
|
-type session() :: #{
|
||||||
@ -466,21 +465,6 @@ get_merchant_terms(Party, Shop, DomainRevision, Timestamp, VS) ->
|
|||||||
),
|
),
|
||||||
Terms.
|
Terms.
|
||||||
|
|
||||||
-spec get_provider_terminal_terms(route(), hg_varset:varset(), hg_domain:revision()) ->
|
|
||||||
dmsl_domain_thrift:'PaymentsProvisionTerms'() | undefined.
|
|
||||||
get_provider_terminal_terms(?route(ProviderRef, TerminalRef), VS, Revision) ->
|
|
||||||
PreparedVS = hg_varset:prepare_varset(VS),
|
|
||||||
{Client, Context} = get_party_client(),
|
|
||||||
{ok, TermsSet} = party_client_thrift:compute_provider_terminal_terms(
|
|
||||||
ProviderRef,
|
|
||||||
TerminalRef,
|
|
||||||
Revision,
|
|
||||||
PreparedVS,
|
|
||||||
Client,
|
|
||||||
Context
|
|
||||||
),
|
|
||||||
TermsSet#domain_ProvisionTermSet.payments.
|
|
||||||
|
|
||||||
assert_contract_active(#domain_Contract{status = {active, _}}) ->
|
assert_contract_active(#domain_Contract{status = {active, _}}) ->
|
||||||
ok;
|
ok;
|
||||||
assert_contract_active(#domain_Contract{status = Status}) ->
|
assert_contract_active(#domain_Contract{status = Status}) ->
|
||||||
@ -774,19 +758,22 @@ gather_routes(PaymentInstitution, VS, Revision, St) ->
|
|||||||
Payment = get_payment(St),
|
Payment = get_payment(St),
|
||||||
Predestination = choose_routing_predestination(Payment),
|
Predestination = choose_routing_predestination(Payment),
|
||||||
case
|
case
|
||||||
hg_routing_rule:gather_routes(
|
hg_routing:gather_routes(
|
||||||
Predestination,
|
Predestination,
|
||||||
PaymentInstitution,
|
PaymentInstitution,
|
||||||
VS,
|
VS,
|
||||||
Revision
|
Revision
|
||||||
)
|
)
|
||||||
of
|
of
|
||||||
{[], RejectContext} ->
|
{ok, {[], RejectedRoutes}} ->
|
||||||
_ = log_reject_context(unknown, RejectContext),
|
_ = log_rejected_routes(unknown, RejectedRoutes, VS),
|
||||||
throw({no_route_found, unknown});
|
throw({no_route_found, unknown});
|
||||||
{Routes, RejectContext} ->
|
{ok, {Routes, RejectedRoutes}} ->
|
||||||
_ = log_misconfigurations(RejectContext),
|
_ = log_rejected_routes(unknown, RejectedRoutes, VS),
|
||||||
Routes
|
Routes;
|
||||||
|
{error, {misconfiguration, _Reason} = Error} ->
|
||||||
|
_ = log_misconfigurations(Error),
|
||||||
|
throw({no_route_found, misconfiguration})
|
||||||
end.
|
end.
|
||||||
|
|
||||||
-spec check_risk_score(risk_score()) -> ok | {error, risk_score_is_too_high}.
|
-spec check_risk_score(risk_score()) -> ok | {error, risk_score_is_too_high}.
|
||||||
@ -806,42 +793,25 @@ choose_routing_predestination(#domain_InvoicePayment{payer = ?payment_resource_p
|
|||||||
log_route_choice_meta(ChoiceMeta, Revision) ->
|
log_route_choice_meta(ChoiceMeta, Revision) ->
|
||||||
_ = logger:log(info, "Routing decision made", hg_routing:get_logger_metadata(ChoiceMeta, Revision)).
|
_ = logger:log(info, "Routing decision made", hg_routing:get_logger_metadata(ChoiceMeta, Revision)).
|
||||||
|
|
||||||
log_misconfigurations(RejectContext) ->
|
log_misconfigurations({misconfiguration, _} = Error) ->
|
||||||
RejectedProviders = maps:get(rejected_providers, RejectContext),
|
{Format, Details} = hg_routing:prepare_log_message(Error),
|
||||||
RejectedRoutes = maps:get(rejected_routes, RejectContext),
|
_ = logger:warning(Format, Details),
|
||||||
Rejects = RejectedProviders ++ RejectedRoutes,
|
|
||||||
_ = lists:foreach(fun maybe_log_misconfiguration/1, Rejects),
|
|
||||||
ok.
|
ok.
|
||||||
|
|
||||||
maybe_log_misconfiguration({PRef, {'Misconfiguration', Reason}}) ->
|
log_rejected_routes(RejectReason, RejectedRoutes, Varset) ->
|
||||||
Text = "The provider with ref ~p has been misconfigured: ~p",
|
log_rejected_routes(warning, RejectReason, RejectedRoutes, Varset).
|
||||||
_ = logger:warning(Text, [PRef, Reason]);
|
|
||||||
maybe_log_misconfiguration({PRef, TRef, {'Misconfiguration', Reason}}) ->
|
|
||||||
Text = "The route with provider ref ~p and terminal ref ~p has been misconfigured: ~p",
|
|
||||||
_ = logger:warning(Text, [PRef, TRef, Reason]);
|
|
||||||
maybe_log_misconfiguration(_NotMisconfiguration) ->
|
|
||||||
ok.
|
|
||||||
|
|
||||||
log_reject_context(RejectReason, RejectContext) ->
|
log_rejected_routes(Level, RejectReason, RejectedRoutes, Varset) ->
|
||||||
log_reject_context(warning, RejectReason, RejectContext).
|
|
||||||
|
|
||||||
log_reject_context(Level, RejectReason, RejectContext) ->
|
|
||||||
_ = logger:log(
|
_ = logger:log(
|
||||||
Level,
|
Level,
|
||||||
"No route found, reason = ~p, varset: ~p",
|
"No route found, reason = ~p, varset: ~p",
|
||||||
[RejectReason, maps:get(varset, RejectContext)],
|
[RejectReason, Varset],
|
||||||
logger:get_process_metadata()
|
|
||||||
),
|
|
||||||
_ = logger:log(
|
|
||||||
Level,
|
|
||||||
"No route found, reason = ~p, rejected providers: ~p",
|
|
||||||
[RejectReason, maps:get(rejected_providers, RejectContext)],
|
|
||||||
logger:get_process_metadata()
|
logger:get_process_metadata()
|
||||||
),
|
),
|
||||||
_ = logger:log(
|
_ = logger:log(
|
||||||
Level,
|
Level,
|
||||||
"No route found, reason = ~p, rejected routes: ~p",
|
"No route found, reason = ~p, rejected routes: ~p",
|
||||||
[RejectReason, maps:get(rejected_routes, RejectContext)],
|
[RejectReason, RejectedRoutes],
|
||||||
logger:get_process_metadata()
|
logger:get_process_metadata()
|
||||||
),
|
),
|
||||||
ok.
|
ok.
|
||||||
@ -1024,7 +994,7 @@ partial_capture(St0, Reason, Cost, Cart, Opts) ->
|
|||||||
MerchantTerms = get_merchant_payments_terms(Opts, Revision, Timestamp, VS),
|
MerchantTerms = get_merchant_payments_terms(Opts, Revision, Timestamp, VS),
|
||||||
ok = validate_merchant_hold_terms(MerchantTerms),
|
ok = validate_merchant_hold_terms(MerchantTerms),
|
||||||
Route = get_route(St),
|
Route = get_route(St),
|
||||||
ProviderTerms = get_provider_terminal_terms(Route, VS, Revision),
|
ProviderTerms = hg_routing:get_payment_terms(Route, VS, Revision),
|
||||||
ok = validate_provider_holds_terms(ProviderTerms),
|
ok = validate_provider_holds_terms(ProviderTerms),
|
||||||
FinalCashflow = calculate_cashflow(Route, Payment2, MerchantTerms, ProviderTerms, VS, Revision, Opts),
|
FinalCashflow = calculate_cashflow(Route, Payment2, MerchantTerms, ProviderTerms, VS, Revision, Opts),
|
||||||
Changes = start_partial_capture(Reason, Cost, Cart, FinalCashflow),
|
Changes = start_partial_capture(Reason, Cost, Cart, FinalCashflow),
|
||||||
@ -1206,7 +1176,7 @@ make_refund_cashflow(Refund, Payment, Revision, CreatedAt, St, Opts) ->
|
|||||||
VS = collect_validation_varset(St, Opts),
|
VS = collect_validation_varset(St, Opts),
|
||||||
MerchantTerms = get_merchant_refunds_terms(get_merchant_payments_terms(Opts, Revision, CreatedAt, VS)),
|
MerchantTerms = get_merchant_refunds_terms(get_merchant_payments_terms(Opts, Revision, CreatedAt, VS)),
|
||||||
ok = validate_refund(MerchantTerms, Refund, Payment),
|
ok = validate_refund(MerchantTerms, Refund, Payment),
|
||||||
ProviderPaymentsTerms = get_provider_terminal_terms(Route, VS, Revision),
|
ProviderPaymentsTerms = hg_routing:get_payment_terms(Route, VS, Revision),
|
||||||
ProviderTerms = get_provider_refunds_terms(ProviderPaymentsTerms, Refund, Payment),
|
ProviderTerms = get_provider_refunds_terms(ProviderPaymentsTerms, Refund, Payment),
|
||||||
Cashflow = collect_refund_cashflow(MerchantTerms, ProviderTerms),
|
Cashflow = collect_refund_cashflow(MerchantTerms, ProviderTerms),
|
||||||
PaymentInstitutionRef = get_payment_institution_ref(Opts),
|
PaymentInstitutionRef = get_payment_institution_ref(Opts),
|
||||||
@ -1554,7 +1524,7 @@ get_cash_flow_for_target_status({failed, _}, _St, _Opts) ->
|
|||||||
) -> cash_flow().
|
) -> cash_flow().
|
||||||
calculate_cashflow(Route, Payment, Timestamp, VS, Revision, Opts) ->
|
calculate_cashflow(Route, Payment, Timestamp, VS, Revision, Opts) ->
|
||||||
MerchantTerms = get_merchant_payments_terms(Opts, Revision, Timestamp, VS),
|
MerchantTerms = get_merchant_payments_terms(Opts, Revision, Timestamp, VS),
|
||||||
ProviderTerms = get_provider_terminal_terms(Route, VS, Revision),
|
ProviderTerms = hg_routing:get_payment_terms(Route, VS, Revision),
|
||||||
calculate_cashflow(Route, Payment, MerchantTerms, ProviderTerms, VS, Revision, Opts).
|
calculate_cashflow(Route, Payment, MerchantTerms, ProviderTerms, VS, Revision, Opts).
|
||||||
|
|
||||||
-spec calculate_cashflow(
|
-spec calculate_cashflow(
|
||||||
@ -1829,9 +1799,8 @@ process_risk_score(Action, St) ->
|
|||||||
Opts = get_opts(St),
|
Opts = get_opts(St),
|
||||||
Revision = get_payment_revision(St),
|
Revision = get_payment_revision(St),
|
||||||
Payment = get_payment(St),
|
Payment = get_payment(St),
|
||||||
|
VS1 = get_varset(St, #{}),
|
||||||
PaymentInstitutionRef = get_payment_institution_ref(Opts),
|
PaymentInstitutionRef = get_payment_institution_ref(Opts),
|
||||||
VS0 = reconstruct_payment_flow(Payment, #{}),
|
|
||||||
VS1 = collect_validation_varset(get_party(Opts), get_shop(Opts), Payment, VS0),
|
|
||||||
PaymentInstitution = hg_payment_institution:compute_payment_institution(PaymentInstitutionRef, VS1, Revision),
|
PaymentInstitution = hg_payment_institution:compute_payment_institution(PaymentInstitutionRef, VS1, Revision),
|
||||||
RiskScore = repair_inspect(Payment, PaymentInstitution, Opts, St),
|
RiskScore = repair_inspect(Payment, PaymentInstitution, Opts, St),
|
||||||
Events = [?risk_score_changed(RiskScore)],
|
Events = [?risk_score_changed(RiskScore)],
|
||||||
@ -1848,20 +1817,18 @@ process_routing(Action, St) ->
|
|||||||
Opts = get_opts(St),
|
Opts = get_opts(St),
|
||||||
Revision = get_payment_revision(St),
|
Revision = get_payment_revision(St),
|
||||||
Payment = get_payment(St),
|
Payment = get_payment(St),
|
||||||
|
#{payment_tool := PaymentTool} = VS1 = get_varset(St, #{risk_score => get_risk_score(St)}),
|
||||||
CreatedAt = get_payment_created_at(Payment),
|
CreatedAt = get_payment_created_at(Payment),
|
||||||
PaymentInstitutionRef = get_payment_institution_ref(Opts),
|
PaymentInstitutionRef = get_payment_institution_ref(Opts),
|
||||||
VS0 = #{risk_score => get_risk_score(St)},
|
MerchantTerms = get_merchant_payments_terms(Opts, Revision, CreatedAt, VS1),
|
||||||
VS1 = reconstruct_payment_flow(Payment, VS0),
|
VS2 = collect_refund_varset(
|
||||||
#{payment_tool := PaymentTool} = VS2 = collect_validation_varset(get_party(Opts), get_shop(Opts), Payment, VS1),
|
|
||||||
MerchantTerms = get_merchant_payments_terms(Opts, Revision, CreatedAt, VS2),
|
|
||||||
VS3 = collect_refund_varset(
|
|
||||||
MerchantTerms#domain_PaymentsServiceTerms.refunds,
|
MerchantTerms#domain_PaymentsServiceTerms.refunds,
|
||||||
PaymentTool,
|
PaymentTool,
|
||||||
VS2
|
VS1
|
||||||
),
|
),
|
||||||
VS4 = collect_chargeback_varset(
|
VS3 = collect_chargeback_varset(
|
||||||
MerchantTerms#domain_PaymentsServiceTerms.chargebacks,
|
MerchantTerms#domain_PaymentsServiceTerms.chargebacks,
|
||||||
VS3
|
VS2
|
||||||
),
|
),
|
||||||
PaymentInstitution = hg_payment_institution:compute_payment_institution(PaymentInstitutionRef, VS1, Revision),
|
PaymentInstitution = hg_payment_institution:compute_payment_institution(PaymentInstitutionRef, VS1, Revision),
|
||||||
try
|
try
|
||||||
@ -1871,10 +1838,10 @@ process_routing(Action, St) ->
|
|||||||
{ok, PaymentRoute} ->
|
{ok, PaymentRoute} ->
|
||||||
[hg_routing:from_payment_route(PaymentRoute)];
|
[hg_routing:from_payment_route(PaymentRoute)];
|
||||||
undefined ->
|
undefined ->
|
||||||
gather_routes(PaymentInstitution, VS4, Revision, St)
|
gather_routes(PaymentInstitution, VS3, Revision, St)
|
||||||
end,
|
end,
|
||||||
Events = handle_gathered_route_result(
|
Events = handle_gathered_route_result(
|
||||||
filter_limit_overflow_routes(Routes, VS4, St),
|
filter_limit_overflow_routes(Routes, VS3, St),
|
||||||
[hg_routing:to_payment_route(R) || R <- Routes],
|
[hg_routing:to_payment_route(R) || R <- Routes],
|
||||||
Revision
|
Revision
|
||||||
),
|
),
|
||||||
@ -1885,8 +1852,8 @@ process_routing(Action, St) ->
|
|||||||
end.
|
end.
|
||||||
|
|
||||||
handle_gathered_route_result({ok, RoutesNoOverflow}, Routes, Revision) ->
|
handle_gathered_route_result({ok, RoutesNoOverflow}, Routes, Revision) ->
|
||||||
{ChoosenRoute, ChoiceMeta} = hg_routing:choose_route(RoutesNoOverflow),
|
{ChoosenRoute, ChoiceContext} = hg_routing:choose_route(RoutesNoOverflow),
|
||||||
_ = log_route_choice_meta(ChoiceMeta, Revision),
|
_ = log_route_choice_meta(ChoiceContext, Revision),
|
||||||
[?route_changed(hg_routing:to_payment_route(ChoosenRoute), Routes)];
|
[?route_changed(hg_routing:to_payment_route(ChoosenRoute), Routes)];
|
||||||
handle_gathered_route_result({error, not_found}, Routes, _) ->
|
handle_gathered_route_result({error, not_found}, Routes, _) ->
|
||||||
Failure =
|
Failure =
|
||||||
@ -1912,17 +1879,16 @@ handle_choose_route_error(Reason, Events, St, Action) ->
|
|||||||
|
|
||||||
-spec process_cash_flow_building(action(), st()) -> machine_result().
|
-spec process_cash_flow_building(action(), st()) -> machine_result().
|
||||||
process_cash_flow_building(Action, St) ->
|
process_cash_flow_building(Action, St) ->
|
||||||
|
Route = get_route(St),
|
||||||
Opts = get_opts(St),
|
Opts = get_opts(St),
|
||||||
Revision = get_payment_revision(St),
|
Revision = get_payment_revision(St),
|
||||||
Payment = get_payment(St),
|
Payment = get_payment(St),
|
||||||
Invoice = get_invoice(Opts),
|
Invoice = get_invoice(Opts),
|
||||||
Route = get_route(St),
|
VS = get_varset(St, #{}),
|
||||||
Timestamp = get_payment_created_at(Payment),
|
CreatedAt = get_payment_created_at(Payment),
|
||||||
VS0 = reconstruct_payment_flow(Payment, #{}),
|
MerchantTerms = get_merchant_payments_terms(Opts, Revision, CreatedAt, VS),
|
||||||
VS1 = collect_validation_varset(get_party(Opts), get_shop(Opts), Payment, VS0),
|
ProviderTerms = hg_routing:get_payment_terms(Route, VS, Revision),
|
||||||
MerchantTerms = get_merchant_payments_terms(Opts, Revision, Timestamp, VS1),
|
FinalCashflow = calculate_cashflow(Route, Payment, MerchantTerms, ProviderTerms, VS, Revision, Opts),
|
||||||
ProviderTerms = get_provider_terminal_terms(Route, VS1, Revision),
|
|
||||||
FinalCashflow = calculate_cashflow(Route, Payment, MerchantTerms, ProviderTerms, VS1, Revision, Opts),
|
|
||||||
_ = rollback_unused_payment_limits(St),
|
_ = rollback_unused_payment_limits(St),
|
||||||
_Clock = hg_accounting:hold(
|
_Clock = hg_accounting:hold(
|
||||||
construct_payment_plan_id(Invoice, Payment),
|
construct_payment_plan_id(Invoice, Payment),
|
||||||
@ -2539,7 +2505,7 @@ get_provider_terms(St, Revision) ->
|
|||||||
Payment = get_payment(St),
|
Payment = get_payment(St),
|
||||||
VS0 = reconstruct_payment_flow(Payment, #{}),
|
VS0 = reconstruct_payment_flow(Payment, #{}),
|
||||||
VS1 = collect_validation_varset(get_party(Opts), get_shop(Opts), Payment, VS0),
|
VS1 = collect_validation_varset(get_party(Opts), get_shop(Opts), Payment, VS0),
|
||||||
get_provider_terminal_terms(Route, VS1, Revision).
|
hg_routing:get_payment_terms(Route, VS1, Revision).
|
||||||
|
|
||||||
filter_limit_overflow_routes(Routes, VS, St) ->
|
filter_limit_overflow_routes(Routes, VS, St) ->
|
||||||
ok = hold_limit_routes(Routes, VS, St),
|
ok = hold_limit_routes(Routes, VS, St),
|
||||||
@ -2552,13 +2518,14 @@ filter_limit_overflow_routes(Routes, VS, St) ->
|
|||||||
end.
|
end.
|
||||||
|
|
||||||
get_limit_overflow_routes(Routes, VS, St, RejectedRoutes) ->
|
get_limit_overflow_routes(Routes, VS, St, RejectedRoutes) ->
|
||||||
|
Opts = get_opts(St),
|
||||||
Revision = get_payment_revision(St),
|
Revision = get_payment_revision(St),
|
||||||
Payment = get_payment(St),
|
Payment = get_payment(St),
|
||||||
Invoice = get_invoice(get_opts(St)),
|
Invoice = get_invoice(Opts),
|
||||||
lists:foldl(
|
lists:foldl(
|
||||||
fun(Route, {RoutesNoOverflowIn, RejectedIn}) ->
|
fun(Route, {RoutesNoOverflowIn, RejectedIn}) ->
|
||||||
PaymentRoute = hg_routing:to_payment_route(Route),
|
PaymentRoute = hg_routing:to_payment_route(Route),
|
||||||
ProviderTerms = get_provider_terminal_terms(PaymentRoute, VS, Revision),
|
ProviderTerms = hg_routing:get_payment_terms(PaymentRoute, VS, Revision),
|
||||||
TurnoverLimits = get_turnover_limits(ProviderTerms),
|
TurnoverLimits = get_turnover_limits(ProviderTerms),
|
||||||
case hg_limiter:check_limits(TurnoverLimits, Invoice, Payment) of
|
case hg_limiter:check_limits(TurnoverLimits, Invoice, Payment) of
|
||||||
{ok, _} ->
|
{ok, _} ->
|
||||||
@ -2575,13 +2542,14 @@ get_limit_overflow_routes(Routes, VS, St, RejectedRoutes) ->
|
|||||||
).
|
).
|
||||||
|
|
||||||
hold_limit_routes(Routes, VS, St) ->
|
hold_limit_routes(Routes, VS, St) ->
|
||||||
|
Opts = get_opts(St),
|
||||||
Revision = get_payment_revision(St),
|
Revision = get_payment_revision(St),
|
||||||
Payment = get_payment(St),
|
Payment = get_payment(St),
|
||||||
Invoice = get_invoice(get_opts(St)),
|
Invoice = get_invoice(Opts),
|
||||||
lists:foreach(
|
lists:foreach(
|
||||||
fun(Route) ->
|
fun(Route) ->
|
||||||
PaymentRoute = hg_routing:to_payment_route(Route),
|
PaymentRoute = hg_routing:to_payment_route(Route),
|
||||||
ProviderTerms = get_provider_terminal_terms(PaymentRoute, VS, Revision),
|
ProviderTerms = hg_routing:get_payment_terms(PaymentRoute, VS, Revision),
|
||||||
TurnoverLimits = get_turnover_limits(ProviderTerms),
|
TurnoverLimits = get_turnover_limits(ProviderTerms),
|
||||||
ok = hg_limiter:hold_payment_limits(TurnoverLimits, PaymentRoute, Invoice, Payment)
|
ok = hg_limiter:hold_payment_limits(TurnoverLimits, PaymentRoute, Invoice, Payment)
|
||||||
end,
|
end,
|
||||||
@ -2589,15 +2557,14 @@ hold_limit_routes(Routes, VS, St) ->
|
|||||||
).
|
).
|
||||||
|
|
||||||
rollback_payment_limits(Routes, St) ->
|
rollback_payment_limits(Routes, St) ->
|
||||||
Revision = get_payment_revision(St),
|
|
||||||
Opts = get_opts(St),
|
Opts = get_opts(St),
|
||||||
|
Revision = get_payment_revision(St),
|
||||||
Payment = get_payment(St),
|
Payment = get_payment(St),
|
||||||
Invoice = get_invoice(get_opts(St)),
|
Invoice = get_invoice(Opts),
|
||||||
VS0 = reconstruct_payment_flow(Payment, #{}),
|
VS = get_varset(St, #{}),
|
||||||
VS1 = collect_validation_varset(get_party(Opts), get_shop(Opts), Payment, VS0),
|
|
||||||
lists:foreach(
|
lists:foreach(
|
||||||
fun(Route) ->
|
fun(Route) ->
|
||||||
ProviderTerms = get_provider_terminal_terms(Route, VS1, Revision),
|
ProviderTerms = hg_routing:get_payment_terms(Route, VS, Revision),
|
||||||
TurnoverLimits = get_turnover_limits(ProviderTerms),
|
TurnoverLimits = get_turnover_limits(ProviderTerms),
|
||||||
ok = hg_limiter:rollback_payment_limits(TurnoverLimits, Route, Invoice, Payment)
|
ok = hg_limiter:rollback_payment_limits(TurnoverLimits, Route, Invoice, Payment)
|
||||||
end,
|
end,
|
||||||
@ -2615,9 +2582,10 @@ get_turnover_limits(ProviderTerms) ->
|
|||||||
hg_limiter:get_turnover_limits(TurnoverLimitSelector).
|
hg_limiter:get_turnover_limits(TurnoverLimitSelector).
|
||||||
|
|
||||||
commit_payment_limits(#st{capture_params = CaptureParams} = St) ->
|
commit_payment_limits(#st{capture_params = CaptureParams} = St) ->
|
||||||
|
Opts = get_opts(St),
|
||||||
Revision = get_payment_revision(St),
|
Revision = get_payment_revision(St),
|
||||||
Invoice = get_invoice(get_opts(St)),
|
|
||||||
Payment = get_payment(St),
|
Payment = get_payment(St),
|
||||||
|
Invoice = get_invoice(Opts),
|
||||||
Route = get_route(St),
|
Route = get_route(St),
|
||||||
#payproc_InvoicePaymentCaptureParams{cash = CapturedCash} = CaptureParams,
|
#payproc_InvoicePaymentCaptureParams{cash = CapturedCash} = CaptureParams,
|
||||||
ProviderTerms = get_provider_terms(St, Revision),
|
ProviderTerms = get_provider_terms(St, Revision),
|
||||||
@ -2929,6 +2897,13 @@ get_payer_payment_tool(?recurrent_payer(PaymentTool, _, _)) ->
|
|||||||
get_resource_payment_tool(#domain_DisposablePaymentResource{payment_tool = PaymentTool}) ->
|
get_resource_payment_tool(#domain_DisposablePaymentResource{payment_tool = PaymentTool}) ->
|
||||||
PaymentTool.
|
PaymentTool.
|
||||||
|
|
||||||
|
get_varset(St, InitialValue) ->
|
||||||
|
Opts = get_opts(St),
|
||||||
|
Payment = get_payment(St),
|
||||||
|
VS0 = reconstruct_payment_flow(Payment, InitialValue),
|
||||||
|
VS1 = collect_validation_varset(get_party(Opts), get_shop(Opts), Payment, VS0),
|
||||||
|
VS1.
|
||||||
|
|
||||||
%%
|
%%
|
||||||
|
|
||||||
-spec throw_invalid_request(binary()) -> no_return().
|
-spec throw_invalid_request(binary()) -> no_return().
|
||||||
|
@ -422,7 +422,7 @@ build_chargeback_cash_flow(State, Opts) ->
|
|||||||
Shop = hg_party:get_shop(ShopID, Party),
|
Shop = hg_party:get_shop(ShopID, Party),
|
||||||
VS = collect_validation_varset(Party, Shop, Payment, Body),
|
VS = collect_validation_varset(Party, Shop, Payment, Body),
|
||||||
ServiceTerms = get_merchant_chargeback_terms(Party, Shop, VS, Revision, CreatedAt),
|
ServiceTerms = get_merchant_chargeback_terms(Party, Shop, VS, Revision, CreatedAt),
|
||||||
PaymentsTerms = hg_routing:get_payments_terms(Route, Revision),
|
PaymentsTerms = hg_routing:get_payment_terms(Route, VS, Revision),
|
||||||
ProviderTerms = get_provider_chargeback_terms(PaymentsTerms, Payment),
|
ProviderTerms = get_provider_chargeback_terms(PaymentsTerms, Payment),
|
||||||
ServiceCashFlow = get_chargeback_service_cash_flow(ServiceTerms),
|
ServiceCashFlow = get_chargeback_service_cash_flow(ServiceTerms),
|
||||||
ProviderCashFlow = get_chargeback_provider_cash_flow(ProviderTerms),
|
ProviderCashFlow = get_chargeback_provider_cash_flow(ProviderTerms),
|
||||||
|
@ -108,9 +108,7 @@ filtermap_payment_methods_to_set(ItemList) ->
|
|||||||
)
|
)
|
||||||
).
|
).
|
||||||
|
|
||||||
maybe_legacy_bank_card(#domain_BankCard{payment_system_deprecated = PS} = BC) when
|
maybe_legacy_bank_card(#domain_BankCard{payment_system_deprecated = PS} = BC) when PS /= undefined ->
|
||||||
PS /= undefined
|
|
||||||
->
|
|
||||||
#domain_BankCardPaymentMethod{
|
#domain_BankCardPaymentMethod{
|
||||||
payment_system_deprecated = BC#domain_BankCard.payment_system_deprecated,
|
payment_system_deprecated = BC#domain_BankCard.payment_system_deprecated,
|
||||||
is_cvv_empty = genlib:define(BC#domain_BankCard.is_cvv_empty, false),
|
is_cvv_empty = genlib:define(BC#domain_BankCard.is_cvv_empty, false),
|
||||||
@ -120,9 +118,7 @@ maybe_legacy_bank_card(#domain_BankCard{payment_system_deprecated = PS} = BC) wh
|
|||||||
maybe_legacy_bank_card(_) ->
|
maybe_legacy_bank_card(_) ->
|
||||||
undefined.
|
undefined.
|
||||||
|
|
||||||
maybe_bank_card(#domain_BankCard{payment_system = PS} = BC) when
|
maybe_bank_card(#domain_BankCard{payment_system = PS} = BC) when PS /= undefined ->
|
||||||
PS /= undefined
|
|
||||||
->
|
|
||||||
#domain_BankCardPaymentMethod{
|
#domain_BankCardPaymentMethod{
|
||||||
payment_system = PS,
|
payment_system = PS,
|
||||||
is_cvv_empty = genlib:define(BC#domain_BankCard.is_cvv_empty, false),
|
is_cvv_empty = genlib:define(BC#domain_BankCard.is_cvv_empty, false),
|
||||||
|
@ -240,9 +240,9 @@ init(EncodedParams, #{id := RecPaymentToolID}) ->
|
|||||||
try
|
try
|
||||||
check_risk_score(RiskScore),
|
check_risk_score(RiskScore),
|
||||||
NonFailRatedRoutes = gather_routes(PaymentInstitution, VS1, Revision),
|
NonFailRatedRoutes = gather_routes(PaymentInstitution, VS1, Revision),
|
||||||
{ChosenRoute, ChoiceMeta} = hg_routing:choose_route(NonFailRatedRoutes),
|
{ChosenRoute, ChoiceContext} = hg_routing:choose_route(NonFailRatedRoutes),
|
||||||
ChosenPaymentRoute = hg_routing:to_payment_route(ChosenRoute),
|
ChosenPaymentRoute = hg_routing:to_payment_route(ChosenRoute),
|
||||||
_ = logger:log(info, "Routing decision made", hg_routing:get_logger_metadata(ChoiceMeta, Revision)),
|
_ = logger:log(info, "Routing decision made", hg_routing:get_logger_metadata(ChoiceContext, Revision)),
|
||||||
RecPaymentTool2 = set_minimal_payment_cost(RecPaymentTool, ChosenPaymentRoute, VS, Revision),
|
RecPaymentTool2 = set_minimal_payment_cost(RecPaymentTool, ChosenPaymentRoute, VS, Revision),
|
||||||
{ok, {Changes, Action}} = start_session(),
|
{ok, {Changes, Action}} = start_session(),
|
||||||
StartChanges = [
|
StartChanges = [
|
||||||
@ -258,23 +258,25 @@ init(EncodedParams, #{id := RecPaymentToolID}) ->
|
|||||||
throw:risk_score_is_too_high = Error ->
|
throw:risk_score_is_too_high = Error ->
|
||||||
error(handle_route_error(Error, RecPaymentTool));
|
error(handle_route_error(Error, RecPaymentTool));
|
||||||
throw:{no_route_found, {unknown, _}} = Error ->
|
throw:{no_route_found, {unknown, _}} = Error ->
|
||||||
error(handle_route_error(Error, RecPaymentTool))
|
error(handle_route_error(Error, RecPaymentTool, VS1))
|
||||||
end.
|
end.
|
||||||
|
|
||||||
gather_routes(PaymentInstitution, VS, Revision) ->
|
gather_routes(PaymentInstitution, VS, Revision) ->
|
||||||
Predestination = recurrent_paytool,
|
Predestination = recurrent_paytool,
|
||||||
case
|
case
|
||||||
hg_routing_rule:gather_routes(
|
hg_routing:gather_routes(
|
||||||
Predestination,
|
Predestination,
|
||||||
PaymentInstitution,
|
PaymentInstitution,
|
||||||
VS,
|
VS,
|
||||||
Revision
|
Revision
|
||||||
)
|
)
|
||||||
of
|
of
|
||||||
{[], RejectContext} ->
|
{ok, {[], RejectedRoutes}} ->
|
||||||
throw({no_route_found, {unknown, RejectContext}});
|
throw({no_route_found, {unknown, RejectedRoutes}});
|
||||||
{Routes, _RejectContext} ->
|
{ok, {Routes, _RejectContext}} ->
|
||||||
Routes
|
Routes;
|
||||||
|
{error, {misconfiguration, _Reason}} ->
|
||||||
|
throw({no_route_found, misconfiguration})
|
||||||
end.
|
end.
|
||||||
|
|
||||||
%% TODO uncomment after inspect will implement
|
%% TODO uncomment after inspect will implement
|
||||||
@ -361,8 +363,8 @@ validate_risk_score(RiskScore) when RiskScore == low; RiskScore == high ->
|
|||||||
|
|
||||||
handle_route_error(risk_score_is_too_high = Reason, RecPaymentTool) ->
|
handle_route_error(risk_score_is_too_high = Reason, RecPaymentTool) ->
|
||||||
_ = logger:log(info, "No route found, reason = ~p", [Reason], logger:get_process_metadata()),
|
_ = logger:log(info, "No route found, reason = ~p", [Reason], logger:get_process_metadata()),
|
||||||
{misconfiguration, {'No route found for a recurrent payment tool', RecPaymentTool}};
|
{misconfiguration, {'No route found for a recurrent payment tool', RecPaymentTool}}.
|
||||||
handle_route_error({no_route_found, {Reason, RejectContext}}, RecPaymentTool) ->
|
handle_route_error({no_route_found, {Reason, RejectedRoutes}}, RecPaymentTool, Varset) ->
|
||||||
LogFun = fun(Msg, Param) ->
|
LogFun = fun(Msg, Param) ->
|
||||||
_ = logger:log(
|
_ = logger:log(
|
||||||
error,
|
error,
|
||||||
@ -371,9 +373,8 @@ handle_route_error({no_route_found, {Reason, RejectContext}}, RecPaymentTool) ->
|
|||||||
logger:get_process_metadata()
|
logger:get_process_metadata()
|
||||||
)
|
)
|
||||||
end,
|
end,
|
||||||
_ = LogFun("No route found, reason = ~p, varset: ~p", maps:get(varset, RejectContext)),
|
_ = LogFun("No route found, reason = ~p, varset: ~p", Varset),
|
||||||
_ = LogFun("No route found, reason = ~p, rejected providers: ~p", maps:get(rejected_providers, RejectContext)),
|
_ = LogFun("No route found, reason = ~p, rejected routes: ~p", RejectedRoutes),
|
||||||
_ = LogFun("No route found, reason = ~p, rejected routes: ~p", maps:get(rejected_routes, RejectContext)),
|
|
||||||
{misconfiguration, {'No route found for a recurrent payment tool', RecPaymentTool}}.
|
{misconfiguration, {'No route found for a recurrent payment tool', RecPaymentTool}}.
|
||||||
|
|
||||||
start_session() ->
|
start_session() ->
|
||||||
|
@ -6,13 +6,9 @@
|
|||||||
-include_lib("damsel/include/dmsl_payment_processing_thrift.hrl").
|
-include_lib("damsel/include/dmsl_payment_processing_thrift.hrl").
|
||||||
-include_lib("fault_detector_proto/include/fd_proto_fault_detector_thrift.hrl").
|
-include_lib("fault_detector_proto/include/fd_proto_fault_detector_thrift.hrl").
|
||||||
|
|
||||||
-export([gather_fail_rates/1]).
|
-export([gather_routes/4]).
|
||||||
-export([choose_route/1]).
|
-export([choose_route/1]).
|
||||||
-export([choose_rated_route/1]).
|
-export([get_payment_terms/3]).
|
||||||
|
|
||||||
-export([get_payments_terms/2]).
|
|
||||||
|
|
||||||
-export([acceptable_terminal/5]).
|
|
||||||
|
|
||||||
-export([marshal/1]).
|
-export([marshal/1]).
|
||||||
-export([unmarshal/1]).
|
-export([unmarshal/1]).
|
||||||
@ -20,39 +16,41 @@
|
|||||||
-export([get_logger_metadata/2]).
|
-export([get_logger_metadata/2]).
|
||||||
|
|
||||||
-export([from_payment_route/1]).
|
-export([from_payment_route/1]).
|
||||||
|
-export([new/2]).
|
||||||
-export([new/4]).
|
-export([new/4]).
|
||||||
-export([to_payment_route/1]).
|
-export([to_payment_route/1]).
|
||||||
-export([provider_ref/1]).
|
-export([provider_ref/1]).
|
||||||
-export([terminal_ref/1]).
|
-export([terminal_ref/1]).
|
||||||
|
|
||||||
|
-export([prepare_log_message/1]).
|
||||||
|
%% Using in ct
|
||||||
|
-export([gather_fail_rates/1]).
|
||||||
|
-export([choose_rated_route/1]).
|
||||||
%%
|
%%
|
||||||
|
|
||||||
-include("domain.hrl").
|
-include("domain.hrl").
|
||||||
|
|
||||||
-type terms() ::
|
-record(route, {
|
||||||
dmsl_domain_thrift:'PaymentsProvisionTerms'()
|
provider_ref :: dmsl_domain_thrift:'ProviderRef'(),
|
||||||
| dmsl_domain_thrift:'RecurrentPaytoolsProvisionTerms'()
|
terminal_ref :: dmsl_domain_thrift:'TerminalRef'(),
|
||||||
| undefined.
|
weight :: integer(),
|
||||||
|
priority :: integer()
|
||||||
|
}).
|
||||||
|
|
||||||
|
-type route() :: #route{}.
|
||||||
|
-type payment_terms() :: dmsl_domain_thrift:'PaymentsProvisionTerms'().
|
||||||
|
-type payment_institution() :: dmsl_domain_thrift:'PaymentInstitution'().
|
||||||
-type payment_route() :: dmsl_domain_thrift:'PaymentRoute'().
|
-type payment_route() :: dmsl_domain_thrift:'PaymentRoute'().
|
||||||
-type route_predestination() :: payment | recurrent_paytool | recurrent_payment.
|
-type route_predestination() :: payment | recurrent_paytool | recurrent_payment.
|
||||||
|
|
||||||
-define(rejected(Reason), {rejected, Reason}).
|
-define(rejected(Reason), {rejected, Reason}).
|
||||||
|
|
||||||
-type reject_context() :: #{
|
|
||||||
varset := varset(),
|
|
||||||
rejected_providers := list(rejected_provider()),
|
|
||||||
rejected_routes := list(rejected_route())
|
|
||||||
}.
|
|
||||||
|
|
||||||
-type rejected_provider() :: {provider_ref(), Reason :: term()}.
|
|
||||||
-type rejected_route() :: {provider_ref(), terminal_ref(), Reason :: term()}.
|
-type rejected_route() :: {provider_ref(), terminal_ref(), Reason :: term()}.
|
||||||
|
|
||||||
-type provider_ref() :: dmsl_domain_thrift:'ProviderRef'().
|
-type provider_ref() :: dmsl_domain_thrift:'ProviderRef'().
|
||||||
-type terminal() :: dmsl_domain_thrift:'Terminal'().
|
|
||||||
-type terminal_ref() :: dmsl_domain_thrift:'TerminalRef'().
|
-type terminal_ref() :: dmsl_domain_thrift:'TerminalRef'().
|
||||||
|
|
||||||
-type fd_service_stats() :: fd_proto_fault_detector_thrift:'ServiceStatistics'().
|
-type fd_service_stats() :: fd_proto_fault_detector_thrift:'ServiceStatistics'().
|
||||||
-type unweighted_terminal() :: {terminal_ref(), terminal()}.
|
|
||||||
|
|
||||||
-type terminal_priority_rating() :: integer().
|
-type terminal_priority_rating() :: integer().
|
||||||
|
|
||||||
@ -71,38 +69,19 @@
|
|||||||
|
|
||||||
-type route_groups_by_priority() :: #{{availability_condition(), terminal_priority_rating()} => [fail_rated_route()]}.
|
-type route_groups_by_priority() :: #{{availability_condition(), terminal_priority_rating()} => [fail_rated_route()]}.
|
||||||
|
|
||||||
-type route() :: #{
|
|
||||||
provider_ref := dmsl_domain_thrift:'ProviderRef'(),
|
|
||||||
terminal_ref := dmsl_domain_thrift:'TerminalRef'(),
|
|
||||||
weight => integer(),
|
|
||||||
priority := integer()
|
|
||||||
}.
|
|
||||||
|
|
||||||
-type fail_rated_route() :: {route(), provider_status()}.
|
-type fail_rated_route() :: {route(), provider_status()}.
|
||||||
|
|
||||||
-type scored_route() :: {route_scores(), route()}.
|
-type scored_route() :: {route_scores(), route()}.
|
||||||
|
|
||||||
-type route_choice_meta() :: #{
|
-type route_choice_context() :: #{
|
||||||
chosen_route => route(),
|
chosen_route => route(),
|
||||||
preferable_route => route(),
|
preferable_route => route(),
|
||||||
% Contains one of the field names defined in #route_scores{}
|
% Contains one of the field names defined in #route_scores{}
|
||||||
reject_reason => atom()
|
reject_reason => atom()
|
||||||
}.
|
}.
|
||||||
|
|
||||||
-type varset() :: #{
|
-type varset() :: hg_varset:varset().
|
||||||
category => dmsl_domain_thrift:'CategoryRef'(),
|
-type revision() :: hg_domain:revision().
|
||||||
currency => dmsl_domain_thrift:'CurrencyRef'(),
|
|
||||||
cost => dmsl_domain_thrift:'Cash'(),
|
|
||||||
payment_tool => dmsl_domain_thrift:'PaymentTool'(),
|
|
||||||
party_id => dmsl_domain_thrift:'PartyID'(),
|
|
||||||
shop_id => dmsl_domain_thrift:'ShopID'(),
|
|
||||||
risk_score => dmsl_domain_thrift:'RiskScore'(),
|
|
||||||
flow => instant | {hold, dmsl_domain_thrift:'HoldLifetime'()},
|
|
||||||
payout_method => dmsl_domain_thrift:'PayoutMethodRef'(),
|
|
||||||
wallet_id => dmsl_domain_thrift:'WalletID'(),
|
|
||||||
identification_level => dmsl_domain_thrift:'ContractorIdentificationLevel'(),
|
|
||||||
p2p_tool => dmsl_domain_thrift:'P2PTool'()
|
|
||||||
}.
|
|
||||||
|
|
||||||
-record(route_scores, {
|
-record(route_scores, {
|
||||||
availability_condition :: condition_score(),
|
availability_condition :: condition_score(),
|
||||||
@ -114,79 +93,206 @@
|
|||||||
}).
|
}).
|
||||||
|
|
||||||
-type route_scores() :: #route_scores{}.
|
-type route_scores() :: #route_scores{}.
|
||||||
|
-type misconfiguration_error() :: {misconfiguration, {routing_decisions, _} | {routing_candidate, _}}.
|
||||||
|
|
||||||
-export_type([route/0]).
|
-export_type([route/0]).
|
||||||
-export_type([route_predestination/0]).
|
-export_type([route_predestination/0]).
|
||||||
-export_type([reject_context/0]).
|
|
||||||
-export_type([varset/0]).
|
|
||||||
|
|
||||||
-define(DEFAULT_ROUTE_WEIGHT, 0).
|
%% Route accessors
|
||||||
% Set value like in protocol
|
|
||||||
% https://github.com/rbkmoney/damsel/blob/fa979b0e7e5bcf0aff7b55927689368317e0d858/proto/domain.thrift#L2814
|
|
||||||
-define(DEFAULT_ROUTE_PRIORITY, 1000).
|
|
||||||
|
|
||||||
-spec from_payment_route(payment_route()) -> route().
|
-spec new(provider_ref(), terminal_ref()) -> route().
|
||||||
from_payment_route(Route) ->
|
new(ProviderRef, TerminalRef) ->
|
||||||
?route(ProviderRef, TerminalRef) = Route,
|
#route{
|
||||||
#{
|
provider_ref = ProviderRef,
|
||||||
provider_ref => ProviderRef,
|
terminal_ref = TerminalRef,
|
||||||
terminal_ref => TerminalRef,
|
weight = ?DOMAIN_CANDIDATE_WEIGHT,
|
||||||
weight => ?DEFAULT_ROUTE_WEIGHT,
|
priority = ?DOMAIN_CANDIDATE_PRIORITY
|
||||||
priority => ?DEFAULT_ROUTE_PRIORITY
|
|
||||||
}.
|
}.
|
||||||
|
|
||||||
-spec new(provider_ref(), terminal_ref(), integer() | undefined, integer()) -> route().
|
-spec new(provider_ref(), terminal_ref(), integer() | undefined, integer()) -> route().
|
||||||
new(ProviderRef, TerminalRef, undefined, Priority) ->
|
new(ProviderRef, TerminalRef, undefined, Priority) ->
|
||||||
new(ProviderRef, TerminalRef, ?DEFAULT_ROUTE_WEIGHT, Priority);
|
new(ProviderRef, TerminalRef, ?DOMAIN_CANDIDATE_WEIGHT, Priority);
|
||||||
new(ProviderRef, TerminalRef, Weight, Priority) ->
|
new(ProviderRef, TerminalRef, Weight, Priority) ->
|
||||||
#{
|
#route{
|
||||||
provider_ref => ProviderRef,
|
provider_ref = ProviderRef,
|
||||||
terminal_ref => TerminalRef,
|
terminal_ref = TerminalRef,
|
||||||
weight => Weight,
|
weight = Weight,
|
||||||
priority => Priority
|
priority = Priority
|
||||||
}.
|
}.
|
||||||
|
|
||||||
-spec to_payment_route(route()) -> payment_route().
|
|
||||||
to_payment_route(#{provider_ref := _, terminal_ref := _} = Route) ->
|
|
||||||
?route(provider_ref(Route), terminal_ref(Route)).
|
|
||||||
|
|
||||||
-spec provider_ref(route()) -> provider_ref().
|
-spec provider_ref(route()) -> provider_ref().
|
||||||
provider_ref(#{provider_ref := Ref}) ->
|
provider_ref(#route{provider_ref = Ref}) ->
|
||||||
Ref.
|
Ref.
|
||||||
|
|
||||||
-spec terminal_ref(route()) -> terminal_ref().
|
-spec terminal_ref(route()) -> terminal_ref().
|
||||||
terminal_ref(#{terminal_ref := Ref}) ->
|
terminal_ref(#route{terminal_ref = Ref}) ->
|
||||||
Ref.
|
Ref.
|
||||||
|
|
||||||
-spec priority(route()) -> integer().
|
-spec priority(route()) -> integer().
|
||||||
priority(#{priority := Priority}) ->
|
priority(#route{priority = Priority}) ->
|
||||||
Priority.
|
Priority.
|
||||||
|
|
||||||
-spec weight(route()) -> integer().
|
-spec weight(route()) -> integer().
|
||||||
weight(Route) ->
|
weight(#route{weight = Weight}) ->
|
||||||
maps:get(weight, Route).
|
Weight.
|
||||||
|
|
||||||
|
-spec from_payment_route(payment_route()) -> route().
|
||||||
|
from_payment_route(Route) ->
|
||||||
|
?route(ProviderRef, TerminalRef) = Route,
|
||||||
|
#route{
|
||||||
|
provider_ref = ProviderRef,
|
||||||
|
terminal_ref = TerminalRef,
|
||||||
|
weight = ?DOMAIN_CANDIDATE_WEIGHT,
|
||||||
|
priority = ?DOMAIN_CANDIDATE_PRIORITY
|
||||||
|
}.
|
||||||
|
|
||||||
|
-spec to_payment_route(route()) -> payment_route().
|
||||||
|
to_payment_route(#route{} = Route) ->
|
||||||
|
?route(provider_ref(Route), terminal_ref(Route)).
|
||||||
|
|
||||||
-spec set_weight(integer(), route()) -> route().
|
-spec set_weight(integer(), route()) -> route().
|
||||||
set_weight(Weight, Route) ->
|
set_weight(Weight, Route) ->
|
||||||
Route#{weight => Weight}.
|
Route#route{weight = Weight}.
|
||||||
|
|
||||||
|
-spec prepare_log_message(misconfiguration_error()) -> {io:format(), [term()]}.
|
||||||
|
prepare_log_message({misconfiguration, {routing_decisions, Details}}) ->
|
||||||
|
{"PaymentRoutingDecisions couldn't be reduced to candidates, ~p", [Details]};
|
||||||
|
prepare_log_message({misconfiguration, {routing_candidate, Candidate}}) ->
|
||||||
|
{"PaymentRoutingCandidate couldn't be reduced, ~p", [Candidate]}.
|
||||||
|
|
||||||
|
%%
|
||||||
|
|
||||||
|
-spec gather_routes(
|
||||||
|
route_predestination(),
|
||||||
|
payment_institution(),
|
||||||
|
varset(),
|
||||||
|
revision()
|
||||||
|
) ->
|
||||||
|
{ok, {[route()], [rejected_route()]}}
|
||||||
|
| {error, misconfiguration_error()}.
|
||||||
|
gather_routes(_, #domain_PaymentInstitution{payment_routing_rules = undefined}, _, _) ->
|
||||||
|
{ok, {[], []}};
|
||||||
|
gather_routes(Predestination, #domain_PaymentInstitution{payment_routing_rules = RoutingRules}, VS, Revision) ->
|
||||||
|
#domain_RoutingRules{
|
||||||
|
policies = Policies,
|
||||||
|
prohibitions = Prohibitions
|
||||||
|
} = RoutingRules,
|
||||||
|
try
|
||||||
|
Candidates = get_candidates(Policies, VS, Revision),
|
||||||
|
{Accepted, RejectedRoutes} = filter_routes(
|
||||||
|
collect_routes(Predestination, Candidates, VS, Revision),
|
||||||
|
get_table_prohibitions(Prohibitions, VS, Revision)
|
||||||
|
),
|
||||||
|
{ok, {Accepted, RejectedRoutes}}
|
||||||
|
catch
|
||||||
|
throw:{misconfiguration, _Reason} = Error ->
|
||||||
|
{error, Error}
|
||||||
|
end.
|
||||||
|
|
||||||
|
get_table_prohibitions(Prohibitions, VS, Revision) ->
|
||||||
|
RuleSetDeny = compute_rule_set(Prohibitions, VS, Revision),
|
||||||
|
lists:foldr(
|
||||||
|
fun(#domain_RoutingCandidate{terminal = K, description = V}, AccIn) ->
|
||||||
|
AccIn#{K => V}
|
||||||
|
end,
|
||||||
|
#{},
|
||||||
|
get_decisions_candidates(RuleSetDeny)
|
||||||
|
).
|
||||||
|
|
||||||
|
get_candidates(RoutingRule, VS, Revision) ->
|
||||||
|
get_decisions_candidates(
|
||||||
|
compute_rule_set(RoutingRule, VS, Revision)
|
||||||
|
).
|
||||||
|
|
||||||
|
get_decisions_candidates(#domain_RoutingRuleset{decisions = Decisions}) ->
|
||||||
|
case Decisions of
|
||||||
|
{delegates, _Delegates} ->
|
||||||
|
throw({misconfiguration, {routing_decisions, Decisions}});
|
||||||
|
{candidates, Candidates} ->
|
||||||
|
ok = validate_decisions_candidates(Candidates),
|
||||||
|
Candidates
|
||||||
|
end.
|
||||||
|
|
||||||
|
validate_decisions_candidates([]) ->
|
||||||
|
ok;
|
||||||
|
validate_decisions_candidates([#domain_RoutingCandidate{allowed = {constant, true}} | Rest]) ->
|
||||||
|
validate_decisions_candidates(Rest);
|
||||||
|
validate_decisions_candidates([Candidate | _]) ->
|
||||||
|
throw({misconfiguration, {routing_candidate, Candidate}}).
|
||||||
|
|
||||||
|
collect_routes(Predestination, Candidates, VS, Revision) ->
|
||||||
|
lists:foldr(
|
||||||
|
fun(Candidate, {Accepted, Rejected}) ->
|
||||||
|
#domain_RoutingCandidate{
|
||||||
|
terminal = TerminalRef,
|
||||||
|
priority = Priority,
|
||||||
|
weight = Weight
|
||||||
|
} = Candidate,
|
||||||
|
% Looks like overhead, we got Terminal only for provider_ref. Maybe we can remove provider_ref from route().
|
||||||
|
% https://github.com/rbkmoney/hellgate/pull/583#discussion_r682745123
|
||||||
|
#domain_Terminal{
|
||||||
|
provider_ref = ProviderRef
|
||||||
|
} = hg_domain:get(Revision, {terminal, TerminalRef}),
|
||||||
|
try
|
||||||
|
true = acceptable_terminal(Predestination, ProviderRef, TerminalRef, VS, Revision),
|
||||||
|
Route = new(ProviderRef, TerminalRef, Weight, Priority),
|
||||||
|
{[Route | Accepted], Rejected}
|
||||||
|
catch
|
||||||
|
{rejected, Reason} ->
|
||||||
|
{Accepted, [{ProviderRef, TerminalRef, Reason} | Rejected]};
|
||||||
|
error:{misconfiguration, Reason} ->
|
||||||
|
{Accepted, [{ProviderRef, TerminalRef, {'Misconfiguration', Reason}} | Rejected]}
|
||||||
|
end
|
||||||
|
end,
|
||||||
|
{[], []},
|
||||||
|
Candidates
|
||||||
|
).
|
||||||
|
|
||||||
|
filter_routes({Routes, Rejected}, Prohibitions) ->
|
||||||
|
lists:foldr(
|
||||||
|
fun(Route, {AccIn, RejectedIn}) ->
|
||||||
|
TRef = terminal_ref(Route),
|
||||||
|
case maps:find(TRef, Prohibitions) of
|
||||||
|
error ->
|
||||||
|
{[Route | AccIn], RejectedIn};
|
||||||
|
{ok, Description} ->
|
||||||
|
PRef = provider_ref(Route),
|
||||||
|
RejectedOut = [{PRef, TRef, {'RoutingRule', Description}} | RejectedIn],
|
||||||
|
{AccIn, RejectedOut}
|
||||||
|
end
|
||||||
|
end,
|
||||||
|
{[], Rejected},
|
||||||
|
Routes
|
||||||
|
).
|
||||||
|
|
||||||
|
compute_rule_set(RuleSetRef, VS, Revision) ->
|
||||||
|
Ctx = hg_context:load(),
|
||||||
|
{ok, RuleSet} = party_client_thrift:compute_routing_ruleset(
|
||||||
|
RuleSetRef,
|
||||||
|
Revision,
|
||||||
|
hg_varset:prepare_varset(VS),
|
||||||
|
hg_context:get_party_client(Ctx),
|
||||||
|
hg_context:get_party_client_context(Ctx)
|
||||||
|
),
|
||||||
|
RuleSet.
|
||||||
|
|
||||||
-spec gather_fail_rates([route()]) -> [fail_rated_route()].
|
-spec gather_fail_rates([route()]) -> [fail_rated_route()].
|
||||||
gather_fail_rates(Routes) ->
|
gather_fail_rates(Routes) ->
|
||||||
score_routes_with_fault_detector(Routes).
|
score_routes_with_fault_detector(Routes).
|
||||||
|
|
||||||
-spec choose_route([route()]) -> {route(), route_choice_meta()}.
|
-spec choose_route([route()]) -> {route(), route_choice_context()}.
|
||||||
choose_route(Routes) ->
|
choose_route(Routes) ->
|
||||||
FailRatedRoutes = gather_fail_rates(Routes),
|
FailRatedRoutes = gather_fail_rates(Routes),
|
||||||
choose_rated_route(FailRatedRoutes).
|
choose_rated_route(FailRatedRoutes).
|
||||||
|
|
||||||
-spec choose_rated_route([fail_rated_route()]) -> {route(), route_choice_meta()}.
|
-spec choose_rated_route([fail_rated_route()]) -> {route(), route_choice_context()}.
|
||||||
choose_rated_route(FailRatedRoutes) ->
|
choose_rated_route(FailRatedRoutes) ->
|
||||||
BalancedRoutes = balance_routes(FailRatedRoutes),
|
BalancedRoutes = balance_routes(FailRatedRoutes),
|
||||||
ScoredRoutes = score_routes(BalancedRoutes),
|
ScoredRoutes = score_routes(BalancedRoutes),
|
||||||
{ChosenScoredRoute, IdealRoute} = find_best_routes(ScoredRoutes),
|
{ChosenScoredRoute, IdealRoute} = find_best_routes(ScoredRoutes),
|
||||||
RouteChoiceMeta = get_route_choice_meta(ChosenScoredRoute, IdealRoute),
|
RouteChoiceContext = get_route_choice_context(ChosenScoredRoute, IdealRoute),
|
||||||
{_, Route} = ChosenScoredRoute,
|
{_, Route} = ChosenScoredRoute,
|
||||||
{Route, RouteChoiceMeta}.
|
{Route, RouteChoiceContext}.
|
||||||
|
|
||||||
-spec find_best_routes([scored_route()]) -> {Chosen :: scored_route(), Ideal :: scored_route()}.
|
-spec find_best_routes([scored_route()]) -> {Chosen :: scored_route(), Ideal :: scored_route()}.
|
||||||
find_best_routes([Route]) ->
|
find_best_routes([Route]) ->
|
||||||
@ -224,28 +330,28 @@ set_ideal_score({RouteScores, PT}) ->
|
|||||||
PT
|
PT
|
||||||
}.
|
}.
|
||||||
|
|
||||||
get_route_choice_meta({_, SameRoute}, {_, SameRoute}) ->
|
get_route_choice_context({_, SameRoute}, {_, SameRoute}) ->
|
||||||
#{
|
#{
|
||||||
chosen_route => SameRoute
|
chosen_route => SameRoute
|
||||||
};
|
};
|
||||||
get_route_choice_meta({ChosenScores, ChosenRoute}, {IdealScores, IdealRoute}) ->
|
get_route_choice_context({ChosenScores, ChosenRoute}, {IdealScores, IdealRoute}) ->
|
||||||
#{
|
#{
|
||||||
chosen_route => ChosenRoute,
|
chosen_route => ChosenRoute,
|
||||||
preferable_route => IdealRoute,
|
preferable_route => IdealRoute,
|
||||||
reject_reason => map_route_switch_reason(ChosenScores, IdealScores)
|
reject_reason => map_route_switch_reason(ChosenScores, IdealScores)
|
||||||
}.
|
}.
|
||||||
|
|
||||||
-spec get_logger_metadata(route_choice_meta(), hg_domain:revision()) -> LoggerFormattedMetadata :: map().
|
-spec get_logger_metadata(route_choice_context(), revision()) -> LoggerFormattedMetadata :: map().
|
||||||
get_logger_metadata(RouteChoiceMeta, Revision) ->
|
get_logger_metadata(RouteChoiceContext, Revision) ->
|
||||||
#{route_choice_metadata => format_logger_metadata(RouteChoiceMeta, Revision)}.
|
#{route_choice_metadata => format_logger_metadata(RouteChoiceContext, Revision)}.
|
||||||
|
|
||||||
format_logger_metadata(RouteChoiceMeta, Revision) ->
|
format_logger_metadata(RouteChoiceContext, Revision) ->
|
||||||
maps:fold(
|
maps:fold(
|
||||||
fun(K, V, Acc) ->
|
fun(K, V, Acc) ->
|
||||||
Acc ++ format_logger_metadata(K, V, Revision)
|
Acc ++ format_logger_metadata(K, V, Revision)
|
||||||
end,
|
end,
|
||||||
[],
|
[],
|
||||||
RouteChoiceMeta
|
RouteChoiceContext
|
||||||
).
|
).
|
||||||
|
|
||||||
format_logger_metadata(reject_reason, Reason, _) ->
|
format_logger_metadata(reject_reason, Reason, _) ->
|
||||||
@ -259,9 +365,13 @@ add_route_name(Route, Revision) ->
|
|||||||
TerminalRef = terminal_ref(Route),
|
TerminalRef = terminal_ref(Route),
|
||||||
#domain_Provider{name = PName} = hg_domain:get(Revision, {provider, ProviderRef}),
|
#domain_Provider{name = PName} = hg_domain:get(Revision, {provider, ProviderRef}),
|
||||||
#domain_Terminal{name = TName} = hg_domain:get(Revision, {terminal, TerminalRef}),
|
#domain_Terminal{name = TName} = hg_domain:get(Revision, {terminal, TerminalRef}),
|
||||||
genlib_map:compact(Route#{
|
genlib_map:compact(#{
|
||||||
provider_name => PName,
|
provider_name => PName,
|
||||||
terminal_name => TName
|
terminal_name => TName,
|
||||||
|
provider_ref => ProviderRef,
|
||||||
|
terminal_ref => TerminalRef,
|
||||||
|
priority => priority(Route),
|
||||||
|
weight => weight(Route)
|
||||||
}).
|
}).
|
||||||
|
|
||||||
map_route_switch_reason(SameScores, SameScores) ->
|
map_route_switch_reason(SameScores, SameScores) ->
|
||||||
@ -418,20 +528,27 @@ build_fd_availability_service_id(#domain_ProviderRef{id = ID}) ->
|
|||||||
build_fd_conversion_service_id(#domain_ProviderRef{id = ID}) ->
|
build_fd_conversion_service_id(#domain_ProviderRef{id = ID}) ->
|
||||||
hg_fault_detector_client:build_service_id(provider_conversion, ID).
|
hg_fault_detector_client:build_service_id(provider_conversion, ID).
|
||||||
|
|
||||||
-spec get_payments_terms(payment_route(), hg_domain:revision()) -> terms().
|
-spec get_payment_terms(payment_route(), varset(), revision()) -> payment_terms() | undefined.
|
||||||
get_payments_terms(?route(ProviderRef, TerminalRef), Revision) ->
|
get_payment_terms(?route(ProviderRef, TerminalRef), VS, Revision) ->
|
||||||
#domain_Provider{terms = Terms0} = hg_domain:get(Revision, {provider, ProviderRef}),
|
PreparedVS = hg_varset:prepare_varset(VS),
|
||||||
#domain_Terminal{terms = Terms1} = hg_domain:get(Revision, {terminal, TerminalRef}),
|
{Client, Context} = get_party_client(),
|
||||||
Terms = merge_terms(Terms0, Terms1),
|
{ok, TermsSet} = party_client_thrift:compute_provider_terminal_terms(
|
||||||
Terms#domain_ProvisionTermSet.payments.
|
ProviderRef,
|
||||||
|
TerminalRef,
|
||||||
|
Revision,
|
||||||
|
PreparedVS,
|
||||||
|
Client,
|
||||||
|
Context
|
||||||
|
),
|
||||||
|
TermsSet#domain_ProvisionTermSet.payments.
|
||||||
|
|
||||||
-spec acceptable_terminal(
|
-spec acceptable_terminal(
|
||||||
route_predestination(),
|
route_predestination(),
|
||||||
provider_ref(),
|
provider_ref(),
|
||||||
terminal_ref(),
|
terminal_ref(),
|
||||||
varset(),
|
varset(),
|
||||||
hg_domain:revision()
|
revision()
|
||||||
) -> unweighted_terminal() | no_return().
|
) -> true | no_return().
|
||||||
acceptable_terminal(Predestination, ProviderRef, TerminalRef, VS, Revision) ->
|
acceptable_terminal(Predestination, ProviderRef, TerminalRef, VS, Revision) ->
|
||||||
{Client, Context} = get_party_client(),
|
{Client, Context} = get_party_client(),
|
||||||
Result = party_client_thrift:compute_provider_terminal_terms(
|
Result = party_client_thrift:compute_provider_terminal_terms(
|
||||||
@ -442,15 +559,12 @@ acceptable_terminal(Predestination, ProviderRef, TerminalRef, VS, Revision) ->
|
|||||||
Client,
|
Client,
|
||||||
Context
|
Context
|
||||||
),
|
),
|
||||||
ProvisionTermSet =
|
case Result of
|
||||||
case Result of
|
{ok, ProvisionTermSet} ->
|
||||||
{ok, Terms} ->
|
check_terms_acceptability(Predestination, ProvisionTermSet, VS);
|
||||||
Terms;
|
{error, #payproc_ProvisionTermSetUndefined{}} ->
|
||||||
{error, #payproc_ProvisionTermSetUndefined{}} ->
|
throw(?rejected({'ProvisionTermSet', undefined}))
|
||||||
undefined
|
end.
|
||||||
end,
|
|
||||||
_ = check_terms_acceptability(Predestination, ProvisionTermSet, VS),
|
|
||||||
{TerminalRef, hg_domain:get(Revision, {terminal, TerminalRef})}.
|
|
||||||
|
|
||||||
%%
|
%%
|
||||||
|
|
||||||
@ -461,35 +575,13 @@ get_party_client() ->
|
|||||||
{Client, Context}.
|
{Client, Context}.
|
||||||
|
|
||||||
check_terms_acceptability(payment, Terms, VS) ->
|
check_terms_acceptability(payment, Terms, VS) ->
|
||||||
_ = acceptable_provision_payment_terms(Terms, VS);
|
acceptable_payment_terms(Terms#domain_ProvisionTermSet.payments, VS);
|
||||||
check_terms_acceptability(recurrent_paytool, Terms, VS) ->
|
check_terms_acceptability(recurrent_paytool, Terms, VS) ->
|
||||||
_ = acceptable_provision_recurrent_terms(Terms, VS);
|
acceptable_recurrent_paytool_terms(Terms#domain_ProvisionTermSet.recurrent_paytools, VS);
|
||||||
check_terms_acceptability(recurrent_payment, Terms, VS) ->
|
check_terms_acceptability(recurrent_payment, Terms, VS) ->
|
||||||
% Use provider check combined from recurrent_paytool and payment check
|
% Use provider check combined from recurrent_paytool and payment check
|
||||||
_ = acceptable_provision_payment_terms(Terms, VS),
|
_ = acceptable_payment_terms(Terms#domain_ProvisionTermSet.payments, VS),
|
||||||
_ = acceptable_provision_recurrent_terms(Terms, VS).
|
acceptable_recurrent_paytool_terms(Terms#domain_ProvisionTermSet.recurrent_paytools, VS).
|
||||||
|
|
||||||
acceptable_provision_payment_terms(
|
|
||||||
#domain_ProvisionTermSet{
|
|
||||||
payments = PaymentTerms
|
|
||||||
},
|
|
||||||
VS
|
|
||||||
) ->
|
|
||||||
_ = acceptable_payment_terms(PaymentTerms, VS),
|
|
||||||
true;
|
|
||||||
acceptable_provision_payment_terms(undefined, _VS) ->
|
|
||||||
throw(?rejected({'ProvisionTermSet', undefined})).
|
|
||||||
|
|
||||||
acceptable_provision_recurrent_terms(
|
|
||||||
#domain_ProvisionTermSet{
|
|
||||||
recurrent_paytools = RecurrentTerms
|
|
||||||
},
|
|
||||||
VS
|
|
||||||
) ->
|
|
||||||
_ = acceptable_recurrent_paytool_terms(RecurrentTerms, VS),
|
|
||||||
true;
|
|
||||||
acceptable_provision_recurrent_terms(undefined, _VS) ->
|
|
||||||
throw(?rejected({'ProvisionTermSet', undefined})).
|
|
||||||
|
|
||||||
acceptable_payment_terms(
|
acceptable_payment_terms(
|
||||||
#domain_PaymentsProvisionTerms{
|
#domain_PaymentsProvisionTerms{
|
||||||
@ -568,63 +660,6 @@ acceptable_partial_refunds_terms(
|
|||||||
acceptable_partial_refunds_terms(undefined, _RVS) ->
|
acceptable_partial_refunds_terms(undefined, _RVS) ->
|
||||||
throw(?rejected({'PartialRefundsProvisionTerms', undefined})).
|
throw(?rejected({'PartialRefundsProvisionTerms', undefined})).
|
||||||
|
|
||||||
merge_terms(
|
|
||||||
#domain_ProvisionTermSet{
|
|
||||||
payments = PPayments,
|
|
||||||
recurrent_paytools = PRecurrents
|
|
||||||
},
|
|
||||||
#domain_ProvisionTermSet{
|
|
||||||
payments = TPayments,
|
|
||||||
% TODO: Allow to define recurrent terms in terminal
|
|
||||||
recurrent_paytools = _TRecurrents
|
|
||||||
}
|
|
||||||
) ->
|
|
||||||
#domain_ProvisionTermSet{
|
|
||||||
payments = merge_payment_terms(PPayments, TPayments),
|
|
||||||
recurrent_paytools = PRecurrents
|
|
||||||
};
|
|
||||||
merge_terms(ProviderTerms, TerminalTerms) ->
|
|
||||||
hg_utils:select_defined(TerminalTerms, ProviderTerms).
|
|
||||||
|
|
||||||
-spec merge_payment_terms(terms(), terms()) -> terms().
|
|
||||||
merge_payment_terms(
|
|
||||||
#domain_PaymentsProvisionTerms{
|
|
||||||
currencies = PCurrencies,
|
|
||||||
categories = PCategories,
|
|
||||||
payment_methods = PPaymentMethods,
|
|
||||||
cash_limit = PCashLimit,
|
|
||||||
cash_flow = PCashflow,
|
|
||||||
holds = PHolds,
|
|
||||||
refunds = PRefunds,
|
|
||||||
chargebacks = PChargebacks,
|
|
||||||
risk_coverage = PRiskCoverage
|
|
||||||
},
|
|
||||||
#domain_PaymentsProvisionTerms{
|
|
||||||
currencies = TCurrencies,
|
|
||||||
categories = TCategories,
|
|
||||||
payment_methods = TPaymentMethods,
|
|
||||||
cash_limit = TCashLimit,
|
|
||||||
cash_flow = TCashflow,
|
|
||||||
holds = THolds,
|
|
||||||
refunds = TRefunds,
|
|
||||||
chargebacks = TChargebacks,
|
|
||||||
risk_coverage = TRiskCoverage
|
|
||||||
}
|
|
||||||
) ->
|
|
||||||
#domain_PaymentsProvisionTerms{
|
|
||||||
currencies = hg_utils:select_defined(TCurrencies, PCurrencies),
|
|
||||||
categories = hg_utils:select_defined(TCategories, PCategories),
|
|
||||||
payment_methods = hg_utils:select_defined(TPaymentMethods, PPaymentMethods),
|
|
||||||
cash_limit = hg_utils:select_defined(TCashLimit, PCashLimit),
|
|
||||||
cash_flow = hg_utils:select_defined(TCashflow, PCashflow),
|
|
||||||
holds = hg_utils:select_defined(THolds, PHolds),
|
|
||||||
refunds = hg_utils:select_defined(TRefunds, PRefunds),
|
|
||||||
chargebacks = hg_utils:select_defined(TChargebacks, PChargebacks),
|
|
||||||
risk_coverage = hg_utils:select_defined(TRiskCoverage, PRiskCoverage)
|
|
||||||
};
|
|
||||||
merge_payment_terms(ProviderTerms, TerminalTerms) ->
|
|
||||||
hg_utils:select_defined(TerminalTerms, ProviderTerms).
|
|
||||||
|
|
||||||
%%
|
%%
|
||||||
|
|
||||||
acceptable_recurrent_paytool_terms(
|
acceptable_recurrent_paytool_terms(
|
||||||
@ -771,33 +806,33 @@ record_comparsion_test() ->
|
|||||||
balance_routes_test() ->
|
balance_routes_test() ->
|
||||||
Status = {{alive, 0.0}, {normal, 0.0}},
|
Status = {{alive, 0.0}, {normal, 0.0}},
|
||||||
WithWeight = [
|
WithWeight = [
|
||||||
{new(?prv(1), ?trm(1), 1, ?DEFAULT_ROUTE_PRIORITY), Status},
|
{new(?prv(1), ?trm(1), 1, ?DOMAIN_CANDIDATE_PRIORITY), Status},
|
||||||
{new(?prv(2), ?trm(1), 2, ?DEFAULT_ROUTE_PRIORITY), Status},
|
{new(?prv(2), ?trm(1), 2, ?DOMAIN_CANDIDATE_PRIORITY), Status},
|
||||||
{new(?prv(3), ?trm(1), 0, ?DEFAULT_ROUTE_PRIORITY), Status},
|
{new(?prv(3), ?trm(1), 0, ?DOMAIN_CANDIDATE_PRIORITY), Status},
|
||||||
{new(?prv(4), ?trm(1), 1, ?DEFAULT_ROUTE_PRIORITY), Status},
|
{new(?prv(4), ?trm(1), 1, ?DOMAIN_CANDIDATE_PRIORITY), Status},
|
||||||
{new(?prv(5), ?trm(1), 0, ?DEFAULT_ROUTE_PRIORITY), Status}
|
{new(?prv(5), ?trm(1), 0, ?DOMAIN_CANDIDATE_PRIORITY), Status}
|
||||||
],
|
],
|
||||||
|
|
||||||
Result1 = [
|
Result1 = [
|
||||||
{new(?prv(1), ?trm(1), 1, ?DEFAULT_ROUTE_PRIORITY), Status},
|
{new(?prv(1), ?trm(1), 1, ?DOMAIN_CANDIDATE_PRIORITY), Status},
|
||||||
{new(?prv(2), ?trm(1), 0, ?DEFAULT_ROUTE_PRIORITY), Status},
|
{new(?prv(2), ?trm(1), 0, ?DOMAIN_CANDIDATE_PRIORITY), Status},
|
||||||
{new(?prv(3), ?trm(1), 0, ?DEFAULT_ROUTE_PRIORITY), Status},
|
{new(?prv(3), ?trm(1), 0, ?DOMAIN_CANDIDATE_PRIORITY), Status},
|
||||||
{new(?prv(4), ?trm(1), 0, ?DEFAULT_ROUTE_PRIORITY), Status},
|
{new(?prv(4), ?trm(1), 0, ?DOMAIN_CANDIDATE_PRIORITY), Status},
|
||||||
{new(?prv(5), ?trm(1), 0, ?DEFAULT_ROUTE_PRIORITY), Status}
|
{new(?prv(5), ?trm(1), 0, ?DOMAIN_CANDIDATE_PRIORITY), Status}
|
||||||
],
|
],
|
||||||
Result2 = [
|
Result2 = [
|
||||||
{new(?prv(1), ?trm(1), 0, ?DEFAULT_ROUTE_PRIORITY), Status},
|
{new(?prv(1), ?trm(1), 0, ?DOMAIN_CANDIDATE_PRIORITY), Status},
|
||||||
{new(?prv(2), ?trm(1), 1, ?DEFAULT_ROUTE_PRIORITY), Status},
|
{new(?prv(2), ?trm(1), 1, ?DOMAIN_CANDIDATE_PRIORITY), Status},
|
||||||
{new(?prv(3), ?trm(1), 0, ?DEFAULT_ROUTE_PRIORITY), Status},
|
{new(?prv(3), ?trm(1), 0, ?DOMAIN_CANDIDATE_PRIORITY), Status},
|
||||||
{new(?prv(4), ?trm(1), 0, ?DEFAULT_ROUTE_PRIORITY), Status},
|
{new(?prv(4), ?trm(1), 0, ?DOMAIN_CANDIDATE_PRIORITY), Status},
|
||||||
{new(?prv(5), ?trm(1), 0, ?DEFAULT_ROUTE_PRIORITY), Status}
|
{new(?prv(5), ?trm(1), 0, ?DOMAIN_CANDIDATE_PRIORITY), Status}
|
||||||
],
|
],
|
||||||
Result3 = [
|
Result3 = [
|
||||||
{new(?prv(1), ?trm(1), 0, ?DEFAULT_ROUTE_PRIORITY), Status},
|
{new(?prv(1), ?trm(1), 0, ?DOMAIN_CANDIDATE_PRIORITY), Status},
|
||||||
{new(?prv(2), ?trm(1), 0, ?DEFAULT_ROUTE_PRIORITY), Status},
|
{new(?prv(2), ?trm(1), 0, ?DOMAIN_CANDIDATE_PRIORITY), Status},
|
||||||
{new(?prv(3), ?trm(1), 0, ?DEFAULT_ROUTE_PRIORITY), Status},
|
{new(?prv(3), ?trm(1), 0, ?DOMAIN_CANDIDATE_PRIORITY), Status},
|
||||||
{new(?prv(4), ?trm(1), 0, ?DEFAULT_ROUTE_PRIORITY), Status},
|
{new(?prv(4), ?trm(1), 0, ?DOMAIN_CANDIDATE_PRIORITY), Status},
|
||||||
{new(?prv(5), ?trm(1), 0, ?DEFAULT_ROUTE_PRIORITY), Status}
|
{new(?prv(5), ?trm(1), 0, ?DOMAIN_CANDIDATE_PRIORITY), Status}
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
?assertEqual(Result1, lists:reverse(calc_random_condition(0.0, 0.2, WithWeight, []))),
|
?assertEqual(Result1, lists:reverse(calc_random_condition(0.0, 0.2, WithWeight, []))),
|
||||||
@ -809,12 +844,12 @@ balance_routes_test() ->
|
|||||||
balance_routes_with_default_weight_test() ->
|
balance_routes_with_default_weight_test() ->
|
||||||
Status = {{alive, 0.0}, {normal, 0.0}},
|
Status = {{alive, 0.0}, {normal, 0.0}},
|
||||||
Routes = [
|
Routes = [
|
||||||
{new(?prv(1), ?trm(1), 0, ?DEFAULT_ROUTE_PRIORITY), Status},
|
{new(?prv(1), ?trm(1), 0, ?DOMAIN_CANDIDATE_PRIORITY), Status},
|
||||||
{new(?prv(2), ?trm(1), 0, ?DEFAULT_ROUTE_PRIORITY), Status}
|
{new(?prv(2), ?trm(1), 0, ?DOMAIN_CANDIDATE_PRIORITY), Status}
|
||||||
],
|
],
|
||||||
Result = [
|
Result = [
|
||||||
{new(?prv(1), ?trm(1), 0, ?DEFAULT_ROUTE_PRIORITY), Status},
|
{new(?prv(1), ?trm(1), 0, ?DOMAIN_CANDIDATE_PRIORITY), Status},
|
||||||
{new(?prv(2), ?trm(1), 0, ?DEFAULT_ROUTE_PRIORITY), Status}
|
{new(?prv(2), ?trm(1), 0, ?DOMAIN_CANDIDATE_PRIORITY), Status}
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
?assertEqual(Result, set_routes_random_condition(Routes))
|
?assertEqual(Result, set_routes_random_condition(Routes))
|
||||||
|
@ -1,133 +0,0 @@
|
|||||||
-module(hg_routing_rule).
|
|
||||||
|
|
||||||
-include_lib("damsel/include/dmsl_domain_thrift.hrl").
|
|
||||||
|
|
||||||
-export([gather_routes/4]).
|
|
||||||
|
|
||||||
%%
|
|
||||||
-type route_predestination() :: hg_routing:route_predestination().
|
|
||||||
-type payment_institution() :: dmsl_domain_thrift:'PaymentInstitution'().
|
|
||||||
-type route() :: hg_routing:route().
|
|
||||||
-type reject_context() :: hg_routing:reject_context().
|
|
||||||
|
|
||||||
-spec gather_routes(
|
|
||||||
route_predestination(),
|
|
||||||
payment_institution(),
|
|
||||||
hg_routing:varset(),
|
|
||||||
hg_domain:revision()
|
|
||||||
) -> {[route()], reject_context()}.
|
|
||||||
gather_routes(_, #domain_PaymentInstitution{payment_routing_rules = undefined} = PayInst, VS, _) ->
|
|
||||||
logger:log(
|
|
||||||
warning,
|
|
||||||
"Payment routing rules is undefined, PaymentInstitution: ~p",
|
|
||||||
[PayInst]
|
|
||||||
),
|
|
||||||
{[], #{varset => VS, rejected_providers => [], rejected_routes => []}};
|
|
||||||
gather_routes(Predestination, #domain_PaymentInstitution{payment_routing_rules = RoutingRules}, VS, Revision) ->
|
|
||||||
RejectedContext = #{
|
|
||||||
varset => VS,
|
|
||||||
rejected_providers => [],
|
|
||||||
rejected_routes => []
|
|
||||||
},
|
|
||||||
#domain_RoutingRules{
|
|
||||||
policies = Policies,
|
|
||||||
prohibitions = Prohibitions
|
|
||||||
} = RoutingRules,
|
|
||||||
try
|
|
||||||
Candidates = get_candidates(Policies, VS, Revision),
|
|
||||||
{Accepted, RejectedRoutes} = filter_routes(
|
|
||||||
collect_routes(Predestination, Candidates, VS, Revision),
|
|
||||||
get_table_prohibitions(Prohibitions, VS, Revision)
|
|
||||||
),
|
|
||||||
{Accepted, RejectedContext#{rejected_routes => RejectedRoutes}}
|
|
||||||
catch
|
|
||||||
error:{misconfiguration, Reason} ->
|
|
||||||
{[], RejectedContext#{error => Reason}}
|
|
||||||
end.
|
|
||||||
|
|
||||||
get_table_prohibitions(Prohibitions, VS, Revision) ->
|
|
||||||
RuleSetDeny = compute_rule_set(Prohibitions, VS, Revision),
|
|
||||||
lists:foldr(
|
|
||||||
fun(#domain_RoutingCandidate{terminal = K, description = V}, AccIn) ->
|
|
||||||
AccIn#{K => V}
|
|
||||||
end,
|
|
||||||
#{},
|
|
||||||
get_decisions_candidates(RuleSetDeny)
|
|
||||||
).
|
|
||||||
|
|
||||||
get_candidates(RoutingRule, VS, Revision) ->
|
|
||||||
get_decisions_candidates(
|
|
||||||
compute_rule_set(RoutingRule, VS, Revision)
|
|
||||||
).
|
|
||||||
|
|
||||||
get_decisions_candidates(#domain_RoutingRuleset{decisions = Decisions}) ->
|
|
||||||
case Decisions of
|
|
||||||
{delegates, _Delegates} ->
|
|
||||||
error({misconfiguration, {'PaymentRoutingDecisions couldn\'t be reduced to candidates', Decisions}});
|
|
||||||
{candidates, Candidates} ->
|
|
||||||
ok = validate_decisions_candidates(Candidates),
|
|
||||||
Candidates
|
|
||||||
end.
|
|
||||||
|
|
||||||
validate_decisions_candidates([]) ->
|
|
||||||
ok;
|
|
||||||
validate_decisions_candidates([#domain_RoutingCandidate{allowed = {constant, true}} | Rest]) ->
|
|
||||||
validate_decisions_candidates(Rest);
|
|
||||||
validate_decisions_candidates([Candidate | _]) ->
|
|
||||||
error({misconfiguration, {'PaymentRoutingCandidate couldn\'t be reduced', Candidate}}).
|
|
||||||
|
|
||||||
collect_routes(Predestination, Candidates, VS, Revision) ->
|
|
||||||
lists:foldr(
|
|
||||||
fun(Candidate, {Accepted, Rejected}) ->
|
|
||||||
#domain_RoutingCandidate{
|
|
||||||
terminal = TerminalRef,
|
|
||||||
priority = Priority,
|
|
||||||
weight = Weight
|
|
||||||
} = Candidate,
|
|
||||||
% Looks like overhead, we got Terminal only for provider_ref. Maybe we can remove provider_ref from route().
|
|
||||||
% https://github.com/rbkmoney/hellgate/pull/583#discussion_r682745123
|
|
||||||
#domain_Terminal{
|
|
||||||
provider_ref = ProviderRef
|
|
||||||
} = hg_domain:get(Revision, {terminal, TerminalRef}),
|
|
||||||
try
|
|
||||||
{_, _Terminal} = hg_routing:acceptable_terminal(Predestination, ProviderRef, TerminalRef, VS, Revision),
|
|
||||||
Route = hg_routing:new(ProviderRef, TerminalRef, Weight, Priority),
|
|
||||||
{[Route | Accepted], Rejected}
|
|
||||||
catch
|
|
||||||
{rejected, Reason} ->
|
|
||||||
{Accepted, [{ProviderRef, TerminalRef, Reason} | Rejected]};
|
|
||||||
error:{misconfiguration, Reason} ->
|
|
||||||
{Accepted, [{ProviderRef, TerminalRef, {'Misconfiguration', Reason}} | Rejected]}
|
|
||||||
end
|
|
||||||
end,
|
|
||||||
{[], []},
|
|
||||||
Candidates
|
|
||||||
).
|
|
||||||
|
|
||||||
filter_routes({Routes, Rejected}, Prohibitions) ->
|
|
||||||
lists:foldr(
|
|
||||||
fun(Route, {AccIn, RejectedIn}) ->
|
|
||||||
TRef = hg_routing:terminal_ref(Route),
|
|
||||||
case maps:find(TRef, Prohibitions) of
|
|
||||||
error ->
|
|
||||||
{[Route | AccIn], RejectedIn};
|
|
||||||
{ok, Description} ->
|
|
||||||
PRef = hg_routing:provider_ref(Route),
|
|
||||||
RejectedOut = [{PRef, TRef, {'RoutingRule', Description}} | RejectedIn],
|
|
||||||
{AccIn, RejectedOut}
|
|
||||||
end
|
|
||||||
end,
|
|
||||||
{[], Rejected},
|
|
||||||
Routes
|
|
||||||
).
|
|
||||||
|
|
||||||
compute_rule_set(RuleSetRef, VS, Revision) ->
|
|
||||||
Ctx = hg_context:load(),
|
|
||||||
{ok, RuleSet} = party_client_thrift:compute_routing_ruleset(
|
|
||||||
RuleSetRef,
|
|
||||||
Revision,
|
|
||||||
hg_varset:prepare_varset(VS),
|
|
||||||
hg_context:get_party_client(Ctx),
|
|
||||||
hg_context:get_party_client_context(Ctx)
|
|
||||||
),
|
|
||||||
RuleSet.
|
|
@ -41,6 +41,7 @@
|
|||||||
-define(dummy_party_id, <<"dummy_party_id">>).
|
-define(dummy_party_id, <<"dummy_party_id">>).
|
||||||
-define(dummy_shop_id, <<"dummy_shop_id">>).
|
-define(dummy_shop_id, <<"dummy_shop_id">>).
|
||||||
-define(dummy_another_shop_id, <<"dummy_another_shop_id">>).
|
-define(dummy_another_shop_id, <<"dummy_another_shop_id">>).
|
||||||
|
-define(assert_set_equal(S1, S2), ?assertEqual(lists:sort(S1), lists:sort(S2))).
|
||||||
|
|
||||||
-spec init([]) -> {ok, {supervisor:sup_flags(), [supervisor:child_spec()]}}.
|
-spec init([]) -> {ok, {supervisor:sup_flags(), [supervisor:child_spec()]}}.
|
||||||
init([]) ->
|
init([]) ->
|
||||||
@ -184,27 +185,23 @@ no_route_found_for_payment(_C) ->
|
|||||||
Revision = hg_domain:head(),
|
Revision = hg_domain:head(),
|
||||||
PaymentInstitution = hg_domain:get(Revision, {payment_institution, ?pinst(1)}),
|
PaymentInstitution = hg_domain:get(Revision, {payment_institution, ?pinst(1)}),
|
||||||
|
|
||||||
{[], RejectContext} = hg_routing_rule:gather_routes(payment, PaymentInstitution, VS, Revision),
|
{ok, {[], RejectedRoutes}} = hg_routing:gather_routes(payment, PaymentInstitution, VS, Revision),
|
||||||
#{
|
[
|
||||||
rejected_routes := [
|
{?prv(1), ?trm(1), {'PaymentsProvisionTerms', cost}},
|
||||||
{?prv(1), ?trm(1), {'PaymentsProvisionTerms', cost}},
|
{?prv(2), ?trm(2), {'PaymentsProvisionTerms', category}},
|
||||||
{?prv(2), ?trm(2), {'PaymentsProvisionTerms', category}},
|
{?prv(3), ?trm(3), {'PaymentsProvisionTerms', payment_tool}}
|
||||||
{?prv(3), ?trm(3), {'PaymentsProvisionTerms', payment_tool}}
|
] = RejectedRoutes,
|
||||||
]
|
|
||||||
} = RejectContext,
|
|
||||||
|
|
||||||
VS1 = VS#{
|
VS1 = VS#{
|
||||||
currency => ?cur(<<"EUR">>),
|
currency => ?cur(<<"EUR">>),
|
||||||
cost => ?cash(1000, <<"EUR">>)
|
cost => ?cash(1000, <<"EUR">>)
|
||||||
},
|
},
|
||||||
{[], RejectContext1} = hg_routing_rule:gather_routes(payment, PaymentInstitution, VS1, Revision),
|
{ok, {[], RejectedRoutes1}} = hg_routing:gather_routes(payment, PaymentInstitution, VS1, Revision),
|
||||||
#{
|
[
|
||||||
rejected_routes := [
|
{?prv(1), ?trm(1), {'PaymentsProvisionTerms', currency}},
|
||||||
{?prv(1), ?trm(1), {'PaymentsProvisionTerms', currency}},
|
{?prv(2), ?trm(2), {'PaymentsProvisionTerms', category}},
|
||||||
{?prv(2), ?trm(2), {'PaymentsProvisionTerms', category}},
|
{?prv(3), ?trm(3), {'PaymentsProvisionTerms', payment_tool}}
|
||||||
{?prv(3), ?trm(3), {'PaymentsProvisionTerms', payment_tool}}
|
] = RejectedRoutes1.
|
||||||
]
|
|
||||||
} = RejectContext1.
|
|
||||||
|
|
||||||
-spec gather_route_success(config()) -> test_return().
|
-spec gather_route_success(config()) -> test_return().
|
||||||
gather_route_success(_C) ->
|
gather_route_success(_C) ->
|
||||||
@ -220,18 +217,20 @@ gather_route_success(_C) ->
|
|||||||
|
|
||||||
Revision = hg_domain:head(),
|
Revision = hg_domain:head(),
|
||||||
PaymentInstitution = hg_domain:get(Revision, {payment_institution, ?pinst(1)}),
|
PaymentInstitution = hg_domain:get(Revision, {payment_institution, ?pinst(1)}),
|
||||||
{[#{terminal_ref := ?trm(1)}], RejectContext} = hg_routing_rule:gather_routes(
|
{ok, {[Route], RejectedRoutes}} = hg_routing:gather_routes(
|
||||||
payment,
|
payment,
|
||||||
PaymentInstitution,
|
PaymentInstitution,
|
||||||
VS,
|
VS,
|
||||||
Revision
|
Revision
|
||||||
),
|
),
|
||||||
#{
|
?assertMatch(?trm(1), hg_routing:terminal_ref(Route)),
|
||||||
rejected_routes := [
|
?assertMatch(
|
||||||
|
[
|
||||||
{?prv(2), ?trm(2), {'PaymentsProvisionTerms', category}},
|
{?prv(2), ?trm(2), {'PaymentsProvisionTerms', category}},
|
||||||
{?prv(3), ?trm(3), {'PaymentsProvisionTerms', payment_tool}}
|
{?prv(3), ?trm(3), {'PaymentsProvisionTerms', payment_tool}}
|
||||||
]
|
],
|
||||||
} = RejectContext.
|
RejectedRoutes
|
||||||
|
).
|
||||||
|
|
||||||
-spec rejected_by_table_prohibitions(config()) -> test_return().
|
-spec rejected_by_table_prohibitions(config()) -> test_return().
|
||||||
rejected_by_table_prohibitions(_C) ->
|
rejected_by_table_prohibitions(_C) ->
|
||||||
@ -254,15 +253,12 @@ rejected_by_table_prohibitions(_C) ->
|
|||||||
Revision = hg_domain:head(),
|
Revision = hg_domain:head(),
|
||||||
PaymentInstitution = hg_domain:get(Revision, {payment_institution, ?pinst(1)}),
|
PaymentInstitution = hg_domain:get(Revision, {payment_institution, ?pinst(1)}),
|
||||||
|
|
||||||
{[], RejectContext} = hg_routing_rule:gather_routes(payment, PaymentInstitution, VS, Revision),
|
{ok, {[], RejectedRoutes}} = hg_routing:gather_routes(payment, PaymentInstitution, VS, Revision),
|
||||||
|
[
|
||||||
#{
|
{?prv(3), ?trm(3), {'RoutingRule', undefined}},
|
||||||
rejected_routes := [
|
{?prv(1), ?trm(1), {'PaymentsProvisionTerms', payment_tool}},
|
||||||
{?prv(3), ?trm(3), {'RoutingRule', undefined}},
|
{?prv(2), ?trm(2), {'PaymentsProvisionTerms', category}}
|
||||||
{?prv(1), ?trm(1), {'PaymentsProvisionTerms', payment_tool}},
|
] = RejectedRoutes,
|
||||||
{?prv(2), ?trm(2), {'PaymentsProvisionTerms', category}}
|
|
||||||
]
|
|
||||||
} = RejectContext,
|
|
||||||
ok.
|
ok.
|
||||||
|
|
||||||
-spec empty_candidate_ok(config()) -> test_return().
|
-spec empty_candidate_ok(config()) -> test_return().
|
||||||
@ -284,11 +280,7 @@ empty_candidate_ok(_C) ->
|
|||||||
|
|
||||||
Revision = hg_domain:head(),
|
Revision = hg_domain:head(),
|
||||||
PaymentInstitution = hg_domain:get(Revision, {payment_institution, ?pinst(2)}),
|
PaymentInstitution = hg_domain:get(Revision, {payment_institution, ?pinst(2)}),
|
||||||
{[], #{
|
{ok, {[], []}} = hg_routing:gather_routes(payment, PaymentInstitution, VS, Revision).
|
||||||
varset := VS,
|
|
||||||
rejected_routes := [],
|
|
||||||
rejected_providers := []
|
|
||||||
}} = hg_routing_rule:gather_routes(payment, PaymentInstitution, VS, Revision).
|
|
||||||
|
|
||||||
-spec ruleset_misconfig(config()) -> test_return().
|
-spec ruleset_misconfig(config()) -> test_return().
|
||||||
ruleset_misconfig(_C) ->
|
ruleset_misconfig(_C) ->
|
||||||
@ -300,21 +292,22 @@ ruleset_misconfig(_C) ->
|
|||||||
Revision = hg_domain:head(),
|
Revision = hg_domain:head(),
|
||||||
PaymentInstitution = hg_domain:get(Revision, {payment_institution, ?pinst(1)}),
|
PaymentInstitution = hg_domain:get(Revision, {payment_institution, ?pinst(1)}),
|
||||||
|
|
||||||
{[], #{
|
{error, {misconfiguration, {routing_decisions, {delegates, []}}}} = hg_routing:gather_routes(
|
||||||
varset := VS,
|
payment,
|
||||||
rejected_routes := [],
|
PaymentInstitution,
|
||||||
rejected_providers := []
|
VS,
|
||||||
}} = hg_routing_rule:gather_routes(payment, PaymentInstitution, VS, Revision).
|
Revision
|
||||||
|
).
|
||||||
|
|
||||||
-spec routes_selected_for_low_risk_score(config()) -> test_return().
|
-spec routes_selected_for_low_risk_score(config()) -> test_return().
|
||||||
routes_selected_for_low_risk_score(C) ->
|
routes_selected_for_low_risk_score(C) ->
|
||||||
routes_selected_with_risk_score(C, low, [21, 22, 23]).
|
routes_selected_with_risk_score(C, low, [?prv(21), ?prv(22), ?prv(23)]).
|
||||||
|
|
||||||
-spec routes_selected_for_high_risk_score(config()) -> test_return().
|
-spec routes_selected_for_high_risk_score(config()) -> test_return().
|
||||||
routes_selected_for_high_risk_score(C) ->
|
routes_selected_for_high_risk_score(C) ->
|
||||||
routes_selected_with_risk_score(C, high, [22, 23]).
|
routes_selected_with_risk_score(C, high, [?prv(22), ?prv(23)]).
|
||||||
|
|
||||||
routes_selected_with_risk_score(_C, RiskScore, PrvIDList) ->
|
routes_selected_with_risk_score(_C, RiskScore, ProviderRefs) ->
|
||||||
VS = #{
|
VS = #{
|
||||||
category => ?cat(1),
|
category => ?cat(1),
|
||||||
currency => ?cur(<<"RUB">>),
|
currency => ?cur(<<"RUB">>),
|
||||||
@ -326,12 +319,8 @@ routes_selected_with_risk_score(_C, RiskScore, PrvIDList) ->
|
|||||||
},
|
},
|
||||||
Revision = hg_domain:head(),
|
Revision = hg_domain:head(),
|
||||||
PaymentInstitution = hg_domain:get(Revision, {payment_institution, ?pinst(1)}),
|
PaymentInstitution = hg_domain:get(Revision, {payment_institution, ?pinst(1)}),
|
||||||
{SelectedProviders, _} = hg_routing_rule:gather_routes(payment, PaymentInstitution, VS, Revision),
|
{ok, {Routes, _}} = hg_routing:gather_routes(payment, PaymentInstitution, VS, Revision),
|
||||||
Routes = sort_routes(SelectedProviders),
|
?assert_set_equal(ProviderRefs, lists:map(fun hg_routing:provider_ref/1, Routes)).
|
||||||
|
|
||||||
%% Ensure list of selected provider ID match to given
|
|
||||||
PrvIDList = [P || #{provider_ref := ?prv(P)} <- Routes],
|
|
||||||
ok.
|
|
||||||
|
|
||||||
-spec prefer_alive(config()) -> test_return().
|
-spec prefer_alive(config()) -> test_return().
|
||||||
prefer_alive(_C) ->
|
prefer_alive(_C) ->
|
||||||
@ -347,21 +336,13 @@ prefer_alive(_C) ->
|
|||||||
Revision = hg_domain:head(),
|
Revision = hg_domain:head(),
|
||||||
PaymentInstitution = hg_domain:get(Revision, {payment_institution, ?pinst(1)}),
|
PaymentInstitution = hg_domain:get(Revision, {payment_institution, ?pinst(1)}),
|
||||||
|
|
||||||
{RoutesUnordered, _RejectContext} = hg_routing_rule:gather_routes(
|
{ok, {Routes, _RejectedRoutes}} = hg_routing:gather_routes(
|
||||||
payment,
|
payment,
|
||||||
PaymentInstitution,
|
PaymentInstitution,
|
||||||
VS,
|
VS,
|
||||||
Revision
|
Revision
|
||||||
),
|
),
|
||||||
Routes = sort_routes(RoutesUnordered),
|
?assert_set_equal([?prv(21), ?prv(22), ?prv(23)], [hg_routing:provider_ref(R) || R <- Routes]),
|
||||||
?assertMatch(
|
|
||||||
[
|
|
||||||
#{provider_ref := ?prv(21)},
|
|
||||||
#{provider_ref := ?prv(22)},
|
|
||||||
#{provider_ref := ?prv(23)}
|
|
||||||
],
|
|
||||||
Routes
|
|
||||||
),
|
|
||||||
|
|
||||||
Alive = {alive, 0.0},
|
Alive = {alive, 0.0},
|
||||||
Dead = {dead, 1.0},
|
Dead = {dead, 1.0},
|
||||||
@ -371,23 +352,17 @@ prefer_alive(_C) ->
|
|||||||
ProviderStatuses1 = [{Dead, Normal}, {Alive, Normal}, {Dead, Normal}],
|
ProviderStatuses1 = [{Dead, Normal}, {Alive, Normal}, {Dead, Normal}],
|
||||||
ProviderStatuses2 = [{Dead, Normal}, {Dead, Normal}, {Alive, Normal}],
|
ProviderStatuses2 = [{Dead, Normal}, {Dead, Normal}, {Alive, Normal}],
|
||||||
|
|
||||||
FailRatedRoutes0 = lists:zip(Routes, ProviderStatuses0),
|
[{Route1, _}, _, _] = FailRatedRoutes0 = lists:zip(Routes, ProviderStatuses0),
|
||||||
FailRatedRoutes1 = lists:zip(Routes, ProviderStatuses1),
|
[_, {Route2, _}, _] = FailRatedRoutes1 = lists:zip(Routes, ProviderStatuses1),
|
||||||
FailRatedRoutes2 = lists:zip(Routes, ProviderStatuses2),
|
FailRatedRoutes2 = lists:zip(Routes, ProviderStatuses2),
|
||||||
|
|
||||||
Result0 = hg_routing:choose_rated_route(FailRatedRoutes0),
|
{Route1, Meta0} = hg_routing:choose_rated_route(FailRatedRoutes0),
|
||||||
Result1 = hg_routing:choose_rated_route(FailRatedRoutes1),
|
{Route2, Meta1} = hg_routing:choose_rated_route(FailRatedRoutes1),
|
||||||
Result2 = hg_routing:choose_rated_route(FailRatedRoutes2),
|
{Route3, Meta2} = hg_routing:choose_rated_route(FailRatedRoutes2),
|
||||||
|
|
||||||
{#{provider_ref := ?prv(21), terminal_ref := ?trm(21)}, Meta0} = Result0,
|
#{reject_reason := availability_condition, preferable_route := Route3} = Meta0,
|
||||||
{#{provider_ref := ?prv(22), terminal_ref := ?trm(22)}, Meta1} = Result1,
|
#{reject_reason := availability_condition, preferable_route := Route3} = Meta1,
|
||||||
{#{provider_ref := ?prv(23), terminal_ref := ?trm(23)}, Meta2} = Result2,
|
false = maps:is_key(reject_reason, Meta2).
|
||||||
|
|
||||||
#{reject_reason := availability_condition, preferable_route := #{provider_ref := ?prv(23)}} = Meta0,
|
|
||||||
#{reject_reason := availability_condition, preferable_route := #{provider_ref := ?prv(23)}} = Meta1,
|
|
||||||
false = maps:is_key(reject_reason, Meta2),
|
|
||||||
|
|
||||||
ok.
|
|
||||||
|
|
||||||
-spec prefer_normal_conversion(config()) -> test_return().
|
-spec prefer_normal_conversion(config()) -> test_return().
|
||||||
prefer_normal_conversion(_C) ->
|
prefer_normal_conversion(_C) ->
|
||||||
@ -403,21 +378,13 @@ prefer_normal_conversion(_C) ->
|
|||||||
Revision = hg_domain:head(),
|
Revision = hg_domain:head(),
|
||||||
PaymentInstitution = hg_domain:get(Revision, {payment_institution, ?pinst(1)}),
|
PaymentInstitution = hg_domain:get(Revision, {payment_institution, ?pinst(1)}),
|
||||||
|
|
||||||
{RoutesUnordered, _RC} = hg_routing_rule:gather_routes(
|
{ok, {Routes, _RC}} = hg_routing:gather_routes(
|
||||||
payment,
|
payment,
|
||||||
PaymentInstitution,
|
PaymentInstitution,
|
||||||
VS,
|
VS,
|
||||||
Revision
|
Revision
|
||||||
),
|
),
|
||||||
Routes = sort_routes(RoutesUnordered),
|
?assert_set_equal([?prv(21), ?prv(22), ?prv(23)], [hg_routing:provider_ref(R) || R <- Routes]),
|
||||||
?assertMatch(
|
|
||||||
[
|
|
||||||
#{provider_ref := ?prv(21)},
|
|
||||||
#{provider_ref := ?prv(22)},
|
|
||||||
#{provider_ref := ?prv(23)}
|
|
||||||
],
|
|
||||||
Routes
|
|
||||||
),
|
|
||||||
|
|
||||||
Alive = {alive, 0.0},
|
Alive = {alive, 0.0},
|
||||||
Normal = {normal, 0.0},
|
Normal = {normal, 0.0},
|
||||||
@ -426,23 +393,18 @@ prefer_normal_conversion(_C) ->
|
|||||||
ProviderStatuses0 = [{Alive, Normal}, {Alive, Lacking}, {Alive, Lacking}],
|
ProviderStatuses0 = [{Alive, Normal}, {Alive, Lacking}, {Alive, Lacking}],
|
||||||
ProviderStatuses1 = [{Alive, Lacking}, {Alive, Normal}, {Alive, Lacking}],
|
ProviderStatuses1 = [{Alive, Lacking}, {Alive, Normal}, {Alive, Lacking}],
|
||||||
ProviderStatuses2 = [{Alive, Lacking}, {Alive, Lacking}, {Alive, Normal}],
|
ProviderStatuses2 = [{Alive, Lacking}, {Alive, Lacking}, {Alive, Normal}],
|
||||||
FailRatedRoutes0 = lists:zip(Routes, ProviderStatuses0),
|
|
||||||
FailRatedRoutes1 = lists:zip(Routes, ProviderStatuses1),
|
[{Route1, _}, _, _] = FailRatedRoutes0 = lists:zip(Routes, ProviderStatuses0),
|
||||||
|
[_, {Route2, _}, _] = FailRatedRoutes1 = lists:zip(Routes, ProviderStatuses1),
|
||||||
FailRatedRoutes2 = lists:zip(Routes, ProviderStatuses2),
|
FailRatedRoutes2 = lists:zip(Routes, ProviderStatuses2),
|
||||||
|
|
||||||
Result0 = hg_routing:choose_rated_route(FailRatedRoutes0),
|
{Route1, Meta0} = hg_routing:choose_rated_route(FailRatedRoutes0),
|
||||||
Result1 = hg_routing:choose_rated_route(FailRatedRoutes1),
|
{Route2, Meta1} = hg_routing:choose_rated_route(FailRatedRoutes1),
|
||||||
Result2 = hg_routing:choose_rated_route(FailRatedRoutes2),
|
{Route3, Meta2} = hg_routing:choose_rated_route(FailRatedRoutes2),
|
||||||
|
|
||||||
{#{provider_ref := ?prv(21), terminal_ref := ?trm(21)}, Meta0} = Result0,
|
#{reject_reason := conversion_condition, preferable_route := Route3} = Meta0,
|
||||||
{#{provider_ref := ?prv(22), terminal_ref := ?trm(22)}, Meta1} = Result1,
|
#{reject_reason := conversion_condition, preferable_route := Route3} = Meta1,
|
||||||
{#{provider_ref := ?prv(23), terminal_ref := ?trm(23)}, Meta2} = Result2,
|
false = maps:is_key(reject_reason, Meta2).
|
||||||
|
|
||||||
#{reject_reason := conversion_condition, preferable_route := #{provider_ref := ?prv(23)}} = Meta0,
|
|
||||||
#{reject_reason := conversion_condition, preferable_route := #{provider_ref := ?prv(23)}} = Meta1,
|
|
||||||
false = maps:is_key(reject_reason, Meta2),
|
|
||||||
|
|
||||||
ok.
|
|
||||||
|
|
||||||
-spec prefer_higher_availability(config()) -> test_return().
|
-spec prefer_higher_availability(config()) -> test_return().
|
||||||
prefer_higher_availability(_C) ->
|
prefer_higher_availability(_C) ->
|
||||||
@ -458,32 +420,19 @@ prefer_higher_availability(_C) ->
|
|||||||
Revision = hg_domain:head(),
|
Revision = hg_domain:head(),
|
||||||
PaymentInstitution = hg_domain:get(Revision, {payment_institution, ?pinst(1)}),
|
PaymentInstitution = hg_domain:get(Revision, {payment_institution, ?pinst(1)}),
|
||||||
|
|
||||||
{RoutesUnordered, _RC} = hg_routing_rule:gather_routes(
|
{ok, {Routes, _RC}} = hg_routing:gather_routes(
|
||||||
payment,
|
payment,
|
||||||
PaymentInstitution,
|
PaymentInstitution,
|
||||||
VS,
|
VS,
|
||||||
Revision
|
Revision
|
||||||
),
|
),
|
||||||
Routes = sort_routes(RoutesUnordered),
|
?assert_set_equal([?prv(21), ?prv(22), ?prv(23)], [hg_routing:provider_ref(R) || R <- Routes]),
|
||||||
?assertMatch(
|
|
||||||
[
|
|
||||||
#{provider_ref := ?prv(21)},
|
|
||||||
#{provider_ref := ?prv(22)},
|
|
||||||
#{provider_ref := ?prv(23)}
|
|
||||||
],
|
|
||||||
Routes
|
|
||||||
),
|
|
||||||
|
|
||||||
ProviderStatuses = [{{alive, 0.5}, {normal, 0.5}}, {{dead, 0.8}, {lacking, 1.0}}, {{alive, 0.6}, {normal, 0.5}}],
|
ProviderStatuses = [{{alive, 0.5}, {normal, 0.5}}, {{dead, 0.8}, {lacking, 1.0}}, {{alive, 0.6}, {normal, 0.5}}],
|
||||||
FailRatedRoutes = lists:zip(Routes, ProviderStatuses),
|
[{Route1, _}, _, {Route3, _}] = FailRatedRoutes = lists:zip(Routes, ProviderStatuses),
|
||||||
|
|
||||||
Result = hg_routing:choose_rated_route(FailRatedRoutes),
|
Result = hg_routing:choose_rated_route(FailRatedRoutes),
|
||||||
|
?assertMatch({Route1, #{reject_reason := availability, preferable_route := Route3}}, Result).
|
||||||
{#{provider_ref := ?prv(21), terminal_ref := ?trm(21)}, #{
|
|
||||||
reject_reason := availability,
|
|
||||||
preferable_route := #{provider_ref := ?prv(23)}
|
|
||||||
}} = Result,
|
|
||||||
|
|
||||||
ok.
|
|
||||||
|
|
||||||
-spec prefer_higher_conversion(config()) -> test_return().
|
-spec prefer_higher_conversion(config()) -> test_return().
|
||||||
prefer_higher_conversion(_C) ->
|
prefer_higher_conversion(_C) ->
|
||||||
@ -498,31 +447,19 @@ prefer_higher_conversion(_C) ->
|
|||||||
|
|
||||||
Revision = hg_domain:head(),
|
Revision = hg_domain:head(),
|
||||||
PaymentInstitution = hg_domain:get(Revision, {payment_institution, ?pinst(1)}),
|
PaymentInstitution = hg_domain:get(Revision, {payment_institution, ?pinst(1)}),
|
||||||
{RoutesUnordered, _RC} = hg_routing_rule:gather_routes(
|
{ok, {Routes, _RC}} = hg_routing:gather_routes(
|
||||||
payment,
|
payment,
|
||||||
PaymentInstitution,
|
PaymentInstitution,
|
||||||
VS,
|
VS,
|
||||||
Revision
|
Revision
|
||||||
),
|
),
|
||||||
Routes = sort_routes(RoutesUnordered),
|
?assert_set_equal([?prv(21), ?prv(22), ?prv(23)], [hg_routing:provider_ref(R) || R <- Routes]),
|
||||||
?assertMatch(
|
|
||||||
[
|
|
||||||
#{provider_ref := ?prv(21)},
|
|
||||||
#{provider_ref := ?prv(22)},
|
|
||||||
#{provider_ref := ?prv(23)}
|
|
||||||
],
|
|
||||||
Routes
|
|
||||||
),
|
|
||||||
|
|
||||||
ProviderStatuses = [{{dead, 0.8}, {lacking, 1.0}}, {{alive, 0.5}, {normal, 0.3}}, {{alive, 0.5}, {normal, 0.5}}],
|
ProviderStatuses = [{{dead, 0.8}, {lacking, 1.0}}, {{alive, 0.5}, {normal, 0.3}}, {{alive, 0.5}, {normal, 0.5}}],
|
||||||
FailRatedRoutes = lists:zip(Routes, ProviderStatuses),
|
[_, {Route2, _}, {Route3, _}] = FailRatedRoutes = lists:zip(Routes, ProviderStatuses),
|
||||||
|
|
||||||
Result = hg_routing:choose_rated_route(FailRatedRoutes),
|
Result = hg_routing:choose_rated_route(FailRatedRoutes),
|
||||||
{#{provider_ref := ?prv(22), terminal_ref := ?trm(22)}, #{
|
?assertMatch({Route2, #{reject_reason := conversion, preferable_route := Route3}}, Result).
|
||||||
reject_reason := conversion,
|
|
||||||
preferable_route := #{provider_ref := ?prv(23)}
|
|
||||||
}} = Result,
|
|
||||||
ok.
|
|
||||||
|
|
||||||
-spec prefer_weight_over_availability(config()) -> test_return().
|
-spec prefer_weight_over_availability(config()) -> test_return().
|
||||||
prefer_weight_over_availability(_C) ->
|
prefer_weight_over_availability(_C) ->
|
||||||
@ -538,29 +475,19 @@ prefer_weight_over_availability(_C) ->
|
|||||||
Revision = hg_domain:head(),
|
Revision = hg_domain:head(),
|
||||||
PaymentInstitution = hg_domain:get(Revision, {payment_institution, ?pinst(1)}),
|
PaymentInstitution = hg_domain:get(Revision, {payment_institution, ?pinst(1)}),
|
||||||
|
|
||||||
{RoutesUnordered, _RC} = hg_routing_rule:gather_routes(
|
{ok, {Routes, _RC}} = hg_routing:gather_routes(
|
||||||
payment,
|
payment,
|
||||||
PaymentInstitution,
|
PaymentInstitution,
|
||||||
VS,
|
VS,
|
||||||
Revision
|
Revision
|
||||||
),
|
),
|
||||||
Routes = sort_routes(RoutesUnordered),
|
?assert_set_equal([?prv(21), ?prv(22), ?prv(23)], [hg_routing:provider_ref(R) || R <- Routes]),
|
||||||
?assertMatch(
|
|
||||||
[
|
|
||||||
#{provider_ref := ?prv(21)},
|
|
||||||
#{provider_ref := ?prv(22)},
|
|
||||||
#{provider_ref := ?prv(23)}
|
|
||||||
],
|
|
||||||
Routes
|
|
||||||
),
|
|
||||||
|
|
||||||
ProviderStatuses = [{{alive, 0.3}, {normal, 0.3}}, {{alive, 0.5}, {normal, 0.3}}, {{alive, 0.3}, {normal, 0.3}}],
|
ProviderStatuses = [{{alive, 0.3}, {normal, 0.3}}, {{alive, 0.5}, {normal, 0.3}}, {{alive, 0.3}, {normal, 0.3}}],
|
||||||
FailRatedRoutes = lists:zip(Routes, ProviderStatuses),
|
FailRatedRoutes = lists:zip(Routes, ProviderStatuses),
|
||||||
|
|
||||||
Result = hg_routing:choose_rated_route(FailRatedRoutes),
|
Route = hg_routing:new(?prv(22), ?trm(22), 0, 1005),
|
||||||
|
?assertMatch({Route, _}, hg_routing:choose_rated_route(FailRatedRoutes)).
|
||||||
{#{provider_ref := ?prv(22), terminal_ref := ?trm(22)}, _Meta} = Result,
|
|
||||||
ok.
|
|
||||||
|
|
||||||
-spec prefer_weight_over_conversion(config()) -> test_return().
|
-spec prefer_weight_over_conversion(config()) -> test_return().
|
||||||
prefer_weight_over_conversion(_C) ->
|
prefer_weight_over_conversion(_C) ->
|
||||||
@ -574,28 +501,19 @@ prefer_weight_over_conversion(_C) ->
|
|||||||
},
|
},
|
||||||
Revision = hg_domain:head(),
|
Revision = hg_domain:head(),
|
||||||
PaymentInstitution = hg_domain:get(Revision, {payment_institution, ?pinst(1)}),
|
PaymentInstitution = hg_domain:get(Revision, {payment_institution, ?pinst(1)}),
|
||||||
{RoutesUnordered, _RC} = hg_routing_rule:gather_routes(
|
{ok, {Routes, _RC}} = hg_routing:gather_routes(
|
||||||
payment,
|
payment,
|
||||||
PaymentInstitution,
|
PaymentInstitution,
|
||||||
VS,
|
VS,
|
||||||
Revision
|
Revision
|
||||||
),
|
),
|
||||||
Routes = sort_routes(RoutesUnordered),
|
?assert_set_equal([?prv(21), ?prv(22), ?prv(23)], [hg_routing:provider_ref(R) || R <- Routes]),
|
||||||
?assertMatch(
|
|
||||||
[
|
|
||||||
#{provider_ref := ?prv(21)},
|
|
||||||
#{provider_ref := ?prv(22)},
|
|
||||||
#{provider_ref := ?prv(23)}
|
|
||||||
],
|
|
||||||
Routes
|
|
||||||
),
|
|
||||||
|
|
||||||
ProviderStatuses = [{{alive, 0.3}, {normal, 0.5}}, {{alive, 0.3}, {normal, 0.3}}, {{alive, 0.3}, {normal, 0.3}}],
|
ProviderStatuses = [{{alive, 0.3}, {normal, 0.5}}, {{alive, 0.3}, {normal, 0.3}}, {{alive, 0.3}, {normal, 0.3}}],
|
||||||
FailRatedRoutes = lists:zip(Routes, ProviderStatuses),
|
FailRatedRoutes = lists:zip(Routes, ProviderStatuses),
|
||||||
|
|
||||||
Result = hg_routing:choose_rated_route(FailRatedRoutes),
|
Result = hg_routing:choose_rated_route(FailRatedRoutes),
|
||||||
{#{provider_ref := ?prv(22), terminal_ref := ?trm(22)}, _Meta} = Result,
|
{Route, _Meta} = Result,
|
||||||
ok.
|
?assertMatch({?prv(22), ?trm(22)}, {hg_routing:provider_ref(Route), hg_routing:terminal_ref(Route)}).
|
||||||
|
|
||||||
-spec gathers_fail_rated_routes(config()) -> test_return().
|
-spec gathers_fail_rated_routes(config()) -> test_return().
|
||||||
gathers_fail_rated_routes(_C) ->
|
gathers_fail_rated_routes(_C) ->
|
||||||
@ -610,43 +528,25 @@ gathers_fail_rated_routes(_C) ->
|
|||||||
Revision = hg_domain:head(),
|
Revision = hg_domain:head(),
|
||||||
PaymentInstitution = hg_domain:get(Revision, {payment_institution, ?pinst(1)}),
|
PaymentInstitution = hg_domain:get(Revision, {payment_institution, ?pinst(1)}),
|
||||||
|
|
||||||
{Routes0, _RejectContext0} = hg_routing_rule:gather_routes(payment, PaymentInstitution, VS, Revision),
|
{ok, {Routes0, _RejectedRoutes}} = hg_routing:gather_routes(payment, PaymentInstitution, VS, Revision),
|
||||||
Result = hg_routing:gather_fail_rates(Routes0),
|
Result = hg_routing:gather_fail_rates(Routes0),
|
||||||
?assertMatch(
|
?assertEqual(
|
||||||
[
|
[
|
||||||
{#{provider_ref := ?prv(21)}, {{dead, 0.9}, {lacking, 0.9}}},
|
{hg_routing:new(?prv(21), ?trm(21)), {{dead, 0.9}, {lacking, 0.9}}},
|
||||||
{#{provider_ref := ?prv(22)}, {{alive, 0.1}, {normal, 0.1}}},
|
{hg_routing:new(?prv(22), ?trm(22)), {{alive, 0.1}, {normal, 0.1}}},
|
||||||
{#{provider_ref := ?prv(23)}, {{alive, 0.0}, {normal, 0.0}}}
|
{hg_routing:new(?prv(23), ?trm(23)), {{alive, 0.0}, {normal, 0.0}}}
|
||||||
],
|
],
|
||||||
lists:sort(Result)
|
lists:sort(Result)
|
||||||
).
|
).
|
||||||
|
|
||||||
sort_routes(Routes) ->
|
|
||||||
lists:sort(
|
|
||||||
fun(#{provider_ref := ?prv(ID1)}, #{provider_ref := ?prv(ID2)}) ->
|
|
||||||
ID1 < ID2
|
|
||||||
end,
|
|
||||||
Routes
|
|
||||||
).
|
|
||||||
|
|
||||||
%%% Terminal priority tests
|
%%% Terminal priority tests
|
||||||
|
|
||||||
-spec terminal_priority_for_shop(config()) -> test_return().
|
-spec terminal_priority_for_shop(config()) -> test_return().
|
||||||
terminal_priority_for_shop(C) ->
|
terminal_priority_for_shop(C) ->
|
||||||
{
|
Route1 = hg_routing:new(?prv(41), ?trm(41), 0, 10),
|
||||||
#{
|
Route2 = hg_routing:new(?prv(42), ?trm(42), 0, 10),
|
||||||
provider_ref := ?prv(41),
|
?assertMatch({Route1, _}, terminal_priority_for_shop(?dummy_party_id, ?dummy_shop_id, C)),
|
||||||
terminal_ref := ?trm(41)
|
?assertMatch({Route2, _}, terminal_priority_for_shop(?dummy_party_id, ?dummy_another_shop_id, C)).
|
||||||
},
|
|
||||||
_Meta0
|
|
||||||
} = terminal_priority_for_shop(?dummy_party_id, ?dummy_shop_id, C),
|
|
||||||
{
|
|
||||||
#{
|
|
||||||
provider_ref := ?prv(42),
|
|
||||||
terminal_ref := ?trm(42)
|
|
||||||
},
|
|
||||||
_Meta1
|
|
||||||
} = terminal_priority_for_shop(?dummy_party_id, ?dummy_another_shop_id, C).
|
|
||||||
|
|
||||||
terminal_priority_for_shop(PartyID, ShopID, _C) ->
|
terminal_priority_for_shop(PartyID, ShopID, _C) ->
|
||||||
VS = #{
|
VS = #{
|
||||||
@ -660,7 +560,7 @@ terminal_priority_for_shop(PartyID, ShopID, _C) ->
|
|||||||
},
|
},
|
||||||
Revision = hg_domain:head(),
|
Revision = hg_domain:head(),
|
||||||
PaymentInstitution = hg_domain:get(Revision, {payment_institution, ?pinst(1)}),
|
PaymentInstitution = hg_domain:get(Revision, {payment_institution, ?pinst(1)}),
|
||||||
{Routes, _RejectContext} = hg_routing_rule:gather_routes(payment, PaymentInstitution, VS, Revision),
|
{ok, {Routes, _RejectedRoutes}} = hg_routing:gather_routes(payment, PaymentInstitution, VS, Revision),
|
||||||
FailRatedRoutes = hg_routing:gather_fail_rates(Routes),
|
FailRatedRoutes = hg_routing:gather_fail_rates(Routes),
|
||||||
hg_routing:choose_rated_route(FailRatedRoutes).
|
hg_routing:choose_rated_route(FailRatedRoutes).
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user