Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
54 changes: 44 additions & 10 deletions src/KuzzleError.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
'use strict';

import { hilightUserCode } from './utils/stackTrace';

/**
* Standard Kuzzle error.
*
Expand Down Expand Up @@ -38,19 +40,30 @@ export class KuzzleError extends Error {
*/
public count?: number;

constructor (apiError, stack = null) {
/**
* This class represents a Kuzzle API error.
* The SDK stack is needed alongside the protocol used.
* Those information will allow to construct a enhanced stacktrace:
*
* BadRequestError: lol
at new BadRequestError (/home/aschen/projets/kuzzleio/kuzzle/lib/kerror/errors/badRequestError.ts:26:5)
> at BaseController.handler [as sayHello] (/home/aschen/projets/kuzzleio/kuzzle/test.js:9:15)
at doAction (/home/aschen/projets/kuzzleio/kuzzle/lib/api/funnel.js:759:47)
at Funnel.processRequest (/home/aschen/projets/kuzzleio/kuzzle/lib/api/funnel.js:423:34)
|
|
HttpProtocol
|
|
at HttpProtocol.query (/home/aschen/projets/kuzzleio/sdk-javascript/src/protocols/abstract/Base.ts:127:19)
at Proxy.query (/home/aschen/projets/kuzzleio/sdk-javascript/src/Kuzzle.ts:598:26)
> at /home/aschen/projets/kuzzleio/sdk-javascript/test.js:8:18
at processTicksAndRejections (internal/process/task_queues.js:97:5)
*/
constructor (apiError, sdkStack: string, protocol: string) {
super(apiError.message);

this.status = apiError.status;
if (apiError.stack) {
Reflect.defineProperty(this, 'kuzzleStack', {
value: apiError.stack
});
}

if (stack) {
this.stack = stack;
}

this.id = apiError.id;
this.code = apiError.code;
Expand All @@ -60,5 +73,26 @@ export class KuzzleError extends Error {
this.errors = apiError.errors;
this.count = apiError.count;
}

// If we have a stacktrace coming from Kuzzle, merge it with
// the SDK one
if (apiError.stack) {
this.stack = apiError.stack + '\n';
this.stack += ' |\n';
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd put just one |\n.

this.stack += ' |\n';
this.stack += ` ${protocol}\n`;
this.stack += ' |\n';
this.stack += ' |\n';
}
else {
this.stack = `KuzzleError: ${apiError.message}\n`;
}

// Append the SDK stacktrace
this.stack += sdkStack
.split('\n')
.map(hilightUserCode)
.slice(1)
.join('\n');
}
}
2 changes: 1 addition & 1 deletion src/controllers/Auth.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { Jwt } from '../core/Jwt';
import { BaseController } from './Base';
import { User } from '../core/security/User';
import { JSONObject, ApiKey } from '../types';
import { RequestPayload } from 'src/types/RequestPayload';
import { RequestPayload } from '../types/RequestPayload';

/**
* Auth controller
Expand Down
6 changes: 5 additions & 1 deletion src/protocols/WebSocket.ts
Original file line number Diff line number Diff line change
Expand Up @@ -145,7 +145,11 @@ export default class WebSocketProtocol extends BaseProtocolRealtime {
// @deprecated
this.emit('discarded', data);

const error = new KuzzleError(data.error);
const error = new KuzzleError(
data.error,
(new Error().stack),
this.constructor.name);

this.emit('queryError', error, data);
}
};
Expand Down
11 changes: 4 additions & 7 deletions src/protocols/abstract/Base.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,6 @@ import { PendingRequest } from './PendingRequest';
import { JSONObject } from '../../types';
import { RequestPayload } from '../../types/RequestPayload';

const { hilightUserCode } = require('../../utils/stackTrace');

export abstract class KuzzleAbstractProtocol extends KuzzleEventEmitter {
private _pendingRequests: Map<string, PendingRequest>;
private _host: string;
Expand Down Expand Up @@ -136,19 +134,18 @@ Discarded request: ${JSON.stringify(request)}`));
let error;

// Wrap API error but directly throw errors that comes from SDK
if (response.error.id) {
error = new KuzzleError(response.error, stack);
if (response.error.status) {
error = new KuzzleError(response.error, stack, this.constructor.name);
}
else {
// Keep both stacktrace
// Keep both stacktrace because the one we captured in "stack" will give
// more information (async stacktrace are not very talkative)
const lines = stack.split('\n');
lines[0] = '';
response.error.stack += '\n' + lines.join('\n');
error = response.error;
}

error.stack = error.stack.split('\n').map(hilightUserCode).join('\n');

this.emit('queryError', error, request);

if (request.action !== 'logout' && error.id === 'security.token.invalid') {
Expand Down
31 changes: 31 additions & 0 deletions src/utils/stackTrace.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
'use strict';

/**
* Hilight user code
*
* e.g.
* at HttpProtocol.query (/home/aschen/projets/kuzzleio/sdk-javascript/src/protocols/abstract/Base.ts:127:19)
* at Proxy.query (/home/aschen/projets/kuzzleio/sdk-javascript/src/Kuzzle.ts:598:26)
* > at /home/aschen/projets/kuzzleio/sdk-javascript/test.js:8:18
* at processTicksAndRejections (internal/process/task_queues.js:97:5)]
*/
function hilightUserCode (line) {
// ignore first line (error message)
if (! line.includes(' at ')) {
return line;
}

if ( line.includes('sdk-javascript/src/') // ignore kuzzle code
|| (line.indexOf('at /') === -1 && line.charAt(line.indexOf('(') + 1) !== '/') // ignore node internal
|| line.includes('node_modules') // ignore dependencies
) {
return ' ' + line;
}

// hilight user code
return '>' + line;
}

module.exports = {
hilightUserCode,
};
8 changes: 6 additions & 2 deletions test/protocol/Base.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -185,7 +185,9 @@ describe('Common Protocol', () => {
should(error).be.instanceOf(KuzzleError);
should(error.message).be.eql('foo-bar');
should(error.status).be.eql(442);
should(error.kuzzleStack).be.eql('you are the bug');
should(error.stack).match(/you are the bug/);
should(error.stack).match(/KuzzleAbstractProtocol/);
should(error.stack).match(/>\s{4}at Context.<anonymous>/);
});
});

Expand All @@ -210,7 +212,9 @@ describe('Common Protocol', () => {
should(error).be.instanceOf(KuzzleError);
should(error.message).be.eql('foo-bar');
should(error.status).be.eql(206);
should(error.kuzzleStack).be.eql('you are the bug');
should(error.stack).match(/you are the bug/);
should(error.stack).match(/KuzzleAbstractProtocol/);
should(error.stack).match(/>\s{4}at Context.<anonymous>/);
should(error.errors).be.an.Array();
should(error.errors.length).eql(2);
should(error.count).eql(42);
Expand Down