Skip to content

Commit

Permalink
Merge pull request #43 from josemestebandevo/fix/error_handling
Browse files Browse the repository at this point in the history
Fix API errors handling. Extends not catched errors.
  • Loading branch information
tripu authored Oct 18, 2023
2 parents 5d4bb4d + 507bf34 commit efe464f
Show file tree
Hide file tree
Showing 10 changed files with 61 additions and 17 deletions.
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,11 @@

Change log.

## 3.1.4

Fix API error processing to avoid calling done callback.
Catch non catched errors to send them by callbacks as well.

## 3.1.3

Supports very long records by saving heap memory when downloading data.
Expand Down
27 changes: 18 additions & 9 deletions lib/fetchStreamReadable/fetchStreamReadable.js
Original file line number Diff line number Diff line change
Expand Up @@ -162,7 +162,6 @@ async function processReader(
isDone,
downloaded,
reader,
isErrorParsed(status),
status,
callbacks,
isFirst, // handle first chunk?
Expand All @@ -176,6 +175,14 @@ async function processReader(
} while (!isDone);
} catch (e) {
if (e.toString().includes('The user aborted a request.')) console.warn(e);
else if (!e.source) {
console.error('browser-sdk :: unhandled error :: ', e);
callbacks.processError(
e instanceof Error ? { error: e.message, type: e.name } :
typeof e === 'object' ? e :
{ error: e }
);
}
else console.error('browser-sdk :: ', e);
} finally {
cleanPendingEvents(callbacks);
Expand All @@ -187,7 +194,6 @@ async function parseChunk(
done,
value,
reader,
errorParsed,
status,
callbacks,
isFirst,
Expand All @@ -197,16 +203,11 @@ async function parseChunk(
const timerId = setTimeout(() => {
// When no more data needs to be consumed
// or terminal error, break the reading
if (done || errorParsed) {
measurePerformance('b-sdk-parsing', 'b-sdk-start', 'b-sdk-parsing-end');
if (!errorParsed) {
callbacks.processDone();
}
reader.releaseLock();
} else {
if (value && value.length) {
try {
// console.table({bufferString: status.bufferString,
//remains: status.remains, value: textDecoder.decode(value)});
if (done && !value.endsWith(separator)) value += separator;
status.bufferString += value;
parse({
status,
Expand All @@ -220,6 +221,14 @@ async function parseChunk(
clearTimeout(timerId);
}
}
const errorParsed = isErrorParsed(status);
if (done || errorParsed) {
measurePerformance('b-sdk-parsing', 'b-sdk-start', 'b-sdk-parsing-end');
if (!errorParsed) {
callbacks.processDone();
}
reader.releaseLock();
}
resolve();
}, 0);
});
Expand Down
7 changes: 5 additions & 2 deletions lib/fetchStreamReadable/parser/chunksParser.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ const {
isErrorFormat1,
isErrorFormat2,
isErrorFormat3,
isErrorFormat4,
joinMsgsIfObjectHasStrings,
} = require('./parserUtils.js');

Expand Down Expand Up @@ -50,10 +51,10 @@ function parse({ status, callbacks , isFirst = false, separator }) {
}

const lastLineIndex = status.bufferString.lastIndexOf(separator);
const lasLineIsComplete = lastLineIndex === status.bufferString.length;
const lastLineIsComplete = lastLineIndex === status.bufferString.length;
if(lastLineIndex >= 0){
const lines = parseBufferStringJson(status, lastLineIndex, separator);
if(!lasLineIsComplete){
if(!lastLineIsComplete){
status.bufferString = status.bufferString.substring(lastLineIndex + 1);
}else{
status.bufferString = null;
Expand Down Expand Up @@ -81,6 +82,7 @@ function parse({ status, callbacks , isFirst = false, separator }) {
isErrorFormat1(line)
|| isErrorFormat2(line)
|| isErrorFormat3(line)
|| isErrorFormat4(line)
) {
throw processErrorParsed(callbacks, status, line);
} else {
Expand All @@ -94,6 +96,7 @@ function parse({ status, callbacks , isFirst = false, separator }) {
logger(`browser-sdk :: chunksParser :: parse :: id = ${status.id} :: errorParsed = ${status.state}:: errorMsg = ${error.msg} :: bufferString = ${status.bufferString}`);
const e = new Error();
e.message = error.msg;
e.source = error.source;
throw e;
}
}
Expand Down
21 changes: 19 additions & 2 deletions lib/fetchStreamReadable/parser/parserUtils.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ const isErrorFormat2 = (event) =>
event && (event.msg || event.error) && event.status;
const isErrorFormat3 = (event) =>
event && event.e && Array.isArray(event.e) && event.e.length >= 2;
const isErrorFormat4 = (event) => event && event.e && event.e.msg;
const joinMsgsIfObjectHasStrings = (event) => {
if(event && event.object && Array.isArray(event.object)
&& event.object.every(i => (typeof i === 'string'))) {
Expand Down Expand Up @@ -47,6 +48,17 @@ function transformErrorFormat3(e) {
e.object = ['', e.e[1]];
}

function transformErrorFormat4(e, statusCode) {
e.msg = e.e.msg;
if (!e.status && statusCode) {
e.status = statusCode;
}
else if (!e.status && e.e) {
e.status = e.e.code;
}
delete e.e;
}

const parseJson = (s) => {
try {
return JSON.parse(s);
Expand All @@ -68,8 +80,7 @@ const parseBufferStringJson = (status, lastIndex, separator) => {
};

/**
* Normalizes an en error response trying to use
* the passed error properties.
* Normalizes an error response trying to use the passed error properties.
*/
function createErrorResponse(error) {
// in some cases error.object is present with 2 items
Expand All @@ -81,6 +92,7 @@ function createErrorResponse(error) {
error.status = 'status' in error ? error.status : 500;
error.timestamp = 'timestamp' in error ? error.timestamp : 0;
error.cid = 'cid' in error ? error.cid : '';
error.source = 'source' in error ? error.source : 'SDK';
}

function addToPending(cb, cbName, data) {
Expand Down Expand Up @@ -121,6 +133,9 @@ const homogenizeErrorFormat = (errorParsed, statusCode) => {
} else if (isErrorFormat1(errorParsed)) {
// same for format 1
transformErrorFormat1(errorParsed, statusCode);
} else if (isErrorFormat4(errorParsed)) {
// same for format 4
transformErrorFormat4(errorParsed, statusCode);
}
if (!errorParsed.status && statusCode) {
errorParsed.status = statusCode;
Expand All @@ -146,6 +161,7 @@ const homogenizeErrorFormat = (errorParsed, statusCode) => {
errorParsed.object = ['', errorParsed.msg];
}

errorParsed.source = 'API';
};

module.exports = {
Expand All @@ -161,5 +177,6 @@ module.exports = {
isErrorFormat1,
isErrorFormat2,
isErrorFormat3,
isErrorFormat4,
joinMsgsIfObjectHasStrings,
};
4 changes: 2 additions & 2 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@devoinc/browser-sdk",
"version": "3.1.3",
"version": "3.1.4",
"description": "Devo browser SDK",
"author": "Devo Dev Team",
"eslintConfig": {
Expand Down
5 changes: 5 additions & 0 deletions test/fetchStreamReadable.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -195,6 +195,11 @@ describe('fetchStreamReadable', () => {
input.statusCode,
input.separator || separator
).then((result) => {
// source is an attribute injected in some errors, but it should not be
// in the input (out) so it must be deleted before comparing
if (out && out.e && out.e[0].source) delete out.e[0].source;
if (outData && outData.e && outData.e[0].source)
delete outData.e[0].source;
JSON.stringify(out).should.be.equal(JSON.stringify(outData));
result.state.should.be.equal(finalState);
Object.entries(callbacksExpected).forEach((entry) => {
Expand Down
1 change: 1 addition & 0 deletions test/fetchStreamReadable/mocks/errors/errorNdjson.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{"e": {"msg": "com.devo.malote.syntax.ParseException: Encountered \" <ID> \"blablabla \"\" at line 1, column 1.\nWas expecting one of:\n <EOF> \n \"as\" ...\n \"every\" ...\n \"group\" ...\n \"limit\" ...\n \"offset\" ...\n \"order\" ...\n \"pragma\" ...\n \"select\" ...\n \"then\" ...\n \"where\" ...\n \",\" ...\n \";\" ...\n "}}
5 changes: 4 additions & 1 deletion test/fetchStreamReadable/mocks/errorsResponses.js
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,8 @@ const accessNotAllowed = toStrFromTxt('./test/fetchStreamReadable/mocks/errors/a

const unauthorizedMessage = toStrFromTxt('./test/fetchStreamReadable/mocks/errors/unauthorizedMessage.txt');

const errorNdjson = toStrFromTxt('./test/fetchStreamReadable/mocks/errors/errorNdjson.txt');

module.exports = {
genericError1,
specificError1,
Expand All @@ -60,5 +62,6 @@ module.exports = {
unauthorizedMessage,
whileExecutingTask,
wrongFieldName,
wrongOperation
wrongOperation,
errorNdjson,
};
1 change: 1 addition & 0 deletions test/parserUtils.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ describe('parser', () => {
homogenizeErrorFormat(parsedError, parsedError.status);
should(parsedError).have.property('msg');
should(parsedError).have.property('status');
should(parsedError).have.property('source');
createErrorResponse(parsedError);
should(parsedError).have.property('msg');
should(parsedError).have.property('status');
Expand Down

0 comments on commit efe464f

Please sign in to comment.