Skip to content

Commit d3a19b4

Browse files
authored
feat: handle known client errors for salesforce (#303)
1 parent ed90916 commit d3a19b4

File tree

4 files changed

+104
-15
lines changed

4 files changed

+104
-15
lines changed

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@vesselapi/integrations",
3-
"version": "1.0.70",
3+
"version": "1.0.71",
44
"description": "Vessel integrations",
55
"main": "dist/index.js",
66
"module": "dist/index.mjs",

src/platforms/salesforce/client.ts

Lines changed: 74 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
1-
import { Auth, HttpsUrl } from '@/sdk';
1+
import { Auth, HttpsUrl, IntegrationError } from '@/sdk';
22
import { formatUpsertInputWithNative, makeRequestFactory } from '@/sdk/client';
3-
import { shake } from 'radash';
3+
import { crush, guard, shake } from 'radash';
44
import { z } from 'zod';
55
import { formatQuery, salesforceQueryBuilder } from './actions/query-builder';
66
import { SALESFORCE_API_VERSION } from './constants';
@@ -85,7 +85,7 @@ const request = makeRequestFactory(async (auth, options) => {
8585
} else {
8686
throw new Error('Salesforce only supports OAuth2 authentication');
8787
}
88-
});
88+
}, errorMapper);
8989

9090
const query = {
9191
list: <T extends z.ZodType>({
@@ -775,3 +775,74 @@ export const client = {
775775
passthrough: request.passthrough(),
776776
fetch: request.fetch(),
777777
};
778+
779+
const SalesforceErrorCodes = {
780+
INVALID_TYPE: 'Insufficient Permissions',
781+
INVALID_QUERY_FILTER_OPERATOR: 'Invalid value provided',
782+
DUPLICATES_DETECTED: 'Duplicate CRM object(s)',
783+
DUPLICATE_VALUE: 'Duplicate CRM object(s)',
784+
REQUIRED_FIELD_MISSING: 'Required field(s) missing',
785+
MALFORMED_ID:
786+
'The supplied association id is either malformed or for the wrong object type',
787+
INVALID_CROSS_REFERENCE_KEY: 'Invalid association id(s)',
788+
INVALID_EMAIL_ADDRESS: 'Invalid email address',
789+
OBJECT_NOT_FOUND:
790+
'CRM object not found. Either it does not exist, or is not accessible',
791+
NOT_FOUND:
792+
'CRM object not found. Either it does not exist, or is not accessible',
793+
INSUFFICIENT_ACCESS: 'Insufficient permissions or read-only',
794+
INSUFFICIENT_ACCESS_OR_READONLY: 'Insufficient permissions or read-only',
795+
INVALID_FIELD_FOR_INSERT_UPDATE: 'Field value(s) are invalid',
796+
INVALID_FIELD: 'Field value(s) are invalid',
797+
FIELD_INTEGRITY_EXCEPTION: 'Field value(s) are invalid',
798+
JSON_PARSER_ERROR: 'Field value(s) are invalid',
799+
INVALID_OR_NULL_FOR_RESTRICTED_PICKLIST: 'Field value(s) are invalid',
800+
// Happens when someone tries to set an event attendee
801+
// response status back to NEW for an existing invitee.
802+
INVALID_STATUS: 'Field value(s) are invalid',
803+
STORAGE_LIMIT_EXCEEDED:
804+
'Storage limit exceeded for Salesforce instance. Delete some records before creating new ones',
805+
CANNOT_INSERT_UPDATE_ACTIVATE_ENTITY: 'CRM object update failed',
806+
// Happens when you try to delete a record
807+
// that was already deleted.
808+
ENTITY_IS_DELETED: 'CRM object is deleted',
809+
// Happens when you try to perform a CRUD
810+
// operation on an entity that is associated with
811+
// an object you don't have access to.
812+
INSUFFICIENT_ACCESS_ON_CROSS_REFERENCE_ENTITY:
813+
'Insufficient permissions to access an object associated with this object',
814+
STRING_TOO_LONG: 'String field value(s) are too long',
815+
};
816+
817+
const serializeError = (error: any) => {
818+
return (
819+
guard(() =>
820+
JSON.stringify({
821+
name: error.name,
822+
message: error.message,
823+
code: error.code,
824+
stack: error.stack,
825+
cause: error.cause,
826+
...crush(error),
827+
}),
828+
) ?? `${error}`
829+
);
830+
};
831+
832+
function errorMapper(error: any) {
833+
const str = serializeError(error);
834+
const errorKey =
835+
(Object.keys(SalesforceErrorCodes).find((key) =>
836+
str.includes(key),
837+
) as keyof typeof SalesforceErrorCodes) ?? null;
838+
839+
if (!errorKey) return;
840+
841+
return new IntegrationError(SalesforceErrorCodes[errorKey], {
842+
type: 'client',
843+
cause: error,
844+
// We don't want to alert on these because
845+
// they are known issues/errors
846+
alert: false,
847+
});
848+
}

src/sdk/client.ts

Lines changed: 23 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -158,6 +158,11 @@ export const makeRequestFactory = (
158158
query: qs.List;
159159
}
160160
>,
161+
/**
162+
* Used to catch errors thrown during a request. Can
163+
* optionally return a different error to throw instead
164+
*/
165+
errorTransformer?: (error: any) => IntegrationError | undefined,
161166
) => {
162167
function createRequest<
163168
TArgs extends {},
@@ -201,17 +206,24 @@ export const makeRequestFactory = (
201206
Accept: 'application/json',
202207
};
203208

204-
return (
205-
options.method === 'GET' && options.json
206-
? _requestWithAxios
207-
: _requestWithFetch
208-
)({
209-
options: {
210-
...options,
211-
headers,
212-
},
213-
url,
214-
});
209+
try {
210+
return (
211+
options.method === 'GET' && options.json
212+
? _requestWithAxios
213+
: _requestWithFetch
214+
)({
215+
options: {
216+
...options,
217+
headers,
218+
},
219+
url,
220+
});
221+
} catch (err) {
222+
// Run the error through the error transformer
223+
// it may return a different error, if not we
224+
// throw the original error
225+
throw errorTransformer?.(err) ?? err;
226+
}
215227
});
216228
const makeValidatedRequest = async (
217229
auth: Auth,

src/sdk/error.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,12 @@ export type HttpErrorMeta = {
2222
export type ClientErrorMeta = {
2323
type: 'client';
2424
cause?: unknown;
25+
/**
26+
* If set to true this error will be treated
27+
* as an issue that needs attention, causing
28+
* an alert.
29+
*/
30+
alert?: boolean;
2531
};
2632

2733
export class IntegrationError extends Error {

0 commit comments

Comments
 (0)