Skip to content

fix: extract handler transform for ut, handle undefined #530

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

Merged
merged 1 commit into from
Apr 11, 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
23 changes: 23 additions & 0 deletions src/errorHandling.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
import { SfError } from '@salesforce/core';
import { OclifError } from '@oclif/core/lib/interfaces/errors.js';
import { SfCommandError } from './types.js';
import { removeEmpty } from './util.js';

/**
*
Expand Down Expand Up @@ -65,6 +66,28 @@ export const errorIsTypeError = (error: Error | SfError): boolean =>
Boolean(error.stack?.includes('TypeError')) ||
('cause' in error && error.cause instanceof Error && errorIsTypeError(error.cause));

export const errorToSfCommandError = (
codeFromError: number,
error: Error | SfError | SfCommandError,
commandName: string
): SfCommandError => ({
...removeEmpty({
code: codeFromError,
actions: 'actions' in error ? error.actions : null,
context: ('context' in error ? error.context : commandName) ?? commandName,
commandName: ('commandName' in error ? error.commandName : commandName) ?? commandName,
data: 'data' in error ? error.data : null,
result: 'result' in error ? error.result : null,
}),
...{
message: error.message,
name: error.name ?? 'Error',
status: codeFromError,
stack: error.stack,
exitCode: codeFromError,
},
});

/** custom typeGuard for handling the fact the SfCommand doesn't know about oclif error structure */
const isOclifError = <T extends Error | SfError | SfCommandError>(e: T): e is T & OclifError =>
'oclif' in e ? true : false;
28 changes: 4 additions & 24 deletions src/sfCommand.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,7 @@ import { SfCommandError } from './types.js';
import { formatActions, formatError } from './errorFormatting.js';
import { StandardColors } from './ux/standardColors.js';
import { confirm, secretPrompt, PromptInputs } from './ux/prompts.js';
import { removeEmpty } from './util.js';
import { computeErrorCode } from './errorHandling.js';
import { computeErrorCode, errorToSfCommandError } from './errorHandling.js';

Messages.importMessagesDirectoryFromMetaUrl(import.meta.url);
const messages = Messages.loadMessages('@salesforce/sf-plugins-core', 'messages');
Expand All @@ -34,7 +33,7 @@ export type SfCommandInterface = {
configurationVariablesSection?: HelpSection;
envVariablesSection?: HelpSection;
errorCodes?: HelpSection;
} & Command.Class
} & Command.Class;

/**
* A base command that provided common functionality for all sf commands.
Expand Down Expand Up @@ -391,26 +390,7 @@ export abstract class SfCommand<T> extends Command {
const codeFromError = computeErrorCode(error);
process.exitCode = codeFromError;

const sfErrorProperties = removeEmpty({
code: codeFromError,
actions: 'actions' in error ? error.actions : null,
context: 'context' in error ? error.context : this.statics.name,
commandName: 'commandName' in error ? error.commandName : this.statics.name,
data: 'data' in error ? error.data : null,
result: 'result' in error ? error.result : null,
});

// Create printable error object
const sfCommandError: SfCommand.Error = {
...sfErrorProperties,
...{
message: error.message,
name: error.name ?? 'Error',
status: process.exitCode,
stack: error.stack,
exitCode: process.exitCode,
},
};
const sfCommandError = errorToSfCommandError(codeFromError, error, this.statics.name);

if (this.jsonEnabled()) {
this.logJson(this.toErrorJson(sfCommandError));
Expand Down Expand Up @@ -469,6 +449,6 @@ export namespace SfCommand {
status: number;
result: T;
warnings?: Warning[];
}
};
export type Error = SfCommandError;
}
49 changes: 48 additions & 1 deletion test/unit/errorHandling.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
*/
import { expect } from 'chai';
import { SfError } from '@salesforce/core';
import { computeErrorCode, errorIsGack, errorIsTypeError } from '../../src/errorHandling.js';
import { computeErrorCode, errorIsGack, errorIsTypeError, errorToSfCommandError } from '../../src/errorHandling.js';

describe('typeErrors', () => {
let typeError: Error;
Expand Down Expand Up @@ -122,3 +122,50 @@ describe('precedence', () => {
expect(computeErrorCode(e)).to.equal(10);
});
});

describe('errorToSfCommandError', () => {
it('basic', () => {
const result = errorToSfCommandError(1, new Error('foo'), 'the:cmd');
expect(result).to.deep.include({
code: 1,
status: 1,
exitCode: 1,
commandName: 'the:cmd',
context: 'the:cmd',
message: 'foo',
name: 'Error', // this is the default
});
expect(result.stack).to.be.a('string').and.include('Error: foo');
});
describe('context', () => {
it('sfError with context', () => {
const sfError = SfError.create({ name: 'myError', message: 'foo', actions: ['bar'], context: 'myContext' });
const result = errorToSfCommandError(8, sfError, 'the:cmd');
expect(result).to.deep.include({
code: 8,
status: 8,
exitCode: 8,
commandName: 'the:cmd',
context: 'myContext',
message: 'foo',
name: 'myError',
});
expect(result.stack).to.be.a('string').and.include('myError: foo');
});
it('sfError with undefined context', () => {
const sfError = SfError.create({ name: 'myError', message: 'foo', actions: ['bar'], context: undefined });
const result = errorToSfCommandError(8, sfError, 'the:cmd');
expect(result).to.deep.include({
code: 8,
status: 8,
exitCode: 8,
commandName: 'the:cmd',
// defaults to the command name
context: 'the:cmd',
message: 'foo',
name: 'myError',
});
expect(result.stack).to.be.a('string').and.include('myError: foo');
});
});
});
Loading