Skip to content

Commit

Permalink
refactor!: the TxSubmit endpoint no longer adds the stack trace when …
Browse files Browse the repository at this point in the history
…returning domain errors

BREAKING CHANGE:

- stack property of returned errors was removed
  • Loading branch information
AngelCastilloB committed Mar 22, 2023
1 parent 48eba64 commit f018f30
Show file tree
Hide file tree
Showing 5 changed files with 131 additions and 1 deletion.
12 changes: 11 additions & 1 deletion packages/core/src/Cardano/util/txSubmissionErrors.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { CardanoNodeErrors } from '../../CardanoNode';
import { isProductionEnvironment, stripStackTrace } from '@cardano-sdk/util';

/**
* Tests the provided error for an instanceof match in the TxSubmissionErrors object
Expand All @@ -17,10 +18,19 @@ export const asTxSubmissionError = (error: unknown): CardanoNodeErrors.TxSubmiss
if (Array.isArray(error)) {
for (const err of error) {
if (isTxSubmissionError(err)) {
if (isProductionEnvironment()) stripStackTrace(err);

return err;
}
}
return null;
}
return isTxSubmissionError(error) ? error : null;

if (isTxSubmissionError(error)) {
if (isProductionEnvironment()) stripStackTrace(error);

return error;
}

return null;
};
22 changes: 22 additions & 0 deletions packages/util/src/environment.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
/**
* Node.js environment configurations.
*/
export enum Environment {
/**
* Production environment.
*
* Node.js assumes it's always running in a development environment. You can signal Node.js that you are running
* in production by setting the NODE_ENV=production environment variable.
*/
Production = 'production',

/**
* Development environment.
*/
Development = 'development'
}

export const isProductionEnvironment = (): boolean => process.env.NODE_ENV === Environment.Production;

export const getEnvironmentConfiguration = (): Environment =>
isProductionEnvironment() ? Environment.Production : Environment.Development;
29 changes: 29 additions & 0 deletions packages/util/src/errors.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,18 @@ interface ErrorLike {
stack: string;
}

interface WithInnerError {
innerError: string | Error;
}

/**
* Gets whether the given error has an innerError.
*
* @param error The error to be checked for.
*/
const isWithInnerError = (error: unknown): error is WithInnerError =>
error !== null && typeof error === 'object' && 'innerError' in (error as never);

/**
* This type check works as an "error instanceof Error" check, but it let pass also those objects
* which implements the Error interface without inheriting from the same base class
Expand All @@ -22,6 +34,23 @@ const isErrorLike = (error: unknown): error is ErrorLike => {
return typeof message === 'string' && typeof stack === 'string';
};

/**
* Strips the stack trace of all errors and their inner errors recursively.
*
* @param error The error to be stripped of its stack trace.
*/
export const stripStackTrace = (error: unknown) => {
if (!error) return;

if (isErrorLike(error)) {
delete (error as Error).stack;
}

if (isWithInnerError(error)) {
stripStackTrace(error.innerError);
}
};

export class ComposableError<InnerError = unknown> extends CustomError {
private static stackDelimiter = '\n at ';

Expand Down
1 change: 1 addition & 0 deletions packages/util/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,3 +12,4 @@ export * from './network';
export * from './logging';
export * from './RunnableModule';
export * from './opaqueTypes';
export * from './environment';
68 changes: 68 additions & 0 deletions packages/util/test/errors.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
import { ComposableError } from '../';
import { stripStackTrace } from '../src';

class TestError<InnerError = unknown> extends ComposableError<InnerError> {
constructor(innerError: InnerError) {
super('Test error', innerError);
}
}

const throwsError = () => {
throw new Error('a');
};

const throwsComposableError = () => {
try {
throwsError();
} catch (error) {
throw new TestError(error);
}
};

describe('stripStackTrace', () => {
it('doesnt throw if given undefined', () => {
const error = undefined;
expect(() => stripStackTrace(error)).not.toThrow();
});

it('doesnt throw if given null', () => {
const error = null;
expect(() => stripStackTrace(error)).not.toThrow();
});

it('doesnt throw if a non Error object', () => {
const error = { a: 'a' };
expect(() => stripStackTrace(error)).not.toThrow();
expect(() => stripStackTrace(10)).not.toThrow();
expect(() => stripStackTrace('some error')).not.toThrow();
});

it('removes the stack field from the Error object', () => {
try {
throwsError();
} catch (error) {
stripStackTrace(error);
expect((error as Error).stack).toBeUndefined();
}
});

it('removes the stack field from the Error object and inner errors', () => {
try {
throwsComposableError();
} catch (error) {
let testError = error as TestError;
let innerError = testError.innerError as Error;

expect(testError.stack).toBeDefined();
expect(innerError.stack).toBeDefined();

stripStackTrace(error);

testError = error as TestError;
innerError = testError.innerError as Error;

expect(testError.stack).toBeUndefined();
expect(innerError.stack).toBeUndefined();
}
});
});

0 comments on commit f018f30

Please sign in to comment.