mirror of
https://github.com/valitydev/woody_erlang.git
synced 2024-11-06 02:15:19 +00:00
commit
bf74d46150
86
README.md
86
README.md
@ -13,7 +13,10 @@ Erlang реализация [Библиотеки RPC вызовов для об
|
||||
|
||||
```erlang
|
||||
1> EventHandler = my_event_handler. %% реализует woody_event_handler behaviour
|
||||
2> Service = my_money_service. %% реализует thrift_service behaviour (генерируется из .thrift файла)
|
||||
2> Service = {
|
||||
2> my_money_thrift, %% имя модуля, сгенерированного из money.thrift файла
|
||||
2> money %% имя thrift сервиса, заданное в money.thift
|
||||
2> }.
|
||||
3> ThriftHandler = my_money_thrift_service_handler. %% реализует woody_server_thrift_handler behaviour
|
||||
4> Opts = [].
|
||||
5> Handlers = [{"/v1/thrift_money_service",{Service, ThriftHandler, Opts}}].
|
||||
@ -41,42 +44,42 @@ Erlang реализация [Библиотеки RPC вызовов для об
|
||||
9> Function = give_me_money. %% thrift метод
|
||||
10> Args = [100, <<"rub">>].
|
||||
11> Request = {Service, Function, Args}.
|
||||
12> Client = woody_client:new(<<"myUniqRequestID1">>, EventHandler).
|
||||
13> {{ok, Result}, _NextClient} = woody_client:call(Client, Request, #{url => Url}).
|
||||
12> Context = woody_client:new_context(<<"myUniqRequestID1">>, EventHandler).
|
||||
13> {{ok, Result}, _NextContext} = woody_client:call(Context, Request, #{url => Url}).
|
||||
```
|
||||
|
||||
В случае вызова _thrift_ `oneway` функции (_thrift_ реализация _cast_) `woody_client:call/3` вернет `{ok, NextClient}`.
|
||||
В случае вызова _thrift_ `oneway` функции (_thrift_ реализация _cast_) `woody_client:call/3` вернет `{ok, NextContext}`.
|
||||
|
||||
Если сервер бросает `Exception`, описанный в _.thrift_ файле сервиса, `woody_client:call/3` бросит это же исключение в виде: `throw:{{exception, Exception}, NextClient}`, а в случае ошибки RPC вызова: `error:{Reason, NextClient}`.
|
||||
Если сервер бросает `Exception`, описанный в _.thrift_ файле сервиса, `woody_client:call/3` бросит это же исключение в виде: `throw:{{exception, Exception}, NextContext}`, а в случае ошибки RPC вызова: `error:{Reason, NextContext}`.
|
||||
|
||||
`woody_client:call_safe/3` - аналогична `call/3`, но в случае исключений, не бросает их, а возвращает в виде tuple: `{{exception | error, Error}, NextClient}` либо `{{error, Error, Stacktace}, NextClient}`.
|
||||
`woody_client:call_safe/3` - аналогична `call/3`, но в случае исключений, не бросает их, а возвращает в виде tuple: `{{exception | error, Error}, NextContext}` либо `{{error, Error, Stacktace}, NextContext}`.
|
||||
|
||||
```erlang
|
||||
14> Args1 = [1000000, <<"usd">>].
|
||||
15> Request1 = {Service, Function, Args1}.
|
||||
16> Client1 = woody_client:new(<<"myUniqRequestID2">>, EventHandler).
|
||||
17> {{exception, #take_it_easy{}}, _NextClient1} = woody_client:call_safe(Client1, Request1, #{url => Url}).
|
||||
16> Context1 = woody_client:new_context(<<"myUniqRequestID2">>, EventHandler).
|
||||
17> {{exception, #take_it_easy{}}, _NextContext1} = woody_client:call_safe(Context1, Request1, #{url => Url}).
|
||||
```
|
||||
|
||||
`woody_client:call_async/5` позволяет сделать call асинхронно и обработать результат в callback функции. `woody_client:call_async/5` требует также _sup_ref()_ для включения процесса, обрабатывающего RPC вызов, в supervision tree приложения.
|
||||
|
||||
```erlang
|
||||
18> Callback = fun({{ok, Res}, _NextClient2}) -> io:format("Rpc succeeded: ~p~n", [Res]);
|
||||
18> ({{exception, Error}, _NextClient2}) -> io:format("Service exception: ~p~n", [Error]);
|
||||
18> ({{error, _} _NextClient2}) -> io:format("Rpc failed")
|
||||
18> ({{error, _, _} _NextClient2}) -> io:format("Rpc failed")
|
||||
18> Callback = fun({{ok, Res}, _NextContext2}) -> io:format("Rpc succeeded: ~p~n", [Res]);
|
||||
18> ({{exception, Error}, _NextContext2}) -> io:format("Service exception: ~p~n", [Error]);
|
||||
18> ({{error, _} _NextContext2}) -> io:format("Rpc failed")
|
||||
18> ({{error, _, _} _NextContext2}) -> io:format("Rpc failed")
|
||||
18> end.
|
||||
19> Client2 = woody_client:new(<<"myUniqRequestID3">>, EventHandler).
|
||||
20> {ok, Pid, _NextClient2} = woody_client:call_async(SupRef, Callback, Client2, Request, #{url => Url}).
|
||||
19> Context2 = woody_client:new_context(<<"myUniqRequestID3">>, EventHandler).
|
||||
20> {ok, Pid, _NextContext2} = woody_client:call_async(SupRef, Callback, Context2, Request, #{url => Url}).
|
||||
```
|
||||
|
||||
Можно создать пул соединений для thrift клиента: `woody_client_thrift:start_pool/2` и затем использовать его при работе с `woody_client`:
|
||||
Можно создать пул соединений для thrift клиента (например, для установления _keep alive_ соединений с сервером): `woody_client_thrift:start_pool/2` и затем использовать его при работе с `woody_client`:
|
||||
|
||||
```erlang
|
||||
21> Pool = my_client_pool.
|
||||
22> ok = woody_client_thrift:start_pool(Pool, 10).
|
||||
23> Client3 = woody_client:new(<<"myUniqRequestID3">>, EventHandler).
|
||||
24> {{ok, Result}, _NextClient3} = woody_client:call(Client, Request, #{url => Url, pool => Pool}).
|
||||
23> Context3 = woody_client:new_context(<<"myUniqRequestID3">>, EventHandler).
|
||||
24> {{ok, Result}, _NextContext3} = woody_client:call(Context, Request, #{url => Url, pool => Pool}).
|
||||
```
|
||||
|
||||
Закрыть пул можно с помошью `woody_client_thrift:stop_pool/1`.
|
||||
@ -87,49 +90,53 @@ Erlang реализация [Библиотеки RPC вызовов для об
|
||||
-module(my_money_thrift_service_handler).
|
||||
-behaviour(woody_server_thrift_handler).
|
||||
|
||||
%% Auto-generated Thrift types for my_money_service
|
||||
-include("my_money_types.hrl").
|
||||
%% Auto-generated Thrift types from money.thrift
|
||||
-include("my_money_thrift.hrl").
|
||||
|
||||
-export([handle_function/5, handle_error/5]).
|
||||
-export([handle_function/4, handle_error/4]).
|
||||
|
||||
-spec handle_function(woody_t:func(), woody_server_thrift_handler:args(),
|
||||
woody_t:rpc_id(), woody_client:client(), woody_server_thrift_handler:handler_opts())
|
||||
woody_client:context(), woody_server_thrift_handler:handler_opts())
|
||||
->
|
||||
{ok, woody_server_thrift_handler:result()} | no_return().
|
||||
handle_function(give_me_money, Sum = {Amount, Currency}, RpcId, Client, _Opts) ->
|
||||
handle_function(give_me_money, Sum = {Amount, Currency}, Context, _Opts) ->
|
||||
Wallet = <<"localhost:8022/v1/thrift_wallet_service">>,
|
||||
RequestLimits = {my_wallet_service, check_limits, Sum},
|
||||
|
||||
%% Используется Client, полученный handle_function,
|
||||
%% woody_client:new/2 вызывать не надо.
|
||||
case woody_client:call_safe(Client, RequestLimits, #{url => Wallet}) of
|
||||
{{ok, ok}, Client1} ->
|
||||
%% RpcId можно получить из Context, полученного handle_function,
|
||||
%% для использования при логировании.
|
||||
RpcId = woody_client:get_rpc_id(Context),
|
||||
|
||||
%% Логи следует тэгировать RpcId, полученным handle_function.
|
||||
%% Используется Context, полученный handle_function.
|
||||
%% woody_client:new/2 вызывать не надо.
|
||||
case woody_client:call_safe(Context, RequestLimits, #{url => Wallet}) of
|
||||
{{ok, ok}, Context1} ->
|
||||
|
||||
%% Логи следует тэгировать RpcId.
|
||||
lager:info("[~p] giving away ~p ~p",
|
||||
[my_event_handler:format_id(RpcId), Amount, Currency]),
|
||||
[RpcId, Amount, Currency]),
|
||||
RequestMoney = {my_wallet_service, get_money, Sum},
|
||||
|
||||
%% Используется новое значение Client1, полученное из предыдущего вызова
|
||||
%% Используется новое значение Context1, полученное из предыдущего вызова
|
||||
%% woody_client:call_safe/3 (call/3, call_async/5).
|
||||
{{ok, Money}, _Client2} = woody_client:call(Client1, RequestMoney,
|
||||
{{ok, Money}, _Context2} = woody_client:call(Context1, RequestMoney,
|
||||
#{url => Wallet}),
|
||||
{ok, Money};
|
||||
{{exception, #over_limits{}}, _Client1} ->
|
||||
{{exception, #over_limits{}}, _Context1} ->
|
||||
lager:info("[~p] ~p ~p is too much",
|
||||
[my_event_handler:format_id(RpcId), Amount, Currency]),
|
||||
[RpcId, Amount, Currency]),
|
||||
throw(#take_it_easy{})
|
||||
end.
|
||||
|
||||
-spec handle_error(woody_t:func(), woody_server_thrift_handler:error_reason(),
|
||||
woody_t:rpc_id(), woody_client:client(), woody_server_thrift_handler:handler_opts())
|
||||
woody_t:rpc_id(), woody_server_thrift_handler:handler_opts())
|
||||
-> _.
|
||||
handle_error(give_me_money, Error, RpcId, _Client, _Opts) ->
|
||||
handle_error(give_me_money, Error, Context, _Opts) ->
|
||||
lager:info("[~p] got error from thrift: ~p",
|
||||
[my_event_handler:format_id(RpcId), Error]).
|
||||
[woody_client:get_rpc_id(Context), Error]).
|
||||
```
|
||||
|
||||
Показанное в этом наивном примере реализации `my_money_service` использование `Client` и `RpcId` необходимо для корректного логирования _RPC ID_ библиотекой, которое позволяет построить полное дерево RPC вызовов между микросервисами в рамках обработки бизнес сценария.
|
||||
Показанное в этом наивном примере реализации сервиса `my_money` использование `Context` и `RpcId` необходимо для корректного логирования _RPC ID_ библиотекой, которое позволяет построить полное дерево RPC вызовов между микросервисами в рамках обработки бизнес сценария.
|
||||
|
||||
### Woody Event Handler
|
||||
|
||||
@ -139,12 +146,13 @@ handle_error(give_me_money, Error, RpcId, _Client, _Opts) ->
|
||||
-module(my_event_handler).
|
||||
-behaviour(woody_event_handler).
|
||||
|
||||
-export([handle_event/2]).
|
||||
-export([handle_event/3]).
|
||||
|
||||
-spec handle_event(
|
||||
woody_event_handler:event_type(),
|
||||
woody_t:rpc_id(),
|
||||
woody_event_handler:event_meta_type()
|
||||
) -> _.
|
||||
handle_event(Event, Meta) ->
|
||||
lager:info("woody event ~p: ~p", [Event, Meta]).
|
||||
handle_event(Event, RpcId, Meta) ->
|
||||
lager:info("[~p] woody event ~p: ~p", [RpcId, Event, Meta]).
|
||||
```
|
@ -17,5 +17,5 @@
|
||||
{<<"ssl_verify_fun">>,{pkg,<<"ssl_verify_fun">>,<<"1.1.0">>},1},
|
||||
{<<"thrift">>,
|
||||
{git,"https://github.com/rbkmoney/thrift_erlang.git",
|
||||
{ref,"88ded69c8aa9fef4a75c10e2e89061d744bac7b7"}},
|
||||
{ref,"4950a4cbb2d79f400a54664cf58e843fd8efcd59"}},
|
||||
0}].
|
||||
|
@ -11,7 +11,8 @@
|
||||
thrift
|
||||
]},
|
||||
{env, [
|
||||
{acceptors_pool_size, 100}
|
||||
{acceptors_pool_size, 100},
|
||||
{enable_debug, false}
|
||||
]},
|
||||
{modules, []},
|
||||
{maintainers, [
|
||||
|
@ -8,15 +8,16 @@
|
||||
-include("woody_defs.hrl").
|
||||
|
||||
%% API
|
||||
-export([new/2]).
|
||||
-export([make_child_client/2]).
|
||||
-export([next/1]).
|
||||
-export([new_context/2]).
|
||||
-export([get_rpc_id/1]).
|
||||
|
||||
-export([make_child_context/2]).
|
||||
|
||||
-export([call/3]).
|
||||
-export([call_safe/3]).
|
||||
-export([call_async/5]).
|
||||
|
||||
-export_type([client/0, options/0, result_ok/0, result_error/0]).
|
||||
-export_type([context/0, options/0, result_ok/0, result_error/0]).
|
||||
|
||||
-define(ROOT_REQ_PARENT_ID, <<"undefined">>).
|
||||
|
||||
@ -27,30 +28,31 @@
|
||||
-export([init/1]).
|
||||
|
||||
%% behaviour definition
|
||||
-callback call(client(), request(), options()) -> result_ok() | no_return().
|
||||
-callback call(context(), request(), options()) -> result_ok() | no_return().
|
||||
|
||||
|
||||
%%
|
||||
%% API
|
||||
%%
|
||||
-type client() :: #{ %% all elements are mandatory
|
||||
-type context() :: #{ %% all elements are mandatory
|
||||
root_rpc => boolean(),
|
||||
span_id => woody_t:req_id() | undefined,
|
||||
trace_id => woody_t:req_id(),
|
||||
parent_id => woody_t:req_id(),
|
||||
event_handler => woody_t:handler(),
|
||||
seq => non_neg_integer()
|
||||
seq => non_neg_integer(),
|
||||
rpc_id => woody_t:rpc_id() | undefined
|
||||
}.
|
||||
-type class() :: throw | error | exit.
|
||||
-type stacktrace() :: list().
|
||||
|
||||
-type result_ok() :: {ok | {ok, _Response}, client()}.
|
||||
-type result_ok() :: {ok | {ok, _Response}, context()}.
|
||||
|
||||
-type result_error() ::
|
||||
{{exception , woody_client_thrift:except_thrift()} , client()} |
|
||||
{{error , woody_client_thrift:error_protocol()} , client()} |
|
||||
{{error , woody_client_thrift_http_transport:error()} , client()} |
|
||||
{{error , {class(), _Reason, stacktrace()}} , client()}.
|
||||
{{exception , woody_client_thrift:except_thrift()} , context()} |
|
||||
{{error , woody_client_thrift:error_protocol()} , context()} |
|
||||
{{error , woody_client_thrift_http_transport:error()} , context()} |
|
||||
{{error , {class(), _Reason, stacktrace()}} , context()}.
|
||||
|
||||
-type request() :: any().
|
||||
|
||||
@ -62,61 +64,62 @@
|
||||
|
||||
-type callback() :: fun((result_ok() | result_error()) -> _).
|
||||
|
||||
-spec new(woody_t:req_id(), woody_t:handler()) -> client().
|
||||
new(ReqId, EventHandler) ->
|
||||
-spec new_context(woody_t:req_id(), woody_t:handler()) -> context().
|
||||
new_context(ReqId, EventHandler) ->
|
||||
#{
|
||||
root_rpc => true,
|
||||
span_id => ReqId,
|
||||
trace_id => ReqId,
|
||||
parent_id => ?ROOT_REQ_PARENT_ID,
|
||||
seq => 0,
|
||||
event_handler => EventHandler
|
||||
event_handler => EventHandler,
|
||||
rpc_id => undefined
|
||||
}.
|
||||
|
||||
-spec make_child_client(woody_t:rpc_id(), woody_t:handler()) -> client().
|
||||
make_child_client(#{span_id := ReqId, trace_id := TraceId}, EventHandler) ->
|
||||
-spec make_child_context(woody_t:rpc_id(), woody_t:handler()) -> context().
|
||||
make_child_context(RpcId = #{span_id := ReqId, trace_id := TraceId}, EventHandler) ->
|
||||
#{
|
||||
root_rpc => false,
|
||||
span_id => undefined,
|
||||
trace_id => TraceId,
|
||||
parent_id => ReqId,
|
||||
seq => 0,
|
||||
event_handler => EventHandler
|
||||
event_handler => EventHandler,
|
||||
rpc_id => RpcId
|
||||
}.
|
||||
|
||||
-spec next(client()) -> client().
|
||||
next(Client = #{root_rpc := true}) ->
|
||||
Client;
|
||||
next(Client = #{root_rpc := false, seq := Seq}) ->
|
||||
NextSeq = Seq +1,
|
||||
Client#{span_id => make_req_id(NextSeq), seq => NextSeq}.
|
||||
-spec get_rpc_id(context()) -> woody_t:rpc_id() | undefined | no_return().
|
||||
get_rpc_id(#{rpc_id := RpcId}) ->
|
||||
RpcId;
|
||||
get_rpc_id(_) ->
|
||||
error(badarg).
|
||||
|
||||
-spec call(client(), request(), options()) -> result_ok() | no_return().
|
||||
call(Client, Request, Options) ->
|
||||
-spec call(context(), request(), options()) -> result_ok() | no_return().
|
||||
call(Context, Request, Options) ->
|
||||
ProtocolHandler = woody_t:get_protocol_handler(client, Options),
|
||||
ProtocolHandler:call(next(Client), Request, Options).
|
||||
ProtocolHandler:call(next(Context), Request, Options).
|
||||
|
||||
-spec call_safe(client(), request(), options()) -> result_ok() | result_error().
|
||||
call_safe(Client, Request, Options) ->
|
||||
try call(Client, Request, Options)
|
||||
-spec call_safe(context(), request(), options()) -> result_ok() | result_error().
|
||||
call_safe(Context, Request, Options) ->
|
||||
try call(Context, Request, Options)
|
||||
catch
|
||||
%% valid thrift exception
|
||||
throw:{Except = ?except_thrift(_), Client1} ->
|
||||
{Except, Client1};
|
||||
throw:{Except = ?except_thrift(_), Context1} ->
|
||||
{Except, Context1};
|
||||
%% rpc send failed
|
||||
error:{TError = ?error_transport(_), Client1} ->
|
||||
{{error, TError}, Client1};
|
||||
error:{TError = ?error_transport(_), Context1} ->
|
||||
{{error, TError}, Context1};
|
||||
%% thrift protocol error
|
||||
error:{PError = ?error_protocol(_), Client1} ->
|
||||
{{error, PError}, Client1};
|
||||
error:{PError = ?error_protocol(_), Context1} ->
|
||||
{{error, PError}, Context1};
|
||||
%% what else could have happened?
|
||||
Class:Reason ->
|
||||
{{error, {Class, Reason, erlang:get_stacktrace()}}, Client}
|
||||
{{error, {Class, Reason, erlang:get_stacktrace()}}, Context}
|
||||
end.
|
||||
|
||||
-spec call_async(woody_t:sup_ref(), callback(), client(), request(), options()) ->
|
||||
{ok, pid(), client()} | {error, _}.
|
||||
call_async(Sup, Callback, Client, Request, Options) ->
|
||||
-spec call_async(woody_t:sup_ref(), callback(), context(), request(), options()) ->
|
||||
{ok, pid(), context()} | {error, _}.
|
||||
call_async(Sup, Callback, Context, Request, Options) ->
|
||||
_ = woody_t:get_protocol_handler(client, Options),
|
||||
SupervisorSpec = #{
|
||||
id => {?MODULE, woody_clients_sup},
|
||||
@ -131,19 +134,19 @@ call_async(Sup, Callback, Client, Request, Options) ->
|
||||
{error, {already_started, Pid}} -> Pid
|
||||
end,
|
||||
supervisor:start_child(ClientSup,
|
||||
[Callback, Client, Request, Options]).
|
||||
[Callback, Context, Request, Options]).
|
||||
|
||||
%%
|
||||
%% Internal API
|
||||
%%
|
||||
-spec init_call_async(callback(), client(), request(), options()) -> {ok, pid(), client()}.
|
||||
init_call_async(Callback, Client, Request, Options) ->
|
||||
proc_lib:start_link(?MODULE, do_call_async, [Callback, Client, Request, Options]).
|
||||
-spec init_call_async(callback(), context(), request(), options()) -> {ok, pid(), context()}.
|
||||
init_call_async(Callback, Context, Request, Options) ->
|
||||
proc_lib:start_link(?MODULE, do_call_async, [Callback, Context, Request, Options]).
|
||||
|
||||
-spec do_call_async(callback(), client(), request(), options()) -> _.
|
||||
do_call_async(Callback, Client, Request, Options) ->
|
||||
proc_lib:init_ack({ok, self(), next(Client)}),
|
||||
Callback(call_safe(Client, Request, Options)).
|
||||
-spec do_call_async(callback(), context(), request(), options()) -> _.
|
||||
do_call_async(Callback, Context, Request, Options) ->
|
||||
proc_lib:init_ack({ok, self(), next(Context)}),
|
||||
Callback(call_safe(Context, Request, Options)).
|
||||
|
||||
%%
|
||||
%% Supervisor callbacks
|
||||
@ -170,6 +173,13 @@ init(woody_client_sup) ->
|
||||
%%
|
||||
%% Internal functions
|
||||
%%
|
||||
-spec next(context()) -> context().
|
||||
next(Context = #{root_rpc := true}) ->
|
||||
Context;
|
||||
next(Context = #{root_rpc := false, seq := Seq}) ->
|
||||
NextSeq = Seq +1,
|
||||
Context#{span_id => make_req_id(NextSeq), seq => NextSeq}.
|
||||
|
||||
-spec make_req_id(non_neg_integer()) -> woody_t:req_id().
|
||||
make_req_id(Seq) ->
|
||||
BinSeq = genlib:to_binary(Seq),
|
||||
|
@ -20,7 +20,7 @@
|
||||
-export_type([except_thrift/0, error_protocol/0]).
|
||||
|
||||
-define(log_rpc_result(EventHandler, RpcId, Status, Result),
|
||||
woody_event_handler:handle_event(EventHandler, ?EV_SERVICE_RESULT, RpcId#{
|
||||
woody_event_handler:handle_event(EventHandler, ?EV_SERVICE_RESULT, RpcId, #{
|
||||
status => Status, result => Result
|
||||
})
|
||||
).
|
||||
@ -38,14 +38,14 @@ start_pool(Name, PoolSize) when is_integer(PoolSize) ->
|
||||
stop_pool(Name) ->
|
||||
woody_client_thrift_http_transport:stop_client_pool(Name).
|
||||
|
||||
-spec call(woody_client:client(), request(), woody_client:options()) ->
|
||||
-spec call(woody_client:context(), request(), woody_client:options()) ->
|
||||
woody_client:result_ok() | no_return().
|
||||
call(Client = #{event_handler := EventHandler},
|
||||
{Service, Function, Args}, TransportOpts)
|
||||
call(Context = #{event_handler := EventHandler},
|
||||
{Service = {_, ServiceName}, Function, Args}, TransportOpts)
|
||||
->
|
||||
RpcId = maps:with([span_id, trace_id, parent_id], Client),
|
||||
woody_event_handler:handle_event(EventHandler, ?EV_CALL_SERVICE, RpcId#{
|
||||
service => Service,
|
||||
RpcId = maps:with([span_id, trace_id, parent_id], Context),
|
||||
woody_event_handler:handle_event(EventHandler, ?EV_CALL_SERVICE, RpcId, #{
|
||||
service => ServiceName,
|
||||
function => Function,
|
||||
type => get_rpc_type(Service, Function),
|
||||
args => Args
|
||||
@ -54,15 +54,15 @@ call(Client = #{event_handler := EventHandler},
|
||||
do_call(
|
||||
make_thrift_client(RpcId, Service, TransportOpts, EventHandler),
|
||||
Function, Args
|
||||
), RpcId, Client
|
||||
), RpcId, Context
|
||||
).
|
||||
|
||||
|
||||
%%
|
||||
%% Internal functions
|
||||
%%
|
||||
get_rpc_type(Service, Function) ->
|
||||
try get_rpc_type(Service:function_info(Function, reply_type))
|
||||
get_rpc_type({Module, Service}, Function) ->
|
||||
try get_rpc_type(Module:function_info(Service, Function, reply_type))
|
||||
catch
|
||||
error:_ ->
|
||||
error({badarg, {Service,Function}})
|
||||
@ -91,40 +91,40 @@ do_call(Client, Function, Args) ->
|
||||
|
||||
|
||||
format_return({ok, ok}, RpcId,
|
||||
Client = #{event_handler := EventHandler})
|
||||
Context = #{event_handler := EventHandler})
|
||||
->
|
||||
?log_rpc_result(EventHandler, RpcId, ok, ?thrift_cast),
|
||||
{ok, Client};
|
||||
{ok, Context};
|
||||
|
||||
format_return({ok, Result}, RpcId,
|
||||
Client = #{event_handler := EventHandler})
|
||||
Context = #{event_handler := EventHandler})
|
||||
->
|
||||
?log_rpc_result(EventHandler, RpcId, ok, Result),
|
||||
{{ok, Result}, Client};
|
||||
{{ok, Result}, Context};
|
||||
|
||||
%% In case a server violates the requirements and sends
|
||||
%% #TAppiacationException{} with http status code 200.
|
||||
format_return({exception, Result = #'TApplicationException'{}}, RpcId,
|
||||
Client = #{event_handler := EventHandler})
|
||||
Context = #{event_handler := EventHandler})
|
||||
->
|
||||
?log_rpc_result(EventHandler, RpcId, error, Result),
|
||||
error({?error_transport(server_error), Client});
|
||||
error({?error_transport(server_error), Context});
|
||||
|
||||
%% Service threw valid thrift exception
|
||||
format_return(Exception = ?except_thrift(_), RpcId,
|
||||
Client = #{event_handler := EventHandler})
|
||||
Context = #{event_handler := EventHandler})
|
||||
->
|
||||
?log_rpc_result(EventHandler, RpcId, ok, Exception),
|
||||
throw({Exception, Client});
|
||||
throw({Exception, Context});
|
||||
|
||||
format_return({error, Error = ?error_transport(_)}, RpcId,
|
||||
Client = #{event_handler := EventHandler})
|
||||
Context = #{event_handler := EventHandler})
|
||||
->
|
||||
?log_rpc_result(EventHandler, RpcId, error, Error),
|
||||
error({Error, Client});
|
||||
error({Error, Context});
|
||||
|
||||
format_return({error, Error}, RpcId,
|
||||
Client = #{event_handler := EventHandler})
|
||||
Context = #{event_handler := EventHandler})
|
||||
->
|
||||
?log_rpc_result(EventHandler, RpcId, error, Error),
|
||||
error({?error_protocol(Error), Client}).
|
||||
error({?error_protocol(Error), Context}).
|
||||
|
@ -43,9 +43,9 @@
|
||||
{error, ?error_transport(Error)}
|
||||
).
|
||||
|
||||
-define(log_response(EventHandler, Status, Meta),
|
||||
-define(log_response(EventHandler, Status, RpcId, Meta),
|
||||
woody_event_handler:handle_event(EventHandler, ?EV_CLIENT_RECEIVE,
|
||||
Meta#{status =>Status})
|
||||
RpcId, #{status =>Status})
|
||||
).
|
||||
|
||||
-type woody_transport() :: #{
|
||||
@ -133,7 +133,7 @@ flush(Transport = #{
|
||||
],
|
||||
RpcId = maps:with([span_id, trace_id, parent_id], Transport),
|
||||
woody_event_handler:handle_event(EventHandler, ?EV_CLIENT_SEND,
|
||||
RpcId#{url => Url}),
|
||||
RpcId, #{url => Url}),
|
||||
case send(Url, Headers, WBuffer, Options, RpcId, EventHandler) of
|
||||
{ok, Response} ->
|
||||
{Transport#{
|
||||
@ -156,15 +156,15 @@ send(Url, Headers, WBuffer, Options, RpcId, EventHandler) ->
|
||||
case hackney:request(post, Url, Headers, WBuffer, maps:to_list(Options)) of
|
||||
{ok, ResponseCode, _ResponseHeaders, Ref} ->
|
||||
?log_response(EventHandler, get_response_status(ResponseCode),
|
||||
RpcId#{code => ResponseCode}),
|
||||
RpcId, #{code => ResponseCode}),
|
||||
handle_response(ResponseCode, hackney:body(Ref));
|
||||
{error, {closed, _}} ->
|
||||
?log_response(EventHandler, error,
|
||||
RpcId#{reason => partial_response}),
|
||||
RpcId, #{reason => partial_response}),
|
||||
?format_error(partial_response);
|
||||
{error, Reason} ->
|
||||
?log_response(EventHandler, error,
|
||||
RpcId#{reason => Reason}),
|
||||
RpcId, #{reason => Reason}),
|
||||
?format_error(Reason)
|
||||
end.
|
||||
|
||||
|
@ -30,4 +30,5 @@
|
||||
-define(EV_THRIFT_ERROR , 'thrift error').
|
||||
-define(EV_INTERNAL_ERROR , 'internal error').
|
||||
|
||||
-define(EV_DEBUG , 'trace_event').
|
||||
-endif.
|
||||
|
@ -1,7 +1,7 @@
|
||||
-module(woody_event_handler).
|
||||
|
||||
%% API
|
||||
-export([handle_event/3]).
|
||||
-export([handle_event/4]).
|
||||
|
||||
-include("woody_defs.hrl").
|
||||
|
||||
@ -10,33 +10,34 @@
|
||||
%%
|
||||
-export_type([event_type/0, event_meta_type/0, meta_client_send/0, meta_client_receive/0,
|
||||
meta_server_receive/0, meta_server_send/0, meta_invoke_service_handler/0,
|
||||
meta_service_handler_result/0, meta_thrift_error/0, meta_internal_error/0
|
||||
meta_service_handler_result/0, meta_thrift_error/0, meta_internal_error/0,
|
||||
meta_debug/0
|
||||
]).
|
||||
|
||||
-callback handle_event
|
||||
%% mandatory
|
||||
(?EV_CALL_SERVICE , meta_call_service ()) -> _;
|
||||
(?EV_SERVICE_RESULT , meta_service_result ()) -> _;
|
||||
(?EV_CLIENT_SEND , meta_client_send ()) -> _;
|
||||
(?EV_CLIENT_RECEIVE , meta_client_receive ()) -> _;
|
||||
(?EV_SERVER_RECEIVE , meta_server_receive ()) -> _;
|
||||
(?EV_SERVER_SEND , meta_server_send ()) -> _;
|
||||
(?EV_INVOKE_SERVICE_HANDLER , meta_invoke_service_handler ()) -> _;
|
||||
(?EV_SERVICE_HANDLER_RESULT , meta_service_handler_result ()) -> _;
|
||||
(?EV_CALL_SERVICE , woody_t:rpc_id(), meta_call_service ()) -> _;
|
||||
(?EV_SERVICE_RESULT , woody_t:rpc_id(), meta_service_result ()) -> _;
|
||||
(?EV_CLIENT_SEND , woody_t:rpc_id(), meta_client_send ()) -> _;
|
||||
(?EV_CLIENT_RECEIVE , woody_t:rpc_id(), meta_client_receive ()) -> _;
|
||||
(?EV_SERVER_RECEIVE , woody_t:rpc_id(), meta_server_receive ()) -> _;
|
||||
(?EV_SERVER_SEND , woody_t:rpc_id(), meta_server_send ()) -> _;
|
||||
(?EV_INVOKE_SERVICE_HANDLER , woody_t:rpc_id(), meta_invoke_service_handler ()) -> _;
|
||||
(?EV_SERVICE_HANDLER_RESULT , woody_t:rpc_id(), meta_service_handler_result ()) -> _;
|
||||
%% optional
|
||||
(?EV_THRIFT_ERROR , meta_thrift_error ()) -> _;
|
||||
(?EV_INTERNAL_ERROR , meta_internal_error ()) -> _.
|
||||
|
||||
(?EV_THRIFT_ERROR , woody_t:rpc_id(), meta_thrift_error ()) -> _;
|
||||
(?EV_INTERNAL_ERROR , woody_t:rpc_id(), meta_internal_error ()) -> _;
|
||||
(?EV_DEBUG , woody_t:rpc_id()|undefined, meta_debug()) -> _.
|
||||
|
||||
-type event_type() :: ?EV_CALL_SERVICE | ?EV_SERVICE_RESULT | ?EV_CLIENT_SEND | ?EV_CLIENT_RECEIVE |
|
||||
?EV_SERVER_RECEIVE | ?EV_SERVER_SEND | ?EV_INVOKE_SERVICE_HANDLER | ?EV_SERVICE_HANDLER_RESULT |
|
||||
?EV_THRIFT_ERROR | ?EV_INTERNAL_ERROR.
|
||||
?EV_THRIFT_ERROR | ?EV_INTERNAL_ERROR | ?EV_DEBUG.
|
||||
|
||||
-type event_meta_type() :: meta_client_send() | meta_client_receive() |
|
||||
meta_server_receive() | meta_server_send() | meta_invoke_service_handler() |
|
||||
meta_service_handler_result() | meta_thrift_error() | meta_internal_error().
|
||||
|
||||
-type service() :: woody_t:service().
|
||||
-type service() :: woody_t:service_name().
|
||||
-type rpc_type() :: call | cast.
|
||||
-type status() :: ok | error.
|
||||
-type thrift_stage() :: protocol_read | protocol_write.
|
||||
@ -44,9 +45,6 @@
|
||||
|
||||
-type meta_call_service() :: #{
|
||||
%% mandatory
|
||||
span_id => woody_t:req_id(),
|
||||
parent_id => woody_t:req_id(),
|
||||
trace_id => woody_t:req_id(),
|
||||
service => service(),
|
||||
function => woody_t:func(),
|
||||
type => rpc_type(),
|
||||
@ -55,9 +53,6 @@
|
||||
}.
|
||||
-type meta_service_result() :: #{
|
||||
%% mandatory
|
||||
span_id => woody_t:req_id(),
|
||||
parent_id => woody_t:req_id(),
|
||||
trace_id => woody_t:req_id(),
|
||||
status => status(),
|
||||
%% optional
|
||||
result => any()
|
||||
@ -65,17 +60,11 @@
|
||||
|
||||
-type meta_client_send() :: #{
|
||||
%% mandatory
|
||||
span_id => woody_t:req_id(),
|
||||
parent_id => woody_t:req_id(),
|
||||
trace_id => woody_t:req_id(),
|
||||
url => woody_t:url()
|
||||
}.
|
||||
|
||||
-type meta_client_receive() :: #{
|
||||
%% mandatory
|
||||
span_id => woody_t:req_id(),
|
||||
parent_id => woody_t:req_id(),
|
||||
trace_id => woody_t:req_id(),
|
||||
status => status(),
|
||||
%% optional
|
||||
code => any(),
|
||||
@ -84,9 +73,6 @@
|
||||
|
||||
-type meta_server_receive() :: #{
|
||||
%% mandatory
|
||||
span_id => woody_t:req_id(),
|
||||
parent_id => woody_t:req_id(),
|
||||
trace_id => woody_t:req_id(),
|
||||
url => woody_t:url(),
|
||||
status => status(),
|
||||
%% optional
|
||||
@ -95,9 +81,6 @@
|
||||
|
||||
-type meta_server_send() :: #{
|
||||
%% mandatory
|
||||
span_id => woody_t:req_id(),
|
||||
parent_id => woody_t:req_id(),
|
||||
trace_id => woody_t:req_id(),
|
||||
status => status(),
|
||||
%% optional
|
||||
code => pos_integer()
|
||||
@ -105,9 +88,6 @@
|
||||
|
||||
-type meta_invoke_service_handler() :: #{
|
||||
%% mandatory
|
||||
span_id => woody_t:req_id(),
|
||||
parent_id => woody_t:req_id(),
|
||||
trace_id => woody_t:req_id(),
|
||||
service => service(),
|
||||
function => woody_t:func(),
|
||||
%% optional
|
||||
@ -117,9 +97,6 @@
|
||||
|
||||
-type meta_service_handler_result() :: #{
|
||||
%% mandatory
|
||||
span_id => woody_t:req_id(),
|
||||
parent_id => woody_t:req_id(),
|
||||
trace_id => woody_t:req_id(),
|
||||
status => status(),
|
||||
%% optional
|
||||
result => any(),
|
||||
@ -131,40 +108,39 @@
|
||||
|
||||
-type meta_thrift_error() :: #{
|
||||
%% mandatory
|
||||
span_id => woody_t:req_id(),
|
||||
parent_id => woody_t:req_id(),
|
||||
trace_id => woody_t:req_id(),
|
||||
stage => thrift_stage(),
|
||||
reason => any()
|
||||
}.
|
||||
|
||||
-type meta_internal_error() :: #{
|
||||
%% mandatory
|
||||
span_id => woody_t:req_id(),
|
||||
parent_id => woody_t:req_id(),
|
||||
trace_id => woody_t:req_id(),
|
||||
error => any(),
|
||||
reason => any(),
|
||||
%% optional
|
||||
stack => any()
|
||||
}.
|
||||
|
||||
-type meta_debug() :: #{
|
||||
%% mandatory
|
||||
event => atom() | binary()
|
||||
}.
|
||||
|
||||
%%
|
||||
%% API
|
||||
%%
|
||||
-spec handle_event
|
||||
%% mandatory
|
||||
(woody_t:handler() , ?EV_CALL_SERVICE , meta_call_service ()) -> _;
|
||||
(woody_t:handler() , ?EV_SERVICE_RESULT , meta_service_result ()) -> _;
|
||||
(woody_t:handler() , ?EV_CLIENT_SEND , meta_client_send ()) -> _;
|
||||
(woody_t:handler() , ?EV_CLIENT_RECEIVE , meta_client_receive ()) -> _;
|
||||
(woody_t:handler() , ?EV_SERVER_RECEIVE , meta_server_receive ()) -> _;
|
||||
(woody_t:handler() , ?EV_SERVER_SEND , meta_server_send ()) -> _;
|
||||
(woody_t:handler() , ?EV_INVOKE_SERVICE_HANDLER , meta_invoke_service_handler()) -> _;
|
||||
(woody_t:handler() , ?EV_SERVICE_HANDLER_RESULT , meta_service_handler_result()) -> _;
|
||||
(woody_t:handler() , ?EV_CALL_SERVICE , woody_t:rpc_id(), meta_call_service ()) -> _;
|
||||
(woody_t:handler() , ?EV_SERVICE_RESULT , woody_t:rpc_id(), meta_service_result ()) -> _;
|
||||
(woody_t:handler() , ?EV_CLIENT_SEND , woody_t:rpc_id(), meta_client_send ()) -> _;
|
||||
(woody_t:handler() , ?EV_CLIENT_RECEIVE , woody_t:rpc_id(), meta_client_receive ()) -> _;
|
||||
(woody_t:handler() , ?EV_SERVER_RECEIVE , woody_t:rpc_id(), meta_server_receive ()) -> _;
|
||||
(woody_t:handler() , ?EV_SERVER_SEND , woody_t:rpc_id(), meta_server_send ()) -> _;
|
||||
(woody_t:handler() , ?EV_INVOKE_SERVICE_HANDLER , woody_t:rpc_id(), meta_invoke_service_handler()) -> _;
|
||||
(woody_t:handler() , ?EV_SERVICE_HANDLER_RESULT , woody_t:rpc_id(), meta_service_handler_result()) -> _;
|
||||
%% optional
|
||||
(woody_t:handler() , ?EV_THRIFT_ERROR , meta_thrift_error ()) -> _;
|
||||
(woody_t:handler() , ?EV_INTERNAL_ERROR , meta_internal_error ()) -> _.
|
||||
handle_event(Handler, Type, Meta) ->
|
||||
Handler:handle_event(Type, Meta).
|
||||
(woody_t:handler() , ?EV_THRIFT_ERROR , woody_t:rpc_id(), meta_thrift_error ()) -> _;
|
||||
(woody_t:handler() , ?EV_INTERNAL_ERROR , woody_t:rpc_id(), meta_internal_error ()) -> _;
|
||||
(woody_t:handler() , ?EV_DEBUG , woody_t:rpc_id()|undefined, meta_debug()) -> _.
|
||||
handle_event(Handler, Type, RpcId, Meta) ->
|
||||
Handler:handle_event(Type, RpcId, Meta).
|
||||
|
@ -3,14 +3,9 @@
|
||||
|
||||
-module(woody_server).
|
||||
|
||||
-behaviour(supervisor).
|
||||
|
||||
%% API
|
||||
-export([child_spec/2]).
|
||||
|
||||
%% supervisor callbacks
|
||||
-export([init/1]).
|
||||
|
||||
%%
|
||||
%% behaviour definition
|
||||
%%
|
||||
@ -27,24 +22,4 @@
|
||||
-spec child_spec(_Id, options()) -> supervisor:child_spec().
|
||||
child_spec(Id, Options) ->
|
||||
ProtocolHandler = woody_t:get_protocol_handler(server, Options),
|
||||
ServerSpec = ProtocolHandler:child_spec(Id, Options),
|
||||
#{
|
||||
id => Id,
|
||||
start => {supervisor, start_link, [?MODULE, {woody_server, ServerSpec}]},
|
||||
restart => permanent,
|
||||
shutdown => infinity,
|
||||
type => supervisor,
|
||||
modules => [?MODULE]
|
||||
}.
|
||||
|
||||
%%
|
||||
%% Supervisor callbacks
|
||||
%%
|
||||
-spec init({woody_server, supervisor:child_spec()}) -> {ok, {#{}, [#{}, ...]}}.
|
||||
init({woody_server, ChildSpec}) ->
|
||||
{ok, {#{
|
||||
strategy => one_for_one,
|
||||
intensity => 10,
|
||||
period => 10},
|
||||
[ChildSpec]}
|
||||
}.
|
||||
ProtocolHandler:child_spec(Id, Options).
|
||||
|
@ -1,7 +1,7 @@
|
||||
-module(woody_server_thrift_handler).
|
||||
|
||||
%% API
|
||||
-export([start/6]).
|
||||
-export([start/5]).
|
||||
|
||||
-include_lib("thrift/include/thrift_constants.hrl").
|
||||
-include_lib("thrift/include/thrift_protocol.hrl").
|
||||
@ -16,21 +16,21 @@
|
||||
-type args() :: tuple().
|
||||
-export_type([handler_opts/0, args/0, result/0, error_reason/0]).
|
||||
|
||||
-callback handle_function(woody_t:func(), args(), woody_t:rpc_id(),
|
||||
woody_client:client(), handler_opts())
|
||||
-callback handle_function(woody_t:func(), args(),
|
||||
woody_client:context(), handler_opts())
|
||||
->
|
||||
ok | {ok, result()} | {error, result()} | no_return().
|
||||
|
||||
-callback handle_error(woody_t:func(), error_reason(), woody_t:rpc_id(),
|
||||
woody_client:client(), handler_opts())
|
||||
-callback handle_error(woody_t:func(), error_reason(),
|
||||
woody_client:context(), handler_opts())
|
||||
-> _.
|
||||
|
||||
%%
|
||||
%% API
|
||||
%%
|
||||
-define(log_rpc_result(EventHandler, Status, Meta),
|
||||
-define(log_rpc_result(EventHandler, Status, RpcId, Meta),
|
||||
woody_event_handler:handle_event(EventHandler, ?EV_SERVICE_HANDLER_RESULT,
|
||||
Meta#{status => Status})
|
||||
RpcId, Meta#{status => Status})
|
||||
).
|
||||
|
||||
-define(stage_read , protocol_read).
|
||||
@ -41,9 +41,8 @@
|
||||
-define(error_protocol_send , send_error).
|
||||
|
||||
-record(state, {
|
||||
rpc_id :: woody_t:rpc_id(),
|
||||
woody_client :: woody_client:client(),
|
||||
service :: module(),
|
||||
context :: woody_client:context(),
|
||||
service :: woody_t:service(),
|
||||
handler :: woody_t:handler(),
|
||||
handler_opts :: handler_opts(),
|
||||
protocol :: any(),
|
||||
@ -58,19 +57,18 @@
|
||||
-type event_handler() :: woody_t:handler().
|
||||
-type transport_handler() :: woody_t:handler().
|
||||
|
||||
-spec start(thrift_transport:t_transport(), woody_t:rpc_id(), woody_client:client(),
|
||||
-spec start(thrift_transport:t_transport(), woody_client:context(),
|
||||
thrift_handler(), event_handler(), transport_handler())
|
||||
->
|
||||
ok | noreply | {error, _Reason}.
|
||||
start(Transport, RpcId, WoodyClient, {Service, Handler, Opts},
|
||||
{ok | noreply | {error, _Reason}, cowboy_req:req()}.
|
||||
start(Transport, Context, {Service, Handler, Opts},
|
||||
EventHandler, TransportHandler)
|
||||
->
|
||||
{ok, Protocol} = thrift_binary_protocol:new(Transport,
|
||||
[{strict_read, true}, {strict_write, true}]
|
||||
),
|
||||
{Result, Protocol1} = process(#state{
|
||||
rpc_id = RpcId,
|
||||
woody_client = WoodyClient,
|
||||
context = Context,
|
||||
service = Service,
|
||||
handler = Handler,
|
||||
handler_opts = Opts,
|
||||
@ -79,8 +77,8 @@ start(Transport, RpcId, WoodyClient, {Service, Handler, Opts},
|
||||
event_handler = EventHandler,
|
||||
transport_handler = TransportHandler
|
||||
}),
|
||||
thrift_protocol:close_transport(Protocol1),
|
||||
Result.
|
||||
{_, Req} = thrift_protocol:close_transport(Protocol1),
|
||||
{Result, Req}.
|
||||
|
||||
|
||||
%%
|
||||
@ -123,7 +121,7 @@ get_function_name(Function) ->
|
||||
end.
|
||||
|
||||
get_params_type(Service, Function) ->
|
||||
try Service:function_info(Function, params_type)
|
||||
try get_function_info(Service, Function, params_type)
|
||||
catch
|
||||
error:badarg -> ?error_unknown_function
|
||||
end.
|
||||
@ -154,17 +152,18 @@ try_call_handler(Function, Args, State, SeqId) ->
|
||||
end.
|
||||
|
||||
call_handler(Function,Args, #state{
|
||||
rpc_id = RpcId,
|
||||
woody_client = WoodyClient,
|
||||
context = Context,
|
||||
handler = Handler,
|
||||
handler_opts = Opts,
|
||||
service = {_, ServiceName},
|
||||
event_handler = EventHandler})
|
||||
->
|
||||
woody_event_handler:handle_event(EventHandler, ?EV_INVOKE_SERVICE_HANDLER, RpcId#{
|
||||
function => Function, args => Args, options => Opts
|
||||
RpcId = woody_client:get_rpc_id(Context),
|
||||
woody_event_handler:handle_event(EventHandler, ?EV_INVOKE_SERVICE_HANDLER, RpcId, #{
|
||||
service => ServiceName, function => Function, args => Args, options => Opts
|
||||
}),
|
||||
Result = Handler:handle_function(Function, Args, RpcId, WoodyClient, Opts),
|
||||
?log_rpc_result(EventHandler, ok, RpcId#{result => Result}),
|
||||
Result = Handler:handle_function(Function, Args, Context, Opts),
|
||||
?log_rpc_result(EventHandler, ok, RpcId, #{result => Result}),
|
||||
Result.
|
||||
|
||||
handle_result(ok, State, Function, SeqId) ->
|
||||
@ -175,40 +174,41 @@ handle_result({error, Error}, State, Function, SeqId) ->
|
||||
handle_error(State, Function, Error, SeqId).
|
||||
|
||||
handle_success(State = #state{service = Service}, Function, Result, SeqId) ->
|
||||
ReplyType = Service:function_info(Function, reply_type),
|
||||
ReplyType = get_function_info(Service, Function, reply_type),
|
||||
StructName = atom_to_list(Function) ++ "_result",
|
||||
case Result of
|
||||
ok when ReplyType == {struct, []} ->
|
||||
ok when ReplyType == {struct, struct, []} ->
|
||||
send_reply(State, Function, ?tMessageType_REPLY,
|
||||
{ReplyType, {StructName}}, SeqId);
|
||||
ok when ReplyType == oneway_void ->
|
||||
{State, noreply};
|
||||
ReplyData ->
|
||||
Reply = {
|
||||
{struct, [{0, undefined, ReplyType, undefined, undefined}]},
|
||||
{struct, struct, [{0, undefined, ReplyType, undefined, undefined}]},
|
||||
{StructName, ReplyData}
|
||||
},
|
||||
send_reply(State, Function, ?tMessageType_REPLY, Reply, SeqId)
|
||||
end.
|
||||
|
||||
handle_function_catch(State = #state{
|
||||
rpc_id = RpcId,
|
||||
context = Context,
|
||||
service = Service,
|
||||
event_handler = EventHandler
|
||||
}, Function, Class, Reason, Stack, SeqId)
|
||||
->
|
||||
ReplyType = Service:function_info(Function, reply_type),
|
||||
RpcId = woody_client:get_rpc_id(Context),
|
||||
ReplyType = get_function_info(Service, Function, reply_type),
|
||||
case {Class, Reason} of
|
||||
_Error when ReplyType =:= oneway_void ->
|
||||
?log_rpc_result(EventHandler, error,
|
||||
RpcId#{class => Class, reason => Reason, ignore => true}),
|
||||
?log_rpc_result(EventHandler, error, RpcId,
|
||||
#{class => Class, reason => Reason, ignore => true}),
|
||||
{State, noreply};
|
||||
{throw, Exception} when is_tuple(Exception), size(Exception) > 0 ->
|
||||
?log_rpc_result(EventHandler, error,
|
||||
RpcId#{class => throw, reason => Exception, ignore => false}),
|
||||
?log_rpc_result(EventHandler, error, RpcId,
|
||||
#{class => throw, reason => Exception, ignore => false}),
|
||||
handle_exception(State, Function, Exception, SeqId);
|
||||
{error, Reason} ->
|
||||
?log_rpc_result(EventHandler, error, RpcId#{class => error,
|
||||
?log_rpc_result(EventHandler, error, RpcId, #{class => error,
|
||||
reason => Reason, stack => Stack, ignore => false}),
|
||||
Reason1 = if is_tuple(Reason) -> element(1, Reason); true -> Reason end,
|
||||
handle_error(State, Function, Reason1, SeqId)
|
||||
@ -217,7 +217,7 @@ handle_function_catch(State = #state{
|
||||
handle_exception(State = #state{service = Service, transport_handler = Trans},
|
||||
Function, Exception, SeqId)
|
||||
->
|
||||
{struct, XInfo} = ReplySpec = Service:function_info(Function, exceptions),
|
||||
{struct, _, XInfo} = ReplySpec = get_function_info(Service, Function, exceptions),
|
||||
{ExceptionList, FoundExcept} = lists:mapfoldl(
|
||||
fun(X, A) -> get_except(Exception, X, A) end, undefined, XInfo),
|
||||
ExceptionTuple = list_to_tuple([Function | ExceptionList]),
|
||||
@ -230,7 +230,7 @@ handle_exception(State = #state{service = Service, transport_handler = Trans},
|
||||
{ReplySpec, ExceptionTuple}, SeqId)
|
||||
end.
|
||||
|
||||
get_except(Exception, {_Fid, _, {struct, {Module, Type}}, _, _}, _) when
|
||||
get_except(Exception, {_Fid, _, {struct, exception, {Module, Type}}, _, _}, _) when
|
||||
element(1, Exception) =:= Type
|
||||
->
|
||||
{Exception, {Module, Type}};
|
||||
@ -238,7 +238,7 @@ get_except(_, _, TypesModule) ->
|
||||
{undefined, TypesModule}.
|
||||
|
||||
get_except_name(Module, Type) ->
|
||||
{struct, Fields} = Module:struct_info(Type),
|
||||
{struct, exception, Fields} = Module:struct_info(Type),
|
||||
case lists:keyfind(exception_name, 4, Fields) of
|
||||
false -> Type;
|
||||
Field -> element(5, Field)
|
||||
@ -282,27 +282,29 @@ prepare_response({State, {error, Reason}}, FunctionName) ->
|
||||
{handle_protocol_error(State, FunctionName, Reason), State#state.protocol}.
|
||||
|
||||
handle_protocol_error(State = #state{
|
||||
rpc_id = RpcId,
|
||||
context = Context,
|
||||
protocol_stage = Stage,
|
||||
transport_handler = Trans,
|
||||
event_handler = EventHandler}, Function, Reason)
|
||||
->
|
||||
RpcId = woody_client:get_rpc_id(Context),
|
||||
call_error_handler(State, Function, Reason),
|
||||
woody_event_handler:handle_event(EventHandler, ?EV_THRIFT_ERROR,
|
||||
RpcId#{stage => Stage, reason => Reason}),
|
||||
RpcId, #{stage => Stage, reason => Reason}),
|
||||
format_protocol_error(Reason, Trans).
|
||||
|
||||
call_error_handler(#state{
|
||||
rpc_id = RpcId,
|
||||
woody_client = WoodyClient,
|
||||
context = Context,
|
||||
handler = Handler,
|
||||
handler_opts = Opts,
|
||||
event_handler = EventHandler}, Function, Reason) ->
|
||||
event_handler = EventHandler}, Function, Reason)
|
||||
->
|
||||
RpcId = woody_client:get_rpc_id(Context),
|
||||
try
|
||||
Handler:handle_error(Function, Reason, RpcId, WoodyClient, Opts)
|
||||
Handler:handle_error(Function, Reason, Context, Opts)
|
||||
catch
|
||||
Class:Error ->
|
||||
woody_event_handler:handle_event(EventHandler, ?EV_INTERNAL_ERROR, RpcId#{
|
||||
woody_event_handler:handle_event(EventHandler, ?EV_INTERNAL_ERROR, RpcId, #{
|
||||
error => <<"service error handler failed">>,
|
||||
class => Class,
|
||||
reason => Error,
|
||||
@ -329,6 +331,9 @@ format_protocol_error(_Reason, Trans) ->
|
||||
mark_error_to_transport(Trans, transport, "bad request"),
|
||||
{error, bad_request}.
|
||||
|
||||
get_function_info({Module, Service}, Function, Info) ->
|
||||
Module:function_info(Service, Function, Info).
|
||||
|
||||
%% Unfortunately there is no proper way to provide additional info to
|
||||
%% the transport, where the actual send happens: the Protocol object
|
||||
%% representing thrift protocol and transport in this module is opaque.
|
||||
|
@ -42,8 +42,8 @@
|
||||
|
||||
-define(THRIFT_ERROR_KEY, {?MODULE, thrift_error}).
|
||||
|
||||
-define(log_event(EventHandler, Event, Status, Meta),
|
||||
woody_event_handler:handle_event(EventHandler, Event, Meta#{
|
||||
-define(log_event(EventHandler, Event, Status, RpcId, Meta),
|
||||
woody_event_handler:handle_event(EventHandler, Event, RpcId, Meta#{
|
||||
status => Status
|
||||
})
|
||||
).
|
||||
@ -67,7 +67,7 @@ child_spec(Id, #{
|
||||
port := Port,
|
||||
net_opts := NetOpts
|
||||
}) ->
|
||||
_ = check_callback(handle_event, 2, EventHandler),
|
||||
_ = check_callback(handle_event, 3, EventHandler),
|
||||
AcceptorsPool = genlib_app:env(woody, acceptors_pool_size,
|
||||
?DEFAULT_ACCEPTORS_POOLSIZE
|
||||
),
|
||||
@ -83,7 +83,7 @@ check_callback(Callback, Module) ->
|
||||
proplists:get_value(Callback, Module:module_info(exports)).
|
||||
|
||||
validate_handler(Handler) when is_atom(Handler) ->
|
||||
[check_callback(F, 5, Handler) || F <- [handle_function, handle_error]],
|
||||
[check_callback(F, 4, Handler) || F <- [handle_function, handle_error]],
|
||||
Handler.
|
||||
|
||||
get_socket_transport(Ip, Port, Options) ->
|
||||
@ -106,11 +106,36 @@ get_cowboy_config(Handlers, EventHandler) ->
|
||||
} || {PathMatch, {Service, Handler, Opts}} <- Handlers
|
||||
],
|
||||
{ok, _} = application:ensure_all_started(cowboy),
|
||||
[{env, [{dispatch, cowboy_router:compile([{'_', Paths}])}]}].
|
||||
Debug = enable_debug(genlib_app:env(woody, enable_debug), EventHandler),
|
||||
[{env, [{dispatch, cowboy_router:compile([{'_', Paths}])}]}] ++ Debug.
|
||||
|
||||
config() ->
|
||||
[{max_body_length, ?MAX_BODY_LENGTH}].
|
||||
|
||||
enable_debug(true, EventHandler) ->
|
||||
[
|
||||
{onrequest, fun(Req) ->
|
||||
{Url, Req1} = cowboy_req:url(Req),
|
||||
{Headers, Req2} = cowboy_req:headers(Req1),
|
||||
woody_event_handler:handle_event(EventHandler, ?EV_DEBUG, undefined, #{
|
||||
event => transport_onrequest,
|
||||
url => Url,
|
||||
headers => Headers
|
||||
}),
|
||||
Req2 end
|
||||
},
|
||||
{onresponse, fun(Code, Headers, _Body, Req) ->
|
||||
woody_event_handler:handle_event(EventHandler, ?EV_DEBUG, undefined, #{
|
||||
event => transport_onresponse,
|
||||
code => Code,
|
||||
headers => Headers
|
||||
}),
|
||||
Req end
|
||||
}
|
||||
];
|
||||
enable_debug(_, _) ->
|
||||
[].
|
||||
|
||||
|
||||
%%
|
||||
%% thrift_transport callbacks
|
||||
@ -155,7 +180,7 @@ flush(State = #http_req{
|
||||
}) ->
|
||||
{Code, Req1} = add_x_error_header(Req),
|
||||
?log_event(EventHandler, ?EV_SERVER_SEND, reply_status(Code),
|
||||
RpcId#{code => Code}),
|
||||
RpcId, #{code => Code}),
|
||||
{ok, Req2} = cowboy_req:reply(Code, [{<<"content-type">>, ?CONTENT_TYPE_THRIFT}],
|
||||
Body, Req1),
|
||||
{State#http_req{req = Req2, resp_body = <<>>, replied = true}, ok}.
|
||||
@ -163,9 +188,9 @@ flush(State = #http_req{
|
||||
reply_status(200) -> ok;
|
||||
reply_status(_) -> error.
|
||||
|
||||
-spec close(state()) -> {state(), ok}.
|
||||
close(_State) ->
|
||||
{#http_req{}, ok}.
|
||||
-spec close(state()) -> {state(), cowboy_req:req()}.
|
||||
close(#http_req{req = Req}) ->
|
||||
{#http_req{}, Req}.
|
||||
|
||||
-spec mark_thrift_error(logic | transport, _Error) -> _.
|
||||
mark_thrift_error(Type, Error) ->
|
||||
@ -185,7 +210,7 @@ init({_Transport, http}, Req, Opts = [EventHandler | _]) ->
|
||||
check_headers(set_resp_headers(RpcId, Req2), [Url, RpcId | Opts]);
|
||||
{error, ErrorMeta, Req2} ->
|
||||
?log_event(EventHandler, ?EV_SERVER_RECEIVE, error,
|
||||
ErrorMeta#{url => Url, reason => bad_rpc_id}),
|
||||
ErrorMeta, #{url => Url, reason => bad_rpc_id}),
|
||||
reply_error_early(400, Req2)
|
||||
end.
|
||||
|
||||
@ -194,19 +219,19 @@ init({_Transport, http}, Req, Opts = [EventHandler | _]) ->
|
||||
handle(Req, [Url, RpcId, EventHandler, ServerOpts, ThriftHandler]) ->
|
||||
case get_body(Req, ServerOpts) of
|
||||
{ok, Body, Req1} when byte_size(Body) > 0 ->
|
||||
?log_event(EventHandler, ?EV_SERVER_RECEIVE, ok, RpcId#{url => Url}),
|
||||
?log_event(EventHandler, ?EV_SERVER_RECEIVE, ok, RpcId, #{url => Url}),
|
||||
do_handle(RpcId, Body, ThriftHandler, EventHandler, Req1);
|
||||
{ok, <<>>, Req1} ->
|
||||
?log_event(EventHandler, ?EV_SERVER_RECEIVE, error,
|
||||
RpcId#{url => Url, reason => body_empty}),
|
||||
RpcId, #{url => Url, reason => body_empty}),
|
||||
reply_error(400, Req1);
|
||||
{error, body_too_large, Req1} ->
|
||||
?log_event(EventHandler, ?EV_SERVER_RECEIVE, error,
|
||||
RpcId#{url => Url, reason => body_too_large}),
|
||||
RpcId, #{url => Url, reason => body_too_large}),
|
||||
reply_error(413, Req1);
|
||||
{error, Reason, Req1} ->
|
||||
?log_event(EventHandler, ?EV_SERVER_RECEIVE, error,
|
||||
RpcId#{url => Url, reason => {body_read_error, Reason}}),
|
||||
RpcId, #{url => Url, reason => {body_read_error, Reason}}),
|
||||
reply_error(400, Req1)
|
||||
end.
|
||||
|
||||
@ -216,7 +241,7 @@ terminate({normal, _}, _Req, _Status) ->
|
||||
ok;
|
||||
terminate(Reason, _Req, [_, RpcId, EventHandler | _]) ->
|
||||
erlang:erase(?THRIFT_ERROR_KEY),
|
||||
woody_event_handler:handle_event(EventHandler, ?EV_INTERNAL_ERROR, RpcId#{
|
||||
woody_event_handler:handle_event(EventHandler, ?EV_INTERNAL_ERROR, RpcId, #{
|
||||
error => <<"http handler terminated abnormally">>, reason => Reason
|
||||
}),
|
||||
ok.
|
||||
@ -247,14 +272,14 @@ check_method({<<"POST">>, Req}, Opts) ->
|
||||
check_content_type(cowboy_req:header(<<"content-type">>, Req), Opts);
|
||||
check_method({Method, Req}, [Url, RpcId, EventHandler | _]) ->
|
||||
?log_event(EventHandler, ?EV_SERVER_RECEIVE, error,
|
||||
RpcId#{url => Url, reason => {wrong_method, Method}}),
|
||||
RpcId, #{url => Url, reason => {wrong_method, Method}}),
|
||||
reply_error_early(405, cowboy_req:set_resp_header(<<"allow">>, <<"POST">>, Req)).
|
||||
|
||||
check_content_type({?CONTENT_TYPE_THRIFT, Req}, Opts) ->
|
||||
check_accept(cowboy_req:header(<<"accept">>, Req), Opts);
|
||||
check_content_type({BadType, Req}, [Url, RpcId, EventHandler | _]) ->
|
||||
?log_event(EventHandler, ?EV_SERVER_RECEIVE, error,
|
||||
RpcId#{url => Url, reason => {wrong_content_type, BadType}}),
|
||||
RpcId, #{url => Url, reason => {wrong_content_type, BadType}}),
|
||||
reply_error_early(415, Req).
|
||||
|
||||
check_accept({Accept, Req}, Opts) when
|
||||
@ -264,7 +289,7 @@ check_accept({Accept, Req}, Opts) when
|
||||
{ok, Req, Opts};
|
||||
check_accept({BadType, Req1}, [Url, RpcId, EventHandler | _]) ->
|
||||
?log_event(EventHandler, ?EV_SERVER_RECEIVE, error,
|
||||
RpcId#{url => Url, reason => {wrong_client_accept, BadType}}),
|
||||
RpcId, #{url => Url, reason => {wrong_client_accept, BadType}}),
|
||||
reply_error_early(406, Req1).
|
||||
|
||||
get_body(Req, ServerOpts) ->
|
||||
@ -279,17 +304,17 @@ get_body(Req, ServerOpts) ->
|
||||
end.
|
||||
|
||||
do_handle(RpcId, Body, ThriftHander, EventHandler, Req) ->
|
||||
WoodyClient = woody_client:make_child_client(RpcId, EventHandler),
|
||||
Context = woody_client:make_child_context(RpcId, EventHandler),
|
||||
Transport = make_transport(Req, RpcId, Body, EventHandler),
|
||||
case woody_server_thrift_handler:start(Transport, RpcId, WoodyClient, ThriftHander,
|
||||
case woody_server_thrift_handler:start(Transport, Context, ThriftHander,
|
||||
EventHandler, ?MODULE)
|
||||
of
|
||||
ok ->
|
||||
{ok, Req, undefined};
|
||||
{error, Reason} ->
|
||||
handle_error(Reason, RpcId, EventHandler, Req);
|
||||
noreply ->
|
||||
{ok, Req, undefined}
|
||||
{ok, Req1} ->
|
||||
{ok, Req1, undefined};
|
||||
{{error, Reason}, Req1} ->
|
||||
handle_error(Reason, RpcId, EventHandler, Req1);
|
||||
{noreply, Req1} ->
|
||||
{ok, Req1, undefined}
|
||||
end.
|
||||
|
||||
handle_error(bad_request, RpcId, EventHandler, Req) ->
|
||||
@ -298,7 +323,7 @@ handle_error(_Error, RpcId, EventHandler, Req) ->
|
||||
reply_error(500, RpcId, EventHandler, Req).
|
||||
|
||||
reply_error(Code, RpcId, EventHandler, Req) ->
|
||||
?log_event(EventHandler, ?EV_SERVER_SEND, error, RpcId#{code => Code}),
|
||||
?log_event(EventHandler, ?EV_SERVER_SEND, error, RpcId, #{code => Code}),
|
||||
{_, Req1} = add_x_error_header(Req),
|
||||
reply_error(Code, Req1).
|
||||
|
||||
|
@ -18,7 +18,8 @@
|
||||
-type url() :: binary().
|
||||
-type role() :: client | server.
|
||||
|
||||
-type service() :: handler().
|
||||
-type service_name() :: atom().
|
||||
-type service() :: {handler(), service_name()}.
|
||||
-type func() :: atom().
|
||||
|
||||
%% copy-paste from OTP supervsor
|
||||
@ -28,8 +29,8 @@
|
||||
| {'via', Module :: module(), Name :: any()}
|
||||
| pid().
|
||||
|
||||
-export_type([req_id/0, rpc_id/0, service/0, func/0, options/0, handler/0,
|
||||
url/0, role/0, sup_ref/0]).
|
||||
-export_type([req_id/0, rpc_id/0, service/0, service_name/0, func/0, options/0,
|
||||
handler/0, url/0, role/0, sup_ref/0]).
|
||||
|
||||
|
||||
%%
|
||||
|
@ -1,9 +0,0 @@
|
||||
%%
|
||||
%% Autogenerated by Thrift Compiler (1.0.0-dev)
|
||||
%%
|
||||
%% DO NOT EDIT UNLESS YOU ARE SURE THAT YOU KNOW WHAT YOU ARE DOING
|
||||
%%
|
||||
|
||||
|
||||
-include("woody_test_types.hrl").
|
||||
|
@ -1,48 +0,0 @@
|
||||
%%
|
||||
%% Autogenerated by Thrift Compiler (1.0.0-dev)
|
||||
%%
|
||||
%% DO NOT EDIT UNLESS YOU ARE SURE THAT YOU KNOW WHAT YOU ARE DOING
|
||||
%%
|
||||
|
||||
-module(woody_test_powerups_service).
|
||||
-behaviour(thrift_service).
|
||||
|
||||
|
||||
-include("woody_test_powerups_service.hrl").
|
||||
|
||||
-export([function_info/2]).
|
||||
-export([struct_info/1]).
|
||||
-export([function_names/0]).
|
||||
|
||||
function_names() ->
|
||||
[
|
||||
'get_powerup',
|
||||
'like_powerup'
|
||||
].
|
||||
|
||||
struct_info(_) -> erlang:error(badarg).
|
||||
|
||||
% get_powerup(This, Name, Data)
|
||||
function_info('get_powerup', params_type) ->
|
||||
{struct, [
|
||||
{1, undefined, string, 'name', undefined},
|
||||
{2, undefined, string, 'data', undefined}
|
||||
]};
|
||||
function_info('get_powerup', reply_type) ->
|
||||
{struct, {woody_test_types, 'powerup'}};
|
||||
function_info('get_powerup', exceptions) ->
|
||||
{struct, [
|
||||
{1, undefined, {struct, {woody_test_types, 'powerup_failure'}}, 'error', #'powerup_failure'{}}
|
||||
]};
|
||||
% like_powerup(This, Name, Data)
|
||||
function_info('like_powerup', params_type) ->
|
||||
{struct, [
|
||||
{1, undefined, string, 'name', undefined},
|
||||
{2, undefined, string, 'data', undefined}
|
||||
]};
|
||||
function_info('like_powerup', reply_type) ->
|
||||
oneway_void;
|
||||
function_info('like_powerup', exceptions) ->
|
||||
{struct, []};
|
||||
function_info(_Func, _Info) -> erlang:error(badarg).
|
||||
|
@ -1,5 +0,0 @@
|
||||
-ifndef(_woody_test_powerups_service_included).
|
||||
-define(_woody_test_powerups_service_included, yeah).
|
||||
-include("woody_test_types.hrl").
|
||||
|
||||
-endif.
|
271
test/woody_test_thrift.erl
Normal file
271
test/woody_test_thrift.erl
Normal file
@ -0,0 +1,271 @@
|
||||
%%
|
||||
%% Autogenerated by Thrift Compiler (1.0.0-dev)
|
||||
%%
|
||||
%% DO NOT EDIT UNLESS YOU ARE SURE THAT YOU KNOW WHAT YOU ARE DOING
|
||||
%%
|
||||
|
||||
-module(woody_test_thrift).
|
||||
|
||||
-include("woody_test_thrift.hrl").
|
||||
|
||||
-export([enums/0]).
|
||||
-export([typedefs/0]).
|
||||
-export([structs/0]).
|
||||
-export([services/0]).
|
||||
-export([typedef_info/1]).
|
||||
-export([enum_info/1]).
|
||||
-export([struct_info/1]).
|
||||
-export([functions/1]).
|
||||
-export([function_info/3]).
|
||||
|
||||
-export_type([typedef_name/0]).
|
||||
-export_type([enum_name/0]).
|
||||
-export_type([struct_name/0]).
|
||||
-export_type([exception_name/0]).
|
||||
-export_type([service_name/0]).
|
||||
-export_type([function_name/0]).
|
||||
|
||||
|
||||
-export_type([
|
||||
'direction'/0
|
||||
]).
|
||||
-export_type([
|
||||
'weapon'/0,
|
||||
'powerup'/0
|
||||
]).
|
||||
-export_type([
|
||||
'weapon_failure'/0,
|
||||
'powerup_failure'/0
|
||||
]).
|
||||
|
||||
%%
|
||||
%% typedefs
|
||||
%%
|
||||
-type typedef_name() :: none().
|
||||
|
||||
|
||||
%%
|
||||
%% enums
|
||||
%%
|
||||
-type enum_name() ::
|
||||
'direction'.
|
||||
|
||||
%% enum 'direction'
|
||||
-type 'direction'() ::
|
||||
next |
|
||||
prev.
|
||||
|
||||
%%
|
||||
%% structs, unions and exceptions
|
||||
%%
|
||||
-type struct_name() ::
|
||||
'weapon' |
|
||||
'powerup'.
|
||||
|
||||
-type exception_name() ::
|
||||
'weapon_failure' |
|
||||
'powerup_failure'.
|
||||
|
||||
%% struct 'weapon'
|
||||
-type 'weapon'() :: #'weapon'{}.
|
||||
|
||||
%% struct 'powerup'
|
||||
-type 'powerup'() :: #'powerup'{}.
|
||||
|
||||
%% exception 'weapon_failure'
|
||||
-type 'weapon_failure'() :: #'weapon_failure'{}.
|
||||
|
||||
%% exception 'powerup_failure'
|
||||
-type 'powerup_failure'() :: #'powerup_failure'{}.
|
||||
|
||||
%%
|
||||
%% services and functions
|
||||
%%
|
||||
-type service_name() ::
|
||||
'weapons' |
|
||||
'powerups'.
|
||||
|
||||
-type function_name() ::
|
||||
'weapons_service_functions'() |
|
||||
'powerups_service_functions'().
|
||||
|
||||
-type 'weapons_service_functions'() ::
|
||||
'switch_weapon' |
|
||||
'get_weapon'.
|
||||
|
||||
-export_type(['weapons_service_functions'/0]).
|
||||
|
||||
-type 'powerups_service_functions'() ::
|
||||
'get_powerup' |
|
||||
'like_powerup'.
|
||||
|
||||
-export_type(['powerups_service_functions'/0]).
|
||||
|
||||
|
||||
-type struct_flavour() :: struct | exception | union.
|
||||
-type field_num() :: pos_integer().
|
||||
-type field_name() :: atom().
|
||||
-type field_req() :: required | optional | undefined.
|
||||
|
||||
-type type_ref() :: {module(), atom()}.
|
||||
-type field_type() ::
|
||||
bool | byte | i16 | i32 | i64 | string | double |
|
||||
{enum, type_ref()} |
|
||||
{struct, struct_flavour(), type_ref()} |
|
||||
{list, field_type()} |
|
||||
{set, field_type()} |
|
||||
{map, field_type(), field_type()}.
|
||||
|
||||
-type struct_field_info() ::
|
||||
{field_num(), field_req(), field_type(), field_name(), any()}.
|
||||
-type struct_info() ::
|
||||
{struct, struct_flavour(), [struct_field_info()]}.
|
||||
|
||||
-type enum_choice() ::
|
||||
'direction'().
|
||||
|
||||
-type enum_field_info() ::
|
||||
{enum_choice(), integer()}.
|
||||
-type enum_info() ::
|
||||
{enum, [enum_field_info()]}.
|
||||
|
||||
-spec typedefs() -> [typedef_name()].
|
||||
|
||||
typedefs() ->
|
||||
[].
|
||||
|
||||
-spec enums() -> [enum_name()].
|
||||
|
||||
enums() ->
|
||||
[
|
||||
'direction'
|
||||
].
|
||||
|
||||
-spec structs() -> [struct_name()].
|
||||
|
||||
structs() ->
|
||||
[
|
||||
'weapon',
|
||||
'powerup'
|
||||
].
|
||||
|
||||
-spec services() -> [service_name()].
|
||||
|
||||
services() ->
|
||||
[
|
||||
'weapons',
|
||||
'powerups'
|
||||
].
|
||||
|
||||
-spec typedef_info(typedef_name()) -> field_type() | no_return().
|
||||
|
||||
typedef_info(_) -> erlang:error(badarg).
|
||||
|
||||
-spec enum_info(enum_name()) -> enum_info() | no_return().
|
||||
|
||||
enum_info('direction') ->
|
||||
{enum, [
|
||||
{next, 1},
|
||||
{prev, 0}
|
||||
]};
|
||||
|
||||
enum_info(_) -> erlang:error(badarg).
|
||||
|
||||
-spec struct_info(struct_name()) -> struct_info() | no_return().
|
||||
|
||||
struct_info('weapon') ->
|
||||
{struct, struct, [
|
||||
{1, required, string, 'name', undefined},
|
||||
{2, required, i16, 'slot_pos', undefined},
|
||||
{3, optional, i16, 'ammo', undefined}
|
||||
]};
|
||||
|
||||
struct_info('powerup') ->
|
||||
{struct, struct, [
|
||||
{1, required, string, 'name', undefined},
|
||||
{2, optional, i16, 'level', undefined},
|
||||
{3, optional, i16, 'time_left', undefined}
|
||||
]};
|
||||
|
||||
struct_info('weapon_failure') ->
|
||||
{struct, exception, [
|
||||
{1, required, string, 'exception_name', <<"weapon failure">>},
|
||||
{2, required, string, 'code', undefined},
|
||||
{3, optional, string, 'reason', undefined}
|
||||
]};
|
||||
|
||||
struct_info('powerup_failure') ->
|
||||
{struct, exception, [
|
||||
{1, required, string, 'exception_name', <<"powerup failure">>},
|
||||
{2, required, string, 'code', undefined},
|
||||
{3, optional, string, 'reason', undefined}
|
||||
]};
|
||||
|
||||
struct_info(_) -> erlang:error(badarg).
|
||||
|
||||
-spec functions(service_name()) -> [function_name()] | no_return().
|
||||
|
||||
functions('weapons') ->
|
||||
[
|
||||
'switch_weapon',
|
||||
'get_weapon'
|
||||
];
|
||||
|
||||
functions('powerups') ->
|
||||
[
|
||||
'get_powerup',
|
||||
'like_powerup'
|
||||
];
|
||||
|
||||
functions(_) -> error(badarg).
|
||||
|
||||
-spec function_info(service_name(), function_name(), params_type | reply_type | exceptions) ->
|
||||
struct_info() | no_return().
|
||||
|
||||
function_info('weapons', 'switch_weapon', params_type) ->
|
||||
{struct, struct, [
|
||||
{1, undefined, {struct, struct, {woody_test_thrift, 'weapon'}}, 'current_weapon', undefined},
|
||||
{2, undefined, {enum, {woody_test_thrift, 'direction'}}, 'direction', undefined},
|
||||
{3, undefined, i16, 'shift', undefined},
|
||||
{4, undefined, string, 'data', undefined}
|
||||
]};
|
||||
function_info('weapons', 'switch_weapon', reply_type) ->
|
||||
{struct, struct, {woody_test_thrift, 'weapon'}};
|
||||
function_info('weapons', 'switch_weapon', exceptions) ->
|
||||
{struct, struct, [
|
||||
{1, undefined, {struct, exception, {woody_test_thrift, 'weapon_failure'}}, 'error', undefined}
|
||||
]};
|
||||
function_info('weapons', 'get_weapon', params_type) ->
|
||||
{struct, struct, [
|
||||
{1, undefined, string, 'name', undefined},
|
||||
{2, undefined, string, 'data', undefined}
|
||||
]};
|
||||
function_info('weapons', 'get_weapon', reply_type) ->
|
||||
{struct, struct, {woody_test_thrift, 'weapon'}};
|
||||
function_info('weapons', 'get_weapon', exceptions) ->
|
||||
{struct, struct, [
|
||||
{1, undefined, {struct, exception, {woody_test_thrift, 'weapon_failure'}}, 'error', undefined}
|
||||
]};
|
||||
|
||||
function_info('powerups', 'get_powerup', params_type) ->
|
||||
{struct, struct, [
|
||||
{1, undefined, string, 'name', undefined},
|
||||
{2, undefined, string, 'data', undefined}
|
||||
]};
|
||||
function_info('powerups', 'get_powerup', reply_type) ->
|
||||
{struct, struct, {woody_test_thrift, 'powerup'}};
|
||||
function_info('powerups', 'get_powerup', exceptions) ->
|
||||
{struct, struct, [
|
||||
{1, undefined, {struct, exception, {woody_test_thrift, 'powerup_failure'}}, 'error', undefined}
|
||||
]};
|
||||
function_info('powerups', 'like_powerup', params_type) ->
|
||||
{struct, struct, [
|
||||
{1, undefined, string, 'name', undefined},
|
||||
{2, undefined, string, 'data', undefined}
|
||||
]};
|
||||
function_info('powerups', 'like_powerup', reply_type) ->
|
||||
oneway_void;
|
||||
function_info('powerups', 'like_powerup', exceptions) ->
|
||||
{struct, struct, []};
|
||||
|
||||
function_info(_Service, _Function, _InfoType) -> erlang:error(badarg).
|
@ -1,48 +1,34 @@
|
||||
-ifndef(_woody_test_types_included).
|
||||
-define(_woody_test_types_included, yeah).
|
||||
-ifndef(woody_test_thrift_included__).
|
||||
-define(woody_test_thrift_included__, yeah).
|
||||
|
||||
|
||||
-define(WOODY_TEST_DIRECTION_NEXT, 1).
|
||||
-define(WOODY_TEST_DIRECTION_PREV, 0).
|
||||
|
||||
%% struct 'weapon'
|
||||
|
||||
-record('weapon', {
|
||||
'name' :: binary(),
|
||||
'slot_pos' :: integer(),
|
||||
'ammo' :: integer()
|
||||
}).
|
||||
|
||||
-type 'weapon'() :: #'weapon'{}.
|
||||
|
||||
%% struct 'powerup'
|
||||
|
||||
-record('powerup', {
|
||||
'name' :: binary(),
|
||||
'level' :: integer(),
|
||||
'time_left' :: integer()
|
||||
}).
|
||||
|
||||
-type 'powerup'() :: #'powerup'{}.
|
||||
|
||||
%% struct 'weapon_failure'
|
||||
|
||||
%% exception 'weapon_failure'
|
||||
-record('weapon_failure', {
|
||||
'exception_name' = <<"weapon failure">> :: binary(),
|
||||
'code' :: binary(),
|
||||
'reason' :: binary()
|
||||
}).
|
||||
|
||||
-type 'weapon_failure'() :: #'weapon_failure'{}.
|
||||
|
||||
%% struct 'powerup_failure'
|
||||
|
||||
%% exception 'powerup_failure'
|
||||
-record('powerup_failure', {
|
||||
'exception_name' = <<"powerup failure">> :: binary(),
|
||||
'code' :: binary(),
|
||||
'reason' :: binary()
|
||||
}).
|
||||
|
||||
-type 'powerup_failure'() :: #'powerup_failure'{}.
|
||||
|
||||
-endif.
|
@ -1,90 +0,0 @@
|
||||
%%
|
||||
%% Autogenerated by Thrift Compiler (1.0.0-dev)
|
||||
%%
|
||||
%% DO NOT EDIT UNLESS YOU ARE SURE THAT YOU KNOW WHAT YOU ARE DOING
|
||||
%%
|
||||
|
||||
-module(woody_test_types).
|
||||
|
||||
|
||||
-include("woody_test_types.hrl").
|
||||
|
||||
-type type_ref() :: {module(), atom()}.
|
||||
-type field_num() :: pos_integer().
|
||||
-type field_name() :: atom().
|
||||
-type field_req() :: required | optional.
|
||||
-type field_type() ::
|
||||
bool | byte | i16 | i32 | i64 | string | double |
|
||||
{enum, type_ref()} |
|
||||
{struct, type_ref()} |
|
||||
{list, field_type()} |
|
||||
{set, field_type()} |
|
||||
{map, field_type(), field_type()}.
|
||||
|
||||
-type struct_field_info() :: {field_num(), field_req(), field_type(), field_name(), any()}.
|
||||
-type enum_field_info() :: {atom(), integer()}.
|
||||
|
||||
-export([enum_names/0]).
|
||||
-export([struct_names/0]).
|
||||
-export([enum_info/1]).
|
||||
-export([struct_info/1]).
|
||||
|
||||
-spec struct_names() -> [atom()].
|
||||
|
||||
struct_names() ->
|
||||
[
|
||||
'weapon',
|
||||
'powerup',
|
||||
'weapon_failure',
|
||||
'powerup_failure'
|
||||
].
|
||||
|
||||
-spec enum_names() -> [atom()].
|
||||
|
||||
enum_names() ->
|
||||
[
|
||||
'direction'
|
||||
].
|
||||
|
||||
-spec struct_info(atom()) -> {struct, [struct_field_info()]}.
|
||||
|
||||
struct_info('weapon') ->
|
||||
{struct, [
|
||||
{1, required, string, 'name', undefined},
|
||||
{2, required, i16, 'slot_pos', undefined},
|
||||
{3, optional, i16, 'ammo', undefined}
|
||||
]};
|
||||
|
||||
struct_info('powerup') ->
|
||||
{struct, [
|
||||
{1, required, string, 'name', undefined},
|
||||
{2, optional, i16, 'level', undefined},
|
||||
{3, optional, i16, 'time_left', undefined}
|
||||
]};
|
||||
|
||||
struct_info('weapon_failure') ->
|
||||
{struct, [
|
||||
{1, required, string, 'exception_name', <<"weapon failure">>},
|
||||
{2, required, string, 'code', undefined},
|
||||
{3, optional, string, 'reason', undefined}
|
||||
]};
|
||||
|
||||
struct_info('powerup_failure') ->
|
||||
{struct, [
|
||||
{1, required, string, 'exception_name', <<"powerup failure">>},
|
||||
{2, required, string, 'code', undefined},
|
||||
{3, optional, string, 'reason', undefined}
|
||||
]};
|
||||
|
||||
struct_info(_) -> erlang:error(badarg).
|
||||
|
||||
-spec enum_info(atom()) -> {enum, [enum_field_info()]}.
|
||||
|
||||
enum_info('direction') ->
|
||||
{enum, [
|
||||
{next, 1},
|
||||
{prev, 0}
|
||||
]};
|
||||
|
||||
enum_info(_) -> erlang:error(badarg).
|
||||
|
@ -1,52 +0,0 @@
|
||||
%%
|
||||
%% Autogenerated by Thrift Compiler (1.0.0-dev)
|
||||
%%
|
||||
%% DO NOT EDIT UNLESS YOU ARE SURE THAT YOU KNOW WHAT YOU ARE DOING
|
||||
%%
|
||||
|
||||
-module(woody_test_weapons_service).
|
||||
-behaviour(thrift_service).
|
||||
|
||||
|
||||
-include("woody_test_weapons_service.hrl").
|
||||
|
||||
-export([function_info/2]).
|
||||
-export([struct_info/1]).
|
||||
-export([function_names/0]).
|
||||
|
||||
function_names() ->
|
||||
[
|
||||
'switch_weapon',
|
||||
'get_weapon'
|
||||
].
|
||||
|
||||
struct_info(_) -> erlang:error(badarg).
|
||||
|
||||
% switch_weapon(This, Current_weapon, Direction, Shift, Data)
|
||||
function_info('switch_weapon', params_type) ->
|
||||
{struct, [
|
||||
{1, undefined, {struct, {woody_test_types, 'weapon'}}, 'current_weapon', #'weapon'{}},
|
||||
{2, undefined, {enum, {woody_test_types, 'direction'}}, 'direction', undefined},
|
||||
{3, undefined, i16, 'shift', undefined},
|
||||
{4, undefined, string, 'data', undefined}
|
||||
]};
|
||||
function_info('switch_weapon', reply_type) ->
|
||||
{struct, {woody_test_types, 'weapon'}};
|
||||
function_info('switch_weapon', exceptions) ->
|
||||
{struct, [
|
||||
{1, undefined, {struct, {woody_test_types, 'weapon_failure'}}, 'error', #'weapon_failure'{}}
|
||||
]};
|
||||
% get_weapon(This, Name, Data)
|
||||
function_info('get_weapon', params_type) ->
|
||||
{struct, [
|
||||
{1, undefined, string, 'name', undefined},
|
||||
{2, undefined, string, 'data', undefined}
|
||||
]};
|
||||
function_info('get_weapon', reply_type) ->
|
||||
{struct, {woody_test_types, 'weapon'}};
|
||||
function_info('get_weapon', exceptions) ->
|
||||
{struct, [
|
||||
{1, undefined, {struct, {woody_test_types, 'weapon_failure'}}, 'error', #'weapon_failure'{}}
|
||||
]};
|
||||
function_info(_Func, _Info) -> erlang:error(badarg).
|
||||
|
@ -1,5 +0,0 @@
|
||||
-ifndef(_woody_test_weapons_service_included).
|
||||
-define(_woody_test_weapons_service_included, yeah).
|
||||
-include("woody_test_types.hrl").
|
||||
|
||||
-endif.
|
@ -2,7 +2,7 @@
|
||||
|
||||
-include_lib("common_test/include/ct.hrl").
|
||||
|
||||
-include("woody_test_types.hrl").
|
||||
-include("woody_test_thrift.hrl").
|
||||
-include("src/woody_defs.hrl").
|
||||
|
||||
-compile(export_all).
|
||||
@ -15,15 +15,18 @@
|
||||
-export([init/1]).
|
||||
|
||||
%% woody_server_thrift_handler callbacks
|
||||
-export([handle_function/5]).
|
||||
-export([handle_error/5]).
|
||||
-export([handle_function/4]).
|
||||
-export([handle_error/4]).
|
||||
|
||||
%% woody_event_handler callbacks
|
||||
-export([handle_event/2]).
|
||||
-export([handle_event/3]).
|
||||
|
||||
%% internal API
|
||||
-export([call/4, call_safe/4]).
|
||||
|
||||
|
||||
-define(THRIFT_DEFS, woody_test_thrift).
|
||||
|
||||
%% Weapons service
|
||||
-define(SLOTS, #{
|
||||
1 => <<"Impact Hammer">>,
|
||||
@ -164,12 +167,12 @@ start_woody_server(Id, Sup, Services) ->
|
||||
get_handler(powerups) ->
|
||||
{
|
||||
?PATH_POWERUPS,
|
||||
{woody_test_powerups_service, ?MODULE, []}
|
||||
{{?THRIFT_DEFS, powerups}, ?MODULE, []}
|
||||
};
|
||||
get_handler(weapons) ->
|
||||
{
|
||||
?PATH_WEAPONS,
|
||||
{woody_test_weapons_service, ?MODULE, []}
|
||||
{{?THRIFT_DEFS, weapons}, ?MODULE, []}
|
||||
}.
|
||||
|
||||
end_per_test_case(_,C) ->
|
||||
@ -212,18 +215,18 @@ call_handler_throw_test(_) ->
|
||||
call_safe_handler_throw_unexpected_test(_) ->
|
||||
Id = <<"call_safe_handler_throw_unexpected">>,
|
||||
Current = genlib_map:get(<<"Rocket Launcher">>, ?WEAPONS),
|
||||
Client = get_client(Id),
|
||||
Expect = {{error, ?error_transport(server_error)}, Client},
|
||||
Expect = call_safe(Client, weapons, switch_weapon,
|
||||
Context = make_context(Id),
|
||||
Expect = {{error, ?error_transport(server_error)}, Context},
|
||||
Expect = call_safe(Context, weapons, switch_weapon,
|
||||
[Current, next, 1, self_to_bin()]),
|
||||
{ok, _} = receive_msg({Id, Current}).
|
||||
|
||||
call_handler_throw_unexpected_test(_) ->
|
||||
Id = <<"call_handler_throw_unexpected">>,
|
||||
Current = genlib_map:get(<<"Rocket Launcher">>, ?WEAPONS),
|
||||
Client = get_client(Id),
|
||||
Expect = {?error_transport(server_error), Client},
|
||||
try call(Client, weapons, switch_weapon, [Current, next, 1, self_to_bin()])
|
||||
Context = make_context(Id),
|
||||
Expect = {?error_transport(server_error), Context},
|
||||
try call(Context, weapons, switch_weapon, [Current, next, 1, self_to_bin()])
|
||||
catch
|
||||
error:Expect -> ok
|
||||
end,
|
||||
@ -242,25 +245,25 @@ call_handler_error_test(_) ->
|
||||
call_safe_client_transport_error_test(_) ->
|
||||
Gun = 'Wrong Type of Mega Destroyer',
|
||||
Id = <<"call_safe_client_transport_error">>,
|
||||
Client = get_client(Id),
|
||||
{{error, ?error_protocol(_)}, Client} = call_safe(Client,
|
||||
Context = make_context(Id),
|
||||
{{error, ?error_protocol(_)}, Context} = call_safe(Context,
|
||||
weapons, get_weapon, [Gun, self_to_bin()]).
|
||||
|
||||
call_client_transport_error_test(_) ->
|
||||
Gun = 'Wrong Type of Mega Destroyer',
|
||||
Id = <<"call_client_transport_error">>,
|
||||
Client = get_client(Id),
|
||||
try call(Client, weapons, get_weapon, [Gun, self_to_bin()])
|
||||
Context = make_context(Id),
|
||||
try call(Context, weapons, get_weapon, [Gun, self_to_bin()])
|
||||
catch
|
||||
error:{?error_protocol(_), Client} -> ok
|
||||
error:{?error_protocol(_), Context} -> ok
|
||||
end.
|
||||
|
||||
call_safe_server_transport_error_test(_) ->
|
||||
Id = <<"call_safe_server_transport_error">>,
|
||||
Armor = <<"Helmet">>,
|
||||
Client = get_client(Id),
|
||||
Expect = {{error, ?error_transport(server_error)}, Client},
|
||||
Expect = call_safe(Client, powerups, get_powerup,
|
||||
Context = make_context(Id),
|
||||
Expect = {{error, ?error_transport(server_error)}, Context},
|
||||
Expect = call_safe(Context, powerups, get_powerup,
|
||||
[Armor, self_to_bin()]),
|
||||
{ok, _} = receive_msg({Id, Armor}).
|
||||
|
||||
@ -272,9 +275,9 @@ call_handle_error_fails_test(_) ->
|
||||
|
||||
do_call_server_transport_error(Id) ->
|
||||
Armor = <<"Helmet">>,
|
||||
Client = get_client(Id),
|
||||
Expect = {?error_transport(server_error), Client},
|
||||
try call(Client, powerups, get_powerup, [Armor, self_to_bin()])
|
||||
Context = make_context(Id),
|
||||
Expect = {?error_transport(server_error), Context},
|
||||
try call(Context, powerups, get_powerup, [Armor, self_to_bin()])
|
||||
catch
|
||||
error:Expect -> ok
|
||||
end,
|
||||
@ -283,9 +286,9 @@ do_call_server_transport_error(Id) ->
|
||||
call_oneway_void_test(_) ->
|
||||
Id = <<"call_oneway_void_test">>,
|
||||
Armor = <<"Helmet">>,
|
||||
Client = get_client(Id),
|
||||
Expect = {ok, Client},
|
||||
Expect = call(Client, powerups, like_powerup, [Armor, self_to_bin()]),
|
||||
Context = make_context(Id),
|
||||
Expect = {ok, Context},
|
||||
Expect = call(Context, powerups, like_powerup, [Armor, self_to_bin()]),
|
||||
{ok, _} = receive_msg({Id, Armor}).
|
||||
|
||||
call_async_ok_test(C) ->
|
||||
@ -293,28 +296,28 @@ call_async_ok_test(C) ->
|
||||
Pid = self(),
|
||||
Callback = fun(Res) -> collect(Res, Pid) end,
|
||||
Id1 = <<"call_async_ok1">>,
|
||||
Client1 = get_client(Id1),
|
||||
{ok, Pid1, Client1} = get_weapon(Client1, Sup, Callback, <<"Impact Hammer">>),
|
||||
Context1 = make_context(Id1),
|
||||
{ok, Pid1, Context1} = get_weapon(Context1, Sup, Callback, <<"Impact Hammer">>),
|
||||
Id2 = <<"call_async_ok2">>,
|
||||
Client2 = get_client(Id2),
|
||||
{ok, Pid2, Client2} = get_weapon(Client2, Sup, Callback, <<"Flak Cannon">>),
|
||||
{ok, Pid1} = receive_msg({Client1,
|
||||
Context2 = make_context(Id2),
|
||||
{ok, Pid2, Context2} = get_weapon(Context2, Sup, Callback, <<"Flak Cannon">>),
|
||||
{ok, Pid1} = receive_msg({Context1,
|
||||
genlib_map:get(<<"Impact Hammer">>, ?WEAPONS)}),
|
||||
{ok, Pid2} = receive_msg({Client2,
|
||||
{ok, Pid2} = receive_msg({Context2,
|
||||
genlib_map:get(<<"Flak Cannon">>, ?WEAPONS)}).
|
||||
|
||||
get_weapon(Client, Sup, Cb, Gun) ->
|
||||
call_async(Client, weapons, get_weapon, [Gun, <<>>], Sup, Cb).
|
||||
get_weapon(Context, Sup, Cb, Gun) ->
|
||||
call_async(Context, weapons, get_weapon, [Gun, <<>>], Sup, Cb).
|
||||
|
||||
collect({{ok, Result}, Client}, Pid) ->
|
||||
send_msg(Pid, {Client, Result}).
|
||||
collect({{ok, Result}, Context}, Pid) ->
|
||||
send_msg(Pid, {Context, Result}).
|
||||
|
||||
span_ids_sequence_test(_) ->
|
||||
Id = <<"span_ids_sequence">>,
|
||||
Current = genlib_map:get(<<"Enforcer">>, ?WEAPONS),
|
||||
Client = get_client(Id),
|
||||
Expect = {{ok, genlib_map:get(<<"Ripper">>, ?WEAPONS)}, Client},
|
||||
Expect = call(Client, weapons, switch_weapon,
|
||||
Context = make_context(Id),
|
||||
Expect = {{ok, genlib_map:get(<<"Ripper">>, ?WEAPONS)}, Context},
|
||||
Expect = call(Context, weapons, switch_weapon,
|
||||
[Current, next, 1, self_to_bin()]).
|
||||
|
||||
call_with_client_pool_test(_) ->
|
||||
@ -322,11 +325,11 @@ call_with_client_pool_test(_) ->
|
||||
ok = woody_client_thrift:start_pool(Pool, 10),
|
||||
Id = <<"call_with_client_pool">>,
|
||||
Gun = <<"Enforcer">>,
|
||||
Client = get_client(Id),
|
||||
Context = make_context(Id),
|
||||
{Url, Service} = get_service_endpoint(weapons),
|
||||
Expect = {{ok, genlib_map:get(Gun, ?WEAPONS)}, Client},
|
||||
Expect = {{ok, genlib_map:get(Gun, ?WEAPONS)}, Context},
|
||||
Expect = woody_client:call(
|
||||
Client,
|
||||
Context,
|
||||
{Service, get_weapon, [Gun, self_to_bin()]},
|
||||
#{url => Url, pool => Pool}
|
||||
),
|
||||
@ -354,8 +357,8 @@ make_thrift_multiplexed_client(Id, ServiceName, {Url, Service}) ->
|
||||
[{strict_read, true}, {strict_write, true}]
|
||||
),
|
||||
{ok, Protocol1} = thrift_multiplexed_protocol:new(Protocol, ServiceName),
|
||||
{ok, Client} = thrift_client:new(Protocol1, Service),
|
||||
Client.
|
||||
{ok, Context} = thrift_client:new(Protocol1, Service),
|
||||
Context.
|
||||
|
||||
allowed_transport_options_test(_) ->
|
||||
Id = <<"allowed_transport_options">>,
|
||||
@ -364,17 +367,17 @@ allowed_transport_options_test(_) ->
|
||||
{Url, Service} = get_service_endpoint(weapons),
|
||||
Pool = guns,
|
||||
ok = woody_client_thrift:start_pool(Pool, 1),
|
||||
Client = get_client(Id),
|
||||
Context = make_context(Id),
|
||||
Options = #{url => Url, pool => Pool, ssl_options => [], connect_timeout => 0},
|
||||
{{error, ?error_transport(connect_timeout)}, Client} = woody_client:call_safe(
|
||||
Client,
|
||||
{{error, ?error_transport(connect_timeout)}, Context} = woody_client:call_safe(
|
||||
Context,
|
||||
{Service, get_weapon, Args},
|
||||
Options
|
||||
),
|
||||
BadOpt = #{custom_option => 'fire!'},
|
||||
ErrorBadOpt = {badarg, {unsupported_options, BadOpt}},
|
||||
{{error, {error, ErrorBadOpt, _}}, Client} = woody_client:call_safe(
|
||||
Client,
|
||||
{{error, {error, ErrorBadOpt, _}}, Context} = woody_client:call_safe(
|
||||
Context,
|
||||
{Service, get_weapon, Args},
|
||||
maps:merge(Options, BadOpt)
|
||||
),
|
||||
@ -424,15 +427,15 @@ init(_) ->
|
||||
|
||||
%% Weapons
|
||||
handle_function(switch_weapon, {CurrentWeapon, Direction, Shift, To},
|
||||
_RpcId = #{span_id := SpanId, trace_id := TraceId},
|
||||
WoodyClient = #{parent_id := SpanId, trace_id := TraceId}, _Opts)
|
||||
Context = #{ parent_id := SpanId, trace_id := TraceId,
|
||||
rpc_id := #{span_id := SpanId, trace_id := TraceId}}, _Opts)
|
||||
->
|
||||
send_msg(To, {SpanId, CurrentWeapon}),
|
||||
switch_weapon(CurrentWeapon, Direction, Shift, WoodyClient);
|
||||
switch_weapon(CurrentWeapon, Direction, Shift, Context);
|
||||
|
||||
handle_function(get_weapon, {Name, To},
|
||||
#{span_id := SpanId, trace_id := TraceId},
|
||||
#{parent_id := SpanId, trace_id := TraceId}, _Opts)
|
||||
_Context = #{ parent_id := SpanId, trace_id := TraceId,
|
||||
rpc_id := #{span_id := SpanId, trace_id := TraceId}}, _Opts)
|
||||
->
|
||||
send_msg(To,{SpanId, Name}),
|
||||
Res = case genlib_map:get(Name, ?WEAPONS) of
|
||||
@ -445,26 +448,26 @@ handle_function(get_weapon, {Name, To},
|
||||
|
||||
%% Powerups
|
||||
handle_function(get_powerup, {Name, To},
|
||||
#{span_id := SpanId, trace_id := TraceId},
|
||||
#{parent_id := SpanId, trace_id := TraceId}, _Opts)
|
||||
_Context = #{ parent_id := SpanId, trace_id := TraceId,
|
||||
rpc_id := #{span_id := SpanId, trace_id := TraceId}}, _Opts)
|
||||
->
|
||||
send_msg(To, {SpanId, Name}),
|
||||
{ok, genlib_map:get(Name, ?POWERUPS, powerup_unknown)};
|
||||
handle_function(like_powerup, {Name, To},
|
||||
#{span_id := SpanId, trace_id := TraceId},
|
||||
#{parent_id := SpanId, trace_id := TraceId}, _Opts)
|
||||
_Context = #{ parent_id := SpanId, trace_id := TraceId,
|
||||
rpc_id := #{span_id := SpanId, trace_id := TraceId}}, _Opts)
|
||||
->
|
||||
send_msg(To, {SpanId, Name}),
|
||||
ok.
|
||||
|
||||
handle_error(get_powerup, _,
|
||||
#{trace_id := TraceId, span_id := SpanId = <<"call_handle_error_fails">>},
|
||||
#{trace_id := TraceId, parent_id := SpanId}, _Opts)
|
||||
_Context = #{ trace_id := TraceId, span_id := SpanId = <<"call_handle_error_fails">>,
|
||||
rpc_id := #{trace_id := TraceId, parent_id := SpanId}}, _Opts)
|
||||
->
|
||||
error(no_more_powerups);
|
||||
handle_error(_Function, _Reason,
|
||||
_RpcId = #{span_id := SpanId, trace_id := TraceId},
|
||||
_WoodyClient = #{parent_id := SpanId, trace_id := TraceId}, _Opts)
|
||||
_Context = #{ parent_id := SpanId, trace_id := TraceId,
|
||||
rpc_id := #{span_id := SpanId, trace_id := TraceId}}, _Opts)
|
||||
->
|
||||
ok.
|
||||
|
||||
@ -472,33 +475,33 @@ handle_error(_Function, _Reason,
|
||||
%%
|
||||
%% woody_event_handler callbacks
|
||||
%%
|
||||
handle_event(Type, Meta) ->
|
||||
ct:pal(info, "woody event ~p: ~p", [Type, Meta]).
|
||||
handle_event(Type, RpcId, Meta) ->
|
||||
ct:pal(info, "woody event ~p for RPC ID: ~p~n~p", [Type, RpcId, Meta]).
|
||||
|
||||
%%
|
||||
%% internal functions
|
||||
%%
|
||||
get_client(ReqId) ->
|
||||
woody_client:new(ReqId, ?MODULE).
|
||||
make_context(ReqId) ->
|
||||
woody_client:new_context(ReqId, ?MODULE).
|
||||
|
||||
call(Client, ServiceName, Function, Args) ->
|
||||
do_call(call, Client, ServiceName, Function, Args).
|
||||
call(Context, ServiceName, Function, Args) ->
|
||||
do_call(call, Context, ServiceName, Function, Args).
|
||||
|
||||
call_safe(Client, ServiceName, Function, Args) ->
|
||||
do_call(call_safe, Client, ServiceName, Function, Args).
|
||||
call_safe(Context, ServiceName, Function, Args) ->
|
||||
do_call(call_safe, Context, ServiceName, Function, Args).
|
||||
|
||||
do_call(Call, Client, ServiceName, Function, Args) ->
|
||||
do_call(Call, Context, ServiceName, Function, Args) ->
|
||||
{Url, Service} = get_service_endpoint(ServiceName),
|
||||
woody_client:Call(
|
||||
Client,
|
||||
Context,
|
||||
{Service, Function, Args},
|
||||
#{url => Url}
|
||||
).
|
||||
|
||||
call_async(Client, ServiceName, Function, Args, Sup, Callback) ->
|
||||
call_async(Context, ServiceName, Function, Args, Sup, Callback) ->
|
||||
{Url, Service} = get_service_endpoint(ServiceName),
|
||||
woody_client:call_async(Sup, Callback,
|
||||
Client,
|
||||
Context,
|
||||
{Service, Function, Args},
|
||||
#{url => Url}
|
||||
).
|
||||
@ -506,27 +509,27 @@ call_async(Client, ServiceName, Function, Args, Sup, Callback) ->
|
||||
get_service_endpoint(weapons) ->
|
||||
{
|
||||
genlib:to_binary(?URL_BASE ++ ?PATH_WEAPONS),
|
||||
woody_test_weapons_service
|
||||
{?THRIFT_DEFS, weapons}
|
||||
};
|
||||
get_service_endpoint(powerups) ->
|
||||
{
|
||||
genlib:to_binary(?URL_BASE ++ ?PATH_POWERUPS),
|
||||
woody_test_powerups_service
|
||||
{?THRIFT_DEFS, powerups}
|
||||
}.
|
||||
|
||||
gun_test_basic(CallFun, Id, Gun, {ExpectStatus, ExpectRes}, WithMsg) ->
|
||||
Client = get_client(Id),
|
||||
Expect = {{ExpectStatus, ExpectRes}, Client},
|
||||
Expect = ?MODULE:CallFun(Client, weapons, get_weapon, [Gun, self_to_bin()]),
|
||||
Context = make_context(Id),
|
||||
Expect = {{ExpectStatus, ExpectRes}, Context},
|
||||
Expect = ?MODULE:CallFun(Context, weapons, get_weapon, [Gun, self_to_bin()]),
|
||||
case WithMsg of
|
||||
true -> {ok, _} = receive_msg({Id, Gun});
|
||||
_ -> ok
|
||||
end.
|
||||
|
||||
gun_catch_test_basic(Id, Gun, {Class, Exception}, WithMsg) ->
|
||||
Client = get_client(Id),
|
||||
Expect = {Exception, Client},
|
||||
try call(Client, weapons, get_weapon, [Gun, self_to_bin()])
|
||||
Context = make_context(Id),
|
||||
Expect = {Exception, Context},
|
||||
try call(Context, weapons, get_weapon, [Gun, self_to_bin()])
|
||||
catch
|
||||
Class:Expect -> ok
|
||||
end,
|
||||
@ -535,8 +538,8 @@ gun_catch_test_basic(Id, Gun, {Class, Exception}, WithMsg) ->
|
||||
_ -> ok
|
||||
end.
|
||||
|
||||
switch_weapon(CurrentWeapon, Direction, Shift, WoodyClient) ->
|
||||
case call_safe(WoodyClient, weapons, get_weapon,
|
||||
switch_weapon(CurrentWeapon, Direction, Shift, Context) ->
|
||||
case call_safe(Context, weapons, get_weapon,
|
||||
[new_weapon_name(CurrentWeapon, Direction, Shift), self_to_bin()])
|
||||
of
|
||||
{{ok, Weapon}, _} ->
|
||||
@ -544,9 +547,9 @@ switch_weapon(CurrentWeapon, Direction, Shift, WoodyClient) ->
|
||||
{{exception, #weapon_failure{
|
||||
code = <<"weapon_error">>,
|
||||
reason = <<"out of ammo">>
|
||||
}}, NextClient} ->
|
||||
ok = validate_next_client(NextClient, WoodyClient),
|
||||
switch_weapon(CurrentWeapon, Direction, Shift + 1, NextClient)
|
||||
}}, NextContex} ->
|
||||
ok = validate_next_context(NextContex, Context),
|
||||
switch_weapon(CurrentWeapon, Direction, Shift + 1, NextContex)
|
||||
end.
|
||||
|
||||
new_weapon_name(#weapon{slot_pos = Pos}, next, Shift) ->
|
||||
@ -559,7 +562,7 @@ new_weapon_name(Pos) when is_integer(Pos), Pos >= 0, Pos < 10 ->
|
||||
new_weapon_name(_) ->
|
||||
throw(?pos_error).
|
||||
|
||||
validate_next_client(#{seq := NextSeq}, #{seq := Seq}) ->
|
||||
validate_next_context(#{seq := NextSeq}, #{seq := Seq}) ->
|
||||
NextSeq = Seq + 1,
|
||||
ok.
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user