Skip to content

Commit e64f7ac

Browse files
committed
feat: pass messageId to handler. allow handler to not reply. closes #11
1 parent 3cbbc8d commit e64f7ac

File tree

5 files changed

+122
-6
lines changed

5 files changed

+122
-6
lines changed

README.md

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -521,9 +521,21 @@ When the `handler` function is invoked, it will be passed an object with the fol
521521
* `method` {String} - The name of the method being invoked (useful for wildcard handlers).
522522
* `params` {*} - The `params` value passed to the call.
523523
* `signal` {AbortSignal} - A signal which will abort if the underlying connection is dropped (therefore, the response will never be received by the caller). You may choose whether to ignore the signal or not, but it could save you some time if you use it to abort the call early.
524+
* `messageId` {String} - The OCPP Message ID used in the call.
524525

525526
If the invocation of the `handler` resolves or returns, the resolved value will be returned to the caller.
526527
If the invocation of the `handler` rejects or throws, an error will be passed to the caller. By default, the error will be an instance of `RPCGenericError`, although additional error types are possible ([see createRPCError](#createrpcerrortype-message-details)).
528+
If the `handler` returns a `NOREPLY` symbol then no reply will be sent. It will be your responsibility to send the reply by some other means (such as [`sendRaw()`](#clientsendrawmessage)).
529+
530+
##### Example of NOREPLY
531+
532+
```js
533+
const {NOREPLY} = require('ocpp-rpc');
534+
535+
client.handle('WontReply', () => {
536+
return NOREPLY;
537+
});
538+
```
527539

528540
#### client.call(method[, params[, options]])
529541

index.js

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,15 @@
11
const RPCClient = require('./lib/client');
22
const RPCServer = require('./lib/server');
33
const errors = require('./lib/errors');
4+
const symbols = require('./lib/symbols');
45
const { createRPCError } = require('./lib/util');
56
const { createValidator } = require('./lib/validator');
67

78
module.exports = {
89
RPCServer,
910
RPCClient,
10-
...errors,
1111
createRPCError,
1212
createValidator,
13+
...errors,
14+
...symbols,
1315
};

lib/client.js

Lines changed: 16 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,10 @@
1-
const {randomUUID} = require('crypto');
2-
const {EventEmitter, once} = require('events');
3-
const { setTimeout, setInterval } = require('timers/promises');
1+
const { randomUUID} = require('crypto');
2+
const { EventEmitter, once } = require('events');
3+
const { setTimeout } = require('timers/promises');
44
const WebSocket = require('ws');
55
const { ExponentialStrategy } = require('backoff');
6-
const {CONNECTING, OPEN, CLOSING, CLOSED} = WebSocket;
6+
const { CONNECTING, OPEN, CLOSING, CLOSED } = WebSocket;
7+
const { NOREPLY } = require('./symbols');
78
const { TimeoutError, UnexpectedHttpResponse, RPCFrameworkError, RPCGenericError, RPCMessageTypeNotSupportedError } = require('./errors');
89
const { getErrorPlainObject, createRPCError, getPackageIdent } = require('./util');
910
const Queue = require('./queue');
@@ -727,11 +728,21 @@ class RPCClient extends EventEmitter {
727728
}
728729

729730
const ac = new AbortController();
730-
const callPromise = Promise.resolve(handler({method, params, signal: ac.signal}));
731+
const callPromise = Promise.resolve(handler({
732+
messageId: msgId,
733+
method,
734+
params,
735+
signal: ac.signal,
736+
}));
737+
731738
const pending = {abort: ac.abort.bind(ac), promise: callPromise};
732739
this._pendingResponses.set(msgId, pending);
733740
const result = await callPromise;
734741

742+
if (result === NOREPLY) {
743+
return; // don't send a reply
744+
}
745+
735746
payload = [MSG_CALLRESULT, msgId, result];
736747

737748
if (this._strictProtocols.includes(this._protocol)) {

lib/symbols.js

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
module.exports = {
2+
NOREPLY: Symbol("NOREPLY"),
3+
};

test/client.js

Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ const RPCServer = require("../lib/server");
77
const { setTimeout } = require('timers/promises');
88
const { createValidator } = require('../lib/validator');
99
const { createRPCError } = require('../lib/util');
10+
const { NOREPLY } = require('../lib/symbols');
1011
const {CLOSING, CLOSED, CONNECTING} = RPCClient;
1112

1213
describe('RPCClient', function(){
@@ -1593,6 +1594,27 @@ describe('RPCClient', function(){
15931594

15941595
});
15951596

1597+
it('should not reject when making unhandled noReply call', async () => {
1598+
1599+
const {endpoint, close} = await createServer();
1600+
const cli = new RPCClient({
1601+
endpoint,
1602+
identity: 'X',
1603+
});
1604+
1605+
try {
1606+
await cli.connect();
1607+
1608+
const res = await cli.call('UnrecognisedMethod', 1, {noReply: true});
1609+
assert.equal(res, undefined);
1610+
1611+
} finally {
1612+
await cli.close();
1613+
close();
1614+
}
1615+
1616+
});
1617+
15961618
});
15971619

15981620

@@ -1994,6 +2016,72 @@ describe('RPCClient', function(){
19942016

19952017
});
19962018

2019+
2020+
it('should not respond to a call that returns NOREPLY symbol', async () => {
2021+
2022+
const {endpoint, close} = await createServer({}, {
2023+
withClient: cli => {
2024+
cli.handle('NoReply', () => {
2025+
return NOREPLY;
2026+
});
2027+
}
2028+
});
2029+
const cli = new RPCClient({
2030+
endpoint,
2031+
identity: 'X',
2032+
});
2033+
2034+
try {
2035+
await cli.connect();
2036+
2037+
await assert.rejects(cli.call('NoReply', 123, {callTimeoutMs: 50}), TimeoutError);
2038+
2039+
} finally {
2040+
await cli.close();
2041+
close();
2042+
}
2043+
2044+
});
2045+
2046+
2047+
it('should report the message ID', async () => {
2048+
2049+
const {endpoint, close} = await createServer({}, {
2050+
withClient: cli => {
2051+
cli.handle('Manual', async ({messageId}) => {
2052+
await cli.sendRaw(JSON.stringify([
2053+
3,
2054+
messageId,
2055+
{messageId}
2056+
]));
2057+
return NOREPLY;
2058+
});
2059+
}
2060+
});
2061+
const cli = new RPCClient({
2062+
endpoint,
2063+
identity: 'X',
2064+
});
2065+
2066+
try {
2067+
await cli.connect();
2068+
2069+
const callOutProm = once(cli, 'call');
2070+
const {messageId} = await cli.call('Manual');
2071+
const [callOut] = await callOutProm;
2072+
2073+
assert.equal(callOut.outbound, true);
2074+
assert.equal(callOut.payload[0], 2);
2075+
assert.equal(callOut.payload[1], messageId);
2076+
assert.equal(callOut.payload[2], 'Manual');
2077+
2078+
} finally {
2079+
await cli.close();
2080+
close();
2081+
}
2082+
2083+
});
2084+
19972085
});
19982086

19992087

0 commit comments

Comments
 (0)