Skip to content

Commit 22ec82c

Browse files
authored
Fix re-subscription (#602)
When resubscribing after a reconnected event, the SDK use the original subscription request to re-subscribe. The Kuzzle.query method mutate request and inject the JWT inside. Meaning that the request saved for the re-subscription already has a JWT inside. This can be an issue because developers rely on the kuzzle.jwt property of the SDK to know if 1) the token is still valid 2) there are connection information. Now the request and options parameter of the query method are cloned before modification.
1 parent 939cce7 commit 22ec82c

File tree

8 files changed

+46
-83
lines changed

8 files changed

+46
-83
lines changed

doc/7/essentials/events/index.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,7 @@ Triggered whenever Kuzzle responds with an error
8686
**Callback arguments:**
8787

8888
`@param {KuzzleError} error - Error details`
89+
8990
`@param {object} request - Request that caused the error`
9091

9192
## reconnected

src/Kuzzle.ts

Lines changed: 14 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -420,7 +420,9 @@ export class Kuzzle extends KuzzleEventEmitter {
420420
this.startQueuing();
421421
}
422422

423-
this.protocol.addListener('queryError', (err, query) => this.emit('queryError', err, query));
423+
this.protocol.addListener('queryError', ({ error, request }) => {
424+
this.emit('queryError', { error, request });
425+
});
424426

425427
this.protocol.addListener('tokenExpired', () => this.tokenExpired());
426428

@@ -456,20 +458,6 @@ export class Kuzzle extends KuzzleEventEmitter {
456458
this.playQueue();
457459
}
458460

459-
if (this.auth.authenticationToken) {
460-
return this.auth.checkToken()
461-
.then(res => {
462-
// shouldn't obtain an error but let's invalidate the token anyway
463-
if (!res.valid) {
464-
this.auth.authenticationToken = null;
465-
}
466-
})
467-
.catch(() => {
468-
this.auth.authenticationToken = null;
469-
})
470-
.then(() => this.emit('reconnected'));
471-
}
472-
473461
this.emit('reconnected');
474462
});
475463

@@ -523,18 +511,21 @@ export class Kuzzle extends KuzzleEventEmitter {
523511
* - volatile (object, default: null):
524512
* Additional information passed to notifications to other users
525513
*
526-
* @param request
527-
* @param options - Optional arguments
514+
* @param req
515+
* @param opts - Optional arguments
528516
*/
529-
query (request: RequestPayload = {}, options: JSONObject = {}): Promise<ResponsePayload> {
530-
if (typeof request !== 'object' || Array.isArray(request)) {
531-
throw new Error(`Kuzzle.query: Invalid request: ${JSON.stringify(request)}`);
517+
query (req: RequestPayload = {}, opts: JSONObject = {}): Promise<ResponsePayload> {
518+
if (typeof req !== 'object' || Array.isArray(req)) {
519+
throw new Error(`Kuzzle.query: Invalid request: ${JSON.stringify(req)}`);
532520
}
533521

534-
if (typeof options !== 'object' || Array.isArray(options)) {
535-
throw new Error(`Kuzzle.query: Invalid "options" argument: ${JSON.stringify(options)}`);
522+
if (typeof opts !== 'object' || Array.isArray(opts)) {
523+
throw new Error(`Kuzzle.query: Invalid "options" argument: ${JSON.stringify(opts)}`);
536524
}
537525

526+
const request = JSON.parse(JSON.stringify(req));
527+
const options = JSON.parse(JSON.stringify(opts));
528+
538529
if (!request.requestId) {
539530
request.requestId = uuidv4();
540531
}
@@ -590,7 +581,7 @@ export class Kuzzle extends KuzzleEventEmitter {
590581
});
591582
}
592583

593-
this.emit('discarded', {request});
584+
this.emit('discarded', { request });
594585
return Promise.reject(new Error(`Unable to execute request: not connected to a Kuzzle server.
595586
Discarded request: ${JSON.stringify(request)}`));
596587
}

src/protocols/WebSocket.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -150,7 +150,7 @@ export default class WebSocketProtocol extends BaseProtocolRealtime {
150150
(new Error().stack),
151151
this.constructor.name);
152152

153-
this.emit('queryError', error, data);
153+
this.emit('queryError', { error, request: data });
154154
}
155155
};
156156

src/protocols/abstract/Base.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -146,7 +146,7 @@ Discarded request: ${JSON.stringify(request)}`));
146146
error = response.error;
147147
}
148148

149-
this.emit('queryError', error, request);
149+
this.emit('queryError', { error, request });
150150

151151
if (request.action !== 'logout' && error.id === 'security.token.invalid') {
152152
this.emit('tokenExpired');

test/kuzzle/connect.test.js

Lines changed: 4 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,7 @@
1-
const
2-
should = require('should'),
3-
sinon = require('sinon'),
4-
ProtocolMock = require('../mocks/protocol.mock'),
5-
generateJwt = require('../mocks/generateJwt.mock'),
6-
{ Kuzzle } = require('../../src/Kuzzle');
1+
const should = require('should');
2+
const sinon = require('sinon');
3+
const ProtocolMock = require('../mocks/protocol.mock');
4+
const { Kuzzle } = require('../../src/Kuzzle');
75

86
describe('Kuzzle connect', () => {
97

@@ -96,42 +94,6 @@ describe('Kuzzle connect', () => {
9694
});
9795
});
9896

99-
it('should keep a valid JWT at reconnection', () => {
100-
const
101-
jwt = generateJwt(),
102-
kuzzle = new Kuzzle(protocols.somewhereagain);
103-
104-
kuzzle.auth.checkToken = sinon.stub().resolves({
105-
valid: true
106-
});
107-
kuzzle.jwt = jwt;
108-
109-
return kuzzle.connect()
110-
.then(() => {
111-
should(kuzzle.auth.checkToken).be.calledOnce();
112-
113-
should(kuzzle.jwt).be.eql(jwt);
114-
});
115-
});
116-
117-
it('should empty the JWT at reconnection if it has expired', () => {
118-
const
119-
jwt = generateJwt(),
120-
kuzzle = new Kuzzle(protocols.somewhereagain);
121-
122-
kuzzle.auth.checkToken = sinon.stub().resolves({
123-
valid: false
124-
});
125-
kuzzle.jwt = jwt;
126-
127-
return kuzzle.connect()
128-
.then(() => {
129-
should(kuzzle.auth.checkToken).be.calledOnce();
130-
131-
should(kuzzle.jwt).be.null();
132-
});
133-
});
134-
13597
it('should register listeners upon receiving a "disconnect" event', () => {
13698
const
13799
kuzzle = new Kuzzle(protocols.somewhere),

test/kuzzle/protocol.test.js

Lines changed: 14 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,8 @@
1-
const
2-
should = require('should'),
3-
sinon = require('sinon'),
4-
ProtocolMock = require('../mocks/protocol.mock'),
5-
generateJwt = require('../mocks/generateJwt.mock'),
6-
{ Kuzzle } = require('../../src/Kuzzle');
1+
const should = require('should');
2+
const sinon = require('sinon');
3+
const ProtocolMock = require('../mocks/protocol.mock');
4+
const generateJwt = require('../mocks/generateJwt.mock');
5+
const { Kuzzle } = require('../../src/Kuzzle');
76

87
describe('Kuzzle protocol methods', () => {
98
let kuzzle;
@@ -25,17 +24,20 @@ describe('Kuzzle protocol methods', () => {
2524

2625
describe('#events', () => {
2726
it('should propagate protocol "queryError" events', () => {
28-
const
29-
eventStub = sinon.stub(),
30-
error = {message: 'foo-bar'},
31-
query = {foo: 'bar'};
27+
const eventStub = sinon.stub();
28+
const error = { message: 'foo-bar' };
29+
const request = { foo: 'bar' };
30+
3231
kuzzle.connect();
3332
kuzzle.addListener('queryError', eventStub);
3433

35-
kuzzle.protocol.emit('queryError', error, query);
34+
kuzzle.protocol.emit('queryError', { error, request });
3635

3736
should(eventStub).be.calledOnce();
38-
should(eventStub).be.calledWithMatch({message: 'foo-bar'}, {foo: 'bar'});
37+
should(eventStub).be.calledWithMatch({
38+
error: { message: 'foo-bar' },
39+
request: { foo: 'bar'}
40+
});
3941
});
4042

4143
it('should propagate protocol "tokenExpired" events', () => {

test/kuzzle/query.test.js

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -191,7 +191,7 @@ describe('Kuzzle query management', () => {
191191
should(kuzzle.protocol.query).not.be.called();
192192
should(eventStub)
193193
.be.calledOnce()
194-
.be.calledWith({request});
194+
.be.calledWithMatch({ request });
195195

196196
should(kuzzle._offlineQueue.length).be.eql(1);
197197
});
@@ -217,7 +217,7 @@ describe('Kuzzle query management', () => {
217217
should(kuzzle._offlineQueue.length).eql(0);
218218
should(eventStub)
219219
.be.calledOnce()
220-
.be.calledWith({request});
220+
.be.calledWithMatch({ request });
221221
});
222222
});
223223

@@ -233,7 +233,14 @@ describe('Kuzzle query management', () => {
233233
should(kuzzle.protocol.query)
234234
.be.not.be.called();
235235
});
236+
});
237+
238+
it('should clone the original request', async () => {
239+
const request = { controller: 'server', action: 'now' };
240+
241+
await kuzzle.query(request);
236242

243+
should(request).be.eql({ controller: 'server', action: 'now' });
237244
});
238245
});
239246
});

test/protocol/WebSocket.test.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -313,9 +313,9 @@ describe('WebSocket networking module', () => {
313313

314314
let expectedError;
315315
websocket.on('discarded', cb);
316-
websocket.on('queryError', (error, data) => {
316+
websocket.on('queryError', ({ error, request }) => {
317317
expectedError = error;
318-
cb2(error, data);
318+
cb2(error, request);
319319
});
320320
websocket.connect();
321321

0 commit comments

Comments
 (0)