Skip to content

Commit 647f110

Browse files
fix: reduce the scope to only jsforce error MULTIPLE_API_ERRORS
1 parent 985e5e7 commit 647f110

File tree

5 files changed

+38
-103
lines changed

5 files changed

+38
-103
lines changed

src/SfCommandError.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
import { inspect } from 'node:util';
88
import { SfError, StructuredMessage } from '@salesforce/core';
99
import { AnyJson } from '@salesforce/ts-types';
10-
import { computeErrorCode, computeErrorData } from './errorHandling.js';
10+
import { computeErrorCode } from './errorHandling.js';
1111

1212
// These types are 90% the same as SfErrorOptions (but they aren't exported to extend)
1313
type ErrorDataProperties = AnyJson;
@@ -75,7 +75,7 @@ export class SfCommandError extends SfError {
7575
code: 'code' in err && err.code ? err.code : exitCode.toString(10),
7676
cause: sfError.cause,
7777
commandName: 'commandName' in err ? err.commandName : commandName,
78-
data: computeErrorData(err),
78+
data: 'data' in err ? err.data : undefined,
7979
result: 'result' in err ? err.result : undefined,
8080
context: 'context' in err ? err.context : commandName,
8181
warnings,

src/errorFormatting.ts

Lines changed: 25 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,8 @@
77

88
import { inspect } from 'node:util';
99
import type { Ansis } from 'ansis';
10-
import { Mode, Messages, envVars } from '@salesforce/core';
10+
import { Mode, Messages, envVars, SfError } from '@salesforce/core';
11+
import { AnyJson } from '@salesforce/ts-types';
1112
import { StandardColors } from './ux/standardColors.js';
1213
import { SfCommandError } from './SfCommandError.js';
1314
import { Ux } from './ux/ux.js';
@@ -45,7 +46,7 @@ export const formatActions = (
4546
export const formatError = (error: SfCommandError): string =>
4647
[
4748
`${formatErrorPrefix(error)} ${error.message}`,
48-
...formatMultipleErrorMessages(error),
49+
formatMultipleErrorMessages(error),
4950
...formatActions(error.actions ?? []),
5051
error.stack && envVars.getString('SF_ENV') === Mode.DEVELOPMENT
5152
? StandardColors.info(`\n*** Internal Diagnostic ***\n\n${inspect(error)}\n******\n`)
@@ -58,24 +59,27 @@ const formatErrorPrefix = (error: SfCommandError): string =>
5859
const formatErrorCode = (error: SfCommandError): string =>
5960
typeof error.code === 'string' || typeof error.code === 'number' ? ` (${error.code})` : '';
6061

61-
const formatMultipleErrorMessages = (error: SfCommandError): string[] => {
62-
if (!error.data || !Array.isArray(error.data) || error.data.length === 0) {
63-
return [];
64-
}
65-
66-
const errorData = error.data.map((d) => ({
67-
errorCode: (d as { errorCode: string }).errorCode || '',
68-
message: (d as { message: string }).message || '',
69-
}));
62+
const formatMultipleErrorMessages = (error: SfCommandError): string => {
63+
if (error.code === 'MULTIPLE_API_ERRORS' && error.cause) {
64+
const errorData = getErrorData(error.cause as SfError);
65+
if (errorData && Array.isArray(errorData) && errorData.length > 0) {
66+
const errors = errorData.map((d) => ({
67+
errorCode: (d as { errorCode: string }).errorCode || '',
68+
message: (d as { message: string }).message || '',
69+
}));
7070

71-
const ux = new Ux();
72-
return [
73-
ux.makeTable({
74-
data: errorData,
75-
columns: [
76-
{ key: 'errorCode', name: 'Error Code' },
77-
{ key: 'message', name: 'Message' },
78-
],
79-
}),
80-
];
71+
const ux = new Ux();
72+
return ux.makeTable({
73+
data: errors,
74+
columns: [
75+
{ key: 'errorCode', name: 'Error Code' },
76+
{ key: 'message', name: 'Message' },
77+
],
78+
});
79+
}
80+
}
81+
return '';
8182
};
83+
84+
const getErrorData = (error: SfError): AnyJson | undefined =>
85+
'data' in error && error.data ? error.data : error.cause ? getErrorData(error.cause as SfError) : undefined;

src/errorHandling.ts

Lines changed: 0 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,6 @@
77

88
import { SfError } from '@salesforce/core/sfError';
99
import { Errors } from '@oclif/core';
10-
import { AnyJson } from '@salesforce/ts-types';
11-
1210
/**
1311
*
1412
* Takes an error and returns an exit code.
@@ -45,27 +43,6 @@ export const computeErrorCode = (e: Error | SfError | Errors.CLIError): number =
4543
return typeof process.exitCode === 'number' ? process.exitCode : 1;
4644
};
4745

48-
/**
49-
* Computes and extracts error data from different error types.
50-
*
51-
* 1. If the error has a 'data' property with a value, returns that data
52-
* 2. If the error has a 'cause' property with a value:
53-
* - If the cause has a 'data' property, returns cause.data
54-
* - If not, returns undefined
55-
* 3. If none of the above conditions are met, returns undefined
56-
*
57-
* @param e - The error object to extract data from. Can be a standard Error, SfError, or CLIError
58-
* @returns The extracted data as AnyJson or undefined if no data is found
59-
*/
60-
export const computeErrorData = (e: Error | SfError | Errors.CLIError): AnyJson | undefined =>
61-
'data' in e && e.data
62-
? e.data
63-
: 'cause' in e && e.cause
64-
? 'data' in (e.cause as { data: AnyJson | undefined })
65-
? (e.cause as { data: AnyJson | undefined }).data
66-
: undefined
67-
: undefined;
68-
6946
/** identifies gacks via regex. Searches the error message, stack, and recursively checks the cause chain */
7047
export const errorIsGack = (error: Error | SfError): boolean => {
7148
/** see test for samples */

test/unit/errorFormatting.test.ts

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -80,19 +80,24 @@ describe('errorFormatting.formatError()', () => {
8080
expect(errorOutput).to.contain('result: undefined');
8181
});
8282

83-
it('should have correct output for multiple errors in table format ', () => {
83+
it('should have correct output for multiple errors in table format when errorCode is MULTIPLE_API_ERRORS', () => {
84+
const innerError = SfError.create({
85+
message: 'foo',
86+
data: [
87+
{ errorCode: 'ERROR_1', message: 'error 1' },
88+
{ errorCode: 'ERROR_2', message: 'error 2' },
89+
],
90+
});
8491
const sfError = SfError.create({
8592
name: 'myError',
8693
message: 'foo',
8794
actions: ['bar'],
8895
context: 'myContext',
8996
exitCode: 8,
90-
data: [
91-
{ errorCode: 'ERROR_1', message: 'error 1' },
92-
{ errorCode: 'ERROR_2', message: 'error 2' },
93-
],
97+
cause: innerError,
9498
});
9599
const err = SfCommandError.from(sfError, 'thecommand');
100+
err.code = 'MULTIPLE_API_ERRORS';
96101
const errorOutput = formatError(err);
97102
expect(errorOutput).to.match(/Error Code.+Message/);
98103
expect(errorOutput).to.match(/ERROR_1.+error 1/);

test/unit/errorHandling.test.ts

Lines changed: 1 addition & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,7 @@
66
*/
77
import { expect } from 'chai';
88
import { SfError } from '@salesforce/core/sfError';
9-
import { AnyJson } from '@salesforce/ts-types';
10-
import { Errors } from '@oclif/core';
11-
import { computeErrorCode, computeErrorData, errorIsGack, errorIsTypeError } from '../../src/errorHandling.js';
9+
import { computeErrorCode, errorIsGack, errorIsTypeError } from '../../src/errorHandling.js';
1210
import { SfCommandError } from '../../src/SfCommandError.js';
1311

1412
describe('typeErrors', () => {
@@ -199,52 +197,3 @@ describe('SfCommandError.toJson()', () => {
199197
});
200198
});
201199
});
202-
203-
describe('computeErrorData', () => {
204-
interface ErrorWithData extends Error {
205-
data?: AnyJson;
206-
}
207-
208-
it('should return data from error.data when present', () => {
209-
const sfError = SfError.create({
210-
name: 'myError',
211-
message: 'foo',
212-
actions: ['bar'],
213-
context: 'myContext',
214-
exitCode: 8,
215-
data: { foo: 'bar' },
216-
});
217-
expect(computeErrorData(sfError)).to.deep.equal({ foo: 'bar' });
218-
});
219-
220-
it('should return cause.data when error.data is not present but cause.data is', () => {
221-
const sfError = SfError.create({
222-
name: 'myError',
223-
message: 'foo',
224-
actions: ['bar'],
225-
context: 'myContext',
226-
exitCode: 8,
227-
});
228-
const err: ErrorWithData = { name: 'testError', message: 'baz', data: { foo: 'baz' } };
229-
sfError.cause = err;
230-
expect(computeErrorData(sfError)).to.deep.equal({ foo: 'baz' });
231-
});
232-
233-
it('should return undefined when no data or cause is present', () => {
234-
const error = new Error('test error') as ErrorWithData;
235-
expect(computeErrorData(error)).to.be.undefined;
236-
});
237-
238-
it('should handle SfError with data', () => {
239-
const error = new SfError('test error', 'TestError', [], 1, undefined);
240-
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
241-
(error as any).data = { foo: 'bar' };
242-
expect(computeErrorData(error)).to.deep.equal({ foo: 'bar' });
243-
});
244-
245-
it('should handle CLIError with data', () => {
246-
const err = new Errors.CLIError('Nonexistent flag: --INVALID\nSee more help with --help') as ErrorWithData;
247-
err.data = { foo: 'bar' };
248-
expect(computeErrorData(err)).to.deep.equal({ foo: 'bar' });
249-
});
250-
});

0 commit comments

Comments
 (0)