mirror of
https://github.com/valitydev/capi-v2.git
synced 2024-11-06 01:55:20 +00:00
CAPI-23 Add initial project structure (#1)
* CAPI-23 Add initial project structure. Add mock-backend and basic tests
This commit is contained in:
parent
9185ca8366
commit
64d7c30607
28
.gitignore
vendored
28
.gitignore
vendored
@ -1,10 +1,20 @@
|
||||
.eunit
|
||||
deps
|
||||
*.o
|
||||
*.beam
|
||||
*.plt
|
||||
# general
|
||||
log
|
||||
/_build/
|
||||
*~
|
||||
erl_crash.dump
|
||||
ebin
|
||||
rel/example_project
|
||||
.concrete/DEV_MODE
|
||||
.rebar
|
||||
/*.config
|
||||
.tags*
|
||||
*.sublime-workspace
|
||||
.DS_Store
|
||||
|
||||
# wercker
|
||||
/_builds/
|
||||
/_cache/
|
||||
/_projects/
|
||||
/_steps/
|
||||
/_temp/
|
||||
/.wercker/
|
||||
|
||||
# compiled
|
||||
swagger
|
||||
|
3
.gitmodules
vendored
Normal file
3
.gitmodules
vendored
Normal file
@ -0,0 +1,3 @@
|
||||
[submodule "schemes/swag"]
|
||||
path = schemes/swag
|
||||
url = git@github.com:rbkmoney/swag.git
|
80
Makefile
Normal file
80
Makefile
Normal file
@ -0,0 +1,80 @@
|
||||
REBAR := $(shell which rebar3 2>/dev/null || which ./rebar3)
|
||||
RELNAME = capi
|
||||
SUBMODULES = schemes/swag
|
||||
SUBTARGETS = $(patsubst %,%/.git,$(SUBMODULES))
|
||||
|
||||
SWAGGER_SCHEME = schemes/swag/swagger.yaml
|
||||
SWAGGER_APP_PATH = apps/swagger
|
||||
SWAGGER_APP_TARGET = $(SWAGGER_APP_PATH)/rebar.config
|
||||
|
||||
which = $(if $(shell which $(1) 2>/dev/null),\
|
||||
$(shell which $(1) 2>/dev/null),\
|
||||
$(error "Error: could not locate $(1)!"))
|
||||
|
||||
DOCKER = $(call which, docker)
|
||||
PACKER = $(call which, packer)
|
||||
SWAGGER_CODEGEN = $(call which, SWAGGER_CODEGEN)
|
||||
|
||||
.PHONY: all submodules compile devrel start test clean distclean dialyze release containerize swagger_regenerate
|
||||
|
||||
all: compile
|
||||
|
||||
rebar-update:
|
||||
$(REBAR) update
|
||||
|
||||
$(SUBTARGETS): %/.git: %
|
||||
git submodule update --init $<
|
||||
touch $@
|
||||
|
||||
submodules: $(SUBTARGETS) $(SWAGGER_APP_TARGET)
|
||||
|
||||
compile: submodules
|
||||
$(REBAR) compile
|
||||
|
||||
devrel: submodules
|
||||
$(REBAR) release
|
||||
|
||||
start: submodules
|
||||
$(REBAR) run
|
||||
|
||||
test: submodules
|
||||
$(REBAR) ct
|
||||
|
||||
lint: compile
|
||||
elvis rock
|
||||
|
||||
xref: submodules
|
||||
$(REBAR) xref
|
||||
|
||||
clean:
|
||||
$(REBAR) clean
|
||||
|
||||
distclean:
|
||||
$(REBAR) clean -a
|
||||
rm -rfv _build _builds _cache _steps _temp
|
||||
|
||||
dialyze:
|
||||
$(REBAR) dialyzer
|
||||
|
||||
BASE_DIR := $(shell pwd)
|
||||
|
||||
release: ~/.docker/config.json distclean
|
||||
$(DOCKER) run --rm -v $(BASE_DIR):$(BASE_DIR) --workdir $(BASE_DIR) rbkmoney/build rebar3 as prod release
|
||||
|
||||
containerize: release ./packer.json
|
||||
$(PACKER) build packer.json
|
||||
|
||||
~/.docker/config.json:
|
||||
test -f ~/.docker/config.json || (echo "Please run: docker login" ; exit 1)
|
||||
|
||||
# Shitty generation. Will be replaced when a container with swagger-codegen appear
|
||||
define swagger_regenerate
|
||||
rm -rf $(SWAGGER_APP_PATH)
|
||||
$(SWAGGER_CODEGEN) generate -i $(SWAGGER_SCHEME) -l erlang-server -o $(SWAGGER_APP_PATH);
|
||||
endef
|
||||
|
||||
$(SWAGGER_APP_TARGET): $(SWAGGER_SCHEME)
|
||||
$(call swagger_regenerate)
|
||||
|
||||
swagger_regenerate:
|
||||
$(call swagger_regenerate)
|
32
README.md
32
README.md
@ -1,2 +1,30 @@
|
||||
# erlang_capi
|
||||
Erlang CAPI version
|
||||
# capi
|
||||
|
||||
A service that does something
|
||||
|
||||
## Сборка
|
||||
|
||||
Для запуска процесса сборки достаточно выполнить просто:
|
||||
|
||||
make
|
||||
|
||||
Чтобы запустить полученную сборку в режиме разработки и получить стандартный [Erlang shell][2], нужно всего лишь:
|
||||
|
||||
make start
|
||||
|
||||
> _Хозяйке на заметку._ При этом используется стандартный Erlang релиз, собранный при помощи [relx][3] в режиме разработчика.
|
||||
|
||||
Рекомендуется вести разработку и сборку проекта в рамках локальной виртуальной среды, предоставляемой [wercker][1]. Настоятельно рекомендуется прогоны тестовых сценариев проводить только в этой среде.
|
||||
|
||||
$ wercker dev
|
||||
|
||||
> _Хозяйке на заметку._ В зависимости от вашего окружения и операционной системы вам может понадобиться [Docker Machine][4].
|
||||
|
||||
## Документация
|
||||
|
||||
Дальнейшую документацию можно почерпнуть, пройдясь по ссылкам в [соответствующем документе](doc/index.md).
|
||||
|
||||
[1]: http://devcenter.wercker.com/learn/basics/the-wercker-cli.html
|
||||
[2]: http://erlang.org/doc/man/shell.html
|
||||
[3]: https://github.com/erlware/relx
|
||||
[4]: https://docs.docker.com/machine/install-machine/
|
||||
|
19
apps/capi/src/capi.app.src
Normal file
19
apps/capi/src/capi.app.src
Normal file
@ -0,0 +1,19 @@
|
||||
{application, capi , [
|
||||
{description, "A service that does something"},
|
||||
{vsn, "1"},
|
||||
{registered, []},
|
||||
{mod, { capi , []}},
|
||||
{applications, [
|
||||
kernel,
|
||||
stdlib,
|
||||
genlib,
|
||||
swagger
|
||||
]},
|
||||
{env, []},
|
||||
{modules, []},
|
||||
{maintainers, [
|
||||
"Artem Ocheredko <galaxie.stern@gmail.com>"
|
||||
]},
|
||||
{licenses, []},
|
||||
{links, []}
|
||||
]}.
|
21
apps/capi/src/capi.erl
Normal file
21
apps/capi/src/capi.erl
Normal file
@ -0,0 +1,21 @@
|
||||
%% @doc Public API and application startup.
|
||||
%% @end
|
||||
|
||||
-module(capi).
|
||||
-behaviour(application).
|
||||
|
||||
%% Application callbacks
|
||||
-export([start/2]).
|
||||
-export([stop/1]).
|
||||
|
||||
%%
|
||||
|
||||
-spec start(normal, any()) -> {ok, pid()} | {error, any()}.
|
||||
|
||||
start(_StartType, _StartArgs) ->
|
||||
capi_sup:start_link().
|
||||
|
||||
-spec stop(any()) -> ok.
|
||||
|
||||
stop(_State) ->
|
||||
ok.
|
25
apps/capi/src/capi_auth.erl
Normal file
25
apps/capi/src/capi_auth.erl
Normal file
@ -0,0 +1,25 @@
|
||||
-module(capi_auth).
|
||||
|
||||
-export([auth_api_key/2]).
|
||||
|
||||
-type context() :: #{binary() => any()}.
|
||||
|
||||
-spec auth_api_key(ApiKey :: binary(), OperationID :: atom()) -> {true, Context :: context()} | false.
|
||||
auth_api_key(ApiKey, OperationID) ->
|
||||
{ok, Type, Credentials} = parse_auth_token(ApiKey),
|
||||
{ok, Context} = process_auth(Type, Credentials, OperationID),
|
||||
{true, Context}.
|
||||
|
||||
-spec parse_auth_token(ApiKey :: binary()) -> {ok, bearer, Credentials :: binary()} | {error, Reason :: atom()}.
|
||||
parse_auth_token(ApiKey) ->
|
||||
case ApiKey of
|
||||
<<"Bearer ", Credentials/binary>> ->
|
||||
{ok, bearer, Credentials};
|
||||
_ ->
|
||||
{error, unsupported_auth_scheme}
|
||||
end.
|
||||
|
||||
-spec process_auth(Type :: atom(), AuthToken :: binary(), OperationID :: atom()) -> {ok, Context :: context()} | {error, Reason :: atom()}.
|
||||
process_auth(bearer, _AuthToken, _OperationID) ->
|
||||
%% @TODO find a jwt library :(
|
||||
{ok, #{}}.
|
185
apps/capi/src/capi_mock_handler.erl
Normal file
185
apps/capi/src/capi_mock_handler.erl
Normal file
@ -0,0 +1,185 @@
|
||||
-module(capi_mock_handler).
|
||||
|
||||
-behaviour(swagger_logic_handler).
|
||||
-behaviour(gen_server).
|
||||
|
||||
%% API callbacks
|
||||
-export([start_link/0]).
|
||||
-export([handle_request/2]).
|
||||
-export([authorize_api_key/2]).
|
||||
|
||||
%% gen_server callbacks
|
||||
-export([init/1]).
|
||||
-export([handle_call/3]).
|
||||
-export([handle_cast/2]).
|
||||
-export([handle_info/2]).
|
||||
-export([terminate/2]).
|
||||
-export([code_change/3]).
|
||||
|
||||
-record(state, {
|
||||
tid :: ets:tid(),
|
||||
last_id = 0 ::integer()
|
||||
}).
|
||||
|
||||
-spec start_link() -> {ok, Pid :: pid()} | ignore | {error, Error :: any()}.
|
||||
start_link() ->
|
||||
gen_server:start_link({local, ?MODULE}, ?MODULE, [], []).
|
||||
|
||||
-spec authorize_api_key(ApiKey :: binary(), OperationID :: atom()) -> Result :: boolean() | {boolean(), #{binary() => any()}}.
|
||||
authorize_api_key(ApiKey, OperationID) -> capi_auth:auth_api_key(ApiKey, OperationID).
|
||||
|
||||
-spec handle_request(OperationID :: atom(), Req :: #{}) -> {Code :: integer, Headers :: [], Response :: #{}}.
|
||||
handle_request('CreateInvoice', Req) ->
|
||||
InvoiceParams = maps:get('CreateInvoiceArgs', Req),
|
||||
ID = new_id(),
|
||||
Invoice = #{
|
||||
<<"id">> => ID,
|
||||
<<"amount">> => maps:get(<<"amount">>, InvoiceParams),
|
||||
<<"context">> => maps:get(<<"context">>, InvoiceParams),
|
||||
<<"currency">> => maps:get(<<"currency">>, InvoiceParams),
|
||||
<<"description">> => maps:get(<<"description">>, InvoiceParams),
|
||||
<<"dueDate">> => maps:get(<<"dueDate">>, InvoiceParams),
|
||||
<<"product">> => maps:get(<<"product">>, InvoiceParams),
|
||||
<<"shopID">> => maps:get(<<"shopID">>, InvoiceParams)
|
||||
},
|
||||
put_data(ID, invoice, Invoice),
|
||||
Resp = #{
|
||||
<<"id">> => ID
|
||||
},
|
||||
{201, [], Resp};
|
||||
|
||||
handle_request('CreatePayment', Req) ->
|
||||
InvoiceID = maps:get('invoice_id', Req),
|
||||
PaymentParams = maps:get('CreatePaymentArgs', Req),
|
||||
PaymentSession = maps:get(<<"paymentSession">>, PaymentParams),
|
||||
case match_data({{'$1', session}, PaymentSession}) of
|
||||
[[_SessionID]] ->
|
||||
delete_data({{'$1', session}, '_'}),
|
||||
PaymentID = new_id(),
|
||||
Payment = #{
|
||||
<<"id">> => PaymentID ,
|
||||
<<"invoiceID">> => InvoiceID,
|
||||
<<"createdAt">> => <<"2016-12-12 17:00:00">>,
|
||||
<<"status">> => <<"pending">>,
|
||||
<<"paymentToolToken">> => maps:get(<<"paymentToolToken">>, PaymentParams)
|
||||
},
|
||||
put_data(PaymentID, payment, Payment),
|
||||
Resp = #{
|
||||
<<"id">> => PaymentID
|
||||
},
|
||||
{201, [], Resp};
|
||||
_ ->
|
||||
Resp = logic_error(<<"expired_session">>, <<"Payment session is not valid">>),
|
||||
{400, [], Resp}
|
||||
end;
|
||||
|
||||
handle_request('CreatePaymentToolToken', Req) ->
|
||||
Params = maps:get('PaymentTool', Req),
|
||||
Token = tokenize_payment_tool(Params),
|
||||
put_data(new_id(), token, Token),
|
||||
Session = generate_session(),
|
||||
put_data(new_id(), session, Session),
|
||||
Resp = #{
|
||||
<<"token">> => Token,
|
||||
<<"session">> => Session
|
||||
},
|
||||
{201, [], Resp};
|
||||
|
||||
handle_request('GetInvoiceByID', Req) ->
|
||||
InvoiceID = maps:get(invoice_id, Req),
|
||||
[{_, Invoice}] = get_data(InvoiceID, invoice),
|
||||
{200, [], Invoice};
|
||||
|
||||
handle_request('GetInvoiceEvents', _Req) ->
|
||||
Events = [],
|
||||
{200, [], Events};
|
||||
|
||||
handle_request('GetPaymentByID', Req) ->
|
||||
PaymentID = maps:get(payment_id, Req),
|
||||
[{_, Payment}] = get_data(PaymentID, payment),
|
||||
{200, [], Payment};
|
||||
|
||||
handle_request(OperationID, Req) ->
|
||||
io:format(user, "Got request to process: ~p~n", [{OperationID, Req}]),
|
||||
{501, [], <<"Not implemented">>}.
|
||||
|
||||
|
||||
%%%
|
||||
|
||||
-type callref() :: {pid(), Tag :: reference()}.
|
||||
-type st() :: #state{}.
|
||||
|
||||
-spec init( Args :: any()) -> {ok, st()}.
|
||||
init(_Args) ->
|
||||
TID = ets:new(mock_storage, [ordered_set, private, {heir, none}]),
|
||||
{ok, #state{tid = TID}}.
|
||||
|
||||
-spec handle_call(Request :: any(), From :: callref(), st()) -> {reply, term(), st()} | {noreply, st()}.
|
||||
handle_call({put, ID, Type, Data}, _From, State = #state{tid = TID}) ->
|
||||
Result = ets:insert(TID, {wrap_id(ID, Type), Data}),
|
||||
{reply, Result, State};
|
||||
|
||||
handle_call({get, ID, Type}, _From, State = #state{tid = TID}) ->
|
||||
Result = ets:lookup(TID, wrap_id(ID, Type)),
|
||||
{reply, Result, State};
|
||||
|
||||
handle_call({match, Pattern}, _From, State = #state{tid = TID}) ->
|
||||
Result = ets:match(TID, Pattern),
|
||||
{reply, Result, State};
|
||||
|
||||
handle_call({delete, Pattern}, _From, State = #state{tid = TID}) ->
|
||||
Result = ets:match_delete(TID, Pattern),
|
||||
{reply, Result, State};
|
||||
|
||||
handle_call(id, _From, State = #state{last_id = ID}) ->
|
||||
NewID = ID + 1,
|
||||
{reply, NewID, State#state{last_id = NewID}}.
|
||||
|
||||
-spec handle_cast(Request :: any(), st()) -> {noreply, st()}.
|
||||
handle_cast(_Request, State) ->
|
||||
{noreply, State}.
|
||||
|
||||
-spec handle_info(any(), st()) -> {noreply, st()}.
|
||||
handle_info(_Info, State) ->
|
||||
{noreply, State}.
|
||||
|
||||
-spec terminate(any(), st()) -> ok.
|
||||
terminate(_Reason, _State) ->
|
||||
ok.
|
||||
|
||||
-spec code_change(Vsn :: term() | {down, Vsn :: term()}, st(), term()) -> {error, noimpl}.
|
||||
code_change(_OldVsn, _State, _Extra) ->
|
||||
{error, noimpl}.
|
||||
|
||||
put_data(ID, Type, Data) ->
|
||||
gen_server:call(?MODULE, {put, ID, Type, Data}).
|
||||
|
||||
get_data(ID, Type) ->
|
||||
gen_server:call(?MODULE, {get, ID, Type}).
|
||||
|
||||
match_data(Pattern) ->
|
||||
gen_server:call(?MODULE, {match, Pattern}).
|
||||
|
||||
delete_data(Pattern) ->
|
||||
gen_server:call(?MODULE, {delete, Pattern}).
|
||||
|
||||
new_id() ->
|
||||
ID = gen_server:call(?MODULE, id),
|
||||
genlib:to_binary(ID).
|
||||
|
||||
tokenize_payment_tool(Params = #{<<"paymentToolType">> := <<"cardData">>}) ->
|
||||
CardNumber = genlib:to_binary(maps:get(<<"cardNumber">>, Params)),
|
||||
ExpDate = maps:get(<<"expDate">>, Params),
|
||||
erlang:md5(<<CardNumber/binary, ExpDate/binary>>);
|
||||
|
||||
tokenize_payment_tool(_) ->
|
||||
error(unsupported_payment_tool). %%@TODO move this error to upper level
|
||||
|
||||
generate_session() ->
|
||||
integer_to_binary(rand:uniform(100000)).
|
||||
|
||||
logic_error(Code, Message) ->
|
||||
#{code => Code, message => Message}.
|
||||
|
||||
wrap_id(ID, Type) ->
|
||||
{ID, Type}.
|
46
apps/capi/src/capi_sup.erl
Normal file
46
apps/capi/src/capi_sup.erl
Normal file
@ -0,0 +1,46 @@
|
||||
%% @doc Top level supervisor.
|
||||
%% @end
|
||||
|
||||
-module(capi_sup).
|
||||
-behaviour(supervisor).
|
||||
|
||||
%% API
|
||||
-export([start_link/0]).
|
||||
|
||||
%% Supervisor callbacks
|
||||
-export([init/1]).
|
||||
|
||||
%%
|
||||
|
||||
-spec start_link() -> {ok, pid()} | {error, {already_started, pid()}}.
|
||||
|
||||
start_link() ->
|
||||
supervisor:start_link({local, ?MODULE}, ?MODULE, []).
|
||||
|
||||
%%
|
||||
|
||||
-spec init([]) -> {ok, tuple()}.
|
||||
init([]) ->
|
||||
{LogicHandler, LogicHandlerSpec} = get_logic_handler_info(),
|
||||
SwaggerSpec = swagger_server:child_spec(swagger, #{
|
||||
ip => capi_utils:get_hostname_ip(genlib_app:env(capi, host, "0.0.0.0")),
|
||||
port => genlib_app:env(capi, port, 8080),
|
||||
net_opts => [],
|
||||
logic_handler => LogicHandler
|
||||
}),
|
||||
{ok, {
|
||||
{one_for_all, 0, 1}, [LogicHandlerSpec, SwaggerSpec]
|
||||
}}.
|
||||
|
||||
-spec get_logic_handler_info() -> {Handler :: atom(), Spec :: supervisor:child_spec()}.
|
||||
get_logic_handler_info() ->
|
||||
case genlib_app:env(capi, service_type) of
|
||||
mock ->
|
||||
Spec = genlib_app:permanent(
|
||||
{capi_mock_handler, capi_mock_handler, start_link},
|
||||
none,
|
||||
[]
|
||||
),
|
||||
{capi_mock_handler, Spec};
|
||||
undefined -> exit(undefined_service_type)
|
||||
end.
|
16
apps/capi/src/capi_utils.erl
Normal file
16
apps/capi/src/capi_utils.erl
Normal file
@ -0,0 +1,16 @@
|
||||
-module(capi_utils).
|
||||
|
||||
-export([get_hostname_ip/1]).
|
||||
|
||||
-spec get_hostname_ip(Hostname | IP) -> IP when
|
||||
Hostname :: string(),
|
||||
IP :: inet:ip_address().
|
||||
|
||||
get_hostname_ip(Host) ->
|
||||
% TODO: respect preferred address family
|
||||
case inet:getaddr(Host, inet) of
|
||||
{ok, IP} ->
|
||||
IP;
|
||||
{error, Error} ->
|
||||
exit(Error)
|
||||
end.
|
180
apps/capi/test/capi_tests_SUITE.erl
Normal file
180
apps/capi/test/capi_tests_SUITE.erl
Normal file
@ -0,0 +1,180 @@
|
||||
-module(capi_tests_SUITE).
|
||||
|
||||
-include_lib("common_test/include/ct.hrl").
|
||||
|
||||
-export([all/0]).
|
||||
-export([init_per_suite/1]).
|
||||
-export([end_per_suite/1]).
|
||||
|
||||
%% test cases
|
||||
-export([
|
||||
authorization_error_test/1,
|
||||
create_invoice_badard_test/1,
|
||||
create_invoice_ok_test/1,
|
||||
create_payment_ok_test/1,
|
||||
create_payment_tool_token_ok_test/1,
|
||||
get_invoice_by_id_ok_test/1,
|
||||
get_invoice_events_ok_test/1,
|
||||
get_payment_by_id_ok_test/1
|
||||
]).
|
||||
|
||||
-define(CAPI_HOST, "0.0.0.0").
|
||||
-define(CAPI_PORT, 8080).
|
||||
-define(CAPI_SERVICE_TYPE, mock).
|
||||
|
||||
all() ->
|
||||
[
|
||||
authorization_error_test,
|
||||
create_invoice_badard_test,
|
||||
create_invoice_ok_test,
|
||||
create_payment_ok_test,
|
||||
create_payment_tool_token_ok_test,
|
||||
get_invoice_by_id_ok_test,
|
||||
get_invoice_events_ok_test,
|
||||
get_payment_by_id_ok_test
|
||||
].
|
||||
|
||||
%%
|
||||
%% starting/stopping
|
||||
%%
|
||||
init_per_suite(C) ->
|
||||
{_, Seed} = calendar:local_time(),
|
||||
random:seed(Seed),
|
||||
test_configuration(),
|
||||
{ok, Apps1} = application:ensure_all_started(capi),
|
||||
{ok, Apps2} = application:ensure_all_started(hackney),
|
||||
[{apps, Apps1 ++ Apps2} | C].
|
||||
|
||||
end_per_suite(C) ->
|
||||
[application_stop(App) || App <- proplists:get_value(apps, C)].
|
||||
|
||||
application_stop(App = sasl) ->
|
||||
%% hack for preventing sasl deadlock
|
||||
%% http://erlang.org/pipermail/erlang-questions/2014-May/079012.html
|
||||
error_logger:delete_report_handler(cth_log_redirect),
|
||||
_ = application:stop(App),
|
||||
error_logger:add_report_handler(cth_log_redirect),
|
||||
ok;
|
||||
application_stop(App) ->
|
||||
application:stop(App).
|
||||
|
||||
%% tests
|
||||
authorization_error_test(_) ->
|
||||
{ok, 401, _RespHeaders, _Body} = call(get, "/invoices/22?limit=22", #{}, []).
|
||||
|
||||
create_invoice_badard_test(_) ->
|
||||
{ok, 400, _RespHeaders, _Body} = default_call(post, "/invoices", #{}).
|
||||
|
||||
create_invoice_ok_test(_) ->
|
||||
#{<<"id">> := _InvoiceID} = default_create_invoice().
|
||||
|
||||
create_payment_ok_test(_) ->
|
||||
#{<<"id">> := InvoiceID} = default_create_invoice(),
|
||||
#{
|
||||
<<"session">> := PaymentSession,
|
||||
<<"token">> := PaymentToolToken
|
||||
} = default_tokenize_card(),
|
||||
#{<<"id">> := _PaymentID} = default_create_payment(InvoiceID, PaymentSession, PaymentToolToken).
|
||||
|
||||
create_payment_tool_token_ok_test(_) ->
|
||||
#{<<"token">> := _Token, <<"session">> := _Session} = default_tokenize_card().
|
||||
|
||||
get_invoice_by_id_ok_test(_) ->
|
||||
#{<<"id">> := InvoiceID} = default_create_invoice(),
|
||||
Path = "/invoices/" ++ genlib:to_list(InvoiceID),
|
||||
{ok, 200, _RespHeaders, _Body} = default_call(get, Path, #{}).
|
||||
|
||||
get_invoice_events_ok_test(_) ->
|
||||
#{<<"id">> := InvoiceID} = default_create_invoice(),
|
||||
#{
|
||||
<<"session">> := PaymentSession,
|
||||
<<"token">> := PaymentToolToken
|
||||
} = default_tokenize_card(),
|
||||
#{<<"id">> := _PaymentID} = default_create_payment(InvoiceID, PaymentSession, PaymentToolToken),
|
||||
|
||||
timer:sleep(1000),
|
||||
Path = "/invoices/" ++ genlib:to_list(InvoiceID) ++ "/events/?limit=100",
|
||||
{ok, 200, _RespHeaders, _Body} = default_call(get, Path, #{}).
|
||||
|
||||
get_payment_by_id_ok_test(_) ->
|
||||
#{<<"id">> := InvoiceID} = default_create_invoice(),
|
||||
#{
|
||||
<<"session">> := PaymentSession,
|
||||
<<"token">> := PaymentToolToken
|
||||
} = default_tokenize_card(),
|
||||
#{<<"id">> := PaymentID} = default_create_payment(InvoiceID, PaymentSession, PaymentToolToken),
|
||||
|
||||
Path = "/invoices/" ++ genlib:to_list(InvoiceID) ++ "/payments/" ++ genlib:to_list(PaymentID),
|
||||
{ok, 200, _RespHeaders, _Body} = default_call(get, Path, #{}).
|
||||
%% helpers
|
||||
|
||||
test_configuration() ->
|
||||
application:set_env(capi, host, ?CAPI_HOST),
|
||||
application:set_env(capi, port, ?CAPI_PORT),
|
||||
application:set_env(capi, service_type, ?CAPI_SERVICE_TYPE).
|
||||
|
||||
default_call(Method, Path, Body) ->
|
||||
call(Method, Path, Body, [x_request_id_header(), auth_header(), json_content_type_header()]).
|
||||
|
||||
call(Method, Path, Body, Headers) ->
|
||||
Url = get_url(Path),
|
||||
PreparedBody = jsx:encode(Body),
|
||||
{ok, Code, RespHeaders, ClientRef} = hackney:request(Method, Url, Headers, PreparedBody),
|
||||
{ok, Code, RespHeaders, get_body(ClientRef)}.
|
||||
|
||||
get_url(Path) ->
|
||||
?CAPI_HOST ++ ":" ++ integer_to_list(?CAPI_PORT) ++ Path.
|
||||
|
||||
x_request_id_header() ->
|
||||
{<<"X-Request-ID">>, integer_to_binary(rand:uniform(100000))}.
|
||||
|
||||
auth_header() ->
|
||||
{<<"Authorization">>, <<"Bearer ", (auth_token())/binary>>} .
|
||||
|
||||
auth_token() ->
|
||||
<<"I can't find JWT library :(">>.
|
||||
|
||||
json_content_type_header() ->
|
||||
{<<"Content-Type">>, <<"application/json">>}.
|
||||
|
||||
default_create_invoice() ->
|
||||
Req = #{
|
||||
<<"shopID">> => <<"test_shop_id">>,
|
||||
<<"amount">> => 100000,
|
||||
<<"currency">> => <<"RUB">>,
|
||||
<<"context">> => #{
|
||||
<<"invoice_dummy_context">> => <<"test_value">>
|
||||
},
|
||||
<<"dueDate">> => <<"2017-07-11 10:00:00">>,
|
||||
<<"product">> => <<"test_product">>,
|
||||
<<"description">> => <<"test_invoice_description">>
|
||||
},
|
||||
{ok, 201, _RespHeaders, Body} = default_call(post, "/invoices", Req),
|
||||
decode_body(Body).
|
||||
|
||||
default_tokenize_card() ->
|
||||
Req = #{
|
||||
<<"paymentToolType">> => <<"cardData">>,
|
||||
<<"cardHolder">> => <<"Alexander Weinerschnitzel">>,
|
||||
<<"cardNumber">> => 4111111111111111,
|
||||
<<"expDate">> => <<"08/27">>,
|
||||
<<"cvv">> => <<"232">>
|
||||
},
|
||||
{ok, 201, _RespHeaders, Body} = default_call(post, "/payment_tools", Req),
|
||||
decode_body(Body).
|
||||
|
||||
default_create_payment(InvoiceID, PaymentSession, PaymentToolToken) ->
|
||||
Req = #{
|
||||
<<"paymentSession">> => PaymentSession,
|
||||
<<"paymentToolToken">> => PaymentToolToken
|
||||
},
|
||||
Path = "/invoices/" ++ genlib:to_list(InvoiceID) ++ "/payments",
|
||||
{ok, 201, _RespHeaders, Body} = default_call(post, Path, Req),
|
||||
decode_body(Body).
|
||||
|
||||
get_body(ClientRef) ->
|
||||
{ok, Body} = hackney:body(ClientRef),
|
||||
Body.
|
||||
|
||||
decode_body(Body) ->
|
||||
jsx:decode(Body, [return_maps]).
|
7
config/sys.config
Normal file
7
config/sys.config
Normal file
@ -0,0 +1,7 @@
|
||||
[
|
||||
{ capi , [
|
||||
{host, "0.0.0.0"},
|
||||
{port, 8080},
|
||||
{service_type, mock}
|
||||
]}
|
||||
].
|
6
config/vm.args
Normal file
6
config/vm.args
Normal file
@ -0,0 +1,6 @@
|
||||
-sname capi
|
||||
|
||||
-setcookie capi_cookie
|
||||
|
||||
+K true
|
||||
+A 10
|
5
doc/index.md
Normal file
5
doc/index.md
Normal file
@ -0,0 +1,5 @@
|
||||
# Документация
|
||||
|
||||
1. [Общее описание](overview.md)
|
||||
1. [Установка](install.md)
|
||||
1. [Первоначалная настройка](configuration.md)
|
24
packer.json
Normal file
24
packer.json
Normal file
@ -0,0 +1,24 @@
|
||||
{
|
||||
"builders": [
|
||||
{
|
||||
"type": "docker",
|
||||
"image": "rbkmoney/service_erlang",
|
||||
"pull": "true",
|
||||
"commit": "true"
|
||||
}
|
||||
],
|
||||
"provisioners": [
|
||||
{
|
||||
"type": "file",
|
||||
"source": "./_build/prod/rel/capi",
|
||||
"destination": "/opt/"
|
||||
}
|
||||
],
|
||||
"post-processors": [
|
||||
{
|
||||
"type": "docker-tag",
|
||||
"repository": "rbkmoney/capi_erlang"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
21
rebar.lock
Normal file
21
rebar.lock
Normal file
@ -0,0 +1,21 @@
|
||||
[{<<"certifi">>,{pkg,<<"certifi">>,<<"0.4.0">>},1},
|
||||
{<<"cowboy">>,{pkg,<<"cowboy">>,<<"1.0.4">>},0},
|
||||
{<<"cowlib">>,{pkg,<<"cowlib">>,<<"1.0.2">>},1},
|
||||
{<<"genlib">>,
|
||||
{git,"https://github.com/rbkmoney/genlib.git",
|
||||
{ref,"66db7fe296465a875b6894eb5ac944c90f82f913"}},
|
||||
0},
|
||||
{<<"hackney">>,{pkg,<<"hackney">>,<<"1.5.7">>},0},
|
||||
{<<"idna">>,{pkg,<<"idna">>,<<"1.2.0">>},1},
|
||||
{<<"jesse">>,
|
||||
{git,"https://github.com/for-GET/jesse.git",
|
||||
{ref,"f4270eb0a9bc64291c6e2205645b45b5b7b686f8"}},
|
||||
0},
|
||||
{<<"jsx">>,
|
||||
{git,"https://github.com/talentdeficit/jsx.git",
|
||||
{ref,"3074d4865b3385a050badf7828ad31490d860df5"}},
|
||||
0},
|
||||
{<<"metrics">>,{pkg,<<"metrics">>,<<"1.0.1">>},1},
|
||||
{<<"mimerl">>,{pkg,<<"mimerl">>,<<"1.0.2">>},1},
|
||||
{<<"ranch">>,{pkg,<<"ranch">>,<<"1.2.1">>},1},
|
||||
{<<"ssl_verify_fun">>,{pkg,<<"ssl_verify_fun">>,<<"1.1.0">>},1}].
|
1
schemes/swag
Submodule
1
schemes/swag
Submodule
@ -0,0 +1 @@
|
||||
Subproject commit 60b0337c99be8546179f9473adcc1596395dc831
|
16
wercker.yml
Normal file
16
wercker.yml
Normal file
@ -0,0 +1,16 @@
|
||||
box: erlang:18
|
||||
|
||||
dev:
|
||||
steps:
|
||||
- internal/shell:
|
||||
code: make compile
|
||||
|
||||
build:
|
||||
steps:
|
||||
- script:
|
||||
name: run test suite
|
||||
code: make test
|
||||
after-steps:
|
||||
- slack-notifier:
|
||||
url: ${SLACK_WEBHOOK_URL}
|
||||
username: "wercker"
|
Loading…
Reference in New Issue
Block a user