Skip to content

Commit ae56acf

Browse files
authored
Merge pull request #5 from sjinks/pass-extra-to-events
fix: pass the extra argument to event methods
2 parents 5045090 + 877eafe commit ae56acf

File tree

4 files changed

+74
-48
lines changed

4 files changed

+74
-48
lines changed

src/dispatcher.cpp

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -25,12 +25,12 @@ std::string dispatcher::parse_and_process_request(const std::string& request, co
2525
req = nlohmann::json::parse(request);
2626
}
2727
catch (const nlohmann::json::exception& e) {
28-
this->on_request();
28+
this->on_request(extra);
2929
const auto json = dispatcher_private::generate_error_response(
3030
exception(exception::PARSE_ERROR, e.what()), nlohmann::json(nullptr)
3131
);
3232

33-
this->on_request_processed({}, exception::PARSE_ERROR);
33+
this->on_request_processed({}, exception::PARSE_ERROR, extra);
3434
return json.dump();
3535
}
3636

@@ -43,17 +43,17 @@ std::string dispatcher::process_request(const nlohmann::json& request, const nlo
4343
return json.is_discarded() ? std::string{} : json.dump();
4444
}
4545

46-
void dispatcher::on_request()
46+
void dispatcher::on_request(const nlohmann::json&)
4747
{
4848
// Do nothing
4949
}
5050

51-
void dispatcher::on_method(const std::string&)
51+
void dispatcher::on_method(const std::string&, const nlohmann::json&)
5252
{
5353
// Do nothing
5454
}
5555

56-
void dispatcher::on_request_processed(const std::string&, int)
56+
void dispatcher::on_request_processed(const std::string&, int, const nlohmann::json&)
5757
{
5858
// Do nothing
5959
}

src/dispatcher.h

Lines changed: 22 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -160,7 +160,7 @@ class WWA_JSONRPC_EXPORT dispatcher {
160160
* return std::accumulate(v.begin(), v.end(), 0);
161161
* });
162162
* ```
163-
* @note If the handler accepts a single `nlohmann::json` argument, it will *any* parameters. For example:
163+
* @note If the handler accepts a single `nlohmann::json` argument, it will accept *any* parameters. For example:
164164
* ```cpp
165165
* void handler(const nlohmann::json& params)
166166
* {
@@ -184,12 +184,12 @@ class WWA_JSONRPC_EXPORT dispatcher {
184184
*
185185
* @par Exception Handling:
186186
* If the hander function throws an exception (derived from `std::exception`), the exception will be caught, and the error will be returned in the JSON response:
187-
* 1. json_rpc::exception will be converted to a JSON RPC error object using json_rpc::exception::to_json();
188-
* 2. other exceptions derived from std::exception will be converted to a JSON RPC error object with code @a -32603 (exception::INTERNAL_ERROR)
187+
* 1. `json_rpc::exception` will be converted to a JSON RPC error object using json_rpc::exception::to_json();
188+
* 2. other exceptions derived from `std::exception` will be converted to a JSON RPC error object with code @a -32603 (`exception::INTERNAL_ERROR`)
189189
* and the exception message ([what()](https://en.cppreference.com/w/cpp/error/exception/what)) as the error message.
190190
*/
191191
template<typename F>
192-
void add(std::string_view method, F&& f)
192+
inline void add(std::string_view method, F&& f)
193193
{
194194
this->add(method, std::forward<F>(f), nullptr);
195195
}
@@ -251,7 +251,7 @@ class WWA_JSONRPC_EXPORT dispatcher {
251251
* @see add()
252252
*/
253253
template<typename F>
254-
void add_ex(std::string_view method, F&& f)
254+
inline void add_ex(std::string_view method, F&& f)
255255
{
256256
this->add_ex(method, std::forward<F>(f), nullptr);
257257
}
@@ -274,7 +274,7 @@ class WWA_JSONRPC_EXPORT dispatcher {
274274
* @see add()
275275
*/
276276
template<typename C, typename F>
277-
void add_ex(std::string_view method, F&& f, C instance)
277+
inline void add_ex(std::string_view method, F&& f, C instance)
278278
{
279279
using traits = details::function_traits<std::decay_t<F>>;
280280
using ArgsTuple = typename traits::args_tuple;
@@ -293,7 +293,7 @@ class WWA_JSONRPC_EXPORT dispatcher {
293293
* @brief Parses and processes a JSON RPC request.
294294
*
295295
* @param request The JSON RPC request as a string.
296-
* @param extra An optional JSON object that can be passed to the handler function (only for handlers added with add_ex()).
296+
* @param extra Optional data that can be passed to the handler function (only for handlers added with add_ex()).
297297
* @return The response serialized into a JSON string.
298298
* @retval "" If the request is a [Notification](https://www.jsonrpc.org/specification#notification), the method returns an empty string.
299299
*
@@ -318,7 +318,7 @@ class WWA_JSONRPC_EXPORT dispatcher {
318318
* @brief Processes a JSON RPC request.
319319
*
320320
* @param request The JSON RPC request as a `nlohmann::json` object.
321-
* @param extra An optional JSON object that can be passed to the handler function (only for handlers added with add_ex()).
321+
* @param extra Optional data that can be passed to the handler function (only for handlers added with add_ex()).
322322
* @return The response serialized into a JSON string.
323323
* @retval "" If the request is a [Notification](https://www.jsonrpc.org/specification#notification), the method returns an empty string.
324324
*
@@ -365,20 +365,27 @@ class WWA_JSONRPC_EXPORT dispatcher {
365365
* @details This method does nothing by default. It is intended to be overridden in a derived class.
366366
* For example, it can be used to log requests or increment a counter.
367367
*
368+
* @param extra Additional information that was passed to `process_request()`.
369+
* Since `on_request()` is called before the request is parsed, the @a extra` parameter will not contain fields
370+
* from the request itself (i.e., @a `extra['extra']` will not be set).
371+
*
368372
* @note In the case of a valid [batch request](https://www.jsonrpc.org/specification#batch),
369373
* this method is invoked for every request in the batch but **not** for the batch itself.
370374
* However, if the batch request is invalid (e.g., is empty), this method is invoked once with an empty method name.
371375
*/
372-
virtual void on_request();
376+
virtual void on_request(const nlohmann::json& extra);
373377

374378
/**
375379
* @brief Invoked right before the method handler is called.
376380
*
377381
* @details This method does nothing by default. It is intended to be overridden in a derived class. For example, it can start a timer to measure the method execution time.
378382
*
383+
* @param extra Additional information that was passed to `process_request()`. If @a extra is a JSON object,
384+
* it will contain all extra fields from the JSON RPC request (in @a extra['extra'], see `process_request()`).
385+
*
379386
* @param method The name of the method to be called.
380387
*/
381-
virtual void on_method(const std::string& method);
388+
virtual void on_method(const std::string& method, const nlohmann::json& extra);
382389

383390
/**
384391
* @brief Invoked after the method handler is called.
@@ -390,8 +397,11 @@ class WWA_JSONRPC_EXPORT dispatcher {
390397
* @param code The result code: 0 if the method was processed successfully, or an error code
391398
* if an exception was thrown (e.g., exception::INTERNAL_ERROR)
392399
* or the request could not be processed (e.g., exception::INVALID_PARAMS).
400+
* @param extra Additional information that was passed to `process_request()`. If the request had already been successfully parsed
401+
* before `on_request_processed()` was called, the @a extra parameter will contain all extra fields from the JSON RPC request
402+
* (in @a extra['extra'], see `process_request()`).
393403
*/
394-
virtual void on_request_processed(const std::string& method, int code);
404+
virtual void on_request_processed(const std::string& method, int code, const nlohmann::json& extra);
395405

396406
private:
397407
/**
@@ -442,7 +452,7 @@ class WWA_JSONRPC_EXPORT dispatcher {
442452
* This helps catch potential errors early in the development process and improves the overall robustness of the code.
443453
*/
444454
template<typename C, typename F, typename Extra, typename Args>
445-
constexpr auto create_closure(C inst, F&& f) const
455+
inline constexpr auto create_closure(C inst, F&& f) const
446456
{
447457
static_assert((std::is_pointer_v<C> && std::is_class_v<std::remove_pointer_t<C>>) || std::is_null_pointer_v<C>);
448458
return [func = std::forward<F>(f), inst](const nlohmann::json& extra, const nlohmann::json& params) {

src/dispatcher_p.cpp

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -102,8 +102,8 @@ jsonrpc_request dispatcher_private::parse_request(const nlohmann::json& request,
102102
nlohmann::json dispatcher_private::process_batch_request(const nlohmann::json& request, const nlohmann::json& extra)
103103
{
104104
if (request.empty()) {
105-
this->q_ptr->on_request();
106-
this->q_ptr->on_request_processed({}, exception::INVALID_REQUEST);
105+
this->q_ptr->on_request(extra);
106+
this->q_ptr->on_request_processed({}, exception::INVALID_REQUEST, extra);
107107
return dispatcher_private::generate_error_response(
108108
exception(exception::INVALID_REQUEST, err_empty_batch), nlohmann::json(nullptr)
109109
);
@@ -112,13 +112,13 @@ nlohmann::json dispatcher_private::process_batch_request(const nlohmann::json& r
112112
auto response = nlohmann::json::array();
113113
for (const auto& req : request) {
114114
if (!req.is_object()) {
115-
this->q_ptr->on_request();
115+
this->q_ptr->on_request(extra);
116116
const auto r = dispatcher_private::generate_error_response(
117117
exception(exception::INVALID_REQUEST, err_not_jsonrpc_2_0_request), nlohmann::json(nullptr)
118118
);
119119

120120
response.push_back(r);
121-
this->q_ptr->on_request_processed({}, exception::INVALID_REQUEST);
121+
this->q_ptr->on_request_processed({}, exception::INVALID_REQUEST, extra);
122122
}
123123
else if (const auto res = this->process_request(req, extra); !res.is_discarded()) {
124124
response.push_back(res);
@@ -135,21 +135,21 @@ nlohmann::json dispatcher_private::process_request(const nlohmann::json& request
135135
return this->process_batch_request(request, extra);
136136
}
137137

138-
this->q_ptr->on_request();
138+
this->q_ptr->on_request(extra);
139139
auto request_id = request.contains("id") ? request["id"] : nlohmann::json(nullptr);
140140
if (!is_valid_request_id(request_id)) {
141141
request_id = nlohmann::json(nullptr);
142142
}
143143

144144
std::string method;
145+
nlohmann::json extra_data = extra;
145146
try {
146147
nlohmann::json extra_fields;
147148
auto req = dispatcher_private::parse_request(request, extra_fields);
148149
dispatcher_private::validate_request(req);
149150
method = req.method;
150151
request_id = req.id;
151152

152-
nlohmann::json extra_data = extra;
153153
if (extra.is_object() && !extra_fields.empty()) {
154154
extra_data["extra"] = extra_fields;
155155
}
@@ -163,12 +163,12 @@ nlohmann::json dispatcher_private::process_request(const nlohmann::json& request
163163
return nlohmann::json(nlohmann::json::value_t::discarded);
164164
}
165165
catch (const exception& e) {
166-
this->q_ptr->on_request_processed(method, e.code());
166+
this->q_ptr->on_request_processed(method, e.code(), extra_data);
167167
return request_id.is_discarded() ? nlohmann::json(nlohmann::json::value_t::discarded)
168168
: dispatcher_private::generate_error_response(e, request_id);
169169
}
170170
catch (const std::exception& e) {
171-
this->q_ptr->on_request_processed(method, exception::INTERNAL_ERROR);
171+
this->q_ptr->on_request_processed(method, exception::INTERNAL_ERROR, extra_data);
172172
return request_id.is_discarded() ? nlohmann::json(nlohmann::json::value_t::discarded)
173173
: dispatcher_private::generate_error_response(
174174
exception(exception::INTERNAL_ERROR, e.what()), request_id
@@ -204,9 +204,9 @@ nlohmann::json
204204
dispatcher_private::invoke(const std::string& method, const nlohmann::json& params, const nlohmann::json& extra)
205205
{
206206
if (const auto it = this->m_methods.find(method); it != this->m_methods.end()) {
207-
this->q_ptr->on_method(method);
207+
this->q_ptr->on_method(method, extra);
208208
const auto response = it->second(extra, params);
209-
this->q_ptr->on_request_processed(method, 0);
209+
this->q_ptr->on_request_processed(method, 0, extra);
210210
return response;
211211
}
212212

test/test_instrumentation.cpp

Lines changed: 37 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,9 @@
66

77
class mocked_dispatcher : public wwa::json_rpc::dispatcher {
88
public:
9-
MOCK_METHOD(void, on_request, (), (override));
10-
MOCK_METHOD(void, on_method, (const std::string&), (override));
11-
MOCK_METHOD(void, on_request_processed, (const std::string&, int), (override));
9+
MOCK_METHOD(void, on_request, (const nlohmann::json&), (override));
10+
MOCK_METHOD(void, on_method, (const std::string&, const nlohmann::json&), (override));
11+
MOCK_METHOD(void, on_request_processed, (const std::string&, int, const nlohmann::json&), (override));
1212
};
1313

1414
class InstrumentationTest : public ::testing::Test {
@@ -34,8 +34,11 @@ TEST_F(InstrumentationTest, BadRequest)
3434

3535
{
3636
const testing::InSequence s;
37-
EXPECT_CALL(dispatcher, on_request());
38-
EXPECT_CALL(dispatcher, on_request_processed(std::string{}, wwa::json_rpc::exception::INVALID_REQUEST));
37+
EXPECT_CALL(dispatcher, on_request(nlohmann::json::object()));
38+
EXPECT_CALL(
39+
dispatcher,
40+
on_request_processed(std::string{}, wwa::json_rpc::exception::INVALID_REQUEST, nlohmann::json::object())
41+
);
3942
}
4043

4144
dispatcher.process_request(input);
@@ -44,30 +47,43 @@ TEST_F(InstrumentationTest, BadRequest)
4447
TEST_F(InstrumentationTest, BatchRequest)
4548
{
4649
const auto input = R"([
47-
{"jsonrpc": "2.0", "method": "add", "params": [1, 2], "id": 1},
48-
{"jsonrpc": "2.0", "method": "subtract", "params": [2, 1], "id": 2},
49-
{"jsonrpc": "2.0", "method": "add", "params": ["2", "3"], "id": 3},
50-
{"jsonrpc": "2.0", "method": "bad", "id": 4}
50+
{"jsonrpc": "2.0", "method": "add", "params": [1, 2], "id": 1, "extra": "extra1" },
51+
{"jsonrpc": "2.0", "method": "subtract", "params": [2, 1], "id": 2, "extra": "extra2"},
52+
{"jsonrpc": "2.0", "method": "add", "params": ["2", "3"], "id": 3, "extra": "extra3"},
53+
{"jsonrpc": "2.0", "method": "bad", "id": 4, "extra": "extra4"},
54+
{"extra": "extra5"}
5155
])"_json;
5256
auto& dispatcher = this->dispatcher();
5357

58+
const auto extra_data = R"({"ip": "127.0.0.1"})"_json;
59+
const auto expected_data_1 = nlohmann::json({{"ip", "127.0.0.1"}, {"extra", {{"extra", "extra1"}}}});
60+
const auto expected_data_2 = nlohmann::json({{"ip", "127.0.0.1"}, {"extra", {{"extra", "extra2"}}}});
61+
const auto expected_data_3 = nlohmann::json({{"ip", "127.0.0.1"}, {"extra", {{"extra", "extra3"}}}});
62+
const auto expected_data_4 = nlohmann::json({{"ip", "127.0.0.1"}, {"extra", {{"extra", "extra4"}}}});
63+
const auto& expected_data_5 = extra_data;
64+
5465
{
5566
const testing::InSequence s;
56-
EXPECT_CALL(dispatcher, on_request());
57-
EXPECT_CALL(dispatcher, on_method("add"));
58-
EXPECT_CALL(dispatcher, on_request_processed("add", 0));
67+
EXPECT_CALL(dispatcher, on_request(extra_data));
68+
EXPECT_CALL(dispatcher, on_method("add", expected_data_1));
69+
EXPECT_CALL(dispatcher, on_request_processed("add", 0, expected_data_1));
70+
71+
EXPECT_CALL(dispatcher, on_request(extra_data));
72+
EXPECT_CALL(dispatcher, on_method("subtract", expected_data_2));
73+
EXPECT_CALL(dispatcher, on_request_processed("subtract", 0, expected_data_2));
5974

60-
EXPECT_CALL(dispatcher, on_request());
61-
EXPECT_CALL(dispatcher, on_method("subtract"));
62-
EXPECT_CALL(dispatcher, on_request_processed("subtract", 0));
75+
EXPECT_CALL(dispatcher, on_request(extra_data));
76+
EXPECT_CALL(dispatcher, on_method("add", expected_data_3));
77+
EXPECT_CALL(dispatcher, on_request_processed("add", wwa::json_rpc::exception::INVALID_PARAMS, expected_data_3));
6378

64-
EXPECT_CALL(dispatcher, on_request());
65-
EXPECT_CALL(dispatcher, on_method("add"));
66-
EXPECT_CALL(dispatcher, on_request_processed("add", wwa::json_rpc::exception::INVALID_PARAMS));
79+
EXPECT_CALL(dispatcher, on_request(extra_data));
80+
EXPECT_CALL(
81+
dispatcher, on_request_processed("bad", wwa::json_rpc::exception::METHOD_NOT_FOUND, expected_data_4)
82+
);
6783

68-
EXPECT_CALL(dispatcher, on_request());
69-
EXPECT_CALL(dispatcher, on_request_processed("bad", wwa::json_rpc::exception::METHOD_NOT_FOUND));
84+
EXPECT_CALL(dispatcher, on_request(extra_data));
85+
EXPECT_CALL(dispatcher, on_request_processed("", wwa::json_rpc::exception::INVALID_REQUEST, expected_data_5));
7086
}
7187

72-
dispatcher.process_request(input);
88+
dispatcher.process_request(input, extra_data);
7389
}

0 commit comments

Comments
 (0)