Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: wrap-error-like-objects #1108

Merged
merged 3 commits into from
Jul 22, 2024
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
35 changes: 24 additions & 11 deletions src/sfError.ts
Original file line number Diff line number Diff line change
Expand Up @@ -136,17 +136,12 @@ export class SfError<T extends ErrorDataProperties = ErrorDataProperties> extend
}

const sfError =
err instanceof Error
? // a basic error with message and name. We make it the cause to preserve any other properties
SfError.create<T>({
message: err.message,
name: err.name,
cause: err,
})
: // ok, something was throws that wasn't error or string. Convert it to an Error that preserves the information as the cause and wrap that.
SfError.wrap<T>(
new Error(`SfError.wrap received type ${typeof err} but expects type Error or string`, { cause: err })
);
fromBasicError<T>(err) ??
fromErrorLikeObject<T>(err) ??
// something was thrown that wasn't error, error-like object or string. Convert it to an Error that preserves the information as the cause and wrap that.
SfError.wrap<T>(
new Error(`SfError.wrap received type ${typeof err} but expects type Error or string`, { cause: err })
);

// If the original error has a code, use that instead of name.
if (hasString(err, 'code')) {
Expand Down Expand Up @@ -190,3 +185,21 @@ export class SfError<T extends ErrorDataProperties = ErrorDataProperties> extend
};
}
}

const fromBasicError = <T extends ErrorDataProperties>(err: unknown): SfError<T> | undefined =>
err instanceof Error ? SfError.create<T>({ message: err.message, name: err.name, cause: err }) : undefined;

/* an object that is the result of spreading an Error or SfError */
const fromErrorLikeObject = <T extends ErrorDataProperties>(err: unknown): SfError<T> | undefined => {
if (!err || typeof err !== 'object') {
return undefined;
}
if (!('message' in err)) {
return undefined;
}
try {
return SfError.create<T>(err as Error);
} catch {
return undefined;
}
};
61 changes: 46 additions & 15 deletions test/unit/sfErrorTest.ts
Original file line number Diff line number Diff line change
Expand Up @@ -162,21 +162,7 @@ describe('SfError', () => {
assert(mySfError.cause instanceof Error);
expect(mySfError.cause.cause).to.equal(wrapMe);
});
it('an object', () => {
const wrapMe = { a: 2 };
const mySfError = SfError.wrap(wrapMe);
expect(mySfError).to.be.an.instanceOf(SfError);
assert(mySfError.cause instanceof Error);
expect(mySfError.cause.cause).to.equal(wrapMe);
});
it('an object that has a code', () => {
const wrapMe = { a: 2, code: 'foo' };
const mySfError = SfError.wrap(wrapMe);
expect(mySfError).to.be.an.instanceOf(SfError);
assert(mySfError.cause instanceof Error);
expect(mySfError.cause.cause).to.equal(wrapMe);
expect(mySfError.code).to.equal('foo');
});

it('an array', () => {
const wrapMe = [1, 5, 6];
const mySfError = SfError.wrap(wrapMe);
Expand All @@ -191,6 +177,51 @@ describe('SfError', () => {
assert(mySfError.cause instanceof Error);
expect(mySfError.cause.cause).to.equal(wrapMe);
});
describe('objects', () => {
describe('error-like', () => {
it('an object with only a message', () => {
const wrapMe = { message: 'foo' };
const mySfError = SfError.wrap(wrapMe);
expect(mySfError).to.be.an.instanceOf(SfError);
expect(mySfError.message).to.equal('foo');
expect(mySfError.exitCode).to.equal(1);
expect(mySfError.name).to.equal('SfError');
});
it('an object with several props and code', () => {
const wrapMe = { message: 'foo', name: 'bar', code: 'baz', exitCode: 100 };
const mySfError = SfError.wrap(wrapMe);
expect(mySfError).to.be.an.instanceOf(SfError);
expect(mySfError.message).to.equal('foo');
expect(mySfError.exitCode).to.equal(100);
expect(mySfError.name).to.equal('bar');
expect(mySfError.code).to.equal('baz');
});
});
describe('not error-like', () => {
it('an object', () => {
const wrapMe = { a: 2 };
const mySfError = SfError.wrap(wrapMe);
expect(mySfError).to.be.an.instanceOf(SfError);
assert(mySfError.cause instanceof Error);
expect(mySfError.cause.cause).to.equal(wrapMe);
});
it('empty object', () => {
const wrapMe = {};
const mySfError = SfError.wrap(wrapMe);
expect(mySfError).to.be.an.instanceOf(SfError);
assert(mySfError.cause instanceof Error);
expect(mySfError.cause.cause).to.equal(wrapMe);
});
it('an object that has a code', () => {
const wrapMe = { a: 2, code: 'foo' };
const mySfError = SfError.wrap(wrapMe);
expect(mySfError).to.be.an.instanceOf(SfError);
assert(mySfError.cause instanceof Error);
expect(mySfError.cause.cause).to.equal(wrapMe);
expect(mySfError.code).to.equal('foo');
});
});
});
});
});

Expand Down
Loading