diff --git a/Dockerfile b/Dockerfile index ee06622..ec0732d 100644 --- a/Dockerfile +++ b/Dockerfile @@ -2,10 +2,10 @@ ARG OTP_VERSION # Build the release FROM docker.io/library/erlang:${OTP_VERSION} AS builder +SHELL ["/bin/bash", "-o", "pipefail", "-c"] # Install thrift compiler ARG THRIFT_VERSION - ARG TARGETARCH RUN wget -q -O- "https://github.com/valitydev/thrift/releases/download/${THRIFT_VERSION}/thrift-${THRIFT_VERSION}-linux-${TARGETARCH}.tar.gz" \ | tar -xvz -C /usr/local/bin/ @@ -16,8 +16,8 @@ COPY . /build/ # Build the release WORKDIR /build -RUN rebar3 compile -RUN rebar3 as prod release +RUN rebar3 compile && \ + rebar3 as prod release # Make a runner image FROM docker.io/library/erlang:${OTP_VERSION}-slim @@ -28,15 +28,15 @@ ARG SERVICE_NAME ENV CHARSET=UTF-8 ENV LANG=C.UTF-8 -# Expose SERVICE_NAME as env so CMD expands properly on start -ENV SERVICE_NAME=${SERVICE_NAME} - # Set runtime WORKDIR /opt/${SERVICE_NAME} COPY --from=builder /build/_build/prod/rel/${SERVICE_NAME} /opt/${SERVICE_NAME} +RUN echo "#!/bin/sh" >> /entrypoint.sh && \ + echo "exec /opt/${SERVICE_NAME}/bin/${SERVICE_NAME} foreground" >> /entrypoint.sh && \ + chmod +x /entrypoint.sh ENTRYPOINT [] -CMD /opt/${SERVICE_NAME}/bin/${SERVICE_NAME} foreground +CMD ["/entrypoint.sh"] EXPOSE 8022 diff --git a/Dockerfile.dev b/Dockerfile.dev index b2805aa..e4cfa53 100644 --- a/Dockerfile.dev +++ b/Dockerfile.dev @@ -1,13 +1,17 @@ ARG OTP_VERSION FROM docker.io/library/erlang:${OTP_VERSION} +SHELL ["/bin/bash", "-o", "pipefail", "-c"] +# Install thrift compiler ARG THRIFT_VERSION -ARG BUILDARCH - -RUN wget -q -O- "https://github.com/valitydev/thrift/releases/download/${THRIFT_VERSION}/thrift-${THRIFT_VERSION}-linux-${BUILDARCH}.tar.gz" \ +ARG TARGETARCH +RUN wget -q -O- "https://github.com/valitydev/thrift/releases/download/${THRIFT_VERSION}/thrift-${THRIFT_VERSION}-linux-${TARGETARCH}.tar.gz" \ | tar -xvz -C /usr/local/bin/ +# Set env ENV CHARSET=UTF-8 ENV LANG=C.UTF-8 -CMD /bin/bash + +# Set runtime +CMD ["/bin/bash"] diff --git a/apps/party_management/src/party_management.app.src b/apps/party_management/src/party_management.app.src index 87319b5..0ce9ed7 100644 --- a/apps/party_management/src/party_management.app.src +++ b/apps/party_management/src/party_management.app.src @@ -16,7 +16,6 @@ scoper, % should be before any scoper event handler usage gproc, dmt_client, - woody_user_identity, payproc_errors, erl_health, cache diff --git a/apps/party_management/src/pm_access_control.erl b/apps/party_management/src/pm_access_control.erl deleted file mode 100644 index 4bc953e..0000000 --- a/apps/party_management/src/pm_access_control.erl +++ /dev/null @@ -1,16 +0,0 @@ --module(pm_access_control). - -%%% HG access controll - --export([check_user/2]). - --spec check_user(woody_user_identity:user_identity(), dmsl_domain_thrift:'PartyID'()) -> ok | invalid_user. -check_user(#{id := PartyID, realm := <<"external">>}, PartyID) -> - ok; -check_user(#{id := _AnyID, realm := <<"internal">>}, _PartyID) -> - ok; -%% @TODO must be deleted when we get rid of #payproc_ServiceUser -check_user(#{id := _AnyID, realm := <<"service">>}, _PartyID) -> - ok; -check_user(_, _) -> - invalid_user. diff --git a/apps/party_management/src/pm_context.erl b/apps/party_management/src/pm_context.erl index e190dd9..225e036 100644 --- a/apps/party_management/src/pm_context.erl +++ b/apps/party_management/src/pm_context.erl @@ -7,15 +7,12 @@ -export([cleanup/0]). -export([get_woody_context/1]). --export([set_user_identity/2]). -opaque context() :: #{ - woody_context := woody_context(), - user_identity => user_identity() + woody_context := woody_context() }. -type options() :: #{ - user_identity => user_identity(), woody_context => woody_context() }. @@ -24,7 +21,6 @@ %% Internal types --type user_identity() :: woody_user_identity:user_identity(). -type woody_context() :: woody_context:ctx(). %% TODO change when moved to separate app @@ -61,14 +57,9 @@ cleanup() -> ok. -spec get_woody_context(context()) -> woody_context(). -get_woody_context(Context) -> - #{woody_context := WoodyContext} = ensure_woody_user_info_set(Context), +get_woody_context(#{woody_context := WoodyContext}) -> WoodyContext. --spec set_user_identity(user_identity(), context()) -> context(). -set_user_identity(Identity, Context) -> - Context#{user_identity => Identity}. - %% Internal functions -spec ensure_woody_context_exists(options()) -> options(). @@ -76,10 +67,3 @@ ensure_woody_context_exists(#{woody_context := _WoodyContext} = Options) -> Options; ensure_woody_context_exists(Options) -> Options#{woody_context => woody_context:new()}. - --spec ensure_woody_user_info_set(context()) -> context(). -ensure_woody_user_info_set(#{user_identity := Identity, woody_context := WoodyContext} = Context) -> - NewWoodyContext = woody_user_identity:put(Identity, WoodyContext), - Context#{woody_context := NewWoodyContext}; -ensure_woody_user_info_set(Context) -> - Context. diff --git a/apps/party_management/src/pm_party_handler.erl b/apps/party_management/src/pm_party_handler.erl index 020d277..9cf466d 100644 --- a/apps/party_management/src/pm_party_handler.erl +++ b/apps/party_management/src/pm_party_handler.erl @@ -14,25 +14,27 @@ handle_function(Func, Args, Opts) -> scoper:scope( partymgmt, - fun() -> handle_function_(Func, Args, Opts) end + fun() -> + handle_function_(Func, remove_user_info_arg(Func, Args), Opts) + end ). -spec handle_function_(woody:func(), woody:args(), pm_woody_wrapper:handler_opts()) -> term() | no_return(). %% Party -handle_function_('Create', {UserInfo, PartyID, PartyParams}, _Opts) -> - ok = set_meta_and_check_access(UserInfo, PartyID), +handle_function_('Create', {PartyID, PartyParams}, _Opts) -> + _ = set_party_mgmt_meta(PartyID), pm_party_machine:start(PartyID, PartyParams); -handle_function_('Checkout', {UserInfo, PartyID, RevisionParam}, _Opts) -> - ok = set_meta_and_check_access(UserInfo, PartyID), +handle_function_('Checkout', {PartyID, RevisionParam}, _Opts) -> + _ = set_party_mgmt_meta(PartyID), checkout_party(PartyID, RevisionParam, #payproc_InvalidPartyRevision{}); -handle_function_('Get', {UserInfo, PartyID}, _Opts) -> - ok = set_meta_and_check_access(UserInfo, PartyID), +handle_function_('Get', {PartyID}, _Opts) -> + _ = set_party_mgmt_meta(PartyID), pm_party_machine:get_party(PartyID); -handle_function_('GetRevision', {UserInfo, PartyID}, _Opts) -> - ok = set_meta_and_check_access(UserInfo, PartyID), +handle_function_('GetRevision', {PartyID}, _Opts) -> + _ = set_party_mgmt_meta(PartyID), pm_party_machine:get_last_revision(PartyID); -handle_function_('GetStatus', {UserInfo, PartyID}, _Opts) -> - ok = set_meta_and_check_access(UserInfo, PartyID), +handle_function_('GetStatus', {PartyID}, _Opts) -> + _ = set_party_mgmt_meta(PartyID), pm_party_machine:get_status(PartyID); handle_function_(Fun, Args, _Opts) when Fun =:= 'Block' orelse @@ -40,18 +42,17 @@ handle_function_(Fun, Args, _Opts) when Fun =:= 'Suspend' orelse Fun =:= 'Activate' -> - UserInfo = erlang:element(1, Args), - PartyID = erlang:element(2, Args), - ok = set_meta_and_check_access(UserInfo, PartyID), + PartyID = erlang:element(1, Args), + _ = set_party_mgmt_meta(PartyID), call(PartyID, Fun, Args); %% Contract -handle_function_('GetContract', {UserInfo, PartyID, ContractID}, _Opts) -> - ok = set_meta_and_check_access(UserInfo, PartyID), +handle_function_('GetContract', {PartyID, ContractID}, _Opts) -> + _ = set_party_mgmt_meta(PartyID), Party = pm_party_machine:get_party(PartyID), ensure_contract(pm_party:get_contract(ContractID, Party)); handle_function_('ComputeContractTerms', Args, _Opts) -> - {UserInfo, PartyID, ContractID, Timestamp, PartyRevisionParams, DomainRevision, Varset} = Args, - ok = set_meta_and_check_access(UserInfo, PartyID), + {PartyID, ContractID, Timestamp, PartyRevisionParams, DomainRevision, Varset} = Args, + _ = set_party_mgmt_meta(PartyID), Party = checkout_party(PartyID, PartyRevisionParams), Contract = ensure_contract(pm_party:get_contract(ContractID, Party)), VS = @@ -73,19 +74,19 @@ handle_function_('ComputeContractTerms', Args, _Opts) -> Terms = pm_party:get_terms(Contract, Timestamp, DomainRevision), pm_party:reduce_terms(Terms, DecodedVS, DomainRevision); %% Shop -handle_function_('GetShop', {UserInfo, PartyID, ID}, _Opts) -> - ok = set_meta_and_check_access(UserInfo, PartyID), +handle_function_('GetShop', {PartyID, ID}, _Opts) -> + _ = set_party_mgmt_meta(PartyID), Party = pm_party_machine:get_party(PartyID), ensure_shop(pm_party:get_shop(ID, Party)); -handle_function_('GetShopContract', {UserInfo, PartyID, ID}, _Opts) -> - ok = set_meta_and_check_access(UserInfo, PartyID), +handle_function_('GetShopContract', {PartyID, ID}, _Opts) -> + _ = set_party_mgmt_meta(PartyID), Party = pm_party_machine:get_party(PartyID), Shop = ensure_shop(pm_party:get_shop(ID, Party)), Contract = pm_party:get_contract(Shop#domain_Shop.contract_id, Party), Contractor = pm_party:get_contractor(Contract#domain_Contract.contractor_id, Party), #payproc_ShopContract{shop = Shop, contract = Contract, contractor = Contractor}; -handle_function_('ComputeShopTerms', {UserInfo, PartyID, ShopID, Timestamp, PartyRevision, Varset}, _Opts) -> - ok = set_meta_and_check_access(UserInfo, PartyID), +handle_function_('ComputeShopTerms', {PartyID, ShopID, Timestamp, PartyRevision, Varset}, _Opts) -> + _ = set_party_mgmt_meta(PartyID), Party = checkout_party(PartyID, PartyRevision), Shop = ensure_shop(pm_party:get_shop(ShopID, Party)), Contract = pm_party:get_contract(Shop#domain_Shop.contract_id, Party), @@ -105,16 +106,15 @@ handle_function_(Fun, Args, _Opts) when Fun =:= 'SuspendShop' orelse Fun =:= 'ActivateShop' -> - UserInfo = erlang:element(1, Args), - PartyID = erlang:element(2, Args), - ok = set_meta_and_check_access(UserInfo, PartyID), + PartyID = erlang:element(1, Args), + _ = set_party_mgmt_meta(PartyID), call(PartyID, Fun, Args); %% Claim -handle_function_('GetClaim', {UserInfo, PartyID, ID}, _Opts) -> - ok = set_meta_and_check_access(UserInfo, PartyID), +handle_function_('GetClaim', {PartyID, ID}, _Opts) -> + _ = set_party_mgmt_meta(PartyID), pm_party_machine:get_claim(ID, PartyID); -handle_function_('GetClaims', {UserInfo, PartyID}, _Opts) -> - ok = set_meta_and_check_access(UserInfo, PartyID), +handle_function_('GetClaims', {PartyID}, _Opts) -> + _ = set_party_mgmt_meta(PartyID), pm_party_machine:get_claims(PartyID); handle_function_(Fun, Args, _Opts) when Fun =:= 'CreateClaim' orelse @@ -123,36 +123,33 @@ handle_function_(Fun, Args, _Opts) when Fun =:= 'DenyClaim' orelse Fun =:= 'RevokeClaim' -> - UserInfo = erlang:element(1, Args), - PartyID = erlang:element(2, Args), - ok = set_meta_and_check_access(UserInfo, PartyID), + PartyID = erlang:element(1, Args), + _ = set_party_mgmt_meta(PartyID), call(PartyID, Fun, Args); %% Event -handle_function_('GetEvents', {UserInfo, PartyID, Range}, _Opts) -> - ok = set_meta_and_check_access(UserInfo, PartyID), +handle_function_('GetEvents', {PartyID, Range}, _Opts) -> + _ = set_party_mgmt_meta(PartyID), #payproc_EventRange{'after' = AfterID, limit = Limit} = Range, pm_party_machine:get_public_history(PartyID, AfterID, Limit); %% ShopAccount -handle_function_('GetAccountState', {UserInfo, PartyID, AccountID}, _Opts) -> - ok = set_meta_and_check_access(UserInfo, PartyID), +handle_function_('GetAccountState', {PartyID, AccountID}, _Opts) -> + _ = set_party_mgmt_meta(PartyID), Party = pm_party_machine:get_party(PartyID), pm_party:get_account_state(AccountID, Party); -handle_function_('GetShopAccount', {UserInfo, PartyID, ShopID}, _Opts) -> - ok = set_meta_and_check_access(UserInfo, PartyID), +handle_function_('GetShopAccount', {PartyID, ShopID}, _Opts) -> + _ = set_party_mgmt_meta(PartyID), Party = pm_party_machine:get_party(PartyID), pm_party:get_shop_account(ShopID, Party); %% Providers handle_function_('ComputeProvider', Args, _Opts) -> - {UserInfo, ProviderRef, DomainRevision, Varset} = Args, - ok = assume_user_identity(UserInfo), + {ProviderRef, DomainRevision, Varset} = Args, Provider = get_provider(ProviderRef, DomainRevision), VS = pm_varset:decode_varset(Varset), ComputedProvider = pm_provider:reduce_provider(Provider, VS, DomainRevision), _ = assert_provider_reduced(ComputedProvider), ComputedProvider; handle_function_('ComputeProviderTerminalTerms', Args, _Opts) -> - {UserInfo, ProviderRef, TerminalRef, DomainRevision, Varset} = Args, - ok = assume_user_identity(UserInfo), + {ProviderRef, TerminalRef, DomainRevision, Varset} = Args, Provider = get_provider(ProviderRef, DomainRevision), Terminal = get_terminal(TerminalRef, DomainRevision), VS = pm_varset:decode_varset(Varset), @@ -185,49 +182,42 @@ handle_function_('ComputeProviderTerminal', {TerminalRef, DomainRevision, Varset }; %% Globals handle_function_('ComputeGlobals', Args, _Opts) -> - {UserInfo, DomainRevision, Varset} = Args, - ok = assume_user_identity(UserInfo), + {DomainRevision, Varset} = Args, Globals = get_globals(DomainRevision), VS = pm_varset:decode_varset(Varset), pm_globals:reduce_globals(Globals, VS, DomainRevision); %% RuleSets -%% Deprecated, will be replaced by 'ComputeRoutingRuleset' -handle_function_('ComputePaymentRoutingRuleset', Args, _Opts) -> - {UserInfo, RuleSetRef, DomainRevision, Varset} = Args, - ok = assume_user_identity(UserInfo), - RuleSet = get_payment_routing_ruleset(RuleSetRef, DomainRevision), - VS = pm_varset:decode_varset(Varset), - pm_ruleset:reduce_payment_routing_ruleset(RuleSet, VS, DomainRevision); -handle_function_('ComputeRoutingRuleset', Args, _Opts) -> - {UserInfo, RuleSetRef, DomainRevision, Varset} = Args, - ok = assume_user_identity(UserInfo), +handle_function_(ComputeRulesetFun, Args, _Opts) when + %% 'ComputePaymentRoutingRuleset' is deprecated, will be replaced by 'ComputeRoutingRuleset' + ComputeRulesetFun =:= 'ComputePaymentRoutingRuleset' orelse + ComputeRulesetFun =:= 'ComputeRoutingRuleset' +-> + {RuleSetRef, DomainRevision, Varset} = Args, RuleSet = get_payment_routing_ruleset(RuleSetRef, DomainRevision), VS = pm_varset:decode_varset(Varset), pm_ruleset:reduce_payment_routing_ruleset(RuleSet, VS, DomainRevision); %% PartyMeta -handle_function_('GetMeta', {UserInfo, PartyID}, _Opts) -> - ok = set_meta_and_check_access(UserInfo, PartyID), +handle_function_('GetMeta', {PartyID}, _Opts) -> + _ = set_party_mgmt_meta(PartyID), pm_party_machine:get_meta(PartyID); -handle_function_('GetMetaData', {UserInfo, PartyID, NS}, _Opts) -> - ok = set_meta_and_check_access(UserInfo, PartyID), +handle_function_('GetMetaData', {PartyID, NS}, _Opts) -> + _ = set_party_mgmt_meta(PartyID), pm_party_machine:get_metadata(NS, PartyID); handle_function_(Fun, Args, _Opts) when Fun =:= 'SetMetaData' orelse Fun =:= 'RemoveMetaData' -> - UserInfo = erlang:element(1, Args), - PartyID = erlang:element(2, Args), - ok = set_meta_and_check_access(UserInfo, PartyID), + PartyID = erlang:element(1, Args), + _ = set_party_mgmt_meta(PartyID), call(PartyID, Fun, Args); %% Payment Institutions handle_function_( 'ComputePaymentInstitutionTerms', - {UserInfo, PaymentInstitutionRef, Varset}, + {PaymentInstitutionRef, Varset}, _Opts ) -> - ok = assume_user_identity(UserInfo), Revision = pm_domain:head(), PaymentInstitution = get_payment_institution(PaymentInstitutionRef, Revision), VS = pm_varset:decode_varset(Varset), @@ -235,8 +225,7 @@ handle_function_( Terms = pm_party:get_terms(ContractTemplate, pm_datetime:format_now(), Revision), pm_party:reduce_terms(Terms, VS, Revision); handle_function_('ComputePaymentInstitution', Args, _Opts) -> - {UserInfo, PaymentInstitutionRef, DomainRevision, Varset} = Args, - ok = assume_user_identity(UserInfo), + {PaymentInstitutionRef, DomainRevision, Varset} = Args, PaymentInstitution = get_payment_institution(PaymentInstitutionRef, DomainRevision), VS = pm_varset:decode_varset(Varset), pm_payment_institution:reduce_payment_institution(PaymentInstitution, VS, DomainRevision); @@ -244,10 +233,10 @@ handle_function_('ComputePaymentInstitution', Args, _Opts) -> handle_function_( 'ComputePayoutCashFlow', - {UserInfo, PartyID, #payproc_PayoutParams{id = ShopID, amount = Amount, timestamp = Timestamp} = PayoutParams}, + {PartyID, #payproc_PayoutParams{id = ShopID, amount = Amount, timestamp = Timestamp} = PayoutParams}, _Opts ) -> - ok = set_meta_and_check_access(UserInfo, PartyID), + _ = set_party_mgmt_meta(PartyID), Party = checkout_party(PartyID, {timestamp, Timestamp}), Shop = ensure_shop(pm_party:get_shop(ShopID, Party)), Contract = pm_party:get_contract(Shop#domain_Shop.contract_id, Party), @@ -272,8 +261,27 @@ handle_function_( %% +%% @TODO Delete after protocol migration +%% This is a migration measure to make sure we can accept both old and new (with no userinfo) protocol here +remove_user_info_arg('ComputeProviderTerminal', Args0) -> + Args0; +remove_user_info_arg(_Func, Args0) -> + erlang:delete_element(1, Args0). + +add_user_info_arg('ComputeProviderTerminal', Args0) -> + Args0; +add_user_info_arg(_Func, Args0) -> + erlang:insert_element(1, Args0, undefined). + +%% + call(PartyID, FunctionName, Args) -> - pm_party_machine:call(PartyID, party_management, {'PartyManagement', FunctionName}, Args). + pm_party_machine:call( + PartyID, + party_management, + {'PartyManagement', FunctionName}, + add_user_info_arg(FunctionName, Args) + ). %% @@ -287,23 +295,6 @@ get_payout_tool(_Shop, Contract, #payproc_PayoutParams{payout_tool_id = ToolID}) get_payout_tool(Shop, Contract, _PayoutParams) -> pm_contract:get_payout_tool(Shop#domain_Shop.payout_tool_id, Contract). -set_meta_and_check_access(UserInfo, PartyID) -> - ok = assume_user_identity(UserInfo), - _ = set_party_mgmt_meta(PartyID), - assert_party_accessible(PartyID). - --spec assert_party_accessible( - dmsl_domain_thrift:'PartyID'() -) -> ok | no_return(). -assert_party_accessible(PartyID) -> - UserIdentity = pm_woody_handler_utils:get_user_identity(), - case pm_access_control:check_user(UserIdentity, PartyID) of - ok -> - ok; - invalid_user -> - throw(#payproc_InvalidUser{}) - end. - assert_provider_reduced(#domain_Provider{terms = Terms}) -> assert_provider_terms_reduced(Terms). @@ -315,9 +306,6 @@ assert_provider_terms_reduced(undefined) -> set_party_mgmt_meta(PartyID) -> scoper:add_meta(#{party_id => PartyID}). -assume_user_identity(UserInfo) -> - pm_woody_handler_utils:assume_user_identity(UserInfo). - checkout_party(PartyID, RevisionParam) -> checkout_party(PartyID, RevisionParam, #payproc_PartyNotExistsYet{}). diff --git a/apps/party_management/src/pm_party_machine.erl b/apps/party_management/src/pm_party_machine.erl index fa16493..a2e0f24 100644 --- a/apps/party_management/src/pm_party_machine.erl +++ b/apps/party_management/src/pm_party_machine.erl @@ -113,7 +113,7 @@ process_signal(timeout, _Machine) -> -spec process_call(call(), pm_machine:machine()) -> {pm_machine:response(), pm_machine:result()}. process_call({{'PartyManagement', Fun}, Args}, Machine) -> PartyID = erlang:element(2, Args), - process_call_(PartyID, Fun, Args, Machine); + process_call_(PartyID, Fun, remove_user_info_arg(Args), Machine); process_call({{'ClaimCommitter', Fun}, Args}, Machine) -> PartyID = erlang:element(1, Args), process_call_(PartyID, Fun, Args, Machine). @@ -142,36 +142,39 @@ process_call_(PartyID, Fun, Args, Machine) -> respond_w_exception(Exception) end. +remove_user_info_arg(Args0) -> + erlang:delete_element(1, Args0). + %% Party -handle_call('Block', {_, _PartyID, Reason}, AuxSt, St) -> +handle_call('Block', {_PartyID, Reason}, AuxSt, St) -> handle_block(party, Reason, AuxSt, St); -handle_call('Unblock', {_, _PartyID, Reason}, AuxSt, St) -> +handle_call('Unblock', {_PartyID, Reason}, AuxSt, St) -> handle_unblock(party, Reason, AuxSt, St); -handle_call('Suspend', {_, _PartyID}, AuxSt, St) -> +handle_call('Suspend', {_PartyID}, AuxSt, St) -> handle_suspend(party, AuxSt, St); -handle_call('Activate', {_, _PartyID}, AuxSt, St) -> +handle_call('Activate', {_PartyID}, AuxSt, St) -> handle_activate(party, AuxSt, St); %% Shop -handle_call('BlockShop', {_, _PartyID, ID, Reason}, AuxSt, St) -> +handle_call('BlockShop', {_PartyID, ID, Reason}, AuxSt, St) -> handle_block({shop, ID}, Reason, AuxSt, St); -handle_call('UnblockShop', {_, _PartyID, ID, Reason}, AuxSt, St) -> +handle_call('UnblockShop', {_PartyID, ID, Reason}, AuxSt, St) -> handle_unblock({shop, ID}, Reason, AuxSt, St); -handle_call('SuspendShop', {_, _PartyID, ID}, AuxSt, St) -> +handle_call('SuspendShop', {_PartyID, ID}, AuxSt, St) -> handle_suspend({shop, ID}, AuxSt, St); -handle_call('ActivateShop', {_, _PartyID, ID}, AuxSt, St) -> +handle_call('ActivateShop', {_PartyID, ID}, AuxSt, St) -> handle_activate({shop, ID}, AuxSt, St); %% PartyMeta -handle_call('SetMetaData', {_, _PartyID, NS, Data}, AuxSt, St) -> +handle_call('SetMetaData', {_PartyID, NS, Data}, AuxSt, St) -> respond( ok, [?party_meta_set(NS, Data)], AuxSt, St ); -handle_call('RemoveMetaData', {_, _PartyID, NS}, AuxSt, St) -> +handle_call('RemoveMetaData', {_PartyID, NS}, AuxSt, St) -> _ = get_st_metadata(NS, St), respond( ok, @@ -181,7 +184,7 @@ handle_call('RemoveMetaData', {_, _PartyID, NS}, AuxSt, St) -> ); %% Claim -handle_call('CreateClaim', {_, _PartyID, Changeset}, AuxSt, St) -> +handle_call('CreateClaim', {_PartyID, Changeset}, AuxSt, St) -> ok = assert_party_operable(St), {Claim, Changes} = create_claim(Changeset, St), respond( @@ -190,7 +193,7 @@ handle_call('CreateClaim', {_, _PartyID, Changeset}, AuxSt, St) -> AuxSt, St ); -handle_call('UpdateClaim', {_, _PartyID, ID, ClaimRevision, Changeset}, AuxSt, St) -> +handle_call('UpdateClaim', {_PartyID, ID, ClaimRevision, Changeset}, AuxSt, St) -> ok = assert_party_operable(St), ok = assert_claim_modification_allowed(ID, ClaimRevision, St), respond( @@ -199,7 +202,7 @@ handle_call('UpdateClaim', {_, _PartyID, ID, ClaimRevision, Changeset}, AuxSt, S AuxSt, St ); -handle_call('AcceptClaim', {_, _PartyID, ID, ClaimRevision}, AuxSt, St) -> +handle_call('AcceptClaim', {_PartyID, ID, ClaimRevision}, AuxSt, St) -> ok = assert_claim_modification_allowed(ID, ClaimRevision, St), Timestamp = pm_datetime:format_now(), Revision = get_next_party_revision(St), @@ -215,7 +218,7 @@ handle_call('AcceptClaim', {_, _PartyID, ID, ClaimRevision}, AuxSt, St) -> AuxSt, St ); -handle_call('DenyClaim', {_, _PartyID, ID, ClaimRevision, Reason}, AuxSt, St) -> +handle_call('DenyClaim', {_PartyID, ID, ClaimRevision, Reason}, AuxSt, St) -> ok = assert_claim_modification_allowed(ID, ClaimRevision, St), Timestamp = pm_datetime:format_now(), Claim = pm_claim:deny(Reason, Timestamp, get_st_claim(ID, St)), @@ -225,7 +228,7 @@ handle_call('DenyClaim', {_, _PartyID, ID, ClaimRevision, Reason}, AuxSt, St) -> AuxSt, St ); -handle_call('RevokeClaim', {_, _PartyID, ID, ClaimRevision, Reason}, AuxSt, St) -> +handle_call('RevokeClaim', {_PartyID, ID, ClaimRevision, Reason}, AuxSt, St) -> ok = assert_party_operable(St), ok = assert_claim_modification_allowed(ID, ClaimRevision, St), Timestamp = pm_datetime:format_now(), diff --git a/apps/party_management/src/pm_woody_handler_utils.erl b/apps/party_management/src/pm_woody_handler_utils.erl deleted file mode 100644 index 4ba727e..0000000 --- a/apps/party_management/src/pm_woody_handler_utils.erl +++ /dev/null @@ -1,45 +0,0 @@ --module(pm_woody_handler_utils). - --include_lib("damsel/include/dmsl_payment_processing_thrift.hrl"). - --type user_info() :: dmsl_payment_processing_thrift:'UserInfo'(). --type user_identity() :: woody_user_identity:user_identity(). - --export([get_user_identity/0]). --export([assume_user_identity/1]). - --spec get_user_identity() -> woody_user_identity:user_identity() | undefined. -get_user_identity() -> - try - Context = pm_context:load(), - woody_user_identity:get(pm_context:get_woody_context(Context)) - catch - throw:{missing_required, _Key} -> - undefined - end. - --spec set_user_identity(user_identity()) -> ok. -set_user_identity(UserIdentity) -> - pm_context:save(pm_context:set_user_identity(UserIdentity, pm_context:load())). - --spec assume_user_identity(user_info()) -> ok. -assume_user_identity(UserInfo) -> - case get_user_identity() of - V when V /= undefined -> - ok; - undefined -> - set_user_identity(map_user_info(UserInfo)) - end. - -map_user_info(#payproc_UserInfo{id = PartyID, type = Type}) -> - #{ - id => PartyID, - realm => map_user_type(Type) - }. - -map_user_type({external_user, #payproc_ExternalUser{}}) -> - <<"external">>; -map_user_type({internal_user, #payproc_InternalUser{}}) -> - <<"internal">>; -map_user_type({service_user, #payproc_ServiceUser{}}) -> - <<"service">>. diff --git a/apps/party_management/src/pm_woody_wrapper.erl b/apps/party_management/src/pm_woody_wrapper.erl index 74cfb83..e5aaf8e 100644 --- a/apps/party_management/src/pm_woody_wrapper.erl +++ b/apps/party_management/src/pm_woody_wrapper.erl @@ -11,8 +11,7 @@ -type handler_opts() :: #{ handler := module(), - default_handling_timeout => timeout(), - user_identity => undefined | woody_user_identity:user_identity() + default_handling_timeout => timeout() }. -type client_opts() :: #{ diff --git a/apps/party_management/test/pm_claim_committer_SUITE.erl b/apps/party_management/test/pm_claim_committer_SUITE.erl index 1f143c6..6ad3e7a 100644 --- a/apps/party_management/test/pm_claim_committer_SUITE.erl +++ b/apps/party_management/test/pm_claim_committer_SUITE.erl @@ -77,7 +77,7 @@ init_per_suite(C) -> {Apps, _Ret} = pm_ct_helper:start_apps([woody, scoper, dmt_client, party_management]), _ = pm_domain:insert(construct_domain_fixture()), PartyID = erlang:list_to_binary([?MODULE_STRING, ".", erlang:integer_to_list(erlang:system_time())]), - ApiClient = pm_ct_helper:create_client(PartyID), + ApiClient = pm_ct_helper:create_client(), [{apps, Apps}, {party_id, PartyID}, {api_client, ApiClient} | C]. -spec end_per_suite(config()) -> _. diff --git a/apps/party_management/test/pm_ct_helper.erl b/apps/party_management/test/pm_ct_helper.erl index 69360de..85176f0 100644 --- a/apps/party_management/test/pm_ct_helper.erl +++ b/apps/party_management/test/pm_ct_helper.erl @@ -6,8 +6,8 @@ -export([cfg/2]). +-export([create_client/0]). -export([create_client/1]). --export([create_client/2]). -export([create_party_and_shop/5]). -export([create_battle_ready_shop/5]). @@ -156,19 +156,16 @@ cfg(Key, Config) -> %% --spec create_client(woody_user_identity:id()) -> pm_client_api:t(). -create_client(UserID) -> - create_client_w_context(UserID, woody_context:new()). +-spec create_client() -> pm_client_api:t(). +create_client() -> + create_client_w_context(woody_context:new()). --spec create_client(woody_user_identity:id(), woody:trace_id()) -> pm_client_api:t(). -create_client(UserID, TraceID) -> - create_client_w_context(UserID, woody_context:new(TraceID)). +-spec create_client(woody:trace_id()) -> pm_client_api:t(). +create_client(TraceID) -> + create_client_w_context(woody_context:new(TraceID)). -create_client_w_context(UserID, WoodyCtx) -> - pm_client_api:new(woody_user_identity:put(make_user_identity(UserID), WoodyCtx)). - -make_user_identity(UserID) -> - #{id => genlib:to_binary(UserID), realm => <<"external">>}. +create_client_w_context(WoodyCtx) -> + pm_client_api:new(WoodyCtx). %% diff --git a/apps/party_management/test/pm_party_tests_SUITE.erl b/apps/party_management/test/pm_party_tests_SUITE.erl index 7c9b37f..4f803cf 100644 --- a/apps/party_management/test/pm_party_tests_SUITE.erl +++ b/apps/party_management/test/pm_party_tests_SUITE.erl @@ -70,8 +70,6 @@ -export([shop_account_set_retrieval/1]). -export([shop_account_retrieval/1]). --export([party_access_control/1]). - -export([contract_not_found/1]). -export([contract_creation/1]). -export([contract_terms_retrieval/1]). @@ -88,7 +86,9 @@ -export([contract_w2w_terms/1]). -export([compute_payment_institution_terms/1]). +-export([compute_payment_institution/1]). -export([compute_payout_cash_flow/1]). +-export([compute_payout_cash_flow_payout_tool/1]). -export([contractor_creation/1]). -export([contractor_modification/1]). @@ -131,7 +131,6 @@ cfg(Key, C) -> -spec all() -> [{group, group_name()}]. all() -> [ - {group, party_access_control}, {group, party_creation}, {group, party_revisioning}, {group, party_blocking_suspension}, @@ -156,10 +155,6 @@ groups() -> party_already_exists, party_retrieval ]}, - {party_access_control, [sequence], [ - party_creation, - party_access_control - ]}, {party_revisioning, [sequence], [ party_creation, party_get_initial_revision, @@ -207,6 +202,7 @@ groups() -> contract_payout_tool_creation, contract_payout_tool_modification, compute_payment_institution_terms, + compute_payment_institution, contract_w2w_terms ]}, {shop_management, [sequence], [ @@ -221,6 +217,7 @@ groups() -> shop_already_exists, shop_update, compute_payout_cash_flow, + compute_payout_cash_flow_payout_tool, {group, shop_blocking_suspension} ]}, {shop_blocking_suspension, [sequence], [ @@ -305,7 +302,7 @@ init_per_group(shop_blocking_suspension, C) -> C; init_per_group(Group, C) -> PartyID = list_to_binary(lists:concat([Group, ".", erlang:system_time()])), - ApiClient = pm_ct_helper:create_client(PartyID), + ApiClient = pm_ct_helper:create_client(), Client = pm_client_party:start(PartyID, ApiClient), [{party_id, PartyID}, {client, Client} | C]. @@ -478,8 +475,6 @@ end_per_testcase(_Name, _C) -> -spec shop_account_set_retrieval(config()) -> _ | no_return(). -spec shop_account_retrieval(config()) -> _ | no_return(). --spec party_access_control(config()) -> _ | no_return(). - -spec contract_not_found(config()) -> _ | no_return(). -spec contract_creation(config()) -> _ | no_return(). -spec contract_terms_retrieval(config()) -> _ | no_return(). @@ -494,7 +489,9 @@ end_per_testcase(_Name, _C) -> -spec contract_adjustment_creation(config()) -> _ | no_return(). -spec contract_adjustment_expiration(config()) -> _ | no_return(). -spec compute_payment_institution_terms(config()) -> _ | no_return(). +-spec compute_payment_institution(config()) -> _ | no_return(). -spec compute_payout_cash_flow(config()) -> _ | no_return(). +-spec compute_payout_cash_flow_payout_tool(config()) -> _ | no_return(). -spec contract_w2w_terms(config()) -> _ | no_return(). -spec contractor_creation(config()) -> _ | no_return(). -spec contractor_modification(config()) -> _ | no_return(). @@ -917,6 +914,22 @@ compute_payment_institution_terms(C) -> ?assert_different_term_sets(T2, T4), ?assert_different_term_sets(T3, T4). +compute_payment_institution(C) -> + Client = cfg(client, C), + DomainRevision = pm_domain:head(), + TermsFun = fun(PartyID) -> + #domain_PaymentInstitution{} = + pm_client_party:compute_payment_institution( + ?pinst(4), + DomainRevision, + #payproc_Varset{party_id = PartyID}, + Client + ) + end, + T1 = TermsFun(<<"12345">>), + T2 = TermsFun(<<"67890">>), + ?assert_different_term_sets(T1, T2). + -spec check_all_payment_methods(config()) -> _. check_all_payment_methods(C) -> Client = cfg(client, C), @@ -977,6 +990,30 @@ compute_payout_cash_flow(C) -> } ] = pm_client_party:compute_payout_cash_flow(Params, Client). +compute_payout_cash_flow_payout_tool(C) -> + Client = cfg(client, C), + Params = #payproc_PayoutParams{ + id = ?REAL_SHOP_ID, + amount = #domain_Cash{amount = 10000, currency = ?cur(<<"RUB">>)}, + timestamp = pm_datetime:format_now(), + payout_tool_id = <<"1">> + }, + [ + #domain_FinalCashFlowPosting{ + source = #domain_FinalCashFlowAccount{account_type = {merchant, settlement}}, + destination = #domain_FinalCashFlowAccount{account_type = {merchant, payout}}, + volume = #domain_Cash{amount = 7500, currency = ?cur(<<"RUB">>)} + }, + #domain_FinalCashFlowPosting{ + source = #domain_FinalCashFlowAccount{account_type = {merchant, settlement}}, + destination = #domain_FinalCashFlowAccount{account_type = {system, settlement}}, + volume = #domain_Cash{amount = 2500, currency = ?cur(<<"RUB">>)} + } + ] = pm_client_party:compute_payout_cash_flow(Params, Client), + {exception, #payproc_PayoutToolNotFound{}} = pm_client_party:compute_payout_cash_flow( + Params#payproc_PayoutParams{payout_tool_id = <<"Nope">>}, Client + ). + contract_w2w_terms(C) -> Client = cfg(client, C), ContractID = ?REAL_CONTRACT_ID, @@ -1571,56 +1608,6 @@ contract_w_contractor_creation(C) -> ok = accept_claim(Claim, Client), #domain_Contract{id = ContractID, contractor_id = ContractorID} = pm_client_party:get_contract(ContractID, Client). -%% Access control tests - -party_access_control(C) -> - PartyID = cfg(party_id, C), - % External Success - GoodExternalClient = cfg(client, C), - #domain_Party{id = PartyID} = pm_client_party:get(GoodExternalClient), - - % External Reject - BadExternalClient0 = pm_client_party:start( - #payproc_UserInfo{id = <<"FakE1D">>, type = {external_user, #payproc_ExternalUser{}}}, - PartyID, - pm_client_api:new() - ), - ?invalid_user() = pm_client_party:get(BadExternalClient0), - pm_client_party:stop(BadExternalClient0), - - % UserIdentity has priority - UserIdentity = #{ - id => PartyID, - realm => <<"internal">> - }, - Context = woody_user_identity:put(UserIdentity, woody_context:new()), - UserIdentityClient1 = pm_client_party:start( - #payproc_UserInfo{id = <<"FakE1D">>, type = {external_user, #payproc_ExternalUser{}}}, - PartyID, - pm_client_api:new(Context) - ), - #domain_Party{id = PartyID} = pm_client_party:get(UserIdentityClient1), - pm_client_party:stop(UserIdentityClient1), - - % Internal Success - GoodInternalClient = pm_client_party:start( - #payproc_UserInfo{id = <<"F4KE1D">>, type = {internal_user, #payproc_InternalUser{}}}, - PartyID, - pm_client_api:new() - ), - #domain_Party{id = PartyID} = pm_client_party:get(GoodInternalClient), - pm_client_party:stop(GoodInternalClient), - - % Service Success - GoodServiceClient = pm_client_party:start( - #payproc_UserInfo{id = <<"fAkE1D">>, type = {service_user, #payproc_ServiceUser{}}}, - PartyID, - pm_client_api:new() - ), - #domain_Party{id = PartyID} = pm_client_party:get(GoodServiceClient), - pm_client_party:stop(GoodServiceClient), - ok. - %% Compute providers compute_provider_ok(C) -> @@ -2616,6 +2603,31 @@ construct_domain_fixture() -> } }}, + {payment_institution, #domain_PaymentInstitutionObject{ + ref = ?pinst(4), + data = #domain_PaymentInstitution{ + name = <<"Chetky Payments Inc.">>, + system_account_set = + {decisions, [ + #domain_SystemAccountSetDecision{ + if_ = ?partycond(<<"12345">>, undefined), + then_ = + {value, ?sas(2)} + }, + #domain_SystemAccountSetDecision{ + if_ = ?partycond(<<"67890">>, undefined), + then_ = + {value, ?sas(1)} + } + ]}, + default_contract_template = {value, ?tmpl(2)}, + providers = {value, ?ordset([])}, + inspector = {value, ?insp(1)}, + residences = [], + realm = live + } + }}, + {globals, #domain_GlobalsObject{ ref = #domain_GlobalsRef{}, data = #domain_Globals{ diff --git a/apps/pm_client/src/pm_client_api.erl b/apps/pm_client/src/pm_client_api.erl index be22498..0cda834 100644 --- a/apps/pm_client/src/pm_client_api.erl +++ b/apps/pm_client/src/pm_client_api.erl @@ -1,6 +1,5 @@ -module(pm_client_api). --export([new/0]). -export([new/1]). -export([call/4]). @@ -10,10 +9,6 @@ -type t() :: woody_context:ctx(). --spec new() -> t(). -new() -> - woody_context:new(). - -spec new(woody_context:ctx()) -> t(). new(Context) -> Context. diff --git a/apps/pm_client/src/pm_client_party.erl b/apps/pm_client/src/pm_client_party.erl index 4c7c892..d2a970d 100644 --- a/apps/pm_client/src/pm_client_party.erl +++ b/apps/pm_client/src/pm_client_party.erl @@ -3,8 +3,6 @@ -include_lib("damsel/include/dmsl_payment_processing_thrift.hrl"). -export([start/2]). --export([start/3]). --export([start_link/2]). -export([stop/1]). -export([create/2]). @@ -28,6 +26,7 @@ -export([get_shop_contract/2]). -export([compute_shop_terms/5]). -export([compute_payment_institution_terms/3]). +-export([compute_payment_institution/4]). -export([compute_payout_cash_flow/2]). -export([block_shop/3]). @@ -61,13 +60,9 @@ -export([init/1]). -export([handle_call/3]). -export([handle_cast/2]). --export([handle_info/2]). --export([terminate/2]). --export([code_change/3]). %% --type user_info() :: dmsl_payment_processing_thrift:'UserInfo'(). -type party_id() :: dmsl_domain_thrift:'PartyID'(). -type party_params() :: dmsl_payment_processing_thrift:'PartyParams'(). -type domain_revision() :: dmsl_domain_thrift:'DataRevision'(). @@ -95,18 +90,7 @@ -spec start(party_id(), pm_client_api:t()) -> pid(). start(PartyID, ApiClient) -> - start(start, undefined, PartyID, ApiClient). - --spec start(user_info(), party_id(), pm_client_api:t()) -> pid(). -start(UserInfo, PartyID, ApiClient) -> - start(start, UserInfo, PartyID, ApiClient). - --spec start_link(party_id(), pm_client_api:t()) -> pid(). -start_link(PartyID, ApiClient) -> - start(start_link, undefined, PartyID, ApiClient). - -start(Mode, UserInfo, PartyID, ApiClient) -> - {ok, Pid} = gen_server:Mode(?MODULE, {UserInfo, PartyID, ApiClient}, []), + {ok, Pid} = gen_server:start(?MODULE, {PartyID, ApiClient}, []), Pid. -spec stop(pid()) -> ok. @@ -190,6 +174,11 @@ compute_contract_terms(ID, Timestamp, PartyRevision, DomainRevision, Varset, Cli compute_payment_institution_terms(Ref, Varset, Client) -> call(Client, 'ComputePaymentInstitutionTerms', with_user_info([Ref, Varset])). +-spec compute_payment_institution(payment_intitution_ref(), domain_revision(), varset(), pid()) -> + dmsl_domain_thrift:'TermSet'() | woody_error:business_error(). +compute_payment_institution(Ref, DomainRevision, Varset, Client) -> + call(Client, 'ComputePaymentInstitution', with_user_info([Ref, DomainRevision, Varset])). + -spec compute_payout_cash_flow(dmsl_payment_processing_thrift:'PayoutParams'(), pid()) -> dmsl_domain_thrift:'FinalCashFlow'() | woody_error:business_error(). compute_payout_cash_flow(Params, Client) -> @@ -322,7 +311,6 @@ map_result_error({error, Error}) -> -type event() :: dmsl_payment_processing_thrift:'Event'(). -record(state, { - user_info :: user_info(), party_id :: party_id(), poller :: pm_client_event_poller:st(event()), client :: pm_client_api:t() @@ -331,14 +319,13 @@ map_result_error({error, Error}) -> -type state() :: #state{}. -type callref() :: {pid(), Tag :: reference()}. --spec init({user_info(), party_id(), pm_client_api:t()}) -> {ok, state()}. -init({UserInfo, PartyID, ApiClient}) -> +-spec init({party_id(), pm_client_api:t()}) -> {ok, state()}. +init({PartyID, ApiClient}) -> {ok, #state{ - user_info = UserInfo, party_id = PartyID, client = ApiClient, poller = pm_client_event_poller:new( - {party_management, 'GetEvents', [UserInfo, PartyID]}, + {party_management, 'GetEvents', [undefined, PartyID]}, fun(Event) -> Event#payproc_Event.id end ) }}. @@ -374,21 +361,8 @@ handle_cast(Cast, State) -> _ = logger:warning("unexpected cast received: ~tp", [Cast]), {noreply, State}. --spec handle_info(_, state()) -> {noreply, state()}. -handle_info(Info, State) -> - _ = logger:warning("unexpected info received: ~tp", [Info]), - {noreply, State}. - --spec terminate(Reason, state()) -> ok when Reason :: normal | shutdown | {shutdown, term()} | term(). -terminate(_Reason, _State) -> - ok. - --spec code_change(Vsn | {down, Vsn}, state(), term()) -> {error, noimpl} when Vsn :: term(). -code_change(_OldVsn, _State, _Extra) -> - {error, noimpl}. - with_user_info(Args) -> - [fun(St) -> St#state.user_info end | Args]. + [undefined | Args]. with_user_info_party_id(Args) -> - [fun(St) -> St#state.user_info end, fun(St) -> St#state.party_id end | Args]. + [undefined, fun(St) -> St#state.party_id end | Args]. diff --git a/docker-compose.yml b/docker-compose.yml index cf2a335..10b6014 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -16,7 +16,7 @@ services: machinegun: condition: service_healthy dominant: - condition: service_started + condition: service_healthy shumway: condition: service_healthy ports: @@ -24,20 +24,20 @@ services: command: /sbin/init dominant: - image: ghcr.io/valitydev/dominant:sha-d3f2615 + image: ghcr.io/valitydev/dominant:sha-eb1cccb depends_on: - machinegun ports: - "8022" command: /opt/dominant/bin/dominant foreground healthcheck: - test: curl http://localhost:8022/health + test: "/opt/dominant/bin/dominant ping" interval: 5s timeout: 1s retries: 20 machinegun: - image: docker.io/rbkmoney/machinegun:c05a8c18cd4f7966d70b6ad84cac9429cdfe37ae + image: ghcr.io/valitydev/machinegun:sha-7f0a21a ports: - "8022" command: /opt/machinegun/bin/machinegun foreground @@ -45,7 +45,7 @@ services: - ./test/machinegun/config.yaml:/opt/machinegun/etc/config.yaml - ./test/machinegun/cookie:/opt/machinegun/etc/cookie healthcheck: - test: curl http://localhost:8022/health + test: "/opt/machinegun/bin/machinegun ping" interval: 5s timeout: 1s retries: 20 diff --git a/rebar.config b/rebar.config index b754126..4311ce8 100644 --- a/rebar.config +++ b/rebar.config @@ -30,7 +30,6 @@ {gproc, "0.9.0"}, {genlib, {git, "https://github.com/valitydev/genlib.git", {branch, "master"}}}, {woody, {git, "https://github.com/valitydev/woody_erlang.git", {branch, "master"}}}, - {woody_user_identity, {git, "https://github.com/valitydev/woody_erlang_user_identity.git", {branch, "master"}}}, {damsel, {git, "https://github.com/valitydev/damsel.git", {branch, "master"}}}, {payproc_errors, {git, "https://github.com/valitydev/payproc-errors-erlang.git", {branch, "master"}}}, {mg_proto, {git, "https://github.com/valitydev/machinegun-proto.git", {branch, "master"}}}, diff --git a/rebar.lock b/rebar.lock index e6217e8..23e07b3 100644 --- a/rebar.lock +++ b/rebar.lock @@ -64,11 +64,8 @@ {<<"woody">>, {git,"https://github.com/valitydev/woody_erlang.git", {ref,"0c2e16dfc8a51f6f63fcd74df982178a9aeab322"}}, - 0}, - {<<"woody_user_identity">>, - {git,"https://github.com/valitydev/woody_erlang_user_identity.git", - {ref,"a480762fea8d7c08f105fb39ca809482b6cb042e"}}, - 0}]}. + 0} + ]}. [ {pkg_hash,[ {<<"cache">>, <<"B23A5FE7095445A88412A6E614C933377E0137B44FFED77C9B3FEF1A731A20B2">>},