mirror of
https://github.com/valitydev/hellgate.git
synced 2024-11-06 02:45:20 +00:00
TD-820: Refactors routing to support critical score rejections (#105)
* TD-820: Refactors routing to support critical score rejections * Moves routing context helpers to appropriate module * Splits large functions, fixes misconfiguration error bug * Fixes second fail rates query * Fixes routes equality check * Extracts route struct functions * Extracts routing context into separate module * Fixes rejected routes ordering consistency * Fixes funx naming and adds routes equality tests * Makes rejected routes reasons more explicit in code * Adds routing ctx pipeline test * Adds specified routing sub failures * Updates tests; replaces static errors translation * Fixes ubiquity of availability status naming * Updates route reject error codes * Bumps damsel
This commit is contained in:
parent
42af922405
commit
7305ff03e6
@ -5,8 +5,8 @@
|
|||||||
activity :: hg_invoice_payment:activity(),
|
activity :: hg_invoice_payment:activity(),
|
||||||
payment :: undefined | hg_invoice_payment:payment(),
|
payment :: undefined | hg_invoice_payment:payment(),
|
||||||
risk_score :: undefined | hg_inspector:risk_score(),
|
risk_score :: undefined | hg_inspector:risk_score(),
|
||||||
routes = [] :: [hg_routing:payment_route()],
|
routes = [] :: [hg_route:payment_route()],
|
||||||
candidate_routes :: undefined | [hg_routing:payment_route()],
|
candidate_routes :: undefined | [hg_route:payment_route()],
|
||||||
interim_payment_status :: undefined | hg_invoice_payment:payment_status(),
|
interim_payment_status :: undefined | hg_invoice_payment:payment_status(),
|
||||||
new_cash :: undefined | hg_cash:cash(),
|
new_cash :: undefined | hg_cash:cash(),
|
||||||
new_cash_provided :: undefined | boolean(),
|
new_cash_provided :: undefined | boolean(),
|
||||||
|
@ -39,7 +39,7 @@
|
|||||||
-type payment() :: dmsl_domain_thrift:'InvoicePayment'().
|
-type payment() :: dmsl_domain_thrift:'InvoicePayment'().
|
||||||
-type party() :: dmsl_domain_thrift:'Party'().
|
-type party() :: dmsl_domain_thrift:'Party'().
|
||||||
-type shop() :: dmsl_domain_thrift:'Shop'().
|
-type shop() :: dmsl_domain_thrift:'Shop'().
|
||||||
-type route() :: hg_routing:payment_route().
|
-type route() :: hg_route:payment_route().
|
||||||
-type payment_institution() :: dmsl_domain_thrift:'PaymentInstitution'().
|
-type payment_institution() :: dmsl_domain_thrift:'PaymentInstitution'().
|
||||||
-type provider() :: dmsl_domain_thrift:'Provider'().
|
-type provider() :: dmsl_domain_thrift:'Provider'().
|
||||||
-type varset() :: hg_varset:varset().
|
-type varset() :: hg_varset:varset().
|
||||||
|
@ -12,7 +12,7 @@
|
|||||||
-spec is_triggered(
|
-spec is_triggered(
|
||||||
cascade_behaviour() | undefined,
|
cascade_behaviour() | undefined,
|
||||||
operation_failure(),
|
operation_failure(),
|
||||||
hg_routing:payment_route(),
|
hg_route:payment_route(),
|
||||||
[hg_session:t()]
|
[hg_session:t()]
|
||||||
) ->
|
) ->
|
||||||
boolean().
|
boolean().
|
||||||
|
@ -29,7 +29,7 @@
|
|||||||
|
|
||||||
-type shop_id() :: dmsl_domain_thrift:'ShopID'().
|
-type shop_id() :: dmsl_domain_thrift:'ShopID'().
|
||||||
-type party_id() :: dmsl_domain_thrift:'PartyID'().
|
-type party_id() :: dmsl_domain_thrift:'PartyID'().
|
||||||
-type route() :: hg_routing:payment_route().
|
-type route() :: hg_route:payment_route().
|
||||||
|
|
||||||
%%
|
%%
|
||||||
|
|
||||||
|
@ -190,7 +190,7 @@
|
|||||||
-type target() :: dmsl_domain_thrift:'TargetInvoicePaymentStatus'().
|
-type target() :: dmsl_domain_thrift:'TargetInvoicePaymentStatus'().
|
||||||
-type session_target_type() :: 'processed' | 'captured' | 'cancelled' | 'refunded'.
|
-type session_target_type() :: 'processed' | 'captured' | 'cancelled' | 'refunded'.
|
||||||
-type risk_score() :: hg_inspector:risk_score().
|
-type risk_score() :: hg_inspector:risk_score().
|
||||||
-type route() :: hg_routing:payment_route().
|
-type route() :: hg_route:payment_route().
|
||||||
-type final_cash_flow() :: hg_cashflow:final_cash_flow().
|
-type final_cash_flow() :: hg_cashflow:final_cash_flow().
|
||||||
-type trx_info() :: dmsl_domain_thrift:'TransactionInfo'().
|
-type trx_info() :: dmsl_domain_thrift:'TransactionInfo'().
|
||||||
-type tag() :: dmsl_proxy_provider_thrift:'CallbackTag'().
|
-type tag() :: dmsl_proxy_provider_thrift:'CallbackTag'().
|
||||||
@ -235,6 +235,8 @@
|
|||||||
|
|
||||||
%%
|
%%
|
||||||
|
|
||||||
|
-define(LOG_MD(Level, Format, Args), logger:log(Level, Format, Args, logger:get_process_metadata())).
|
||||||
|
|
||||||
-spec get_party_revision(st()) -> {hg_party:party_revision(), hg_datetime:timestamp()}.
|
-spec get_party_revision(st()) -> {hg_party:party_revision(), hg_datetime:timestamp()}.
|
||||||
get_party_revision(#st{activity = {payment, _}} = St) ->
|
get_party_revision(#st{activity = {payment, _}} = St) ->
|
||||||
#domain_InvoicePayment{party_revision = Revision, created_at = Timestamp} = get_payment(St),
|
#domain_InvoicePayment{party_revision = Revision, created_at = Timestamp} = get_payment(St),
|
||||||
@ -767,31 +769,12 @@ gather_routes(PaymentInstitution, VS, Revision, St) ->
|
|||||||
Payer = get_payment_payer(St),
|
Payer = get_payment_payer(St),
|
||||||
PaymentTool = get_payer_payment_tool(Payer),
|
PaymentTool = get_payer_payment_tool(Payer),
|
||||||
ClientIP = get_payer_client_ip(Payer),
|
ClientIP = get_payer_client_ip(Payer),
|
||||||
case
|
hg_routing:gather_routes(Predestination, PaymentInstitution, VS, Revision, #{
|
||||||
hg_routing:gather_routes(
|
|
||||||
Predestination,
|
|
||||||
PaymentInstitution,
|
|
||||||
VS,
|
|
||||||
Revision,
|
|
||||||
#{
|
|
||||||
currency => Currency,
|
currency => Currency,
|
||||||
payment_tool => PaymentTool,
|
payment_tool => PaymentTool,
|
||||||
party_id => PartyID,
|
party_id => PartyID,
|
||||||
client_ip => ClientIP
|
client_ip => ClientIP
|
||||||
}
|
}).
|
||||||
)
|
|
||||||
of
|
|
||||||
{ok, {[], RejectedRoutes}} ->
|
|
||||||
_ = log_rejected_routes(no_route_found, RejectedRoutes, VS),
|
|
||||||
throw({no_route_found, {rejected_routes, RejectedRoutes}});
|
|
||||||
{ok, {Routes, RejectedRoutes}} ->
|
|
||||||
erlang:length(RejectedRoutes) > 0 andalso
|
|
||||||
log_rejected_routes(rejected_route_found, RejectedRoutes, VS),
|
|
||||||
Routes;
|
|
||||||
{error, {misconfiguration, _Reason} = Error} ->
|
|
||||||
_ = log_misconfigurations(Error),
|
|
||||||
throw({no_route_found, Error})
|
|
||||||
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}.
|
||||||
check_risk_score(fatal) ->
|
check_risk_score(fatal) ->
|
||||||
@ -807,58 +790,35 @@ choose_routing_predestination(#domain_InvoicePayment{payer = ?payment_resource_p
|
|||||||
|
|
||||||
% Other payers has predefined routes
|
% Other payers has predefined routes
|
||||||
|
|
||||||
log_route_choice_meta(ChoiceMeta, Revision) ->
|
log_route_choice_meta(#{choice_meta := undefined}, _Revision) ->
|
||||||
|
ok;
|
||||||
|
log_route_choice_meta(#{choice_meta := ChoiceMeta}, Revision) ->
|
||||||
Metadata = hg_routing:get_logger_metadata(ChoiceMeta, Revision),
|
Metadata = hg_routing:get_logger_metadata(ChoiceMeta, Revision),
|
||||||
_ = logger:log(info, "Routing decision made", #{routing => Metadata}).
|
logger:log(info, "Routing decision made", #{routing => Metadata}).
|
||||||
|
|
||||||
log_misconfigurations({misconfiguration, _} = Error) ->
|
maybe_log_misconfigurations({misconfiguration, _} = Error) ->
|
||||||
{Format, Details} = hg_routing:prepare_log_message(Error),
|
{Format, Details} = hg_routing:prepare_log_message(Error),
|
||||||
_ = logger:warning(Format, Details),
|
?LOG_MD(warning, Format, Details);
|
||||||
|
maybe_log_misconfigurations(_Error) ->
|
||||||
ok.
|
ok.
|
||||||
|
|
||||||
log_rejected_routes(no_route_found, RejectedRoutes, Varset) ->
|
log_rejected_routes(_, [], _Varset) ->
|
||||||
_ = logger:log(
|
|
||||||
warning,
|
|
||||||
"No route found for varset: ~p",
|
|
||||||
[Varset],
|
|
||||||
logger:get_process_metadata()
|
|
||||||
),
|
|
||||||
_ = logger:log(
|
|
||||||
warning,
|
|
||||||
"No route found, rejected routes: ~p",
|
|
||||||
[RejectedRoutes],
|
|
||||||
logger:get_process_metadata()
|
|
||||||
),
|
|
||||||
ok;
|
ok;
|
||||||
log_rejected_routes(limit_hold_reject, RejectedRoutes, _Varset) ->
|
log_rejected_routes(all, Routes, VS) ->
|
||||||
_ = logger:log(
|
?LOG_MD(warning, "No route found for varset: ~p", [VS]),
|
||||||
warning,
|
?LOG_MD(warning, "No route found, rejected routes: ~p", [Routes]);
|
||||||
"Limiter hold error caused route candidates to be rejected: ~p",
|
log_rejected_routes(limit_misconfiguration, Routes, _VS) ->
|
||||||
[RejectedRoutes],
|
?LOG_MD(warning, "Limiter hold error caused route candidates to be rejected: ~p", [Routes]);
|
||||||
logger:get_process_metadata()
|
log_rejected_routes(limit_overflow, Routes, _VS) ->
|
||||||
),
|
?LOG_MD(info, "Limit overflow caused route candidates to be rejected: ~p", [Routes]);
|
||||||
ok;
|
log_rejected_routes(adapter_unavailable, Routes, _VS) ->
|
||||||
log_rejected_routes(limit_overflow_reject, RejectedRoutes, _Varset) ->
|
?LOG_MD(info, "Adapter unavailability caused route candidates to be rejected: ~p", [Routes]);
|
||||||
_ = logger:log(
|
log_rejected_routes(provider_conversion_is_too_low, Routes, _VS) ->
|
||||||
info,
|
?LOG_MD(info, "Lacking conversion of provider caused route candidates to be rejected: ~p", [Routes]);
|
||||||
"Limit overflow caused route candidates to be rejected: ~p",
|
log_rejected_routes(forbidden, Routes, VS) ->
|
||||||
[RejectedRoutes],
|
?LOG_MD(info, "Rejected routes found for varset: ~p", [VS]),
|
||||||
logger:get_process_metadata()
|
?LOG_MD(info, "Rejected routes found, rejected routes: ~p", [Routes]);
|
||||||
),
|
log_rejected_routes(_, _Routes, _VS) ->
|
||||||
ok;
|
|
||||||
log_rejected_routes(rejected_route_found, RejectedRoutes, Varset) ->
|
|
||||||
_ = logger:log(
|
|
||||||
info,
|
|
||||||
"Rejected routes found for varset: ~p",
|
|
||||||
[Varset],
|
|
||||||
logger:get_process_metadata()
|
|
||||||
),
|
|
||||||
_ = logger:log(
|
|
||||||
info,
|
|
||||||
"Rejected routes found, rejected routes: ~p",
|
|
||||||
[RejectedRoutes],
|
|
||||||
logger:get_process_metadata()
|
|
||||||
),
|
|
||||||
ok.
|
ok.
|
||||||
|
|
||||||
validate_refund_time(RefundCreatedAt, PaymentCreatedAt, TimeSpanSelector) ->
|
validate_refund_time(RefundCreatedAt, PaymentCreatedAt, TimeSpanSelector) ->
|
||||||
@ -1979,24 +1939,58 @@ process_risk_score(Action, St) ->
|
|||||||
-spec process_routing(action(), st()) -> machine_result().
|
-spec process_routing(action(), st()) -> machine_result().
|
||||||
process_routing(Action, St) ->
|
process_routing(Action, St) ->
|
||||||
{PaymentInstitution, VS, Revision} = route_args(St),
|
{PaymentInstitution, VS, Revision} = route_args(St),
|
||||||
try
|
Ctx0 = hg_routing_ctx:with_guard(build_routing_context(PaymentInstitution, VS, Revision, St)),
|
||||||
AllRoutes = get_candidates(PaymentInstitution, VS, Revision, St),
|
%% NOTE We need to handle routing errors differently if route not found
|
||||||
AvailableRoutes = filter_out_attempted_routes(AllRoutes, St),
|
%% before the pipeline.
|
||||||
%% Since this is routing step then current attempt is not yet accounted for in `St`.
|
case hg_routing_ctx:error(Ctx0) of
|
||||||
Iter = get_iter(St) + 1,
|
undefined ->
|
||||||
Events = handle_gathered_route_result(
|
Ctx1 = run_routing_decision_pipeline(Ctx0, VS, St),
|
||||||
filter_limit_overflow_routes(AvailableRoutes, VS, Iter, St),
|
_ = [
|
||||||
[hg_routing:to_payment_route(R) || R <- AllRoutes],
|
log_rejected_routes(Group, RejectedRoutes, VS)
|
||||||
[hg_routing:to_payment_route(R) || R <- AvailableRoutes],
|
|| {Group, RejectedRoutes} <- hg_routing_ctx:rejections(Ctx0)
|
||||||
Revision,
|
],
|
||||||
St
|
Events = produce_routing_events(Ctx1, Revision, St),
|
||||||
),
|
{next, {Events, hg_machine_action:set_timeout(0, Action)}};
|
||||||
{next, {Events, hg_machine_action:set_timeout(0, Action)}}
|
Error ->
|
||||||
catch
|
ok = maybe_log_misconfigurations(Error),
|
||||||
throw:{no_route_found, Reason} ->
|
ok = log_rejected_routes(all, hg_routing_ctx:rejected_routes(Ctx0), VS),
|
||||||
handle_choose_route_error(Reason, [], St, Action)
|
handle_choose_route_error(Error, [], St, Action)
|
||||||
end.
|
end.
|
||||||
|
|
||||||
|
run_routing_decision_pipeline(Ctx0, VS, St) ->
|
||||||
|
hg_routing_ctx:pipeline(
|
||||||
|
Ctx0,
|
||||||
|
[
|
||||||
|
fun(Ctx) -> filter_attempted_routes(Ctx, St) end,
|
||||||
|
%% Since this is routing step then current attempt is not yet
|
||||||
|
%% accounted for in `St`.
|
||||||
|
fun(Ctx) -> filter_routes_with_limit_hold(Ctx, VS, get_iter(St) + 1, St) end,
|
||||||
|
fun(Ctx) -> filter_routes_by_limit_overflow(Ctx, VS, St) end,
|
||||||
|
fun hg_routing:filter_by_critical_provider_status/1,
|
||||||
|
fun hg_routing:choose_route_with_ctx/1
|
||||||
|
]
|
||||||
|
).
|
||||||
|
|
||||||
|
produce_routing_events(Ctx = #{error := Error}, _Revision, St) when Error =/= undefined ->
|
||||||
|
%% TODO Pass failure subcode from error. Say, if last candidates were
|
||||||
|
%% rejected because of provider gone critical, then use subcode to highlight
|
||||||
|
%% the offender. Like 'provider_dead' or 'conversion_lacking'.
|
||||||
|
Failure = genlib:define(St#st.failure, construct_routing_failure(Error)),
|
||||||
|
InitialCandidates = [hg_route:to_payment_route(R) || R <- hg_routing_ctx:initial_candidates(Ctx)],
|
||||||
|
Route = hd(InitialCandidates),
|
||||||
|
Candidates = ordsets:from_list(InitialCandidates),
|
||||||
|
%% For protocol compatability we set choosen route in route_changed event.
|
||||||
|
%% It doesn't influence cash_flow building because this step will be
|
||||||
|
%% skipped. And all limit's 'hold' operations will be rolled back.
|
||||||
|
%% For same purpose in cascade routing we use route from unfiltered list of
|
||||||
|
%% originally resolved candidates.
|
||||||
|
[?route_changed(Route, Candidates), ?payment_rollback_started(Failure)];
|
||||||
|
produce_routing_events(Ctx, Revision, _St) ->
|
||||||
|
ok = log_route_choice_meta(Ctx, Revision),
|
||||||
|
Route = hg_route:to_payment_route(hg_routing_ctx:choosen_route(Ctx)),
|
||||||
|
Candidates = ordsets:from_list([hg_route:to_payment_route(R) || R <- hg_routing_ctx:candidates(Ctx)]),
|
||||||
|
[?route_changed(Route, Candidates)].
|
||||||
|
|
||||||
route_args(St) ->
|
route_args(St) ->
|
||||||
Opts = get_opts(St),
|
Opts = get_opts(St),
|
||||||
Revision = get_payment_revision(St),
|
Revision = get_payment_revision(St),
|
||||||
@ -2010,60 +2004,54 @@ route_args(St) ->
|
|||||||
PaymentInstitution = hg_payment_institution:compute_payment_institution(PaymentInstitutionRef, VS1, Revision),
|
PaymentInstitution = hg_payment_institution:compute_payment_institution(PaymentInstitutionRef, VS1, Revision),
|
||||||
{PaymentInstitution, VS3, Revision}.
|
{PaymentInstitution, VS3, Revision}.
|
||||||
|
|
||||||
get_candidates(PaymentInstitution, VS, Revision, St) ->
|
build_routing_context(PaymentInstitution, VS, Revision, St) ->
|
||||||
Payer = get_payment_payer(St),
|
Payer = get_payment_payer(St),
|
||||||
case get_predefined_route(Payer) of
|
case get_predefined_route(Payer) of
|
||||||
{ok, PaymentRoute} ->
|
{ok, PaymentRoute} ->
|
||||||
[hg_routing:from_payment_route(PaymentRoute)];
|
hg_routing_ctx:new([hg_route:from_payment_route(PaymentRoute)]);
|
||||||
undefined ->
|
undefined ->
|
||||||
gather_routes(PaymentInstitution, VS, Revision, St)
|
gather_routes(PaymentInstitution, VS, Revision, St)
|
||||||
end.
|
end.
|
||||||
|
|
||||||
filter_out_attempted_routes(Routes, #st{routes = AttemptedRoutes}) ->
|
filter_attempted_routes(Ctx, #st{routes = AttemptedRoutes}) ->
|
||||||
lists:filter(
|
lists:foldr(
|
||||||
fun(Route) -> not lists:member(hg_routing:to_payment_route(Route), AttemptedRoutes) end,
|
fun(R, C) ->
|
||||||
Routes
|
R1 = hg_route:from_payment_route(R),
|
||||||
|
R2 = hg_route:to_rejected_route(R1, {'AlreadyAttempted', undefined}),
|
||||||
|
hg_routing_ctx:reject(already_attempted, R2, C)
|
||||||
|
end,
|
||||||
|
Ctx,
|
||||||
|
AttemptedRoutes
|
||||||
).
|
).
|
||||||
|
|
||||||
handle_gathered_route_result({ok, RoutesNoOverflow}, _Routes, CandidateRoutes, Revision, _St) ->
|
handle_choose_route_error(Error, Events, St, Action) ->
|
||||||
{ChoosenRoute, ChoiceContext} = hg_routing:choose_route(RoutesNoOverflow),
|
Failure = construct_routing_failure(Error),
|
||||||
_ = log_route_choice_meta(ChoiceContext, Revision),
|
|
||||||
[?route_changed(hg_routing:to_payment_route(ChoosenRoute), ordsets:from_list(CandidateRoutes))];
|
|
||||||
handle_gathered_route_result({error, _Reason}, Routes, CandidateRoutes, _Revision, #st{failure = Failure}) when
|
|
||||||
Failure =/= undefined
|
|
||||||
->
|
|
||||||
%% Pass original failure if it is set
|
|
||||||
handle_gathered_route_result_(Routes, CandidateRoutes, Failure);
|
|
||||||
handle_gathered_route_result({error, {not_found, RejectedRoutes}}, Routes, CandidateRoutes, _Revision, _St) ->
|
|
||||||
Failure = construct_routing_failure(forbidden, genlib:format({rejected_routes, RejectedRoutes})),
|
|
||||||
handle_gathered_route_result_(Routes, CandidateRoutes, Failure).
|
|
||||||
|
|
||||||
handle_gathered_route_result_(Routes, CandidateRoutes, Failure) ->
|
|
||||||
[Route | _] = Routes,
|
|
||||||
%% For protocol compatability we set choosen route in route_changed event.
|
|
||||||
%% It doesn't influence cash_flow building because this step will be skipped. And all limit's 'hold' operations
|
|
||||||
%% will be rolled back.
|
|
||||||
%% For same purpose in cascade routing we use route form unfiltered list of originally resolved candidates.
|
|
||||||
[?route_changed(Route, ordsets:from_list(CandidateRoutes)), ?payment_rollback_started(Failure)].
|
|
||||||
|
|
||||||
handle_choose_route_error({rejected_routes, _RejectedRoutes} = Reason, Events, St, Action) ->
|
|
||||||
do_handle_routing_error(unknown, genlib:format(Reason), Events, St, Action);
|
|
||||||
handle_choose_route_error({misconfiguration, _Details} = Reason, Events, St, Action) ->
|
|
||||||
do_handle_routing_error(unknown, genlib:format(Reason), Events, St, Action);
|
|
||||||
handle_choose_route_error(Reason, Events, St, Action) when is_atom(Reason) ->
|
|
||||||
do_handle_routing_error(Reason, undefined, Events, St, Action).
|
|
||||||
|
|
||||||
do_handle_routing_error(SubCode, Reason, Events, St, Action) ->
|
|
||||||
Failure = construct_routing_failure(SubCode, Reason),
|
|
||||||
process_failure(get_activity(St), Events, Action, Failure, St).
|
process_failure(get_activity(St), Events, Action, Failure, St).
|
||||||
|
|
||||||
construct_routing_failure(SubCode, Reason) ->
|
%% NOTE See damsel payproc errors (proto/payment_processing_errors.thrift) for no route found
|
||||||
{failure,
|
|
||||||
payproc_errors:construct(
|
construct_routing_failure({rejected_routes, {forbidden, RejectedRoutes}}) ->
|
||||||
'PaymentFailure',
|
construct_routing_failure([forbidden], genlib:format(RejectedRoutes));
|
||||||
{no_route_found, {SubCode, #payproc_error_GeneralFailure{}}},
|
construct_routing_failure({rejected_routes, {SubCode, RejectedRoutes}}) when
|
||||||
Reason
|
SubCode =:= limit_misconfiguration orelse
|
||||||
)}.
|
SubCode =:= limit_overflow orelse
|
||||||
|
SubCode =:= adapter_unavailable orelse
|
||||||
|
SubCode =:= provider_conversion_is_too_low
|
||||||
|
->
|
||||||
|
construct_routing_failure([rejected, SubCode], genlib:format(RejectedRoutes));
|
||||||
|
construct_routing_failure({misconfiguration = Code, Details}) ->
|
||||||
|
construct_routing_failure([unknown, {unknown_error, atom_to_binary(Code)}], genlib:format(Details));
|
||||||
|
construct_routing_failure(Code = risk_score_is_too_high) ->
|
||||||
|
construct_routing_failure([Code], undefined);
|
||||||
|
construct_routing_failure(Error) when is_atom(Error) ->
|
||||||
|
construct_routing_failure([{unknown_error, Error}], undefined).
|
||||||
|
|
||||||
|
construct_routing_failure(Codes, Reason) ->
|
||||||
|
{failure, payproc_errors:construct('PaymentFailure', mk_static_error([no_route_found | Codes]), Reason)}.
|
||||||
|
|
||||||
|
mk_static_error([_ | _] = Codes) -> mk_static_error_(#payproc_error_GeneralFailure{}, lists:reverse(Codes)).
|
||||||
|
mk_static_error_(T, []) -> T;
|
||||||
|
mk_static_error_(Sub, [Code | Codes]) -> mk_static_error_({Code, Sub}, Codes).
|
||||||
|
|
||||||
-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) ->
|
||||||
@ -2280,7 +2268,7 @@ process_result({payment, processing_accounter}, Action, St0 = #st{new_cash = Cos
|
|||||||
FinalCashflow = calculate_cashflow(Context, Opts),
|
FinalCashflow = calculate_cashflow(Context, Opts),
|
||||||
%% Hold limits (only for chosen route) for new cashflow
|
%% Hold limits (only for chosen route) for new cashflow
|
||||||
{_PaymentInstitution, RouteVS, _Revision} = route_args(St1),
|
{_PaymentInstitution, RouteVS, _Revision} = route_args(St1),
|
||||||
Routes = [hg_routing:from_payment_route(Route)],
|
Routes = [hg_route:from_payment_route(Route)],
|
||||||
_ = hold_limit_routes(Routes, RouteVS, get_iter(St1), St1),
|
_ = hold_limit_routes(Routes, RouteVS, get_iter(St1), St1),
|
||||||
%% Hold cashflow
|
%% Hold cashflow
|
||||||
St2 = St1#st{new_cash_flow = FinalCashflow},
|
St2 = St1#st{new_cash_flow = FinalCashflow},
|
||||||
@ -2487,43 +2475,45 @@ get_provider_terms(St, Revision) ->
|
|||||||
VS1 = collect_validation_varset(get_party(Opts), get_shop(Opts), Payment, VS0),
|
VS1 = collect_validation_varset(get_party(Opts), get_shop(Opts), Payment, VS0),
|
||||||
hg_routing:get_payment_terms(Route, VS1, Revision).
|
hg_routing:get_payment_terms(Route, VS1, Revision).
|
||||||
|
|
||||||
filter_limit_overflow_routes(Routes, VS, Iter, St) ->
|
filter_routes_with_limit_hold(Ctx, VS, Iter, St) ->
|
||||||
{UsableRoutes, HoldRejectedRoutes} = hold_limit_routes(Routes, VS, Iter, St),
|
{_Routes, RejectedRoutes} = hold_limit_routes(hg_routing_ctx:candidates(Ctx), VS, Iter, St),
|
||||||
case get_limit_overflow_routes(UsableRoutes, VS, St) of
|
reject_routes(limit_misconfiguration, RejectedRoutes, Ctx).
|
||||||
{[], RejectedRoutesOut} ->
|
|
||||||
{error, {not_found, RejectedRoutesOut ++ HoldRejectedRoutes}};
|
filter_routes_by_limit_overflow(Ctx, VS, St) ->
|
||||||
{RoutesNoOverflow, _} ->
|
{_Routes, RejectedRoutes} = get_limit_overflow_routes(hg_routing_ctx:candidates(Ctx), VS, St),
|
||||||
{ok, RoutesNoOverflow}
|
reject_routes(limit_overflow, RejectedRoutes, Ctx).
|
||||||
end.
|
|
||||||
|
reject_routes(GroupReason, RejectedRoutes, Ctx) ->
|
||||||
|
lists:foldr(
|
||||||
|
fun(R, C) -> hg_routing_ctx:reject(GroupReason, R, C) end,
|
||||||
|
Ctx,
|
||||||
|
RejectedRoutes
|
||||||
|
).
|
||||||
|
|
||||||
get_limit_overflow_routes(Routes, VS, St) ->
|
get_limit_overflow_routes(Routes, VS, 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),
|
||||||
{_Routes, RejectedRoutes} =
|
lists:foldl(
|
||||||
Result = lists:foldl(
|
|
||||||
fun(Route, {RoutesNoOverflowIn, RejectedIn}) ->
|
fun(Route, {RoutesNoOverflowIn, RejectedIn}) ->
|
||||||
PaymentRoute = hg_routing:to_payment_route(Route),
|
PaymentRoute = hg_route:to_payment_route(Route),
|
||||||
ProviderTerms = hg_routing:get_payment_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, PaymentRoute) of
|
case hg_limiter:check_limits(TurnoverLimits, Invoice, Payment, PaymentRoute) of
|
||||||
{ok, _} ->
|
{ok, _} ->
|
||||||
{[Route | RoutesNoOverflowIn], RejectedIn};
|
{[Route | RoutesNoOverflowIn], RejectedIn};
|
||||||
{error, {limit_overflow, IDs}} ->
|
{error, {limit_overflow, IDs}} ->
|
||||||
RejectedRoute = hg_routing:to_rejected_route(Route, {'LimitOverflow', IDs}),
|
RejectedRoute = hg_route:to_rejected_route(Route, {'LimitOverflow', IDs}),
|
||||||
{RoutesNoOverflowIn, [RejectedRoute | RejectedIn]}
|
{RoutesNoOverflowIn, [RejectedRoute | RejectedIn]}
|
||||||
end
|
end
|
||||||
end,
|
end,
|
||||||
{[], []},
|
{[], []},
|
||||||
Routes
|
Routes
|
||||||
),
|
).
|
||||||
erlang:length(RejectedRoutes) > 0 andalso
|
|
||||||
log_rejected_routes(limit_overflow_reject, RejectedRoutes, VS),
|
|
||||||
Result.
|
|
||||||
|
|
||||||
-spec hold_limit_routes([hg_routing:route()], hg_varset:varset(), pos_integer(), st()) ->
|
-spec hold_limit_routes([hg_route:t()], hg_varset:varset(), pos_integer(), st()) ->
|
||||||
{[hg_routing:route()], [hg_routing:rejected_route()]}.
|
{[hg_route:t()], [hg_route:rejected_route()]}.
|
||||||
hold_limit_routes(Routes0, VS, Iter, St) ->
|
hold_limit_routes(Routes0, VS, Iter, St) ->
|
||||||
Opts = get_opts(St),
|
Opts = get_opts(St),
|
||||||
Revision = get_payment_revision(St),
|
Revision = get_payment_revision(St),
|
||||||
@ -2531,7 +2521,7 @@ hold_limit_routes(Routes0, VS, Iter, St) ->
|
|||||||
Invoice = get_invoice(Opts),
|
Invoice = get_invoice(Opts),
|
||||||
{Routes1, Rejected} = lists:foldl(
|
{Routes1, Rejected} = lists:foldl(
|
||||||
fun(Route, {LimitHeldRoutes, RejectedRoutes} = Acc) ->
|
fun(Route, {LimitHeldRoutes, RejectedRoutes} = Acc) ->
|
||||||
PaymentRoute = hg_routing:to_payment_route(Route),
|
PaymentRoute = hg_route:to_payment_route(Route),
|
||||||
ProviderTerms = hg_routing:get_payment_terms(PaymentRoute, VS, Revision),
|
ProviderTerms = hg_routing:get_payment_terms(PaymentRoute, VS, Revision),
|
||||||
TurnoverLimits = get_turnover_limits(ProviderTerms),
|
TurnoverLimits = get_turnover_limits(ProviderTerms),
|
||||||
try
|
try
|
||||||
@ -2549,13 +2539,11 @@ hold_limit_routes(Routes0, VS, Iter, St) ->
|
|||||||
{[], []},
|
{[], []},
|
||||||
Routes0
|
Routes0
|
||||||
),
|
),
|
||||||
erlang:length(Rejected) > 0 andalso
|
|
||||||
log_rejected_routes(limit_hold_reject, Rejected, VS),
|
|
||||||
{lists:reverse(Routes1), Rejected}.
|
{lists:reverse(Routes1), Rejected}.
|
||||||
|
|
||||||
do_reject_route(LimiterError, Route, TurnoverLimits, {LimitHeldRoutes, RejectedRoutes}) ->
|
do_reject_route(LimiterError, Route, TurnoverLimits, {LimitHeldRoutes, RejectedRoutes}) ->
|
||||||
Reason = {'LimitHoldError', [T#domain_TurnoverLimit.id || T <- TurnoverLimits], LimiterError},
|
LimitsIDs = [T#domain_TurnoverLimit.id || T <- TurnoverLimits],
|
||||||
RejectedRoute = hg_routing:to_rejected_route(Route, Reason),
|
RejectedRoute = hg_route:to_rejected_route(Route, {'LimitHoldError', LimitsIDs, LimiterError}),
|
||||||
{LimitHeldRoutes, [RejectedRoute | RejectedRoutes]}.
|
{LimitHeldRoutes, [RejectedRoute | RejectedRoutes]}.
|
||||||
|
|
||||||
rollback_payment_limits(Routes, Iter, St) ->
|
rollback_payment_limits(Routes, Iter, St) ->
|
||||||
@ -3285,12 +3273,9 @@ log_cascade_attempt_context(
|
|||||||
#domain_PaymentsServiceTerms{attempt_limit = AttemptLimit},
|
#domain_PaymentsServiceTerms{attempt_limit = AttemptLimit},
|
||||||
#st{routes = AttemptedRoutes}
|
#st{routes = AttemptedRoutes}
|
||||||
) ->
|
) ->
|
||||||
_ = logger:log(
|
?LOG_MD(info, "Cascade context: merchant payment terms' attempt limit '~p', attempted routes: ~p", [
|
||||||
info,
|
AttemptLimit, AttemptedRoutes
|
||||||
"Cascade context: merchant payment terms' attempt limit '~p', attempted routes: ~p",
|
]).
|
||||||
[AttemptLimit, AttemptedRoutes],
|
|
||||||
logger:get_process_metadata()
|
|
||||||
).
|
|
||||||
|
|
||||||
get_routing_attempt_limit_value(undefined) ->
|
get_routing_attempt_limit_value(undefined) ->
|
||||||
1;
|
1;
|
||||||
@ -3365,19 +3350,19 @@ accrue_status_timing(Name, Opts, #st{timings = Timings}) ->
|
|||||||
-spec get_limit_values(st()) -> route_limit_context().
|
-spec get_limit_values(st()) -> route_limit_context().
|
||||||
get_limit_values(St) ->
|
get_limit_values(St) ->
|
||||||
{PaymentInstitution, VS, Revision} = route_args(St),
|
{PaymentInstitution, VS, Revision} = route_args(St),
|
||||||
Routes = get_candidates(PaymentInstitution, VS, Revision, St),
|
Ctx = build_routing_context(PaymentInstitution, VS, Revision, St),
|
||||||
Payment = get_payment(St),
|
Payment = get_payment(St),
|
||||||
Invoice = get_invoice(get_opts(St)),
|
Invoice = get_invoice(get_opts(St)),
|
||||||
lists:foldl(
|
lists:foldl(
|
||||||
fun(Route, Acc) ->
|
fun(Route, Acc) ->
|
||||||
PaymentRoute = hg_routing:to_payment_route(Route),
|
PaymentRoute = hg_route:to_payment_route(Route),
|
||||||
ProviderTerms = hg_routing:get_payment_terms(PaymentRoute, VS, Revision),
|
ProviderTerms = hg_routing:get_payment_terms(PaymentRoute, VS, Revision),
|
||||||
TurnoverLimits = get_turnover_limits(ProviderTerms),
|
TurnoverLimits = get_turnover_limits(ProviderTerms),
|
||||||
TurnoverLimitValues = hg_limiter:get_limit_values(TurnoverLimits, Invoice, Payment, PaymentRoute),
|
TurnoverLimitValues = hg_limiter:get_limit_values(TurnoverLimits, Invoice, Payment, PaymentRoute),
|
||||||
Acc#{PaymentRoute => TurnoverLimitValues}
|
Acc#{PaymentRoute => TurnoverLimitValues}
|
||||||
end,
|
end,
|
||||||
#{},
|
#{},
|
||||||
Routes
|
hg_routing_ctx:candidates(Ctx)
|
||||||
).
|
).
|
||||||
|
|
||||||
-spec get_limit_values(st(), opts()) -> route_limit_context().
|
-spec get_limit_values(st(), opts()) -> route_limit_context().
|
||||||
@ -3797,24 +3782,24 @@ get_route_cascade_behaviour(Route, Revision) ->
|
|||||||
|
|
||||||
-spec test() -> _.
|
-spec test() -> _.
|
||||||
|
|
||||||
-spec filter_out_attempted_routes_test_() -> [_].
|
-spec filter_attempted_routes_test_() -> [_].
|
||||||
filter_out_attempted_routes_test_() ->
|
filter_attempted_routes_test_() ->
|
||||||
[R1, R2, R3] = [
|
[R1, R2, R3] = [
|
||||||
hg_routing:new(
|
hg_route:new(
|
||||||
#domain_ProviderRef{id = 171},
|
#domain_ProviderRef{id = 171},
|
||||||
#domain_TerminalRef{id = 307},
|
#domain_TerminalRef{id = 307},
|
||||||
20,
|
20,
|
||||||
1000,
|
1000,
|
||||||
#{client_ip => <<127, 0, 0, 1>>}
|
#{client_ip => <<127, 0, 0, 1>>}
|
||||||
),
|
),
|
||||||
hg_routing:new(
|
hg_route:new(
|
||||||
#domain_ProviderRef{id = 171},
|
#domain_ProviderRef{id = 171},
|
||||||
#domain_TerminalRef{id = 344},
|
#domain_TerminalRef{id = 344},
|
||||||
80,
|
80,
|
||||||
1000,
|
1000,
|
||||||
#{}
|
#{}
|
||||||
),
|
),
|
||||||
hg_routing:new(
|
hg_route:new(
|
||||||
#domain_ProviderRef{id = 162},
|
#domain_ProviderRef{id = 162},
|
||||||
#domain_TerminalRef{id = 227},
|
#domain_TerminalRef{id = 227},
|
||||||
1,
|
1,
|
||||||
@ -3823,10 +3808,10 @@ filter_out_attempted_routes_test_() ->
|
|||||||
)
|
)
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
?_assert(
|
?_assertMatch(
|
||||||
[] ==
|
#{candidates := []},
|
||||||
filter_out_attempted_routes(
|
filter_attempted_routes(
|
||||||
[],
|
hg_routing_ctx:new([]),
|
||||||
#st{
|
#st{
|
||||||
activity = idle,
|
activity = idle,
|
||||||
routes = [
|
routes = [
|
||||||
@ -3838,16 +3823,17 @@ filter_out_attempted_routes_test_() ->
|
|||||||
}
|
}
|
||||||
)
|
)
|
||||||
),
|
),
|
||||||
?_assert(
|
?_assertMatch(
|
||||||
[] == filter_out_attempted_routes([], #st{activity = idle, routes = []})
|
#{candidates := []}, filter_attempted_routes(hg_routing_ctx:new([]), #st{activity = idle, routes = []})
|
||||||
),
|
),
|
||||||
?_assert(
|
?_assertMatch(
|
||||||
[R1, R2, R3] == filter_out_attempted_routes([R1, R2, R3], #st{activity = idle, routes = []})
|
#{candidates := [R1, R2, R3]},
|
||||||
|
filter_attempted_routes(hg_routing_ctx:new([R1, R2, R3]), #st{activity = idle, routes = []})
|
||||||
),
|
),
|
||||||
?_assert(
|
?_assertMatch(
|
||||||
[R1, R2] ==
|
#{candidates := [R1, R2]},
|
||||||
filter_out_attempted_routes(
|
filter_attempted_routes(
|
||||||
[R1, R2, R3],
|
hg_routing_ctx:new([R1, R2, R3]),
|
||||||
#st{
|
#st{
|
||||||
activity = idle,
|
activity = idle,
|
||||||
routes = [
|
routes = [
|
||||||
@ -3859,10 +3845,10 @@ filter_out_attempted_routes_test_() ->
|
|||||||
}
|
}
|
||||||
)
|
)
|
||||||
),
|
),
|
||||||
?_assert(
|
?_assertMatch(
|
||||||
[] ==
|
#{candidates := []},
|
||||||
filter_out_attempted_routes(
|
filter_attempted_routes(
|
||||||
[R1, R2, R3],
|
hg_routing_ctx:new([R1, R2, R3]),
|
||||||
#st{
|
#st{
|
||||||
activity = idle,
|
activity = idle,
|
||||||
routes = [
|
routes = [
|
||||||
|
@ -11,7 +11,7 @@
|
|||||||
-type turnover_limit() :: dmsl_domain_thrift:'TurnoverLimit'().
|
-type turnover_limit() :: dmsl_domain_thrift:'TurnoverLimit'().
|
||||||
-type invoice() :: dmsl_domain_thrift:'Invoice'().
|
-type invoice() :: dmsl_domain_thrift:'Invoice'().
|
||||||
-type payment() :: dmsl_domain_thrift:'InvoicePayment'().
|
-type payment() :: dmsl_domain_thrift:'InvoicePayment'().
|
||||||
-type route() :: hg_routing:payment_route().
|
-type route() :: hg_route:payment_route().
|
||||||
-type refund() :: hg_invoice_payment:domain_refund().
|
-type refund() :: hg_invoice_payment:domain_refund().
|
||||||
-type cash() :: dmsl_domain_thrift:'Cash'().
|
-type cash() :: dmsl_domain_thrift:'Cash'().
|
||||||
-type handling_flag() :: ignore_business_error.
|
-type handling_flag() :: ignore_business_error.
|
||||||
|
@ -20,7 +20,7 @@
|
|||||||
%%
|
%%
|
||||||
|
|
||||||
-type trx_info() :: hg_invoice_payment:trx_info().
|
-type trx_info() :: hg_invoice_payment:trx_info().
|
||||||
-type route() :: hg_routing:payment_route().
|
-type route() :: hg_route:payment_route().
|
||||||
|
|
||||||
-type change() :: dmsl_payproc_thrift:'SessionChangePayload'().
|
-type change() :: dmsl_payproc_thrift:'SessionChangePayload'().
|
||||||
-type proxy_state() :: dmsl_base_thrift:'Opaque'().
|
-type proxy_state() :: dmsl_base_thrift:'Opaque'().
|
||||||
|
@ -51,7 +51,7 @@
|
|||||||
-type rec_payment_tool_change() :: dmsl_payproc_thrift:'RecurrentPaymentToolChange'().
|
-type rec_payment_tool_change() :: dmsl_payproc_thrift:'RecurrentPaymentToolChange'().
|
||||||
-type rec_payment_tool_params() :: dmsl_payproc_thrift:'RecurrentPaymentToolParams'().
|
-type rec_payment_tool_params() :: dmsl_payproc_thrift:'RecurrentPaymentToolParams'().
|
||||||
|
|
||||||
-type route() :: hg_routing:payment_route().
|
-type route() :: hg_route:payment_route().
|
||||||
-type risk_score() :: hg_inspector:risk_score().
|
-type risk_score() :: hg_inspector:risk_score().
|
||||||
-type shop() :: dmsl_domain_thrift:'Shop'().
|
-type shop() :: dmsl_domain_thrift:'Shop'().
|
||||||
-type party() :: dmsl_domain_thrift:'Party'().
|
-type party() :: dmsl_domain_thrift:'Party'().
|
||||||
@ -251,7 +251,7 @@ init(EncodedParams, #{id := RecPaymentToolID}) ->
|
|||||||
client_ip => get_client_info_ip(Params#payproc_RecurrentPaymentToolParams.payment_resource)
|
client_ip => get_client_info_ip(Params#payproc_RecurrentPaymentToolParams.payment_resource)
|
||||||
}),
|
}),
|
||||||
{ChosenRoute, ChoiceContext} = hg_routing:choose_route(NonFailRatedRoutes),
|
{ChosenRoute, ChoiceContext} = hg_routing:choose_route(NonFailRatedRoutes),
|
||||||
ChosenPaymentRoute = hg_routing:to_payment_route(ChosenRoute),
|
ChosenPaymentRoute = hg_route:to_payment_route(ChosenRoute),
|
||||||
LoggerMetadata = hg_routing:get_logger_metadata(ChoiceContext, Revision),
|
LoggerMetadata = hg_routing:get_logger_metadata(ChoiceContext, Revision),
|
||||||
_ = logger:log(info, "Routing decision made", #{routing => LoggerMetadata}),
|
_ = logger:log(info, "Routing decision made", #{routing => LoggerMetadata}),
|
||||||
RecPaymentTool2 = set_minimal_payment_cost(RecPaymentTool, ChosenPaymentRoute, VS, Revision),
|
RecPaymentTool2 = set_minimal_payment_cost(RecPaymentTool, ChosenPaymentRoute, VS, Revision),
|
||||||
@ -274,20 +274,13 @@ init(EncodedParams, #{id := RecPaymentToolID}) ->
|
|||||||
|
|
||||||
gather_routes(PaymentInstitution, VS, Revision, Ctx) ->
|
gather_routes(PaymentInstitution, VS, Revision, Ctx) ->
|
||||||
Predestination = recurrent_paytool,
|
Predestination = recurrent_paytool,
|
||||||
case
|
RoutingCtx = hg_routing:gather_routes(Predestination, PaymentInstitution, VS, Revision, Ctx),
|
||||||
hg_routing:gather_routes(
|
case {hg_routing_ctx:candidates(RoutingCtx), hg_routing_ctx:error(RoutingCtx)} of
|
||||||
Predestination,
|
{[], undefined} ->
|
||||||
PaymentInstitution,
|
throw({no_route_found, {unknown, hg_routing_ctx:rejected_routes(RoutingCtx)}});
|
||||||
VS,
|
{Routes, undefined} ->
|
||||||
Revision,
|
|
||||||
Ctx
|
|
||||||
)
|
|
||||||
of
|
|
||||||
{ok, {[], RejectedRoutes}} ->
|
|
||||||
throw({no_route_found, {unknown, RejectedRoutes}});
|
|
||||||
{ok, {Routes, _RejectContext}} ->
|
|
||||||
Routes;
|
Routes;
|
||||||
{error, {misconfiguration, _Reason}} ->
|
{_Routes, {misconfiguration, _Reason}} ->
|
||||||
throw({no_route_found, misconfiguration})
|
throw({no_route_found, misconfiguration})
|
||||||
end.
|
end.
|
||||||
|
|
||||||
|
191
apps/hellgate/src/hg_route.erl
Normal file
191
apps/hellgate/src/hg_route.erl
Normal file
@ -0,0 +1,191 @@
|
|||||||
|
-module(hg_route).
|
||||||
|
|
||||||
|
-export([new/2]).
|
||||||
|
-export([new/4]).
|
||||||
|
-export([new/5]).
|
||||||
|
-export([new/6]).
|
||||||
|
-export([provider_ref/1]).
|
||||||
|
-export([terminal_ref/1]).
|
||||||
|
-export([priority/1]).
|
||||||
|
-export([weight/1]).
|
||||||
|
-export([set_weight/2]).
|
||||||
|
-export([pin/1]).
|
||||||
|
-export([fd_overrides/1]).
|
||||||
|
|
||||||
|
-export([equal/2]).
|
||||||
|
|
||||||
|
-export([from_payment_route/1]).
|
||||||
|
-export([to_payment_route/1]).
|
||||||
|
-export([to_rejected_route/2]).
|
||||||
|
|
||||||
|
%%
|
||||||
|
|
||||||
|
-include("domain.hrl").
|
||||||
|
|
||||||
|
-record(route, {
|
||||||
|
provider_ref :: dmsl_domain_thrift:'ProviderRef'(),
|
||||||
|
terminal_ref :: dmsl_domain_thrift:'TerminalRef'(),
|
||||||
|
weight :: integer(),
|
||||||
|
priority :: integer(),
|
||||||
|
pin :: pin(),
|
||||||
|
fd_overrides :: fd_overrides()
|
||||||
|
}).
|
||||||
|
|
||||||
|
-type t() :: #route{}.
|
||||||
|
-type payment_route() :: dmsl_domain_thrift:'PaymentRoute'().
|
||||||
|
-type route_rejection_reason() :: {atom(), _DescOrAttrs} | {atom(), _DescOrAttrs1, _DescOrAttrs2}.
|
||||||
|
-type rejected_route() :: {provider_ref(), terminal_ref(), route_rejection_reason()}.
|
||||||
|
-type provider_ref() :: dmsl_domain_thrift:'ProviderRef'().
|
||||||
|
-type terminal_ref() :: dmsl_domain_thrift:'TerminalRef'().
|
||||||
|
-type fd_overrides() :: dmsl_domain_thrift:'RouteFaultDetectorOverrides'().
|
||||||
|
|
||||||
|
-type currency() :: dmsl_domain_thrift:'CurrencyRef'().
|
||||||
|
-type payment_tool() :: dmsl_domain_thrift:'PaymentTool'().
|
||||||
|
-type party_id() :: dmsl_domain_thrift:'PartyID'().
|
||||||
|
-type client_ip() :: dmsl_domain_thrift:'IPAddress'().
|
||||||
|
|
||||||
|
-type pin() :: #{
|
||||||
|
currency => currency(),
|
||||||
|
payment_tool => payment_tool(),
|
||||||
|
party_id => party_id(),
|
||||||
|
client_ip => client_ip() | undefined
|
||||||
|
}.
|
||||||
|
|
||||||
|
-export_type([t/0]).
|
||||||
|
-export_type([provider_ref/0]).
|
||||||
|
-export_type([terminal_ref/0]).
|
||||||
|
-export_type([payment_route/0]).
|
||||||
|
-export_type([rejected_route/0]).
|
||||||
|
|
||||||
|
%%
|
||||||
|
|
||||||
|
-spec new(provider_ref(), terminal_ref()) -> t().
|
||||||
|
new(ProviderRef, TerminalRef) ->
|
||||||
|
new(
|
||||||
|
ProviderRef,
|
||||||
|
TerminalRef,
|
||||||
|
?DOMAIN_CANDIDATE_WEIGHT,
|
||||||
|
?DOMAIN_CANDIDATE_PRIORITY
|
||||||
|
).
|
||||||
|
|
||||||
|
-spec new(provider_ref(), terminal_ref(), integer() | undefined, integer()) -> t().
|
||||||
|
new(ProviderRef, TerminalRef, Weight, Priority) ->
|
||||||
|
new(ProviderRef, TerminalRef, Weight, Priority, #{}).
|
||||||
|
|
||||||
|
-spec new(provider_ref(), terminal_ref(), integer() | undefined, integer(), pin()) -> t().
|
||||||
|
new(ProviderRef, TerminalRef, undefined, Priority, Pin) ->
|
||||||
|
new(ProviderRef, TerminalRef, ?DOMAIN_CANDIDATE_WEIGHT, Priority, Pin);
|
||||||
|
new(ProviderRef, TerminalRef, Weight, Priority, Pin) ->
|
||||||
|
new(ProviderRef, TerminalRef, Weight, Priority, Pin, #domain_RouteFaultDetectorOverrides{}).
|
||||||
|
|
||||||
|
-spec new(provider_ref(), terminal_ref(), integer(), integer(), pin(), fd_overrides() | undefined) -> t().
|
||||||
|
new(ProviderRef, TerminalRef, Weight, Priority, Pin, undefined) ->
|
||||||
|
new(ProviderRef, TerminalRef, Weight, Priority, Pin, #domain_RouteFaultDetectorOverrides{});
|
||||||
|
new(ProviderRef, TerminalRef, Weight, Priority, Pin, FdOverrides) ->
|
||||||
|
#route{
|
||||||
|
provider_ref = ProviderRef,
|
||||||
|
terminal_ref = TerminalRef,
|
||||||
|
weight = Weight,
|
||||||
|
priority = Priority,
|
||||||
|
pin = Pin,
|
||||||
|
fd_overrides = FdOverrides
|
||||||
|
}.
|
||||||
|
|
||||||
|
-spec provider_ref(t()) -> provider_ref().
|
||||||
|
provider_ref(#route{provider_ref = Ref}) ->
|
||||||
|
Ref.
|
||||||
|
|
||||||
|
-spec terminal_ref(t()) -> terminal_ref().
|
||||||
|
terminal_ref(#route{terminal_ref = Ref}) ->
|
||||||
|
Ref.
|
||||||
|
|
||||||
|
-spec priority(t()) -> integer().
|
||||||
|
priority(#route{priority = Priority}) ->
|
||||||
|
Priority.
|
||||||
|
|
||||||
|
-spec weight(t()) -> integer().
|
||||||
|
weight(#route{weight = Weight}) ->
|
||||||
|
Weight.
|
||||||
|
|
||||||
|
-spec pin(t()) -> pin() | undefined.
|
||||||
|
pin(#route{pin = Pin}) ->
|
||||||
|
Pin.
|
||||||
|
|
||||||
|
-spec fd_overrides(t()) -> fd_overrides().
|
||||||
|
fd_overrides(#route{fd_overrides = FdOverrides}) ->
|
||||||
|
FdOverrides.
|
||||||
|
|
||||||
|
-spec set_weight(integer(), t()) -> t().
|
||||||
|
set_weight(Weight, Route) ->
|
||||||
|
Route#route{weight = Weight}.
|
||||||
|
|
||||||
|
-spec equal(R, R) -> boolean() when
|
||||||
|
R :: t() | rejected_route() | payment_route() | {provider_ref(), terminal_ref()}.
|
||||||
|
equal(A, B) ->
|
||||||
|
routes_equal_(route_ref(A), route_ref(B)).
|
||||||
|
|
||||||
|
%%
|
||||||
|
|
||||||
|
-spec from_payment_route(payment_route()) -> t().
|
||||||
|
from_payment_route(Route) ->
|
||||||
|
?route(ProviderRef, TerminalRef) = Route,
|
||||||
|
new(ProviderRef, TerminalRef).
|
||||||
|
|
||||||
|
-spec to_payment_route(t()) -> payment_route().
|
||||||
|
to_payment_route(#route{} = Route) ->
|
||||||
|
?route(provider_ref(Route), terminal_ref(Route)).
|
||||||
|
|
||||||
|
-spec to_rejected_route(t(), route_rejection_reason()) -> rejected_route().
|
||||||
|
to_rejected_route(Route, Reason) ->
|
||||||
|
{provider_ref(Route), terminal_ref(Route), Reason}.
|
||||||
|
|
||||||
|
%%
|
||||||
|
|
||||||
|
routes_equal_(A, A) when A =/= undefined ->
|
||||||
|
true;
|
||||||
|
routes_equal_(_A, _B) ->
|
||||||
|
false.
|
||||||
|
|
||||||
|
route_ref(#route{provider_ref = Prv, terminal_ref = Trm}) ->
|
||||||
|
{Prv, Trm};
|
||||||
|
route_ref(#domain_PaymentRoute{provider = Prv, terminal = Trm}) ->
|
||||||
|
{Prv, Trm};
|
||||||
|
route_ref({Prv, Trm}) ->
|
||||||
|
{Prv, Trm};
|
||||||
|
route_ref({Prv, Trm, _RejectionReason}) ->
|
||||||
|
{Prv, Trm};
|
||||||
|
route_ref(_) ->
|
||||||
|
undefined.
|
||||||
|
|
||||||
|
%%
|
||||||
|
|
||||||
|
-ifdef(TEST).
|
||||||
|
|
||||||
|
-include_lib("eunit/include/eunit.hrl").
|
||||||
|
|
||||||
|
-define(prv(ID), #domain_ProviderRef{id = ID}).
|
||||||
|
-define(trm(ID), #domain_TerminalRef{id = ID}).
|
||||||
|
|
||||||
|
-spec test() -> _.
|
||||||
|
|
||||||
|
-spec routes_equality_test_() -> [_].
|
||||||
|
routes_equality_test_() ->
|
||||||
|
lists:flatten([
|
||||||
|
[?_assert(equal(A, B)) || {A, B} <- route_pairs({?prv(1), ?trm(1)}, {?prv(1), ?trm(1)})],
|
||||||
|
[?_assertNot(equal(A, B)) || {A, B} <- route_pairs({?prv(1), ?trm(1)}, {?prv(1), ?trm(2)})],
|
||||||
|
[?_assertNot(equal(A, B)) || {A, B} <- route_pairs({?prv(1), ?trm(1)}, {?prv(2), ?trm(1)})],
|
||||||
|
[?_assertNot(equal(A, B)) || {A, B} <- route_pairs({?prv(1), ?trm(1)}, {?prv(2), ?trm(2)})]
|
||||||
|
]).
|
||||||
|
|
||||||
|
route_pairs({Prv1, Trm1}, {Prv2, Trm2}) ->
|
||||||
|
Fs = [
|
||||||
|
fun(X) -> X end,
|
||||||
|
fun to_payment_route/1,
|
||||||
|
fun(X) -> to_rejected_route(X, {test, <<"whatever">>}) end,
|
||||||
|
fun(X) -> {provider_ref(X), terminal_ref(X)} end
|
||||||
|
],
|
||||||
|
A = new(Prv1, Trm1),
|
||||||
|
B = new(Prv2, Trm2),
|
||||||
|
lists:flatten([[{F1(A), F2(B)} || F1 <- Fs] || F2 <- Fs]).
|
||||||
|
|
||||||
|
-endif.
|
@ -7,55 +7,30 @@
|
|||||||
-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_routes/5]).
|
-export([gather_routes/5]).
|
||||||
|
-export([rate_routes/1]).
|
||||||
-export([choose_route/1]).
|
-export([choose_route/1]).
|
||||||
|
-export([choose_rated_route/1]).
|
||||||
|
|
||||||
-export([get_payment_terms/3]).
|
-export([get_payment_terms/3]).
|
||||||
|
|
||||||
-export([get_logger_metadata/2]).
|
-export([get_logger_metadata/2]).
|
||||||
|
|
||||||
-export([from_payment_route/1]).
|
|
||||||
-export([new/2]).
|
|
||||||
-export([new/4]).
|
|
||||||
-export([new/5]).
|
|
||||||
-export([new/6]).
|
|
||||||
-export([to_payment_route/1]).
|
|
||||||
-export([to_rejected_route/2]).
|
|
||||||
-export([provider_ref/1]).
|
|
||||||
-export([terminal_ref/1]).
|
|
||||||
|
|
||||||
-export([prepare_log_message/1]).
|
-export([prepare_log_message/1]).
|
||||||
|
|
||||||
%%
|
%%
|
||||||
|
|
||||||
|
-export([filter_by_critical_provider_status/1]).
|
||||||
|
-export([choose_route_with_ctx/1]).
|
||||||
|
|
||||||
|
%%
|
||||||
|
|
||||||
-include("domain.hrl").
|
-include("domain.hrl").
|
||||||
|
|
||||||
-record(route, {
|
|
||||||
provider_ref :: dmsl_domain_thrift:'ProviderRef'(),
|
|
||||||
terminal_ref :: dmsl_domain_thrift:'TerminalRef'(),
|
|
||||||
weight :: integer(),
|
|
||||||
priority :: integer(),
|
|
||||||
pin :: pin(),
|
|
||||||
fd_overrides :: fd_overrides()
|
|
||||||
}).
|
|
||||||
|
|
||||||
-type pin() :: #{
|
|
||||||
currency => currency(),
|
|
||||||
payment_tool => payment_tool(),
|
|
||||||
party_id => party_id(),
|
|
||||||
client_ip => client_ip() | undefined
|
|
||||||
}.
|
|
||||||
-type route() :: #route{}.
|
|
||||||
-type payment_terms() :: dmsl_domain_thrift:'PaymentsProvisionTerms'().
|
-type payment_terms() :: dmsl_domain_thrift:'PaymentsProvisionTerms'().
|
||||||
-type payment_institution() :: dmsl_domain_thrift:'PaymentInstitution'().
|
-type payment_institution() :: dmsl_domain_thrift:'PaymentInstitution'().
|
||||||
-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 rejected_route() :: {provider_ref(), terminal_ref(), Reason :: term()}.
|
|
||||||
|
|
||||||
-type provider_ref() :: dmsl_domain_thrift:'ProviderRef'().
|
|
||||||
-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 terminal_priority_rating() :: integer().
|
-type terminal_priority_rating() :: integer().
|
||||||
@ -75,13 +50,13 @@
|
|||||||
|
|
||||||
-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 fail_rated_route() :: {route(), provider_status()}.
|
-type fail_rated_route() :: {hg_route:t(), provider_status()}.
|
||||||
|
|
||||||
-type scored_route() :: {route_scores(), route()}.
|
-type scored_route() :: {route_scores(), hg_route:t()}.
|
||||||
|
|
||||||
-type route_choice_context() :: #{
|
-type route_choice_context() :: #{
|
||||||
chosen_route => route(),
|
chosen_route => hg_route:t(),
|
||||||
preferable_route => route(),
|
preferable_route => hg_route:t(),
|
||||||
% 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()
|
||||||
}.
|
}.
|
||||||
@ -100,7 +75,6 @@
|
|||||||
|
|
||||||
-type varset() :: hg_varset:varset().
|
-type varset() :: hg_varset:varset().
|
||||||
-type revision() :: hg_domain:revision().
|
-type revision() :: hg_domain:revision().
|
||||||
-type fd_overrides() :: dmsl_domain_thrift:'RouteFaultDetectorOverrides'().
|
|
||||||
|
|
||||||
-record(route_scores, {
|
-record(route_scores, {
|
||||||
availability_condition :: condition_score(),
|
availability_condition :: condition_score(),
|
||||||
@ -115,84 +89,56 @@
|
|||||||
-type route_scores() :: #route_scores{}.
|
-type route_scores() :: #route_scores{}.
|
||||||
-type misconfiguration_error() :: {misconfiguration, {routing_decisions, _} | {routing_candidate, _}}.
|
-type misconfiguration_error() :: {misconfiguration, {routing_decisions, _} | {routing_candidate, _}}.
|
||||||
|
|
||||||
-export_type([route/0]).
|
|
||||||
-export_type([payment_route/0]).
|
|
||||||
-export_type([rejected_route/0]).
|
|
||||||
-export_type([route_predestination/0]).
|
-export_type([route_predestination/0]).
|
||||||
|
-export_type([route_choice_context/0]).
|
||||||
|
-export_type([fail_rated_route/0]).
|
||||||
|
|
||||||
%% Route accessors
|
%%
|
||||||
|
|
||||||
-spec new(provider_ref(), terminal_ref()) -> route().
|
-spec filter_by_critical_provider_status(T) -> T when T :: hg_routing_ctx:t().
|
||||||
new(ProviderRef, TerminalRef) ->
|
filter_by_critical_provider_status(Ctx) ->
|
||||||
new(
|
RoutesFailRates = rate_routes(hg_routing_ctx:candidates(Ctx)),
|
||||||
ProviderRef,
|
lists:foldr(
|
||||||
TerminalRef,
|
fun
|
||||||
?DOMAIN_CANDIDATE_WEIGHT,
|
({R, {{dead, _} = AvailabilityStatus, _ConversionStatus}}, C) ->
|
||||||
?DOMAIN_CANDIDATE_PRIORITY
|
R1 = hg_route:to_rejected_route(R, {'ProviderDead', AvailabilityStatus}),
|
||||||
|
hg_routing_ctx:reject(adapter_unavailable, R1, C);
|
||||||
|
({R, {_AvailabitlyStatus, ConversionStatus = {lacking, _}}}, C) ->
|
||||||
|
R1 = hg_route:to_rejected_route(R, {'ConversionLacking', ConversionStatus}),
|
||||||
|
hg_routing_ctx:reject(provider_conversion_is_too_low, R1, C);
|
||||||
|
({_R, _ProviderStatus}, C) ->
|
||||||
|
C
|
||||||
|
end,
|
||||||
|
hg_routing_ctx:with_fail_rates(RoutesFailRates, Ctx),
|
||||||
|
RoutesFailRates
|
||||||
).
|
).
|
||||||
|
|
||||||
-spec new(provider_ref(), terminal_ref(), integer() | undefined, integer()) -> route().
|
-spec choose_route_with_ctx(T) -> T when T :: hg_routing_ctx:t().
|
||||||
new(ProviderRef, TerminalRef, Weight, Priority) ->
|
choose_route_with_ctx(Ctx) ->
|
||||||
new(ProviderRef, TerminalRef, Weight, Priority, #{}).
|
Candidates = hg_routing_ctx:candidates(Ctx),
|
||||||
|
{ChoosenRoute, ChoiceContext} =
|
||||||
|
case hg_routing_ctx:fail_rates(Ctx) of
|
||||||
|
undefined ->
|
||||||
|
choose_route(Candidates);
|
||||||
|
FailRates ->
|
||||||
|
RatedCandidates = filter_rated_routes_with_candidates(FailRates, Candidates),
|
||||||
|
choose_rated_route(RatedCandidates)
|
||||||
|
end,
|
||||||
|
hg_routing_ctx:set_choosen(ChoosenRoute, ChoiceContext, Ctx).
|
||||||
|
|
||||||
-spec new(provider_ref(), terminal_ref(), integer() | undefined, integer(), pin()) -> route().
|
filter_rated_routes_with_candidates(FailRates, Candidates) ->
|
||||||
new(ProviderRef, TerminalRef, undefined, Priority, Pin) ->
|
lists:foldr(
|
||||||
new(ProviderRef, TerminalRef, ?DOMAIN_CANDIDATE_WEIGHT, Priority, Pin);
|
fun({R, _PS} = FR, Res) ->
|
||||||
new(ProviderRef, TerminalRef, Weight, Priority, Pin) ->
|
case lists:any(fun(CR) -> hg_route:equal(CR, R) end, Candidates) of
|
||||||
new(ProviderRef, TerminalRef, Weight, Priority, Pin, #domain_RouteFaultDetectorOverrides{}).
|
true -> [FR | Res];
|
||||||
|
_Else -> Res
|
||||||
|
end
|
||||||
|
end,
|
||||||
|
[],
|
||||||
|
FailRates
|
||||||
|
).
|
||||||
|
|
||||||
-spec new(provider_ref(), terminal_ref(), integer(), integer(), pin(), fd_overrides() | undefined) -> route().
|
%%
|
||||||
new(ProviderRef, TerminalRef, Weight, Priority, Pin, undefined) ->
|
|
||||||
new(ProviderRef, TerminalRef, Weight, Priority, Pin, #domain_RouteFaultDetectorOverrides{});
|
|
||||||
new(ProviderRef, TerminalRef, Weight, Priority, Pin, FdOverrides) ->
|
|
||||||
#route{
|
|
||||||
provider_ref = ProviderRef,
|
|
||||||
terminal_ref = TerminalRef,
|
|
||||||
weight = Weight,
|
|
||||||
priority = Priority,
|
|
||||||
pin = Pin,
|
|
||||||
fd_overrides = FdOverrides
|
|
||||||
}.
|
|
||||||
|
|
||||||
-spec provider_ref(route()) -> provider_ref().
|
|
||||||
provider_ref(#route{provider_ref = Ref}) ->
|
|
||||||
Ref.
|
|
||||||
|
|
||||||
-spec terminal_ref(route()) -> terminal_ref().
|
|
||||||
terminal_ref(#route{terminal_ref = Ref}) ->
|
|
||||||
Ref.
|
|
||||||
|
|
||||||
-spec priority(route()) -> integer().
|
|
||||||
priority(#route{priority = Priority}) ->
|
|
||||||
Priority.
|
|
||||||
|
|
||||||
-spec weight(route()) -> integer().
|
|
||||||
weight(#route{weight = Weight}) ->
|
|
||||||
Weight.
|
|
||||||
|
|
||||||
-spec pin(route()) -> pin() | undefined.
|
|
||||||
pin(#route{pin = Pin}) ->
|
|
||||||
Pin.
|
|
||||||
|
|
||||||
fd_overrides(#route{fd_overrides = FdOverrides}) ->
|
|
||||||
FdOverrides.
|
|
||||||
|
|
||||||
-spec from_payment_route(payment_route()) -> route().
|
|
||||||
from_payment_route(Route) ->
|
|
||||||
?route(ProviderRef, TerminalRef) = Route,
|
|
||||||
new(ProviderRef, TerminalRef).
|
|
||||||
|
|
||||||
-spec to_payment_route(route()) -> payment_route().
|
|
||||||
to_payment_route(#route{} = Route) ->
|
|
||||||
?route(provider_ref(Route), terminal_ref(Route)).
|
|
||||||
|
|
||||||
-spec to_rejected_route(route(), term()) -> rejected_route().
|
|
||||||
to_rejected_route(Route, Reason) ->
|
|
||||||
{provider_ref(Route), terminal_ref(Route), Reason}.
|
|
||||||
|
|
||||||
-spec set_weight(integer(), route()) -> route().
|
|
||||||
set_weight(Weight, Route) ->
|
|
||||||
Route#route{weight = Weight}.
|
|
||||||
|
|
||||||
-spec prepare_log_message(misconfiguration_error()) -> {io:format(), [term()]}.
|
-spec prepare_log_message(misconfiguration_error()) -> {io:format(), [term()]}.
|
||||||
prepare_log_message({misconfiguration, {routing_decisions, Details}}) ->
|
prepare_log_message({misconfiguration, {routing_decisions, Details}}) ->
|
||||||
@ -202,17 +148,10 @@ prepare_log_message({misconfiguration, {routing_candidate, Candidate}}) ->
|
|||||||
|
|
||||||
%%
|
%%
|
||||||
|
|
||||||
-spec gather_routes(
|
-spec gather_routes(route_predestination(), payment_institution(), varset(), revision(), gather_route_context()) ->
|
||||||
route_predestination(),
|
hg_routing_ctx:t().
|
||||||
payment_institution(),
|
|
||||||
varset(),
|
|
||||||
revision(),
|
|
||||||
gather_route_context()
|
|
||||||
) ->
|
|
||||||
{ok, {[route()], [rejected_route()]}}
|
|
||||||
| {error, misconfiguration_error()}.
|
|
||||||
gather_routes(_, #domain_PaymentInstitution{payment_routing_rules = undefined}, _, _, _) ->
|
gather_routes(_, #domain_PaymentInstitution{payment_routing_rules = undefined}, _, _, _) ->
|
||||||
{ok, {[], []}};
|
hg_routing_ctx:new([]);
|
||||||
gather_routes(Predestination, #domain_PaymentInstitution{payment_routing_rules = RoutingRules}, VS, Revision, Ctx) ->
|
gather_routes(Predestination, #domain_PaymentInstitution{payment_routing_rules = RoutingRules}, VS, Revision, Ctx) ->
|
||||||
#domain_RoutingRules{
|
#domain_RoutingRules{
|
||||||
policies = Policies,
|
policies = Policies,
|
||||||
@ -224,10 +163,14 @@ gather_routes(Predestination, #domain_PaymentInstitution{payment_routing_rules =
|
|||||||
collect_routes(Predestination, Candidates, VS, Revision, Ctx),
|
collect_routes(Predestination, Candidates, VS, Revision, Ctx),
|
||||||
get_table_prohibitions(Prohibitions, VS, Revision)
|
get_table_prohibitions(Prohibitions, VS, Revision)
|
||||||
),
|
),
|
||||||
{ok, {Accepted, RejectedRoutes}}
|
lists:foldr(
|
||||||
|
fun(R, C) -> hg_routing_ctx:reject(forbidden, R, C) end,
|
||||||
|
hg_routing_ctx:new(Accepted),
|
||||||
|
lists:reverse(RejectedRoutes)
|
||||||
|
)
|
||||||
catch
|
catch
|
||||||
throw:{misconfiguration, _Reason} = Error ->
|
throw:{misconfiguration, _Reason} = Error ->
|
||||||
{error, Error}
|
hg_routing_ctx:set_error(Error, hg_routing_ctx:new([]))
|
||||||
end.
|
end.
|
||||||
|
|
||||||
get_table_prohibitions(Prohibitions, VS, Revision) ->
|
get_table_prohibitions(Prohibitions, VS, Revision) ->
|
||||||
@ -270,7 +213,8 @@ collect_routes(Predestination, Candidates, VS, Revision, Ctx) ->
|
|||||||
weight = Weight,
|
weight = Weight,
|
||||||
pin = Pin
|
pin = Pin
|
||||||
} = Candidate,
|
} = Candidate,
|
||||||
% Looks like overhead, we got Terminal only for provider_ref. Maybe we can remove provider_ref from route().
|
% Looks like overhead, we got Terminal only for provider_ref. Maybe
|
||||||
|
% we can remove provider_ref from hg_route:t().
|
||||||
% https://github.com/rbkmoney/hellgate/pull/583#discussion_r682745123
|
% https://github.com/rbkmoney/hellgate/pull/583#discussion_r682745123
|
||||||
#domain_Terminal{
|
#domain_Terminal{
|
||||||
provider_ref = ProviderRef,
|
provider_ref = ProviderRef,
|
||||||
@ -279,7 +223,7 @@ collect_routes(Predestination, Candidates, VS, Revision, Ctx) ->
|
|||||||
GatheredPinInfo = gather_pin_info(Pin, Ctx),
|
GatheredPinInfo = gather_pin_info(Pin, Ctx),
|
||||||
try
|
try
|
||||||
true = acceptable_terminal(Predestination, ProviderRef, TerminalRef, VS, Revision),
|
true = acceptable_terminal(Predestination, ProviderRef, TerminalRef, VS, Revision),
|
||||||
Route = new(ProviderRef, TerminalRef, Weight, Priority, GatheredPinInfo, FdOverrides),
|
Route = hg_route:new(ProviderRef, TerminalRef, Weight, Priority, GatheredPinInfo, FdOverrides),
|
||||||
{[Route | Accepted], Rejected}
|
{[Route | Accepted], Rejected}
|
||||||
catch
|
catch
|
||||||
{rejected, Reason} ->
|
{rejected, Reason} ->
|
||||||
@ -307,13 +251,12 @@ gather_pin_info(#domain_RoutingPin{features = Features}, Ctx) ->
|
|||||||
filter_routes({Routes, Rejected}, Prohibitions) ->
|
filter_routes({Routes, Rejected}, Prohibitions) ->
|
||||||
lists:foldr(
|
lists:foldr(
|
||||||
fun(Route, {AccIn, RejectedIn}) ->
|
fun(Route, {AccIn, RejectedIn}) ->
|
||||||
TRef = terminal_ref(Route),
|
TRef = hg_route:terminal_ref(Route),
|
||||||
case maps:find(TRef, Prohibitions) of
|
case maps:find(TRef, Prohibitions) of
|
||||||
error ->
|
error ->
|
||||||
{[Route | AccIn], RejectedIn};
|
{[Route | AccIn], RejectedIn};
|
||||||
{ok, Description} ->
|
{ok, Description} ->
|
||||||
PRef = provider_ref(Route),
|
RejectedOut = [hg_route:to_rejected_route(Route, {'RoutingRule', Description}) | RejectedIn],
|
||||||
RejectedOut = [{PRef, TRef, {'RoutingRule', Description}} | RejectedIn],
|
|
||||||
{AccIn, RejectedOut}
|
{AccIn, RejectedOut}
|
||||||
end
|
end
|
||||||
end,
|
end,
|
||||||
@ -332,16 +275,16 @@ compute_rule_set(RuleSetRef, VS, Revision) ->
|
|||||||
),
|
),
|
||||||
RuleSet.
|
RuleSet.
|
||||||
|
|
||||||
-spec gather_fail_rates([route()]) -> [fail_rated_route()].
|
-spec rate_routes([hg_route:t()]) -> [fail_rated_route()].
|
||||||
gather_fail_rates(Routes) ->
|
rate_routes(Routes) ->
|
||||||
score_routes_with_fault_detector(Routes).
|
score_routes_with_fault_detector(Routes).
|
||||||
|
|
||||||
-spec choose_route([route()]) -> {route(), route_choice_context()}.
|
-spec choose_route([hg_route:t()]) -> {hg_route:t(), route_choice_context()}.
|
||||||
choose_route(Routes) ->
|
choose_route(Routes) ->
|
||||||
FailRatedRoutes = gather_fail_rates(Routes),
|
FailRatedRoutes = rate_routes(Routes),
|
||||||
choose_rated_route(FailRatedRoutes).
|
choose_rated_route(FailRatedRoutes).
|
||||||
|
|
||||||
-spec choose_rated_route([fail_rated_route()]) -> {route(), route_choice_context()}.
|
-spec choose_rated_route([fail_rated_route()]) -> {hg_route:t(), 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),
|
||||||
@ -413,15 +356,15 @@ format_logger_metadata(Meta, Route, Revision) when
|
|||||||
Meta =:= chosen_route;
|
Meta =:= chosen_route;
|
||||||
Meta =:= preferable_route
|
Meta =:= preferable_route
|
||||||
->
|
->
|
||||||
ProviderRef = #domain_ProviderRef{id = ProviderID} = provider_ref(Route),
|
ProviderRef = #domain_ProviderRef{id = ProviderID} = hg_route:provider_ref(Route),
|
||||||
TerminalRef = #domain_TerminalRef{id = TerminalID} = terminal_ref(Route),
|
TerminalRef = #domain_TerminalRef{id = TerminalID} = hg_route:terminal_ref(Route),
|
||||||
#domain_Provider{name = ProviderName} = hg_domain:get(Revision, {provider, ProviderRef}),
|
#domain_Provider{name = ProviderName} = hg_domain:get(Revision, {provider, ProviderRef}),
|
||||||
#domain_Terminal{name = TerminalName} = hg_domain:get(Revision, {terminal, TerminalRef}),
|
#domain_Terminal{name = TerminalName} = hg_domain:get(Revision, {terminal, TerminalRef}),
|
||||||
genlib_map:compact(#{
|
genlib_map:compact(#{
|
||||||
provider => #{id => ProviderID, name => ProviderName},
|
provider => #{id => ProviderID, name => ProviderName},
|
||||||
terminal => #{id => TerminalID, name => TerminalName},
|
terminal => #{id => TerminalID, name => TerminalName},
|
||||||
priority => priority(Route),
|
priority => hg_route:priority(Route),
|
||||||
weight => weight(Route)
|
weight => hg_route:weight(Route)
|
||||||
}).
|
}).
|
||||||
|
|
||||||
map_route_switch_reason(SameScores, SameScores) ->
|
map_route_switch_reason(SameScores, SameScores) ->
|
||||||
@ -452,7 +395,7 @@ balance_routes(FailRatedRoutes) ->
|
|||||||
|
|
||||||
-spec group_routes_by_priority(fail_rated_route(), Acc :: route_groups_by_priority()) -> route_groups_by_priority().
|
-spec group_routes_by_priority(fail_rated_route(), Acc :: route_groups_by_priority()) -> route_groups_by_priority().
|
||||||
group_routes_by_priority(FailRatedRoute = {Route, {ProviderCondition, _}}, SortedRoutes) ->
|
group_routes_by_priority(FailRatedRoute = {Route, {ProviderCondition, _}}, SortedRoutes) ->
|
||||||
TerminalPriority = priority(Route),
|
TerminalPriority = hg_route:priority(Route),
|
||||||
Key = {ProviderCondition, TerminalPriority},
|
Key = {ProviderCondition, TerminalPriority},
|
||||||
Routes = maps:get(Key, SortedRoutes, []),
|
Routes = maps:get(Key, SortedRoutes, []),
|
||||||
SortedRoutes#{Key => [FailRatedRoute | Routes]}.
|
SortedRoutes#{Key => [FailRatedRoute | Routes]}.
|
||||||
@ -476,7 +419,7 @@ set_routes_random_condition(Routes) ->
|
|||||||
get_summary_weight(FailRatedRoutes) ->
|
get_summary_weight(FailRatedRoutes) ->
|
||||||
lists:foldl(
|
lists:foldl(
|
||||||
fun({Route, _}, Acc) ->
|
fun({Route, _}, Acc) ->
|
||||||
Weight = weight(Route),
|
Weight = hg_route:weight(Route),
|
||||||
Acc + Weight
|
Acc + Weight
|
||||||
end,
|
end,
|
||||||
0,
|
0,
|
||||||
@ -487,14 +430,14 @@ calc_random_condition(_, _, [], Routes) ->
|
|||||||
Routes;
|
Routes;
|
||||||
calc_random_condition(StartFrom, Random, [FailRatedRoute | Rest], Routes) ->
|
calc_random_condition(StartFrom, Random, [FailRatedRoute | Rest], Routes) ->
|
||||||
{Route, Status} = FailRatedRoute,
|
{Route, Status} = FailRatedRoute,
|
||||||
Weight = weight(Route),
|
Weight = hg_route:weight(Route),
|
||||||
InRange = (Random >= StartFrom) and (Random < StartFrom + Weight),
|
InRange = (Random >= StartFrom) and (Random < StartFrom + Weight),
|
||||||
case InRange of
|
case InRange of
|
||||||
true ->
|
true ->
|
||||||
NewRoute = set_weight(1, Route),
|
NewRoute = hg_route:set_weight(1, Route),
|
||||||
calc_random_condition(StartFrom + Weight, Random, Rest, [{NewRoute, Status} | Routes]);
|
calc_random_condition(StartFrom + Weight, Random, Rest, [{NewRoute, Status} | Routes]);
|
||||||
false ->
|
false ->
|
||||||
NewRoute = set_weight(0, Route),
|
NewRoute = hg_route:set_weight(0, Route),
|
||||||
calc_random_condition(StartFrom + Weight, Random, Rest, [{NewRoute, Status} | Routes])
|
calc_random_condition(StartFrom + Weight, Random, Rest, [{NewRoute, Status} | Routes])
|
||||||
end.
|
end.
|
||||||
|
|
||||||
@ -503,9 +446,9 @@ score_routes(Routes) ->
|
|||||||
[{score_route(FailRatedRoute), Route} || {Route, _} = FailRatedRoute <- Routes].
|
[{score_route(FailRatedRoute), Route} || {Route, _} = FailRatedRoute <- Routes].
|
||||||
|
|
||||||
score_route({Route, ProviderStatus}) ->
|
score_route({Route, ProviderStatus}) ->
|
||||||
PriorityRate = priority(Route),
|
PriorityRate = hg_route:priority(Route),
|
||||||
RandomCondition = weight(Route),
|
RandomCondition = hg_route:weight(Route),
|
||||||
Pin = pin(Route),
|
Pin = hg_route:pin(Route),
|
||||||
PinHash = erlang:phash2(Pin),
|
PinHash = erlang:phash2(Pin),
|
||||||
{AvailabilityStatus, ConversionStatus} = ProviderStatus,
|
{AvailabilityStatus, ConversionStatus} = ProviderStatus,
|
||||||
{AvailabilityCondition, Availability} = get_availability_score(AvailabilityStatus),
|
{AvailabilityCondition, Availability} = get_availability_score(AvailabilityStatus),
|
||||||
@ -526,7 +469,7 @@ get_availability_score({dead, FailRate}) -> {0, 1.0 - FailRate}.
|
|||||||
get_conversion_score({normal, FailRate}) -> {1, 1.0 - FailRate};
|
get_conversion_score({normal, FailRate}) -> {1, 1.0 - FailRate};
|
||||||
get_conversion_score({lacking, FailRate}) -> {0, 1.0 - FailRate}.
|
get_conversion_score({lacking, FailRate}) -> {0, 1.0 - FailRate}.
|
||||||
|
|
||||||
-spec score_routes_with_fault_detector([route()]) -> [fail_rated_route()].
|
-spec score_routes_with_fault_detector([hg_route:t()]) -> [fail_rated_route()].
|
||||||
score_routes_with_fault_detector([]) ->
|
score_routes_with_fault_detector([]) ->
|
||||||
[];
|
[];
|
||||||
score_routes_with_fault_detector(Routes) ->
|
score_routes_with_fault_detector(Routes) ->
|
||||||
@ -534,20 +477,19 @@ score_routes_with_fault_detector(Routes) ->
|
|||||||
FDStats = hg_fault_detector_client:get_statistics(IDs),
|
FDStats = hg_fault_detector_client:get_statistics(IDs),
|
||||||
[{R, get_provider_status(R, FDStats)} || R <- Routes].
|
[{R, get_provider_status(R, FDStats)} || R <- Routes].
|
||||||
|
|
||||||
-spec get_provider_status(route(), [fd_service_stats()]) -> provider_status().
|
-spec get_provider_status(hg_route:t(), [fd_service_stats()]) -> provider_status().
|
||||||
get_provider_status(Route, FDStats) ->
|
get_provider_status(Route, FDStats) ->
|
||||||
ProviderRef = provider_ref(Route),
|
ProviderRef = hg_route:provider_ref(Route),
|
||||||
FdOverrides = fd_overrides(Route),
|
FdOverrides = hg_route:fd_overrides(Route),
|
||||||
AvailabilityServiceID = build_fd_availability_service_id(ProviderRef),
|
AvailabilityServiceID = build_fd_availability_service_id(ProviderRef),
|
||||||
ConversionServiceID = build_fd_conversion_service_id(ProviderRef),
|
ConversionServiceID = build_fd_conversion_service_id(ProviderRef),
|
||||||
AvailabilityStatus = get_provider_availability_status(FdOverrides, AvailabilityServiceID, FDStats),
|
AvailabilityStatus = get_adapter_availability_status(FdOverrides, AvailabilityServiceID, FDStats),
|
||||||
ConversionStatus = get_provider_conversion_status(FdOverrides, ConversionServiceID, FDStats),
|
ConversionStatus = get_provider_conversion_status(FdOverrides, ConversionServiceID, FDStats),
|
||||||
{AvailabilityStatus, ConversionStatus}.
|
{AvailabilityStatus, ConversionStatus}.
|
||||||
|
get_adapter_availability_status(#domain_RouteFaultDetectorOverrides{enabled = true}, _FDID, _Stats) ->
|
||||||
get_provider_availability_status(#domain_RouteFaultDetectorOverrides{enabled = true}, _FDID, _Stats) ->
|
|
||||||
%% ignore fd statistic if set override
|
%% ignore fd statistic if set override
|
||||||
{alive, 0.0};
|
{alive, 0.0};
|
||||||
get_provider_availability_status(_, FDID, Stats) ->
|
get_adapter_availability_status(_, FDID, Stats) ->
|
||||||
AvailabilityConfig = maps:get(availability, genlib_app:env(hellgate, fault_detector, #{}), #{}),
|
AvailabilityConfig = maps:get(availability, genlib_app:env(hellgate, fault_detector, #{}), #{}),
|
||||||
CriticalFailRate = maps:get(critical_fail_rate, AvailabilityConfig, 0.7),
|
CriticalFailRate = maps:get(critical_fail_rate, AvailabilityConfig, 0.7),
|
||||||
case lists:keysearch(FDID, #fault_detector_ServiceStatistics.service_id, Stats) of
|
case lists:keysearch(FDID, #fault_detector_ServiceStatistics.service_id, Stats) of
|
||||||
@ -578,7 +520,7 @@ build_ids(Routes) ->
|
|||||||
lists:foldl(fun build_fd_ids/2, [], Routes).
|
lists:foldl(fun build_fd_ids/2, [], Routes).
|
||||||
|
|
||||||
build_fd_ids(Route, IDs) ->
|
build_fd_ids(Route, IDs) ->
|
||||||
ProviderRef = provider_ref(Route),
|
ProviderRef = hg_route:provider_ref(Route),
|
||||||
AvailabilityID = build_fd_availability_service_id(ProviderRef),
|
AvailabilityID = build_fd_availability_service_id(ProviderRef),
|
||||||
ConversionID = build_fd_conversion_service_id(ProviderRef),
|
ConversionID = build_fd_conversion_service_id(ProviderRef),
|
||||||
[AvailabilityID, ConversionID | IDs].
|
[AvailabilityID, ConversionID | IDs].
|
||||||
@ -589,7 +531,7 @@ 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_payment_terms(payment_route(), varset(), revision()) -> payment_terms() | undefined.
|
-spec get_payment_terms(hg_route:payment_route(), varset(), revision()) -> payment_terms() | undefined.
|
||||||
get_payment_terms(?route(ProviderRef, TerminalRef), VS, Revision) ->
|
get_payment_terms(?route(ProviderRef, TerminalRef), VS, Revision) ->
|
||||||
PreparedVS = hg_varset:prepare_varset(VS),
|
PreparedVS = hg_varset:prepare_varset(VS),
|
||||||
{Client, Context} = get_party_client(),
|
{Client, Context} = get_party_client(),
|
||||||
@ -605,8 +547,8 @@ get_payment_terms(?route(ProviderRef, TerminalRef), VS, Revision) ->
|
|||||||
|
|
||||||
-spec acceptable_terminal(
|
-spec acceptable_terminal(
|
||||||
route_predestination(),
|
route_predestination(),
|
||||||
provider_ref(),
|
hg_route:provider_ref(),
|
||||||
terminal_ref(),
|
hg_route:terminal_ref(),
|
||||||
varset(),
|
varset(),
|
||||||
revision()
|
revision()
|
||||||
) -> true | no_return().
|
) -> true | no_return().
|
||||||
@ -732,7 +674,7 @@ acceptable_allow(_ParentName, _Type, {constant, true}) ->
|
|||||||
acceptable_allow(ParentName, Type, {constant, false}) ->
|
acceptable_allow(ParentName, Type, {constant, false}) ->
|
||||||
throw(?rejected({ParentName, Type}));
|
throw(?rejected({ParentName, Type}));
|
||||||
acceptable_allow(_ParentName, Type, Ambiguous) ->
|
acceptable_allow(_ParentName, Type, Ambiguous) ->
|
||||||
error({misconfiguration, {'Could not reduce predicate to a value', {Type, Ambiguous}}}).
|
erlang:error({misconfiguration, {'Could not reduce predicate to a value', {Type, Ambiguous}}}).
|
||||||
|
|
||||||
%%
|
%%
|
||||||
|
|
||||||
@ -773,7 +715,7 @@ get_selector_value(Name, Selector) ->
|
|||||||
{value, V} ->
|
{value, V} ->
|
||||||
V;
|
V;
|
||||||
Ambiguous ->
|
Ambiguous ->
|
||||||
error({misconfiguration, {'Could not reduce selector to a value', {Name, Ambiguous}}})
|
erlang:error({misconfiguration, {'Could not reduce selector to a value', {Name, Ambiguous}}})
|
||||||
end.
|
end.
|
||||||
|
|
||||||
getv(Name, VS) ->
|
getv(Name, VS) ->
|
||||||
@ -826,33 +768,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, ?DOMAIN_CANDIDATE_PRIORITY), Status},
|
{hg_route:new(?prv(1), ?trm(1), 1, ?DOMAIN_CANDIDATE_PRIORITY), Status},
|
||||||
{new(?prv(2), ?trm(1), 2, ?DOMAIN_CANDIDATE_PRIORITY), Status},
|
{hg_route:new(?prv(2), ?trm(1), 2, ?DOMAIN_CANDIDATE_PRIORITY), Status},
|
||||||
{new(?prv(3), ?trm(1), 0, ?DOMAIN_CANDIDATE_PRIORITY), Status},
|
{hg_route:new(?prv(3), ?trm(1), 0, ?DOMAIN_CANDIDATE_PRIORITY), Status},
|
||||||
{new(?prv(4), ?trm(1), 1, ?DOMAIN_CANDIDATE_PRIORITY), Status},
|
{hg_route:new(?prv(4), ?trm(1), 1, ?DOMAIN_CANDIDATE_PRIORITY), Status},
|
||||||
{new(?prv(5), ?trm(1), 0, ?DOMAIN_CANDIDATE_PRIORITY), Status}
|
{hg_route:new(?prv(5), ?trm(1), 0, ?DOMAIN_CANDIDATE_PRIORITY), Status}
|
||||||
],
|
],
|
||||||
|
|
||||||
Result1 = [
|
Result1 = [
|
||||||
{new(?prv(1), ?trm(1), 1, ?DOMAIN_CANDIDATE_PRIORITY), Status},
|
{hg_route:new(?prv(1), ?trm(1), 1, ?DOMAIN_CANDIDATE_PRIORITY), Status},
|
||||||
{new(?prv(2), ?trm(1), 0, ?DOMAIN_CANDIDATE_PRIORITY), Status},
|
{hg_route:new(?prv(2), ?trm(1), 0, ?DOMAIN_CANDIDATE_PRIORITY), Status},
|
||||||
{new(?prv(3), ?trm(1), 0, ?DOMAIN_CANDIDATE_PRIORITY), Status},
|
{hg_route:new(?prv(3), ?trm(1), 0, ?DOMAIN_CANDIDATE_PRIORITY), Status},
|
||||||
{new(?prv(4), ?trm(1), 0, ?DOMAIN_CANDIDATE_PRIORITY), Status},
|
{hg_route:new(?prv(4), ?trm(1), 0, ?DOMAIN_CANDIDATE_PRIORITY), Status},
|
||||||
{new(?prv(5), ?trm(1), 0, ?DOMAIN_CANDIDATE_PRIORITY), Status}
|
{hg_route:new(?prv(5), ?trm(1), 0, ?DOMAIN_CANDIDATE_PRIORITY), Status}
|
||||||
],
|
],
|
||||||
Result2 = [
|
Result2 = [
|
||||||
{new(?prv(1), ?trm(1), 0, ?DOMAIN_CANDIDATE_PRIORITY), Status},
|
{hg_route:new(?prv(1), ?trm(1), 0, ?DOMAIN_CANDIDATE_PRIORITY), Status},
|
||||||
{new(?prv(2), ?trm(1), 1, ?DOMAIN_CANDIDATE_PRIORITY), Status},
|
{hg_route:new(?prv(2), ?trm(1), 1, ?DOMAIN_CANDIDATE_PRIORITY), Status},
|
||||||
{new(?prv(3), ?trm(1), 0, ?DOMAIN_CANDIDATE_PRIORITY), Status},
|
{hg_route:new(?prv(3), ?trm(1), 0, ?DOMAIN_CANDIDATE_PRIORITY), Status},
|
||||||
{new(?prv(4), ?trm(1), 0, ?DOMAIN_CANDIDATE_PRIORITY), Status},
|
{hg_route:new(?prv(4), ?trm(1), 0, ?DOMAIN_CANDIDATE_PRIORITY), Status},
|
||||||
{new(?prv(5), ?trm(1), 0, ?DOMAIN_CANDIDATE_PRIORITY), Status}
|
{hg_route:new(?prv(5), ?trm(1), 0, ?DOMAIN_CANDIDATE_PRIORITY), Status}
|
||||||
],
|
],
|
||||||
Result3 = [
|
Result3 = [
|
||||||
{new(?prv(1), ?trm(1), 0, ?DOMAIN_CANDIDATE_PRIORITY), Status},
|
{hg_route:new(?prv(1), ?trm(1), 0, ?DOMAIN_CANDIDATE_PRIORITY), Status},
|
||||||
{new(?prv(2), ?trm(1), 0, ?DOMAIN_CANDIDATE_PRIORITY), Status},
|
{hg_route:new(?prv(2), ?trm(1), 0, ?DOMAIN_CANDIDATE_PRIORITY), Status},
|
||||||
{new(?prv(3), ?trm(1), 0, ?DOMAIN_CANDIDATE_PRIORITY), Status},
|
{hg_route:new(?prv(3), ?trm(1), 0, ?DOMAIN_CANDIDATE_PRIORITY), Status},
|
||||||
{new(?prv(4), ?trm(1), 0, ?DOMAIN_CANDIDATE_PRIORITY), Status},
|
{hg_route:new(?prv(4), ?trm(1), 0, ?DOMAIN_CANDIDATE_PRIORITY), Status},
|
||||||
{new(?prv(5), ?trm(1), 0, ?DOMAIN_CANDIDATE_PRIORITY), Status}
|
{hg_route: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, []))),
|
||||||
@ -864,12 +806,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, ?DOMAIN_CANDIDATE_PRIORITY), Status},
|
{hg_route:new(?prv(1), ?trm(1), 0, ?DOMAIN_CANDIDATE_PRIORITY), Status},
|
||||||
{new(?prv(2), ?trm(1), 0, ?DOMAIN_CANDIDATE_PRIORITY), Status}
|
{hg_route:new(?prv(2), ?trm(1), 0, ?DOMAIN_CANDIDATE_PRIORITY), Status}
|
||||||
],
|
],
|
||||||
Result = [
|
Result = [
|
||||||
{new(?prv(1), ?trm(1), 0, ?DOMAIN_CANDIDATE_PRIORITY), Status},
|
{hg_route:new(?prv(1), ?trm(1), 0, ?DOMAIN_CANDIDATE_PRIORITY), Status},
|
||||||
{new(?prv(2), ?trm(1), 0, ?DOMAIN_CANDIDATE_PRIORITY), Status}
|
{hg_route:new(?prv(2), ?trm(1), 0, ?DOMAIN_CANDIDATE_PRIORITY), Status}
|
||||||
],
|
],
|
||||||
?_assertEqual(Result, set_routes_random_condition(Routes)).
|
?_assertEqual(Result, set_routes_random_condition(Routes)).
|
||||||
|
|
||||||
@ -880,10 +822,10 @@ preferable_route_scoring_test_() ->
|
|||||||
StatusDead = {{dead, 0.4}, {lacking, 0.6}},
|
StatusDead = {{dead, 0.4}, {lacking, 0.6}},
|
||||||
StatusDegraded = {{alive, 0.1}, {normal, 0.1}},
|
StatusDegraded = {{alive, 0.1}, {normal, 0.1}},
|
||||||
StatusBroken = {{alive, 0.1}, {lacking, 0.8}},
|
StatusBroken = {{alive, 0.1}, {lacking, 0.8}},
|
||||||
RoutePreferred1 = new(?prv(1), ?trm(1), 0, 1),
|
RoutePreferred1 = hg_route:new(?prv(1), ?trm(1), 0, 1),
|
||||||
RoutePreferred2 = new(?prv(1), ?trm(2), 0, 1),
|
RoutePreferred2 = hg_route:new(?prv(1), ?trm(2), 0, 1),
|
||||||
RoutePreferred3 = new(?prv(1), ?trm(3), 0, 1),
|
RoutePreferred3 = hg_route:new(?prv(1), ?trm(3), 0, 1),
|
||||||
RouteFallback = new(?prv(2), ?trm(2), 0, 0),
|
RouteFallback = hg_route:new(?prv(2), ?trm(2), 0, 0),
|
||||||
[
|
[
|
||||||
?_assertMatch(
|
?_assertMatch(
|
||||||
{RoutePreferred1, #{}},
|
{RoutePreferred1, #{}},
|
||||||
@ -950,9 +892,9 @@ preferable_route_scoring_test_() ->
|
|||||||
|
|
||||||
-spec prefer_weight_over_availability_test() -> _.
|
-spec prefer_weight_over_availability_test() -> _.
|
||||||
prefer_weight_over_availability_test() ->
|
prefer_weight_over_availability_test() ->
|
||||||
Route1 = new(?prv(1), ?trm(1), 0, 1000),
|
Route1 = hg_route:new(?prv(1), ?trm(1), 0, 1000),
|
||||||
Route2 = new(?prv(2), ?trm(2), 0, 1005),
|
Route2 = hg_route:new(?prv(2), ?trm(2), 0, 1005),
|
||||||
Route3 = new(?prv(3), ?trm(3), 0, 1000),
|
Route3 = hg_route:new(?prv(3), ?trm(3), 0, 1000),
|
||||||
Routes = [Route1, Route2, Route3],
|
Routes = [Route1, Route2, Route3],
|
||||||
|
|
||||||
ProviderStatuses = [
|
ProviderStatuses = [
|
||||||
@ -965,9 +907,9 @@ prefer_weight_over_availability_test() ->
|
|||||||
|
|
||||||
-spec prefer_weight_over_conversion_test() -> _.
|
-spec prefer_weight_over_conversion_test() -> _.
|
||||||
prefer_weight_over_conversion_test() ->
|
prefer_weight_over_conversion_test() ->
|
||||||
Route1 = new(?prv(1), ?trm(1), 0, 1000),
|
Route1 = hg_route:new(?prv(1), ?trm(1), 0, 1000),
|
||||||
Route2 = new(?prv(2), ?trm(2), 0, 1005),
|
Route2 = hg_route:new(?prv(2), ?trm(2), 0, 1005),
|
||||||
Route3 = new(?prv(3), ?trm(3), 0, 1000),
|
Route3 = hg_route:new(?prv(3), ?trm(3), 0, 1000),
|
||||||
Routes = [Route1, Route2, Route3],
|
Routes = [Route1, Route2, Route3],
|
||||||
|
|
||||||
ProviderStatuses = [
|
ProviderStatuses = [
|
||||||
|
215
apps/hellgate/src/hg_routing_ctx.erl
Normal file
215
apps/hellgate/src/hg_routing_ctx.erl
Normal file
@ -0,0 +1,215 @@
|
|||||||
|
-module(hg_routing_ctx).
|
||||||
|
|
||||||
|
-export([new/1]).
|
||||||
|
-export([with_fail_rates/2]).
|
||||||
|
-export([fail_rates/1]).
|
||||||
|
-export([set_choosen/3]).
|
||||||
|
-export([set_error/2]).
|
||||||
|
-export([error/1]).
|
||||||
|
-export([reject/3]).
|
||||||
|
-export([rejected_routes/1]).
|
||||||
|
-export([rejections/1]).
|
||||||
|
-export([candidates/1]).
|
||||||
|
-export([initial_candidates/1]).
|
||||||
|
-export([choosen_route/1]).
|
||||||
|
-export([process/2]).
|
||||||
|
-export([with_guard/1]).
|
||||||
|
-export([pipeline/2]).
|
||||||
|
|
||||||
|
-type rejection_group() :: atom().
|
||||||
|
-type error() :: {atom(), _Description}.
|
||||||
|
|
||||||
|
-type t() :: #{
|
||||||
|
initial_candidates := [hg_route:t()],
|
||||||
|
candidates := [hg_route:t()],
|
||||||
|
rejections := #{rejection_group() => [hg_route:rejected_route()]},
|
||||||
|
latest_rejection := rejection_group() | undefined,
|
||||||
|
error := error() | undefined,
|
||||||
|
choosen_route := hg_route:t() | undefined,
|
||||||
|
choice_meta := hg_routing:route_choice_context() | undefined,
|
||||||
|
fail_rates => [hg_routing:fail_rated_route()]
|
||||||
|
}.
|
||||||
|
|
||||||
|
-export_type([t/0]).
|
||||||
|
|
||||||
|
%%
|
||||||
|
|
||||||
|
-spec new([hg_route:t()]) -> t().
|
||||||
|
new(Candidates) ->
|
||||||
|
#{
|
||||||
|
initial_candidates => Candidates,
|
||||||
|
candidates => Candidates,
|
||||||
|
rejections => #{},
|
||||||
|
latest_rejection => undefined,
|
||||||
|
error => undefined,
|
||||||
|
choosen_route => undefined,
|
||||||
|
choice_meta => undefined
|
||||||
|
}.
|
||||||
|
|
||||||
|
-spec with_fail_rates([hg_routing:fail_rated_route()], t()) -> t().
|
||||||
|
with_fail_rates(FailRates, Ctx) ->
|
||||||
|
maps:put(fail_rates, FailRates, Ctx).
|
||||||
|
|
||||||
|
-spec fail_rates(t()) -> [hg_routing:fail_rated_route()] | undefined.
|
||||||
|
fail_rates(Ctx) ->
|
||||||
|
maps:get(fail_rates, Ctx, undefined).
|
||||||
|
|
||||||
|
-spec set_choosen(hg_route:t(), hg_routing:route_choice_context(), t()) -> t().
|
||||||
|
set_choosen(Route, ChoiceMeta, Ctx) ->
|
||||||
|
Ctx#{choosen_route => Route, choice_meta => ChoiceMeta}.
|
||||||
|
|
||||||
|
-spec set_error(term(), t()) -> t().
|
||||||
|
set_error(ErrorReason, Ctx) ->
|
||||||
|
Ctx#{error => ErrorReason}.
|
||||||
|
|
||||||
|
-spec error(t()) -> term() | undefined.
|
||||||
|
error(#{error := Error}) ->
|
||||||
|
Error.
|
||||||
|
|
||||||
|
-spec reject(atom(), hg_route:rejected_route(), t()) -> t().
|
||||||
|
reject(GroupReason, RejectedRoute, Ctx = #{rejections := Rejections, candidates := Candidates}) ->
|
||||||
|
RejectedList = maps:get(GroupReason, Rejections, []) ++ [RejectedRoute],
|
||||||
|
Ctx#{
|
||||||
|
rejections := Rejections#{GroupReason => RejectedList},
|
||||||
|
candidates := exclude_route(RejectedRoute, Candidates),
|
||||||
|
latest_rejection := GroupReason
|
||||||
|
}.
|
||||||
|
|
||||||
|
-spec process(T, fun((T) -> T)) -> T when T :: t().
|
||||||
|
process(Ctx0, Fun) ->
|
||||||
|
case Ctx0 of
|
||||||
|
#{error := undefined} ->
|
||||||
|
with_guard(Fun(Ctx0));
|
||||||
|
ErroneousCtx ->
|
||||||
|
ErroneousCtx
|
||||||
|
end.
|
||||||
|
|
||||||
|
-spec with_guard(t()) -> t().
|
||||||
|
with_guard(Ctx0) ->
|
||||||
|
case Ctx0 of
|
||||||
|
NoRouteCtx = #{candidates := [], error := undefined} ->
|
||||||
|
NoRouteCtx#{error := {rejected_routes, latest_rejected_routes(NoRouteCtx)}};
|
||||||
|
Ctx1 ->
|
||||||
|
Ctx1
|
||||||
|
end.
|
||||||
|
|
||||||
|
-spec pipeline(T, [fun((T) -> T)]) -> T when T :: t().
|
||||||
|
pipeline(Ctx, Funs) ->
|
||||||
|
lists:foldl(fun(F, C) -> process(C, F) end, Ctx, Funs).
|
||||||
|
|
||||||
|
-spec rejected_routes(t()) -> [hg_route:rejected_route()].
|
||||||
|
rejected_routes(#{rejections := Rejections}) ->
|
||||||
|
{_, RejectedRoutes} = lists:unzip(maps:to_list(Rejections)),
|
||||||
|
lists:flatten(RejectedRoutes).
|
||||||
|
|
||||||
|
-spec candidates(t()) -> [hg_route:t()].
|
||||||
|
candidates(#{candidates := Candidates}) ->
|
||||||
|
Candidates.
|
||||||
|
|
||||||
|
-spec initial_candidates(t()) -> [hg_route:t()].
|
||||||
|
initial_candidates(#{initial_candidates := InitialCandidates}) ->
|
||||||
|
InitialCandidates.
|
||||||
|
|
||||||
|
-spec choosen_route(t()) -> hg_route:t() | undefined.
|
||||||
|
choosen_route(#{choosen_route := ChoosenRoute}) ->
|
||||||
|
ChoosenRoute.
|
||||||
|
|
||||||
|
-spec rejections(t()) -> [{atom(), [hg_route:rejected_route()]}].
|
||||||
|
rejections(#{rejections := Rejections}) ->
|
||||||
|
maps:to_list(Rejections).
|
||||||
|
|
||||||
|
%%
|
||||||
|
|
||||||
|
latest_rejected_routes(#{latest_rejection := ReasonGroup, rejections := Rejections}) ->
|
||||||
|
{ReasonGroup, maps:get(ReasonGroup, Rejections, [])}.
|
||||||
|
|
||||||
|
exclude_route(Route, Routes) ->
|
||||||
|
lists:foldr(
|
||||||
|
fun(R, RR) ->
|
||||||
|
case hg_route:equal(Route, R) of
|
||||||
|
true -> RR;
|
||||||
|
_Else -> [R | RR]
|
||||||
|
end
|
||||||
|
end,
|
||||||
|
[],
|
||||||
|
Routes
|
||||||
|
).
|
||||||
|
|
||||||
|
%%
|
||||||
|
|
||||||
|
-ifdef(TEST).
|
||||||
|
|
||||||
|
-include_lib("eunit/include/eunit.hrl").
|
||||||
|
-include_lib("damsel/include/dmsl_domain_thrift.hrl").
|
||||||
|
|
||||||
|
-define(prv(ID), #domain_ProviderRef{id = ID}).
|
||||||
|
-define(trm(ID), #domain_TerminalRef{id = ID}).
|
||||||
|
|
||||||
|
-spec test() -> _.
|
||||||
|
|
||||||
|
-spec route_exclusion_test_() -> [_].
|
||||||
|
route_exclusion_test_() ->
|
||||||
|
RouteA = hg_route:new(?prv(1), ?trm(1)),
|
||||||
|
RouteB = hg_route:new(?prv(1), ?trm(2)),
|
||||||
|
RouteC = hg_route:new(?prv(2), ?trm(1)),
|
||||||
|
[
|
||||||
|
?_assertEqual([], exclude_route(RouteA, [])),
|
||||||
|
?_assertEqual([RouteA, RouteB], exclude_route(RouteC, [RouteA, RouteB])),
|
||||||
|
?_assertEqual([RouteA, RouteB], exclude_route(RouteC, [RouteA, RouteB, RouteC])),
|
||||||
|
?_assertEqual([RouteA, RouteC], exclude_route(RouteB, [RouteA, RouteB, RouteC]))
|
||||||
|
].
|
||||||
|
|
||||||
|
-spec pipeline_test_() -> [_].
|
||||||
|
pipeline_test_() ->
|
||||||
|
RouteA = hg_route:new(?prv(1), ?trm(1)),
|
||||||
|
RouteB = hg_route:new(?prv(1), ?trm(2)),
|
||||||
|
RouteC = hg_route:new(?prv(2), ?trm(1)),
|
||||||
|
RejectedRouteA = hg_route:to_rejected_route(RouteA, {?MODULE, <<"whatever">>}),
|
||||||
|
[
|
||||||
|
?_assertMatch(
|
||||||
|
#{
|
||||||
|
initial_candidates := [RouteA],
|
||||||
|
candidates := [],
|
||||||
|
error := {rejected_routes, {test, [RejectedRouteA]}},
|
||||||
|
choosen_route := undefined
|
||||||
|
},
|
||||||
|
pipeline(new([RouteA]), [fun do_reject_route_a/1])
|
||||||
|
),
|
||||||
|
?_assertMatch(
|
||||||
|
#{
|
||||||
|
initial_candidates := [RouteA, RouteB, RouteC],
|
||||||
|
candidates := [RouteA, RouteB, RouteC],
|
||||||
|
error := undefined,
|
||||||
|
choosen_route := undefined
|
||||||
|
},
|
||||||
|
pipeline(new([RouteA, RouteB, RouteC]), [])
|
||||||
|
),
|
||||||
|
?_assertMatch(
|
||||||
|
#{
|
||||||
|
initial_candidates := [RouteA, RouteB, RouteC],
|
||||||
|
candidates := [RouteB, RouteC],
|
||||||
|
error := undefined,
|
||||||
|
choosen_route := undefined
|
||||||
|
},
|
||||||
|
pipeline(new([RouteA, RouteB, RouteC]), [fun do_reject_route_a/1])
|
||||||
|
),
|
||||||
|
?_assertMatch(
|
||||||
|
#{
|
||||||
|
initial_candidates := [RouteA, RouteB, RouteC],
|
||||||
|
candidates := [RouteB, RouteC],
|
||||||
|
error := undefined,
|
||||||
|
choosen_route := RouteB
|
||||||
|
},
|
||||||
|
pipeline(new([RouteA, RouteB, RouteC]), [fun do_reject_route_a/1, fun do_choose_route_b/1])
|
||||||
|
)
|
||||||
|
].
|
||||||
|
|
||||||
|
do_reject_route_a(Ctx) ->
|
||||||
|
RouteA = hg_route:new(?prv(1), ?trm(1)),
|
||||||
|
reject(test, hg_route:to_rejected_route(RouteA, {?MODULE, <<"whatever">>}), Ctx).
|
||||||
|
|
||||||
|
do_choose_route_b(Ctx) ->
|
||||||
|
RouteB = hg_route:new(?prv(1), ?trm(2)),
|
||||||
|
set_choosen(RouteB, #{}, Ctx).
|
||||||
|
|
||||||
|
-endif.
|
@ -8,6 +8,7 @@
|
|||||||
-include("hg_ct_invoice.hrl").
|
-include("hg_ct_invoice.hrl").
|
||||||
-include_lib("damsel/include/dmsl_repair_thrift.hrl").
|
-include_lib("damsel/include/dmsl_repair_thrift.hrl").
|
||||||
-include_lib("hellgate/include/allocation.hrl").
|
-include_lib("hellgate/include/allocation.hrl").
|
||||||
|
-include_lib("fault_detector_proto/include/fd_proto_fault_detector_thrift.hrl").
|
||||||
|
|
||||||
-include_lib("stdlib/include/assert.hrl").
|
-include_lib("stdlib/include/assert.hrl").
|
||||||
|
|
||||||
@ -200,6 +201,10 @@
|
|||||||
-export([payment_cascade_fail_provider_error/1]).
|
-export([payment_cascade_fail_provider_error/1]).
|
||||||
-export([payment_cascade_fail_ui/1]).
|
-export([payment_cascade_fail_ui/1]).
|
||||||
|
|
||||||
|
-export([route_not_found_provider_unavailable/1]).
|
||||||
|
-export([payment_success_ruleset_provider_available/1]).
|
||||||
|
-export([route_not_found_provider_lacking_conversion/1]).
|
||||||
|
|
||||||
%%
|
%%
|
||||||
|
|
||||||
-behaviour(supervisor).
|
-behaviour(supervisor).
|
||||||
@ -324,7 +329,11 @@ groups() ->
|
|||||||
payment_capture_retries_exceeded,
|
payment_capture_retries_exceeded,
|
||||||
payment_partial_capture_success,
|
payment_partial_capture_success,
|
||||||
payment_error_in_cancel_session_does_not_cause_payment_failure,
|
payment_error_in_cancel_session_does_not_cause_payment_failure,
|
||||||
payment_error_in_capture_session_does_not_cause_payment_failure
|
payment_error_in_capture_session_does_not_cause_payment_failure,
|
||||||
|
|
||||||
|
payment_success_ruleset_provider_available,
|
||||||
|
route_not_found_provider_unavailable,
|
||||||
|
route_not_found_provider_lacking_conversion
|
||||||
]},
|
]},
|
||||||
|
|
||||||
{adjustments, [], [
|
{adjustments, [], [
|
||||||
@ -525,6 +534,7 @@ init_per_suite(C) ->
|
|||||||
{ok, SupPid} = supervisor:start_link(?MODULE, []),
|
{ok, SupPid} = supervisor:start_link(?MODULE, []),
|
||||||
_ = unlink(SupPid),
|
_ = unlink(SupPid),
|
||||||
ok = start_kv_store(SupPid),
|
ok = start_kv_store(SupPid),
|
||||||
|
_ = mock_fault_detector(SupPid),
|
||||||
NewC = [
|
NewC = [
|
||||||
{party_id, PartyID},
|
{party_id, PartyID},
|
||||||
{party_client, PartyClient},
|
{party_client, PartyClient},
|
||||||
@ -1190,28 +1200,30 @@ payment_limit_overflow(C) ->
|
|||||||
|
|
||||||
Failure = create_payment_limit_overflow(PartyID, ShopID, 1000, Client, PmtSys),
|
Failure = create_payment_limit_overflow(PartyID, ShopID, 1000, Client, PmtSys),
|
||||||
ok = hg_limiter_helper:assert_payment_limit_amount(PaymentAmount, Payment, Invoice),
|
ok = hg_limiter_helper:assert_payment_limit_amount(PaymentAmount, Payment, Invoice),
|
||||||
ok = payproc_errors:match(
|
ok = payproc_errors:match('PaymentFailure', Failure, fun({no_route_found, {rejected, {limit_overflow, _}}}) ->
|
||||||
'PaymentFailure',
|
ok
|
||||||
Failure,
|
end).
|
||||||
fun({no_route_found, {forbidden, _}}) -> ok end
|
|
||||||
).
|
|
||||||
|
|
||||||
-spec limit_hold_currency_error(config()) -> test_return().
|
-spec limit_hold_currency_error(config()) -> test_return().
|
||||||
limit_hold_currency_error(C) ->
|
limit_hold_currency_error(C) ->
|
||||||
payment_route_not_found(C).
|
Failure = payment_route_not_found(C),
|
||||||
|
?assertRouteNotFound(Failure, {rejected, {limit_misconfiguration, _}}, <<"[{">>).
|
||||||
|
|
||||||
-spec limit_hold_operation_not_supported(config()) -> test_return().
|
-spec limit_hold_operation_not_supported(config()) -> test_return().
|
||||||
limit_hold_operation_not_supported(C) ->
|
limit_hold_operation_not_supported(C) ->
|
||||||
payment_route_not_found(C).
|
Failure = payment_route_not_found(C),
|
||||||
|
?assertRouteNotFound(Failure, {rejected, {limit_misconfiguration, _}}, <<"[{">>).
|
||||||
|
|
||||||
-spec limit_hold_payment_tool_not_supported(config()) -> test_return().
|
-spec limit_hold_payment_tool_not_supported(config()) -> test_return().
|
||||||
limit_hold_payment_tool_not_supported(C) ->
|
limit_hold_payment_tool_not_supported(C) ->
|
||||||
{PaymentTool, Session} = hg_dummy_provider:make_payment_tool(crypto_currency, ?crypta(<<"bitcoin-ref">>)),
|
{PaymentTool, Session} = hg_dummy_provider:make_payment_tool(crypto_currency, ?crypta(<<"bitcoin-ref">>)),
|
||||||
payment_route_not_found(PaymentTool, Session, C).
|
Failure = payment_route_not_found(PaymentTool, Session, C),
|
||||||
|
?assertRouteNotFound(Failure, {rejected, {limit_misconfiguration, _}}, <<"[{">>).
|
||||||
|
|
||||||
-spec limit_hold_two_routes_failure(config()) -> test_return().
|
-spec limit_hold_two_routes_failure(config()) -> test_return().
|
||||||
limit_hold_two_routes_failure(C) ->
|
limit_hold_two_routes_failure(C) ->
|
||||||
payment_route_not_found(C).
|
Failure = payment_route_not_found(C),
|
||||||
|
?assertRouteNotFound(Failure, {rejected, {limit_overflow, _}}, <<"[{">>).
|
||||||
|
|
||||||
payment_route_not_found(C) ->
|
payment_route_not_found(C) ->
|
||||||
PmtSys = ?pmt_sys(<<"visa-ref">>),
|
PmtSys = ?pmt_sys(<<"visa-ref">>),
|
||||||
@ -1235,7 +1247,8 @@ payment_route_not_found(PaymentTool, Session, C) ->
|
|||||||
_ = start_payment_ev(InvoiceID, Client),
|
_ = start_payment_ev(InvoiceID, Client),
|
||||||
?payment_ev(PaymentID, ?payment_rollback_started({failure, Failure})) =
|
?payment_ev(PaymentID, ?payment_rollback_started({failure, Failure})) =
|
||||||
next_change(InvoiceID, Client),
|
next_change(InvoiceID, Client),
|
||||||
?assertRouteNotFound(Failure, _, <<"{rejected_routes,[{">>).
|
%% NOTE Failure reason is expected to contain non-empty list of rejected routes
|
||||||
|
Failure.
|
||||||
|
|
||||||
-spec switch_provider_after_limit_overflow(config()) -> test_return().
|
-spec switch_provider_after_limit_overflow(config()) -> test_return().
|
||||||
switch_provider_after_limit_overflow(C) ->
|
switch_provider_after_limit_overflow(C) ->
|
||||||
@ -1306,11 +1319,9 @@ refund_limit_success(C) ->
|
|||||||
?payment(PaymentID) = Payment,
|
?payment(PaymentID) = Payment,
|
||||||
|
|
||||||
Failure = create_payment_limit_overflow(PartyID, ShopID, 50000, Client, PmtSys),
|
Failure = create_payment_limit_overflow(PartyID, ShopID, 50000, Client, PmtSys),
|
||||||
ok = payproc_errors:match(
|
ok = payproc_errors:match('PaymentFailure', Failure, fun({no_route_found, {rejected, {limit_overflow, _}}}) ->
|
||||||
'PaymentFailure',
|
ok
|
||||||
Failure,
|
end),
|
||||||
fun({no_route_found, {forbidden, _}}) -> ok end
|
|
||||||
),
|
|
||||||
% create a refund finally
|
% create a refund finally
|
||||||
RefundParams = make_refund_params(),
|
RefundParams = make_refund_params(),
|
||||||
RefundID = execute_payment_refund(InvoiceID, PaymentID, RefundParams, Client),
|
RefundID = execute_payment_refund(InvoiceID, PaymentID, RefundParams, Client),
|
||||||
@ -1435,7 +1446,8 @@ payment_w_misconfigured_routing_failed(C) ->
|
|||||||
?payment_ev(PaymentID, ?risk_score_changed(_)),
|
?payment_ev(PaymentID, ?risk_score_changed(_)),
|
||||||
?payment_ev(PaymentID, ?payment_status_changed(?failed({failure, Failure})))
|
?payment_ev(PaymentID, ?payment_status_changed(?failed({failure, Failure})))
|
||||||
] = next_changes(InvoiceID, 3, Client),
|
] = next_changes(InvoiceID, 3, Client),
|
||||||
?assertRouteNotFound(Failure, {unknown, _}, <<"{misconfiguration,{">>).
|
Reason = genlib:format({routing_decisions, {delegates, []}}),
|
||||||
|
?assertRouteNotFound(Failure, {unknown, {{unknown_error, <<"misconfiguration">>}, _}}, Reason).
|
||||||
|
|
||||||
payment_w_misconfigured_routing_failed_fixture(_Revision, _C) ->
|
payment_w_misconfigured_routing_failed_fixture(_Revision, _C) ->
|
||||||
[
|
[
|
||||||
@ -1757,6 +1769,69 @@ repair_failed_cancel(InvoiceID, PaymentID, Reason, Client) ->
|
|||||||
] = next_changes(InvoiceID, 2, Client),
|
] = next_changes(InvoiceID, 2, Client),
|
||||||
PaymentID.
|
PaymentID.
|
||||||
|
|
||||||
|
-spec payment_success_ruleset_provider_available(config()) -> test_return().
|
||||||
|
payment_success_ruleset_provider_available(C) ->
|
||||||
|
with_fault_detector(
|
||||||
|
mk_fd_stat(?prv(1), {0.5, 0.5}),
|
||||||
|
fun() ->
|
||||||
|
PartyID = cfg(party_id_big_merch, C),
|
||||||
|
RootUrl = cfg(root_url, C),
|
||||||
|
PartyClient = cfg(party_client, C),
|
||||||
|
Client = hg_client_invoicing:start_link(hg_ct_helper:create_client(RootUrl)),
|
||||||
|
ShopID = hg_ct_helper:create_shop(PartyID, ?cat(1), <<"RUB">>, ?tmpl(1), ?pinst(1), PartyClient),
|
||||||
|
InvoiceParams = make_invoice_params(PartyID, ShopID, <<"rubberduck">>, make_due_date(10), make_cash(42000)),
|
||||||
|
InvoiceID = create_invoice(InvoiceParams, Client),
|
||||||
|
?invoice_created(?invoice_w_status(?invoice_unpaid())) = next_change(InvoiceID, Client),
|
||||||
|
PaymentID = process_payment(InvoiceID, make_payment_params(?pmt_sys(<<"visa-ref">>)), Client),
|
||||||
|
PaymentID = await_payment_capture(InvoiceID, PaymentID, Client),
|
||||||
|
?invoice_state(
|
||||||
|
?invoice_w_status(?invoice_paid()),
|
||||||
|
[?payment_state(Payment)]
|
||||||
|
) = hg_client_invoicing:get(InvoiceID, Client),
|
||||||
|
?payment_w_status(PaymentID, ?captured()) = Payment
|
||||||
|
end
|
||||||
|
).
|
||||||
|
|
||||||
|
-spec route_not_found_provider_unavailable(config()) -> test_return().
|
||||||
|
route_not_found_provider_unavailable(C) ->
|
||||||
|
with_fault_detector(
|
||||||
|
mk_fd_stat(?prv(1), {0.5, 0.9}),
|
||||||
|
fun() ->
|
||||||
|
{_InvoiceID, _PaymentID, Failure} = failed_payment_wo_cascade(C),
|
||||||
|
?assertRouteNotFound(Failure, {rejected, {adapter_unavailable, _}}, <<"[{">>)
|
||||||
|
end
|
||||||
|
).
|
||||||
|
|
||||||
|
-spec route_not_found_provider_lacking_conversion(config()) -> test_return().
|
||||||
|
route_not_found_provider_lacking_conversion(C) ->
|
||||||
|
with_fault_detector(
|
||||||
|
mk_fd_stat(?prv(1), {0.9, 0.5}),
|
||||||
|
fun() ->
|
||||||
|
{_InvoiceID, _PaymentID, Failure} = failed_payment_wo_cascade(C),
|
||||||
|
?assertRouteNotFound(Failure, {rejected, {provider_conversion_is_too_low, _}}, <<"[{">>)
|
||||||
|
end
|
||||||
|
).
|
||||||
|
|
||||||
|
failed_payment_wo_cascade(C) ->
|
||||||
|
PartyID = cfg(party_id_big_merch, C),
|
||||||
|
RootUrl = cfg(root_url, C),
|
||||||
|
PartyClient = cfg(party_client, C),
|
||||||
|
Client = hg_client_invoicing:start_link(hg_ct_helper:create_client(RootUrl)),
|
||||||
|
ShopID = hg_ct_helper:create_shop(PartyID, ?cat(1), <<"RUB">>, ?tmpl(1), ?pinst(1), PartyClient),
|
||||||
|
InvoiceParams = make_invoice_params(PartyID, ShopID, <<"rubberduck">>, make_due_date(10), make_cash(42000)),
|
||||||
|
|
||||||
|
InvoiceID = create_invoice(InvoiceParams, Client),
|
||||||
|
?invoice_created(?invoice_w_status(?invoice_unpaid())) = next_change(InvoiceID, Client),
|
||||||
|
|
||||||
|
PaymentParams = make_payment_params(?pmt_sys(<<"visa-ref">>)),
|
||||||
|
?payment_state(?payment(PaymentID)) =
|
||||||
|
hg_client_invoicing:start_payment(InvoiceID, PaymentParams, Client),
|
||||||
|
_ = start_payment_ev(InvoiceID, Client),
|
||||||
|
|
||||||
|
?payment_ev(PaymentID, ?payment_rollback_started({failure, Failure})) =
|
||||||
|
next_change(InvoiceID, Client),
|
||||||
|
{InvoiceID, PaymentID, Failure}.
|
||||||
|
|
||||||
-spec payment_w_terminal_w_payment_service_success(config()) -> _ | no_return().
|
-spec payment_w_terminal_w_payment_service_success(config()) -> _ | no_return().
|
||||||
payment_w_terminal_w_payment_service_success(C) ->
|
payment_w_terminal_w_payment_service_success(C) ->
|
||||||
Client = cfg(client, C),
|
Client = cfg(client, C),
|
||||||
@ -9870,3 +9945,44 @@ construct_term_set_for_partial_capture_provider_permit(Revision, _C) ->
|
|||||||
set_processing_deadline(Timeout, PaymentParams) ->
|
set_processing_deadline(Timeout, PaymentParams) ->
|
||||||
Deadline = woody_deadline:to_binary(woody_deadline:from_timeout(Timeout)),
|
Deadline = woody_deadline:to_binary(woody_deadline:from_timeout(Timeout)),
|
||||||
PaymentParams#payproc_InvoicePaymentParams{processing_deadline = Deadline}.
|
PaymentParams#payproc_InvoicePaymentParams{processing_deadline = Deadline}.
|
||||||
|
|
||||||
|
mk_fd_stat(?prv(ID), {ConversionFailureRate, AvailabilityFailureRate}) ->
|
||||||
|
[
|
||||||
|
mk_fd_stat(<<"provider_conversion">>, ?prv(ID), ConversionFailureRate),
|
||||||
|
mk_fd_stat(<<"adapter_availability">>, ?prv(ID), AvailabilityFailureRate)
|
||||||
|
].
|
||||||
|
|
||||||
|
mk_fd_stat(Type, ?prv(ID), FailureRate) ->
|
||||||
|
#fault_detector_ServiceStatistics{
|
||||||
|
service_id = <<"hellgate_service.", Type/binary, ".", (integer_to_binary(ID))/binary>>,
|
||||||
|
%% NOTE Testsuite config's critical failure threshold is .7
|
||||||
|
failure_rate = FailureRate,
|
||||||
|
%% Those are bullshit values, because we don't actually care for raw numbers
|
||||||
|
operations_count = 10,
|
||||||
|
error_operations_count = 9,
|
||||||
|
overtime_operations_count = 0,
|
||||||
|
success_operations_count = 1
|
||||||
|
}.
|
||||||
|
|
||||||
|
with_fault_detector(Statistics, Fun) ->
|
||||||
|
FDConfig = genlib_app:env(hellgate, fault_detector),
|
||||||
|
_ = application:set_env(hellgate, fault_detector, FDConfig#{enabled => true}),
|
||||||
|
_ = hg_kv_store:put(fd_statistics, Statistics),
|
||||||
|
Result = Fun(),
|
||||||
|
application:set_env(hellgate, fault_detector, FDConfig#{enabled => false}),
|
||||||
|
Result.
|
||||||
|
|
||||||
|
mock_fault_detector(SupPid) ->
|
||||||
|
hg_mock_helper:mock_services(
|
||||||
|
[
|
||||||
|
{fault_detector, fun
|
||||||
|
('InitService', _) ->
|
||||||
|
{ok, {}};
|
||||||
|
('RegisterOperation', _) ->
|
||||||
|
{ok, {}};
|
||||||
|
('GetStatistics', _) ->
|
||||||
|
{ok, hg_kv_store:get(fd_statistics)}
|
||||||
|
end}
|
||||||
|
],
|
||||||
|
SupPid
|
||||||
|
).
|
||||||
|
@ -456,7 +456,9 @@ no_route_found_for_payment(_C) ->
|
|||||||
party_id => ?dummy_party_id,
|
party_id => ?dummy_party_id,
|
||||||
client_ip => undefined
|
client_ip => undefined
|
||||||
},
|
},
|
||||||
{ok, {[], RejectedRoutes1}} = hg_routing:gather_routes(payment, PaymentInstitution, VS, Revision, Ctx0),
|
{ok, {[], RejectedRoutes1}} = unwrap_routing_context(
|
||||||
|
hg_routing:gather_routes(payment, PaymentInstitution, VS, Revision, Ctx0)
|
||||||
|
),
|
||||||
|
|
||||||
?assert_set_equal(
|
?assert_set_equal(
|
||||||
[
|
[
|
||||||
@ -477,7 +479,9 @@ no_route_found_for_payment(_C) ->
|
|||||||
Ctx1 = Ctx0#{
|
Ctx1 = Ctx0#{
|
||||||
currency => Currency1
|
currency => Currency1
|
||||||
},
|
},
|
||||||
{ok, {[], RejectedRoutes2}} = hg_routing:gather_routes(payment, PaymentInstitution, VS1, Revision, Ctx1),
|
{ok, {[], RejectedRoutes2}} = unwrap_routing_context(
|
||||||
|
hg_routing:gather_routes(payment, PaymentInstitution, VS1, Revision, Ctx1)
|
||||||
|
),
|
||||||
?assert_set_equal(
|
?assert_set_equal(
|
||||||
[
|
[
|
||||||
{?prv(1), ?trm(1), {'PaymentsProvisionTerms', currency}},
|
{?prv(1), ?trm(1), {'PaymentsProvisionTerms', currency}},
|
||||||
@ -511,14 +515,10 @@ gather_route_success(_C) ->
|
|||||||
party_id => ?dummy_party_id,
|
party_id => ?dummy_party_id,
|
||||||
client_ip => undefined
|
client_ip => undefined
|
||||||
},
|
},
|
||||||
{ok, {[Route], RejectedRoutes}} = hg_routing:gather_routes(
|
{ok, {[Route], RejectedRoutes}} = unwrap_routing_context(
|
||||||
payment,
|
hg_routing:gather_routes(payment, PaymentInstitution, VS, Revision, Ctx)
|
||||||
PaymentInstitution,
|
|
||||||
VS,
|
|
||||||
Revision,
|
|
||||||
Ctx
|
|
||||||
),
|
),
|
||||||
?assertMatch(?trm(1), hg_routing:terminal_ref(Route)),
|
?assertMatch(?trm(1), hg_route:terminal_ref(Route)),
|
||||||
?assertMatch(
|
?assertMatch(
|
||||||
[
|
[
|
||||||
{?prv(2), ?trm(2), {'PaymentsProvisionTerms', category}},
|
{?prv(2), ?trm(2), {'PaymentsProvisionTerms', category}},
|
||||||
@ -558,7 +558,9 @@ rejected_by_table_prohibitions(_C) ->
|
|||||||
party_id => ?dummy_party_id,
|
party_id => ?dummy_party_id,
|
||||||
client_ip => undefined
|
client_ip => undefined
|
||||||
},
|
},
|
||||||
{ok, {[], RejectedRoutes}} = hg_routing:gather_routes(payment, PaymentInstitution, VS, Revision, Ctx),
|
{ok, {[], RejectedRoutes}} = unwrap_routing_context(
|
||||||
|
hg_routing:gather_routes(payment, PaymentInstitution, VS, Revision, Ctx)
|
||||||
|
),
|
||||||
?assert_set_equal(
|
?assert_set_equal(
|
||||||
[
|
[
|
||||||
{?prv(3), ?trm(3), {'RoutingRule', undefined}},
|
{?prv(3), ?trm(3), {'RoutingRule', undefined}},
|
||||||
@ -600,7 +602,7 @@ empty_candidate_ok(_C) ->
|
|||||||
},
|
},
|
||||||
?assertMatch(
|
?assertMatch(
|
||||||
{ok, {[], []}},
|
{ok, {[], []}},
|
||||||
hg_routing:gather_routes(payment, PaymentInstitution, VS, Revision, Ctx)
|
unwrap_routing_context(hg_routing:gather_routes(payment, PaymentInstitution, VS, Revision, Ctx))
|
||||||
).
|
).
|
||||||
|
|
||||||
-spec ruleset_misconfig(config()) -> test_return().
|
-spec ruleset_misconfig(config()) -> test_return().
|
||||||
@ -620,14 +622,8 @@ ruleset_misconfig(_C) ->
|
|||||||
client_ip => undefined
|
client_ip => undefined
|
||||||
},
|
},
|
||||||
?assertMatch(
|
?assertMatch(
|
||||||
{error, {misconfiguration, {routing_decisions, {delegates, []}}}},
|
{misconfiguration, {routing_decisions, {delegates, []}}},
|
||||||
hg_routing:gather_routes(
|
hg_routing_ctx:error(hg_routing:gather_routes(payment, PaymentInstitution, VS, Revision, Ctx))
|
||||||
payment,
|
|
||||||
PaymentInstitution,
|
|
||||||
VS,
|
|
||||||
Revision,
|
|
||||||
Ctx
|
|
||||||
)
|
|
||||||
).
|
).
|
||||||
|
|
||||||
-spec routes_selected_for_low_risk_score(config()) -> test_return().
|
-spec routes_selected_for_low_risk_score(config()) -> test_return().
|
||||||
@ -658,14 +654,16 @@ routes_selected_with_risk_score(_C, RiskScore, ProviderRefs) ->
|
|||||||
party_id => ?dummy_party_id,
|
party_id => ?dummy_party_id,
|
||||||
client_ip => undefined
|
client_ip => undefined
|
||||||
},
|
},
|
||||||
{ok, {Routes, _}} = hg_routing:gather_routes(payment, PaymentInstitution, VS, Revision, Ctx),
|
{ok, {Routes, _}} = unwrap_routing_context(
|
||||||
?assert_set_equal(ProviderRefs, lists:map(fun hg_routing:provider_ref/1, Routes)).
|
hg_routing:gather_routes(payment, PaymentInstitution, VS, Revision, Ctx)
|
||||||
|
),
|
||||||
|
?assert_set_equal(ProviderRefs, lists:map(fun hg_route:provider_ref/1, Routes)).
|
||||||
|
|
||||||
-spec choice_context_formats_ok(config()) -> test_return().
|
-spec choice_context_formats_ok(config()) -> test_return().
|
||||||
choice_context_formats_ok(_C) ->
|
choice_context_formats_ok(_C) ->
|
||||||
Route1 = hg_routing:new(?prv(1), ?trm(1)),
|
Route1 = hg_route:new(?prv(1), ?trm(1)),
|
||||||
Route2 = hg_routing:new(?prv(2), ?trm(2)),
|
Route2 = hg_route:new(?prv(2), ?trm(2)),
|
||||||
Route3 = hg_routing:new(?prv(3), ?trm(3)),
|
Route3 = hg_route:new(?prv(3), ?trm(3)),
|
||||||
Routes = [Route1, Route2, Route3],
|
Routes = [Route1, Route2, Route3],
|
||||||
|
|
||||||
Revision = ?routing_with_fail_rate_domain_revision,
|
Revision = ?routing_with_fail_rate_domain_revision,
|
||||||
@ -722,19 +720,15 @@ do_gather_routes(Revision, ExpectedRouteTerminal, ExpectedRejectedRoutes) ->
|
|||||||
party_id => ?dummy_party_id,
|
party_id => ?dummy_party_id,
|
||||||
client_ip => undefined
|
client_ip => undefined
|
||||||
},
|
},
|
||||||
{ok, {Routes, RejectedRoutes}} = hg_routing:gather_routes(
|
{ok, {Routes, RejectedRoutes}} = unwrap_routing_context(
|
||||||
payment,
|
hg_routing:gather_routes(payment, PaymentInstitution, VS, Revision, Ctx)
|
||||||
PaymentInstitution,
|
|
||||||
VS,
|
|
||||||
Revision,
|
|
||||||
Ctx
|
|
||||||
),
|
),
|
||||||
case ExpectedRouteTerminal of
|
case ExpectedRouteTerminal of
|
||||||
undefined ->
|
undefined ->
|
||||||
ok;
|
ok;
|
||||||
Terminal ->
|
Terminal ->
|
||||||
[Route] = Routes,
|
[Route] = Routes,
|
||||||
?assertMatch(Terminal, hg_routing:terminal_ref(Route))
|
?assertMatch(Terminal, hg_route:terminal_ref(Route))
|
||||||
end,
|
end,
|
||||||
?assertMatch(ExpectedRejectedRoutes, RejectedRoutes).
|
?assertMatch(ExpectedRejectedRoutes, RejectedRoutes).
|
||||||
|
|
||||||
@ -742,8 +736,8 @@ do_gather_routes(Revision, ExpectedRouteTerminal, ExpectedRejectedRoutes) ->
|
|||||||
|
|
||||||
-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(11), ?trm(11), 0, 10),
|
Route1 = hg_route:new(?prv(11), ?trm(11), 0, 10),
|
||||||
Route2 = hg_routing:new(?prv(12), ?trm(12), 0, 10),
|
Route2 = hg_route:new(?prv(12), ?trm(12), 0, 10),
|
||||||
?assertMatch({Route1, _}, terminal_priority_for_shop(?shop_id_for_ruleset_w_priority_distribution_1, C)),
|
?assertMatch({Route1, _}, terminal_priority_for_shop(?shop_id_for_ruleset_w_priority_distribution_1, C)),
|
||||||
?assertMatch({Route2, _}, terminal_priority_for_shop(?shop_id_for_ruleset_w_priority_distribution_2, C)).
|
?assertMatch({Route2, _}, terminal_priority_for_shop(?shop_id_for_ruleset_w_priority_distribution_2, C)).
|
||||||
|
|
||||||
@ -767,7 +761,9 @@ terminal_priority_for_shop(ShopID, _C) ->
|
|||||||
party_id => ?dummy_party_id,
|
party_id => ?dummy_party_id,
|
||||||
client_ip => undefined
|
client_ip => undefined
|
||||||
},
|
},
|
||||||
{ok, {Routes, _RejectedRoutes}} = hg_routing:gather_routes(payment, PaymentInstitution, VS, Revision, Ctx),
|
{ok, {Routes, _RejectedRoutes}} = unwrap_routing_context(
|
||||||
|
hg_routing:gather_routes(payment, PaymentInstitution, VS, Revision, Ctx)
|
||||||
|
),
|
||||||
hg_routing:choose_route(Routes).
|
hg_routing:choose_route(Routes).
|
||||||
|
|
||||||
-spec gather_pinned_route(config()) -> test_return().
|
-spec gather_pinned_route(config()) -> test_return().
|
||||||
@ -790,16 +786,18 @@ gather_pinned_route(_C) ->
|
|||||||
party_id => ?dummy_party_id,
|
party_id => ?dummy_party_id,
|
||||||
client_ip => undefined
|
client_ip => undefined
|
||||||
},
|
},
|
||||||
{ok, {Routes, _RejectedRoutes}} = hg_routing:gather_routes(payment, PaymentInstitution, VS, Revision, Ctx),
|
{ok, {Routes, _RejectedRoutes}} = unwrap_routing_context(
|
||||||
|
hg_routing:gather_routes(payment, PaymentInstitution, VS, Revision, Ctx)
|
||||||
|
),
|
||||||
Pin = #{
|
Pin = #{
|
||||||
currency => Currency,
|
currency => Currency,
|
||||||
payment_tool => PaymentTool
|
payment_tool => PaymentTool
|
||||||
},
|
},
|
||||||
?assert_set_equal(
|
?assert_set_equal(
|
||||||
[
|
[
|
||||||
hg_routing:new(?prv(1), ?trm(1), 0, 0, Ctx),
|
hg_route:new(?prv(1), ?trm(1), 0, 0, Ctx),
|
||||||
hg_routing:new(?prv(2), ?trm(2), 0, 0, Pin),
|
hg_route:new(?prv(2), ?trm(2), 0, 0, Pin),
|
||||||
hg_routing:new(?prv(3), ?trm(3), 0, 0, Pin)
|
hg_route:new(?prv(3), ?trm(3), 0, 0, Pin)
|
||||||
],
|
],
|
||||||
Routes
|
Routes
|
||||||
).
|
).
|
||||||
@ -823,9 +821,9 @@ choose_pinned_route(_C) ->
|
|||||||
currency => Currency,
|
currency => Currency,
|
||||||
payment_tool => PaymentTool
|
payment_tool => PaymentTool
|
||||||
},
|
},
|
||||||
Route1 = hg_routing:new(?prv(1), ?trm(1), 0, 0, Pin1),
|
Route1 = hg_route:new(?prv(1), ?trm(1), 0, 0, Pin1),
|
||||||
Route2 = hg_routing:new(?prv(1), ?trm(1), 0, 0, Pin2),
|
Route2 = hg_route:new(?prv(1), ?trm(1), 0, 0, Pin2),
|
||||||
Route3 = hg_routing:new(?prv(1), ?trm(1), 0, 0, Pin3),
|
Route3 = hg_route:new(?prv(1), ?trm(1), 0, 0, Pin3),
|
||||||
Routes = [
|
Routes = [
|
||||||
Route1,
|
Route1,
|
||||||
Route2,
|
Route2,
|
||||||
@ -837,9 +835,9 @@ choose_pinned_route(_C) ->
|
|||||||
-spec choose_route_w_override(config()) -> test_return().
|
-spec choose_route_w_override(config()) -> test_return().
|
||||||
choose_route_w_override(_C) ->
|
choose_route_w_override(_C) ->
|
||||||
%% without overrides
|
%% without overrides
|
||||||
Route1 = hg_routing:new(?prv(1), ?trm(1)),
|
Route1 = hg_route:new(?prv(1), ?trm(1)),
|
||||||
Route2 = hg_routing:new(?prv(2), ?trm(2)),
|
Route2 = hg_route:new(?prv(2), ?trm(2)),
|
||||||
Route3 = hg_routing:new(?prv(3), ?trm(3)),
|
Route3 = hg_route:new(?prv(3), ?trm(3)),
|
||||||
Routes = [Route1, Route2, Route3],
|
Routes = [Route1, Route2, Route3],
|
||||||
{
|
{
|
||||||
Route2,
|
Route2,
|
||||||
@ -850,7 +848,7 @@ choose_route_w_override(_C) ->
|
|||||||
} = hg_routing:choose_route(Routes),
|
} = hg_routing:choose_route(Routes),
|
||||||
|
|
||||||
%% with overrides
|
%% with overrides
|
||||||
Route3WithOV = hg_routing:new(?prv(3), ?trm(3), 0, 1000, #{}, #domain_RouteFaultDetectorOverrides{enabled = true}),
|
Route3WithOV = hg_route:new(?prv(3), ?trm(3), 0, 1000, #{}, #domain_RouteFaultDetectorOverrides{enabled = true}),
|
||||||
RoutesWithOV = [Route1, Route2, Route3WithOV],
|
RoutesWithOV = [Route1, Route2, Route3WithOV],
|
||||||
{Route3WithOV, _} = hg_routing:choose_route(RoutesWithOV).
|
{Route3WithOV, _} = hg_routing:choose_route(RoutesWithOV).
|
||||||
|
|
||||||
@ -928,3 +926,6 @@ maybe_set_risk_coverage(false, _) ->
|
|||||||
undefined;
|
undefined;
|
||||||
maybe_set_risk_coverage(true, V) ->
|
maybe_set_risk_coverage(true, V) ->
|
||||||
{value, V}.
|
{value, V}.
|
||||||
|
|
||||||
|
unwrap_routing_context(RoutingCtx) ->
|
||||||
|
{ok, {hg_routing_ctx:candidates(RoutingCtx), hg_routing_ctx:rejected_routes(RoutingCtx)}}.
|
||||||
|
@ -20,7 +20,7 @@
|
|||||||
{<<"ctx">>,{pkg,<<"ctx">>,<<"0.6.0">>},2},
|
{<<"ctx">>,{pkg,<<"ctx">>,<<"0.6.0">>},2},
|
||||||
{<<"damsel">>,
|
{<<"damsel">>,
|
||||||
{git,"https://github.com/valitydev/damsel.git",
|
{git,"https://github.com/valitydev/damsel.git",
|
||||||
{ref,"5b69a5bb59529a22f2bf0bbd78b7539773db3562"}},
|
{ref,"23211ff8c0c45699fa6d78afa55f304e95dbee87"}},
|
||||||
0},
|
0},
|
||||||
{<<"dmt_client">>,
|
{<<"dmt_client">>,
|
||||||
{git,"https://github.com/valitydev/dmt-client.git",
|
{git,"https://github.com/valitydev/dmt-client.git",
|
||||||
|
Loading…
Reference in New Issue
Block a user