From 2b6f56b9340a2545568e82444fe96cb9217e146e Mon Sep 17 00:00:00 2001 From: NinaFan0824 Date: Wed, 21 Feb 2024 12:50:53 +1300 Subject: [PATCH 01/16] add header input type --- ts-force/src/rest/restObject.ts | 121 ++++++++++++++++++++++++++++---- 1 file changed, 109 insertions(+), 12 deletions(-) diff --git a/ts-force/src/rest/restObject.ts b/ts-force/src/rest/restObject.ts index f729ff4..d9a471b 100644 --- a/ts-force/src/rest/restObject.ts +++ b/ts-force/src/rest/restObject.ts @@ -13,8 +13,7 @@ import { SObject } from './sObject'; import { CompositeError, StandardRestError } from './errors'; import { FieldProps } from '..'; import { CalendarDate, calendarToString, getCalendarDate } from '../utils/calendarDate'; -import { AxiosRequestHeaders, AxiosResponse } from 'axios'; -import { STATUS_CODES } from 'http'; +import { RawAxiosRequestHeaders, AxiosHeaders } from 'axios'; export interface DMLResponse { id: string; @@ -23,6 +22,53 @@ export interface DMLResponse { warnings: string[]; } +export type SforceAutoAssignHeader = 'TRUE' | 'FALSE'; +export type SforceCallOptionsHeader = { + client?: string; + defaultNamespace?: string; +}; +export type ContentEncodingHeader = 'gzip' | 'deflate'; +export type ETagHeader = string; +export type IfMatchHeader = string[]; +export type IfNoneMatchHeader = string[]; +export type IfModifiedSinceHeader = Date; +export type IfUnmodifiedSinceHeader = Date; +export type SforceDuplicateRuleHeader = { + allowSave?: boolean; + includeRecordDetails?: boolean; + runAsCurrentUser?: boolean; +}; +export type SforceMruHeader = { + updateMru: boolean; +}; +export type SforceQueryOptionsHeader = { + batchSize: number; +}; +export type PackageVersionHeader = { + package: string; + version: string; +}; + +export type GeneralRequestHeaders = { + 'Sforce-Auto-Assign'?: SforceAutoAssignHeader; + 'Sforce-Call-Options'?: SforceCallOptionsHeader; + 'Content-Encoding'?: ContentEncodingHeader; + ETag?: ETagHeader; + 'Sforce-Duplicate-Rule-Header'?: SforceDuplicateRuleHeader; + 'Sforce-Mru'?: SforceMruHeader; + 'Sforce-Query-Options'?: SforceQueryOptionsHeader; + 'x-sfdc-packageversion'?: PackageVersionHeader; +}; + +export type PatchOrPostRequestHeaders = { + 'If-Match'?: IfMatchHeader; + 'If-None-Match'?: IfNoneMatchHeader; + 'If-Modified-Since'?: IfModifiedSinceHeader; + 'If-Unmodified-Since'?: IfUnmodifiedSinceHeader; +}; + +export type RequestHeadersInput = GeneralRequestHeaders & PatchOrPostRequestHeaders; + export interface QueryOpts { /* Optional rest instance to use */ restInstance?: Rest; @@ -229,12 +275,7 @@ export abstract class RestObject extends SObject { * @returns {Promise} * @memberof RestObject */ - public async update(opts?: { - refresh?: boolean; - sendAllFields?: boolean; - ifModifiedSince?: Date; - ifUnmodifiedSince?: Date; - }): Promise { + public async update(opts?: { refresh?: boolean; sendAllFields?: boolean; headers?: RequestHeadersInput }): Promise { opts = opts || {}; if (this.id == null) { throw new Error('Must have Id to update!'); @@ -242,13 +283,11 @@ export abstract class RestObject extends SObject { if (opts.refresh === true) { return this.updateComposite(opts.sendAllFields); } else { - const headers = {}; - if (opts.ifModifiedSince) headers['If-Modified-Since'] = opts.ifModifiedSince.toUTCString() - if (opts.ifUnmodifiedSince) headers['If-Unmodified-Since'] = opts.ifUnmodifiedSince.toUTCString() + const headerOutput = this.setHeader(opts.headers); const response = await this._client.request.patch( `${this.attributes.url}/${this.id}`, this.toJson({ dmlMode: opts.sendAllFields ? 'update' : 'update_modified_only' }), - { headers } + { headers: headerOutput } ); this._modified.clear(); } @@ -497,4 +536,62 @@ export abstract class RestObject extends SObject { throw e; } } + + private setHeader(headersInput: RequestHeadersInput) { + const headers = {}; + for (const [name, value] of Object.entries(headersInput)) { + switch (name) { + case 'Sforce-Auto-Assign': + headers[name] = value as unknown as boolean; + break; + case 'Sforce-Call-Options': + headers[name] = this.parseSforceCallOptionsHeader(value as SforceCallOptionsHeader); + break; + case 'Content-Encoding': + headers[name] = value as 'gzip' | 'deflate'; + break; + case 'ETag': + headers[name] = value as string; + break; + case 'If-Match': + case 'If-None-Match': + headers[name] = (value as string[]).join(', '); + break; + case 'If-Modified-Since': + case 'If-Unmodified-Since': + headers[name] = (value as Date).toUTCString(); + break; + case 'Sforce-Duplicate-Rule-Header': + headers[name] = this.parseSforceDuplicateRuleHeader(value as SforceDuplicateRuleHeader); + break; + case 'Sforce-Mru': + headers[name] = value as unknown as boolean; + break; + case 'Sforce-Query-Options': + headers[name] = `batchSize=${value}`; + break; + case 'x-sfdc-packageversion': + const packageDetail = value as PackageVersionHeader; + headers[`x-sfdc-packageversion-${packageDetail.package}`] = packageDetail.version; + break; + } + } + + return headers; + } + + private parseSforceCallOptionsHeader = (value: SforceCallOptionsHeader): string => { + const options: string[] = []; + if (value.client) options.push(`client=${value.client}`); + if (value.defaultNamespace) options.push(`defaultNamespace=${value.defaultNamespace}`); + return options.join('; '); + }; + + private parseSforceDuplicateRuleHeader = (value: SforceDuplicateRuleHeader): string => { + const options: string[] = []; + if (value.allowSave !== undefined) options.push(`allowSave=${value.allowSave}`); + if (value.includeRecordDetails !== undefined) options.push(`includeRecordDetails=${value.includeRecordDetails}`); + if (value.runAsCurrentUser !== undefined) options.push(`runAsCurrentUser=${value.runAsCurrentUser}`); + return options.join(', '); + }; } From 95712de2f54932ee379e59be0580e36230d2332c Mon Sep 17 00:00:00 2001 From: NinaFan0824 Date: Wed, 21 Feb 2024 13:00:41 +1300 Subject: [PATCH 02/16] fix --- ts-force/src/rest/restObject.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ts-force/src/rest/restObject.ts b/ts-force/src/rest/restObject.ts index d9a471b..6760bff 100644 --- a/ts-force/src/rest/restObject.ts +++ b/ts-force/src/rest/restObject.ts @@ -555,7 +555,7 @@ export abstract class RestObject extends SObject { break; case 'If-Match': case 'If-None-Match': - headers[name] = (value as string[]).join(', '); + headers[name] = `"${(value as string[]).join('", "')}"`; break; case 'If-Modified-Since': case 'If-Unmodified-Since': From d85eb109ba45fcdcb6a0e2e33a649491c5225d27 Mon Sep 17 00:00:00 2001 From: NinaFan0824 Date: Wed, 21 Feb 2024 15:03:28 +1300 Subject: [PATCH 03/16] update version --- package-lock.json | 4 ++-- package.json | 2 +- ts-force-gen/package-lock.json | 4 ++-- ts-force-gen/package.json | 2 +- ts-force/package-lock.json | 4 ++-- ts-force/package.json | 2 +- 6 files changed, 9 insertions(+), 9 deletions(-) diff --git a/package-lock.json b/package-lock.json index c97dd04..2aec52a 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@vacasaoss/ts-force-project", - "version": "3.4.4", + "version": "3.4.5", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "@vacasaoss/ts-force-project", - "version": "3.4.4", + "version": "3.4.5", "hasInstallScript": true, "license": "ISC" } diff --git a/package.json b/package.json index e4b87ed..44fcdbb 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@vacasaoss/ts-force-project", - "version": "3.4.4", + "version": "3.4.5", "description": "", "scripts": { "postinstall": "cd ts-force && npm install && npm run build && cd ../ts-force-gen && npm install", diff --git a/ts-force-gen/package-lock.json b/ts-force-gen/package-lock.json index b73f5cf..2e894e4 100644 --- a/ts-force-gen/package-lock.json +++ b/ts-force-gen/package-lock.json @@ -1,12 +1,12 @@ { "name": "@vacasaoss/ts-force-gen", - "version": "3.4.4", + "version": "3.4.5", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "@vacasaoss/ts-force-gen", - "version": "3.4.4", + "version": "3.4.5", "license": "BSD-3-Clause", "dependencies": { "@salesforce/core": "^2.12.3", diff --git a/ts-force-gen/package.json b/ts-force-gen/package.json index c0cd74c..4ed18df 100644 --- a/ts-force-gen/package.json +++ b/ts-force-gen/package.json @@ -1,6 +1,6 @@ { "name": "@vacasaoss/ts-force-gen", - "version": "3.4.4", + "version": "3.4.5", "description": "Code generation for ts-force", "main": "build/index.js", "typings": "build/index.d.ts", diff --git a/ts-force/package-lock.json b/ts-force/package-lock.json index 5a6ec6c..209a243 100644 --- a/ts-force/package-lock.json +++ b/ts-force/package-lock.json @@ -1,12 +1,12 @@ { "name": "@vacasaoss/ts-force", - "version": "3.4.4", + "version": "3.4.5", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "@vacasaoss/ts-force", - "version": "3.4.4", + "version": "3.4.5", "license": "BSD-3-Clause", "dependencies": { "@types/cometd": "^4.0.4", diff --git a/ts-force/package.json b/ts-force/package.json index d599107..313ed87 100644 --- a/ts-force/package.json +++ b/ts-force/package.json @@ -1,6 +1,6 @@ { "name": "@vacasaoss/ts-force", - "version": "3.4.4", + "version": "3.4.5", "description": "a typescript client for connecting with salesforce APIs", "main": "build/index.js", "typings": "build/index.d.ts", From 458fa78fa1f9479684038385e860db7727581f47 Mon Sep 17 00:00:00 2001 From: NinaFan0824 Date: Wed, 21 Feb 2024 15:03:42 +1300 Subject: [PATCH 04/16] error handling --- ts-force/src/rest/restObject.ts | 22 +++++++++++++++------- 1 file changed, 15 insertions(+), 7 deletions(-) diff --git a/ts-force/src/rest/restObject.ts b/ts-force/src/rest/restObject.ts index 6760bff..a264d16 100644 --- a/ts-force/src/rest/restObject.ts +++ b/ts-force/src/rest/restObject.ts @@ -49,7 +49,7 @@ export type PackageVersionHeader = { version: string; }; -export type GeneralRequestHeaders = { +export type RequestHeadersInput = { 'Sforce-Auto-Assign'?: SforceAutoAssignHeader; 'Sforce-Call-Options'?: SforceCallOptionsHeader; 'Content-Encoding'?: ContentEncodingHeader; @@ -58,16 +58,14 @@ export type GeneralRequestHeaders = { 'Sforce-Mru'?: SforceMruHeader; 'Sforce-Query-Options'?: SforceQueryOptionsHeader; 'x-sfdc-packageversion'?: PackageVersionHeader; -}; - -export type PatchOrPostRequestHeaders = { - 'If-Match'?: IfMatchHeader; - 'If-None-Match'?: IfNoneMatchHeader; 'If-Modified-Since'?: IfModifiedSinceHeader; 'If-Unmodified-Since'?: IfUnmodifiedSinceHeader; + 'If-Match'?: IfMatchHeader; + 'If-None-Match'?: IfNoneMatchHeader; }; -export type RequestHeadersInput = GeneralRequestHeaders & PatchOrPostRequestHeaders; +export const GetOrHeadRequestHeaders = ['If-Match', 'If-None-Match', 'If-Modified-Since']; +export const PatchOrPostRequestHeaders = ['If-Match', 'If-None-Match', 'If-Unmodified-Since']; export interface QueryOpts { /* Optional rest instance to use */ @@ -289,6 +287,16 @@ export abstract class RestObject extends SObject { this.toJson({ dmlMode: opts.sendAllFields ? 'update' : 'update_modified_only' }), { headers: headerOutput } ); + + // Check if it contains Conditional Request Headers and deal with errors + const containsGetOrHeadHeader = Object.keys(opts.headers).some((key) => GetOrHeadRequestHeaders.includes(key)); + const containsPatchOrPostHeader = Object.keys(opts.headers).some((key) => PatchOrPostRequestHeaders.includes(key)); + if (containsGetOrHeadHeader && response.status === 304) { + throw new Error('Not Modified'); + } else if (containsPatchOrPostHeader && response.status === 412) { + throw new Error('Precondition Failed'); + } + this._modified.clear(); } return this; From c9419cbc30c60d282d4eaee81b82d7b40eaa9f5d Mon Sep 17 00:00:00 2001 From: NinaFan0824 Date: Wed, 21 Feb 2024 15:27:09 +1300 Subject: [PATCH 05/16] fix --- ts-force/src/rest/restObject.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ts-force/src/rest/restObject.ts b/ts-force/src/rest/restObject.ts index a264d16..4cef2da 100644 --- a/ts-force/src/rest/restObject.ts +++ b/ts-force/src/rest/restObject.ts @@ -556,7 +556,7 @@ export abstract class RestObject extends SObject { headers[name] = this.parseSforceCallOptionsHeader(value as SforceCallOptionsHeader); break; case 'Content-Encoding': - headers[name] = value as 'gzip' | 'deflate'; + headers[name] = value; break; case 'ETag': headers[name] = value as string; @@ -576,7 +576,7 @@ export abstract class RestObject extends SObject { headers[name] = value as unknown as boolean; break; case 'Sforce-Query-Options': - headers[name] = `batchSize=${value}`; + headers[name] = `batchSize=${(value as SforceQueryOptionsHeader).batchSize}`; break; case 'x-sfdc-packageversion': const packageDetail = value as PackageVersionHeader; From 6986f72ebdcb017bf4d345a3435623c4b2b07e97 Mon Sep 17 00:00:00 2001 From: NinaFan0824 Date: Wed, 21 Feb 2024 15:43:46 +1300 Subject: [PATCH 06/16] update --- ts-force/src/rest/restHeader.ts | 37 +++++++++ ts-force/src/rest/restObject.ts | 134 ++++++++++++-------------------- 2 files changed, 86 insertions(+), 85 deletions(-) create mode 100644 ts-force/src/rest/restHeader.ts diff --git a/ts-force/src/rest/restHeader.ts b/ts-force/src/rest/restHeader.ts new file mode 100644 index 0000000..e13ba35 --- /dev/null +++ b/ts-force/src/rest/restHeader.ts @@ -0,0 +1,37 @@ +export type SforceCallOptions = { + client?: string; + defaultNamespace?: string; +}; +export type SforceDuplicateRule = { + allowSave?: boolean; + includeRecordDetails?: boolean; + runAsCurrentUser?: boolean; +}; +export type SforceMru = { + updateMru: boolean; +}; +export type SforceQueryOptions = { + batchSize: number; +}; +export type PackageVersion = { + package: string; + version: string; +}; + +export type RequestHeadersInput = { + 'Sforce-Auto-Assign'?: boolean; + 'Sforce-Call-Options'?: SforceCallOptions; + 'Content-Encoding'?: 'gzip' | 'deflate'; + ETag?: string; + 'Sforce-Duplicate-Rule-Header'?: SforceDuplicateRule; + 'Sforce-Mru'?: SforceMru; + 'Sforce-Query-Options'?: SforceQueryOptions; + 'x-sfdc-packageversion'?: PackageVersion; + 'If-Modified-Since'?: Date; + 'If-Unmodified-Since'?: Date; + 'If-Match'?: string[]; + 'If-None-Match'?: string[]; +}; + +export const GetOrHeadRequestHeaders = ['If-Match', 'If-None-Match', 'If-Modified-Since']; +export const PatchOrPostRequestHeaders = ['If-Match', 'If-None-Match', 'If-Unmodified-Since']; diff --git a/ts-force/src/rest/restObject.ts b/ts-force/src/rest/restObject.ts index 4cef2da..81a35ae 100644 --- a/ts-force/src/rest/restObject.ts +++ b/ts-force/src/rest/restObject.ts @@ -14,6 +14,16 @@ import { CompositeError, StandardRestError } from './errors'; import { FieldProps } from '..'; import { CalendarDate, calendarToString, getCalendarDate } from '../utils/calendarDate'; import { RawAxiosRequestHeaders, AxiosHeaders } from 'axios'; +import { + RequestHeadersInput, + GetOrHeadRequestHeaders, + PatchOrPostRequestHeaders, + SforceCallOptions, + SforceDuplicateRule, + SforceMru, + SforceQueryOptions, + PackageVersion, +} from './restHeader'; export interface DMLResponse { id: string; @@ -22,51 +32,6 @@ export interface DMLResponse { warnings: string[]; } -export type SforceAutoAssignHeader = 'TRUE' | 'FALSE'; -export type SforceCallOptionsHeader = { - client?: string; - defaultNamespace?: string; -}; -export type ContentEncodingHeader = 'gzip' | 'deflate'; -export type ETagHeader = string; -export type IfMatchHeader = string[]; -export type IfNoneMatchHeader = string[]; -export type IfModifiedSinceHeader = Date; -export type IfUnmodifiedSinceHeader = Date; -export type SforceDuplicateRuleHeader = { - allowSave?: boolean; - includeRecordDetails?: boolean; - runAsCurrentUser?: boolean; -}; -export type SforceMruHeader = { - updateMru: boolean; -}; -export type SforceQueryOptionsHeader = { - batchSize: number; -}; -export type PackageVersionHeader = { - package: string; - version: string; -}; - -export type RequestHeadersInput = { - 'Sforce-Auto-Assign'?: SforceAutoAssignHeader; - 'Sforce-Call-Options'?: SforceCallOptionsHeader; - 'Content-Encoding'?: ContentEncodingHeader; - ETag?: ETagHeader; - 'Sforce-Duplicate-Rule-Header'?: SforceDuplicateRuleHeader; - 'Sforce-Mru'?: SforceMruHeader; - 'Sforce-Query-Options'?: SforceQueryOptionsHeader; - 'x-sfdc-packageversion'?: PackageVersionHeader; - 'If-Modified-Since'?: IfModifiedSinceHeader; - 'If-Unmodified-Since'?: IfUnmodifiedSinceHeader; - 'If-Match'?: IfMatchHeader; - 'If-None-Match'?: IfNoneMatchHeader; -}; - -export const GetOrHeadRequestHeaders = ['If-Match', 'If-None-Match', 'If-Modified-Since']; -export const PatchOrPostRequestHeaders = ['If-Match', 'If-None-Match', 'If-Unmodified-Since']; - export interface QueryOpts { /* Optional rest instance to use */ restInstance?: Rest; @@ -273,7 +238,7 @@ export abstract class RestObject extends SObject { * @returns {Promise} * @memberof RestObject */ - public async update(opts?: { refresh?: boolean; sendAllFields?: boolean; headers?: RequestHeadersInput }): Promise { + public async update(opts?: { refresh?: boolean; sendAllFields?: boolean; headers?: RequestHeadersInput[] }): Promise { opts = opts || {}; if (this.id == null) { throw new Error('Must have Id to update!'); @@ -545,57 +510,56 @@ export abstract class RestObject extends SObject { } } - private setHeader(headersInput: RequestHeadersInput) { + private setHeader(headersInput: RequestHeadersInput[]) { const headers = {}; - for (const [name, value] of Object.entries(headersInput)) { - switch (name) { - case 'Sforce-Auto-Assign': - headers[name] = value as unknown as boolean; - break; - case 'Sforce-Call-Options': - headers[name] = this.parseSforceCallOptionsHeader(value as SforceCallOptionsHeader); - break; - case 'Content-Encoding': - headers[name] = value; - break; - case 'ETag': - headers[name] = value as string; - break; - case 'If-Match': - case 'If-None-Match': - headers[name] = `"${(value as string[]).join('", "')}"`; - break; - case 'If-Modified-Since': - case 'If-Unmodified-Since': - headers[name] = (value as Date).toUTCString(); - break; - case 'Sforce-Duplicate-Rule-Header': - headers[name] = this.parseSforceDuplicateRuleHeader(value as SforceDuplicateRuleHeader); - break; - case 'Sforce-Mru': - headers[name] = value as unknown as boolean; - break; - case 'Sforce-Query-Options': - headers[name] = `batchSize=${(value as SforceQueryOptionsHeader).batchSize}`; - break; - case 'x-sfdc-packageversion': - const packageDetail = value as PackageVersionHeader; - headers[`x-sfdc-packageversion-${packageDetail.package}`] = packageDetail.version; - break; + for (const header of headersInput) { + for (const [name, value] of Object.entries(headersInput)) { + switch (name) { + case 'Sforce-Auto-Assign': + headers[name] = value as unknown as boolean; + break; + case 'Sforce-Call-Options': + headers[name] = this.parseSforceCallOptions(value as SforceCallOptions); + break; + case 'Content-Encoding': + case 'ETag': + headers[name] = value; + break; + case 'If-Match': + case 'If-None-Match': + headers[name] = `"${(value as string[]).join('", "')}"`; + break; + case 'If-Modified-Since': + case 'If-Unmodified-Since': + headers[name] = (value as Date).toUTCString(); + break; + case 'Sforce-Duplicate-Rule-Header': + headers[name] = this.parseSforceDuplicateRule(value as SforceDuplicateRule); + break; + case 'Sforce-Mru': + headers[name] = `updateMru=${(value as SforceMru).updateMru}`; + break; + case 'Sforce-Query-Options': + headers[name] = `batchSize=${(value as SforceQueryOptions).batchSize}`; + break; + case 'x-sfdc-packageversion': + const packageDetail = value as PackageVersion; + headers[`x-sfdc-packageversion-${packageDetail.package}`] = packageDetail.version; + break; + } } } - return headers; } - private parseSforceCallOptionsHeader = (value: SforceCallOptionsHeader): string => { + private parseSforceCallOptions = (value: SforceCallOptions): string => { const options: string[] = []; if (value.client) options.push(`client=${value.client}`); if (value.defaultNamespace) options.push(`defaultNamespace=${value.defaultNamespace}`); return options.join('; '); }; - private parseSforceDuplicateRuleHeader = (value: SforceDuplicateRuleHeader): string => { + private parseSforceDuplicateRule = (value: SforceDuplicateRule): string => { const options: string[] = []; if (value.allowSave !== undefined) options.push(`allowSave=${value.allowSave}`); if (value.includeRecordDetails !== undefined) options.push(`includeRecordDetails=${value.includeRecordDetails}`); From c6bda2000aefabc952293e958df45c905c56fbf4 Mon Sep 17 00:00:00 2001 From: NinaFan0824 Date: Wed, 21 Feb 2024 16:29:43 +1300 Subject: [PATCH 07/16] change to input array --- ts-force/src/rest/restObject.ts | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/ts-force/src/rest/restObject.ts b/ts-force/src/rest/restObject.ts index 81a35ae..502aa76 100644 --- a/ts-force/src/rest/restObject.ts +++ b/ts-force/src/rest/restObject.ts @@ -513,14 +513,12 @@ export abstract class RestObject extends SObject { private setHeader(headersInput: RequestHeadersInput[]) { const headers = {}; for (const header of headersInput) { - for (const [name, value] of Object.entries(headersInput)) { + for (const [name, value] of Object.entries(header)) { switch (name) { - case 'Sforce-Auto-Assign': - headers[name] = value as unknown as boolean; - break; case 'Sforce-Call-Options': headers[name] = this.parseSforceCallOptions(value as SforceCallOptions); break; + case 'Sforce-Auto-Assign': case 'Content-Encoding': case 'ETag': headers[name] = value; From adfc8e4920c38ea77a347516783ed3e8f2607334 Mon Sep 17 00:00:00 2001 From: NinaFan0824 Date: Wed, 21 Feb 2024 16:32:24 +1300 Subject: [PATCH 08/16] update --- ts-force/src/rest/restHeader.ts | 4 ++-- ts-force/src/rest/restObject.ts | 8 ++++++-- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/ts-force/src/rest/restHeader.ts b/ts-force/src/rest/restHeader.ts index e13ba35..11dfd24 100644 --- a/ts-force/src/rest/restHeader.ts +++ b/ts-force/src/rest/restHeader.ts @@ -33,5 +33,5 @@ export type RequestHeadersInput = { 'If-None-Match'?: string[]; }; -export const GetOrHeadRequestHeaders = ['If-Match', 'If-None-Match', 'If-Modified-Since']; -export const PatchOrPostRequestHeaders = ['If-Match', 'If-None-Match', 'If-Unmodified-Since']; +export const GetOrHeadRequestHeaders: (keyof RequestHeadersInput)[] = ['If-Match', 'If-None-Match', 'If-Modified-Since']; +export const PatchOrPostRequestHeaders: (keyof RequestHeadersInput)[] = ['If-Match', 'If-None-Match', 'If-Unmodified-Since']; diff --git a/ts-force/src/rest/restObject.ts b/ts-force/src/rest/restObject.ts index 502aa76..475104b 100644 --- a/ts-force/src/rest/restObject.ts +++ b/ts-force/src/rest/restObject.ts @@ -254,8 +254,12 @@ export abstract class RestObject extends SObject { ); // Check if it contains Conditional Request Headers and deal with errors - const containsGetOrHeadHeader = Object.keys(opts.headers).some((key) => GetOrHeadRequestHeaders.includes(key)); - const containsPatchOrPostHeader = Object.keys(opts.headers).some((key) => PatchOrPostRequestHeaders.includes(key)); + const containsGetOrHeadHeader = Object.keys(opts.headers).some((key) => + GetOrHeadRequestHeaders.includes(key as keyof RequestHeadersInput) + ); + const containsPatchOrPostHeader = Object.keys(opts.headers).some((key) => + PatchOrPostRequestHeaders.includes(key as keyof RequestHeadersInput) + ); if (containsGetOrHeadHeader && response.status === 304) { throw new Error('Not Modified'); } else if (containsPatchOrPostHeader && response.status === 412) { From aac34e3de1ef7e0bab85510128c30d9c3997fb08 Mon Sep 17 00:00:00 2001 From: NinaFan0824 Date: Thu, 22 Feb 2024 13:28:01 +1300 Subject: [PATCH 09/16] remove not used headers --- ts-force/src/rest/restHeader.ts | 16 ------------ ts-force/src/rest/restObject.ts | 44 ++++----------------------------- 2 files changed, 5 insertions(+), 55 deletions(-) diff --git a/ts-force/src/rest/restHeader.ts b/ts-force/src/rest/restHeader.ts index 11dfd24..86d9ce9 100644 --- a/ts-force/src/rest/restHeader.ts +++ b/ts-force/src/rest/restHeader.ts @@ -1,18 +1,6 @@ -export type SforceCallOptions = { - client?: string; - defaultNamespace?: string; -}; -export type SforceDuplicateRule = { - allowSave?: boolean; - includeRecordDetails?: boolean; - runAsCurrentUser?: boolean; -}; export type SforceMru = { updateMru: boolean; }; -export type SforceQueryOptions = { - batchSize: number; -}; export type PackageVersion = { package: string; version: string; @@ -20,12 +8,8 @@ export type PackageVersion = { export type RequestHeadersInput = { 'Sforce-Auto-Assign'?: boolean; - 'Sforce-Call-Options'?: SforceCallOptions; 'Content-Encoding'?: 'gzip' | 'deflate'; - ETag?: string; - 'Sforce-Duplicate-Rule-Header'?: SforceDuplicateRule; 'Sforce-Mru'?: SforceMru; - 'Sforce-Query-Options'?: SforceQueryOptions; 'x-sfdc-packageversion'?: PackageVersion; 'If-Modified-Since'?: Date; 'If-Unmodified-Since'?: Date; diff --git a/ts-force/src/rest/restObject.ts b/ts-force/src/rest/restObject.ts index 475104b..aa6bb84 100644 --- a/ts-force/src/rest/restObject.ts +++ b/ts-force/src/rest/restObject.ts @@ -10,20 +10,11 @@ import { import { Rest } from './rest'; import { getSFieldProps, SalesforceFieldType, SFieldProperties } from './sObjectDecorators'; import { SObject } from './sObject'; -import { CompositeError, StandardRestError } from './errors'; +import { CompositeError, ConditionalError, StandardRestError } from './errors'; import { FieldProps } from '..'; import { CalendarDate, calendarToString, getCalendarDate } from '../utils/calendarDate'; import { RawAxiosRequestHeaders, AxiosHeaders } from 'axios'; -import { - RequestHeadersInput, - GetOrHeadRequestHeaders, - PatchOrPostRequestHeaders, - SforceCallOptions, - SforceDuplicateRule, - SforceMru, - SforceQueryOptions, - PackageVersion, -} from './restHeader'; +import { RequestHeadersInput, GetOrHeadRequestHeaders, PatchOrPostRequestHeaders, SforceMru, PackageVersion } from './restHeader'; export interface DMLResponse { id: string; @@ -261,9 +252,9 @@ export abstract class RestObject extends SObject { PatchOrPostRequestHeaders.includes(key as keyof RequestHeadersInput) ); if (containsGetOrHeadHeader && response.status === 304) { - throw new Error('Not Modified'); + throw new ConditionalError('Not Modified'); } else if (containsPatchOrPostHeader && response.status === 412) { - throw new Error('Precondition Failed'); + throw new ConditionalError('Precondition Failed'); } this._modified.clear(); @@ -519,13 +510,9 @@ export abstract class RestObject extends SObject { for (const header of headersInput) { for (const [name, value] of Object.entries(header)) { switch (name) { - case 'Sforce-Call-Options': - headers[name] = this.parseSforceCallOptions(value as SforceCallOptions); - break; case 'Sforce-Auto-Assign': case 'Content-Encoding': - case 'ETag': - headers[name] = value; + headers[name] = value.toString(); break; case 'If-Match': case 'If-None-Match': @@ -535,15 +522,9 @@ export abstract class RestObject extends SObject { case 'If-Unmodified-Since': headers[name] = (value as Date).toUTCString(); break; - case 'Sforce-Duplicate-Rule-Header': - headers[name] = this.parseSforceDuplicateRule(value as SforceDuplicateRule); - break; case 'Sforce-Mru': headers[name] = `updateMru=${(value as SforceMru).updateMru}`; break; - case 'Sforce-Query-Options': - headers[name] = `batchSize=${(value as SforceQueryOptions).batchSize}`; - break; case 'x-sfdc-packageversion': const packageDetail = value as PackageVersion; headers[`x-sfdc-packageversion-${packageDetail.package}`] = packageDetail.version; @@ -553,19 +534,4 @@ export abstract class RestObject extends SObject { } return headers; } - - private parseSforceCallOptions = (value: SforceCallOptions): string => { - const options: string[] = []; - if (value.client) options.push(`client=${value.client}`); - if (value.defaultNamespace) options.push(`defaultNamespace=${value.defaultNamespace}`); - return options.join('; '); - }; - - private parseSforceDuplicateRule = (value: SforceDuplicateRule): string => { - const options: string[] = []; - if (value.allowSave !== undefined) options.push(`allowSave=${value.allowSave}`); - if (value.includeRecordDetails !== undefined) options.push(`includeRecordDetails=${value.includeRecordDetails}`); - if (value.runAsCurrentUser !== undefined) options.push(`runAsCurrentUser=${value.runAsCurrentUser}`); - return options.join(', '); - }; } From 77383b08dc3c4dfcf38a6aae0f53bd2d20a7e7db Mon Sep 17 00:00:00 2001 From: NinaFan0824 Date: Thu, 22 Feb 2024 14:25:03 +1300 Subject: [PATCH 10/16] remove Content-Encoding --- ts-force/src/rest/restHeader.ts | 1 - ts-force/src/rest/restObject.ts | 1 - 2 files changed, 2 deletions(-) diff --git a/ts-force/src/rest/restHeader.ts b/ts-force/src/rest/restHeader.ts index 86d9ce9..b884b3f 100644 --- a/ts-force/src/rest/restHeader.ts +++ b/ts-force/src/rest/restHeader.ts @@ -8,7 +8,6 @@ export type PackageVersion = { export type RequestHeadersInput = { 'Sforce-Auto-Assign'?: boolean; - 'Content-Encoding'?: 'gzip' | 'deflate'; 'Sforce-Mru'?: SforceMru; 'x-sfdc-packageversion'?: PackageVersion; 'If-Modified-Since'?: Date; diff --git a/ts-force/src/rest/restObject.ts b/ts-force/src/rest/restObject.ts index aa6bb84..1937e31 100644 --- a/ts-force/src/rest/restObject.ts +++ b/ts-force/src/rest/restObject.ts @@ -511,7 +511,6 @@ export abstract class RestObject extends SObject { for (const [name, value] of Object.entries(header)) { switch (name) { case 'Sforce-Auto-Assign': - case 'Content-Encoding': headers[name] = value.toString(); break; case 'If-Match': From 64df60d20c442a67752ecb85a5f8aef6b4a41871 Mon Sep 17 00:00:00 2001 From: NinaFan0824 Date: Thu, 22 Feb 2024 15:12:49 +1300 Subject: [PATCH 11/16] add generic header --- ts-force/src/rest/restHeader.ts | 14 ++++++++++++-- ts-force/src/rest/restObject.ts | 20 +++++++++----------- 2 files changed, 21 insertions(+), 13 deletions(-) diff --git a/ts-force/src/rest/restHeader.ts b/ts-force/src/rest/restHeader.ts index b884b3f..effc138 100644 --- a/ts-force/src/rest/restHeader.ts +++ b/ts-force/src/rest/restHeader.ts @@ -6,6 +6,11 @@ export type PackageVersion = { version: string; }; +export type GenericHeader = { + header: string; + value: string; +}; + export type RequestHeadersInput = { 'Sforce-Auto-Assign'?: boolean; 'Sforce-Mru'?: SforceMru; @@ -14,7 +19,12 @@ export type RequestHeadersInput = { 'If-Unmodified-Since'?: Date; 'If-Match'?: string[]; 'If-None-Match'?: string[]; + 'Generic-Header'?: GenericHeader; }; -export const GetOrHeadRequestHeaders: (keyof RequestHeadersInput)[] = ['If-Match', 'If-None-Match', 'If-Modified-Since']; -export const PatchOrPostRequestHeaders: (keyof RequestHeadersInput)[] = ['If-Match', 'If-None-Match', 'If-Unmodified-Since']; +export const ConditionalRequestHeaders: (keyof RequestHeadersInput)[] = [ + 'If-Match', + 'If-None-Match', + 'If-Modified-Since', + 'If-Unmodified-Since', +]; diff --git a/ts-force/src/rest/restObject.ts b/ts-force/src/rest/restObject.ts index 1937e31..c82f97e 100644 --- a/ts-force/src/rest/restObject.ts +++ b/ts-force/src/rest/restObject.ts @@ -13,8 +13,7 @@ import { SObject } from './sObject'; import { CompositeError, ConditionalError, StandardRestError } from './errors'; import { FieldProps } from '..'; import { CalendarDate, calendarToString, getCalendarDate } from '../utils/calendarDate'; -import { RawAxiosRequestHeaders, AxiosHeaders } from 'axios'; -import { RequestHeadersInput, GetOrHeadRequestHeaders, PatchOrPostRequestHeaders, SforceMru, PackageVersion } from './restHeader'; +import { RequestHeadersInput, ConditionalRequestHeaders, SforceMru, PackageVersion, GenericHeader } from './restHeader'; export interface DMLResponse { id: string; @@ -245,16 +244,11 @@ export abstract class RestObject extends SObject { ); // Check if it contains Conditional Request Headers and deal with errors - const containsGetOrHeadHeader = Object.keys(opts.headers).some((key) => - GetOrHeadRequestHeaders.includes(key as keyof RequestHeadersInput) + const containsConditionalHeader = Object.keys(opts.headers).some((key) => + ConditionalRequestHeaders.includes(key as keyof RequestHeadersInput) ); - const containsPatchOrPostHeader = Object.keys(opts.headers).some((key) => - PatchOrPostRequestHeaders.includes(key as keyof RequestHeadersInput) - ); - if (containsGetOrHeadHeader && response.status === 304) { - throw new ConditionalError('Not Modified'); - } else if (containsPatchOrPostHeader && response.status === 412) { - throw new ConditionalError('Precondition Failed'); + if (containsConditionalHeader && (response.status === 304 || response.status === 412)) { + throw new ConditionalError('Conditions not met'); } this._modified.clear(); @@ -528,6 +522,10 @@ export abstract class RestObject extends SObject { const packageDetail = value as PackageVersion; headers[`x-sfdc-packageversion-${packageDetail.package}`] = packageDetail.version; break; + case 'Generic-Header': + default: + const defaultValue = value as GenericHeader; + headers[defaultValue.header] = defaultValue.value; } } } From ec61f99c9d9ddad24460ef1df0191f72952a2f31 Mon Sep 17 00:00:00 2001 From: NinaFan0824 Date: Thu, 22 Feb 2024 15:12:59 +1300 Subject: [PATCH 12/16] test update --- ts-force/src/test/rest/restObjects.spec.ts | 292 +++++++++++---------- 1 file changed, 152 insertions(+), 140 deletions(-) diff --git a/ts-force/src/test/rest/restObjects.spec.ts b/ts-force/src/test/rest/restObjects.spec.ts index 7e7c667..be822d9 100644 --- a/ts-force/src/test/rest/restObjects.spec.ts +++ b/ts-force/src/test/rest/restObjects.spec.ts @@ -6,8 +6,6 @@ import { getCalendarDate } from '../../utils/calendarDate'; import { Account, Contact, User } from '../assets/sobs'; import { createDefaultClient } from '../helper'; - - describe('Generated Classes', () => { before(async () => { await createDefaultClient(); @@ -16,7 +14,7 @@ describe('Generated Classes', () => { it('Parent Relationship', async () => { let acc = new Account({ name: 'test account', - website: 'example.com' + website: 'example.com', }); await acc.insert(); @@ -25,22 +23,25 @@ describe('Generated Classes', () => { let contact = new Contact({ accountId: acc.id, firstName: `test`, - lastName: `contact` + lastName: `contact`, }); await contact.insert(); - let contact2 = (await Contact.retrieve(`SELECT ${Contact.FIELDS.name}, ${Contact.FIELDS.account}.${Account.FIELDS.website} FROM ${Contact.API_NAME} WHERE Id = '${contact.id}'`))[0]; + let contact2 = ( + await Contact.retrieve( + `SELECT ${Contact.FIELDS.name}, ${Contact.FIELDS.account}.${Account.FIELDS.website} FROM ${Contact.API_NAME} WHERE Id = '${contact.id}'` + ) + )[0]; expect(contact2.account).not.to.be.empty; expect(contact2.account.website).to.equal(acc.website); - let acc2 = (await Account.retrieve(f => ({ - select: [ - ...f.select('name', 'accountNumber'), - ...f.parent('parent').select('id', 'accountNumber') - ], - where: [{ field: 'id', val: acc.id }] - })))[0]; + let acc2 = ( + await Account.retrieve((f) => ({ + select: [...f.select('name', 'accountNumber'), ...f.parent('parent').select('id', 'accountNumber')], + where: [{ field: 'id', val: acc.id }], + })) + )[0]; // if the lookup rel is blank, it should be null expect(acc2.parent).to.be.null; expect(acc2.name).to.not.be.null; @@ -56,19 +57,18 @@ describe('Generated Classes', () => { it('Child Relationship', async () => { let acc = new Account({ name: 'test account', - website: 'example.com' + website: 'example.com', }); await acc.insert(); expect(acc.id).to.not.be.null; - let acc2 = (await Account.retrieve(f => ({ - select: [ - ...f.select('name', 'accountNumber'), - f.subQuery('contacts', cf => ({ select: cf.select('id', 'firstName') })) - ], - where: [{ field: 'id', val: acc.id }] - })))[0]; + let acc2 = ( + await Account.retrieve((f) => ({ + select: [...f.select('name', 'accountNumber'), f.subQuery('contacts', (cf) => ({ select: cf.select('id', 'firstName') }))], + where: [{ field: 'id', val: acc.id }], + })) + )[0]; expect(acc2.contacts).to.not.be.null; expect(acc2.contacts.length).to.eq(0); @@ -76,17 +76,16 @@ describe('Generated Classes', () => { let contact = new Contact({ accountId: acc.id, firstName: `test`, - lastName: `contact` + lastName: `contact`, }); await contact.insert(); - acc2 = (await Account.retrieve(f => ({ - select: [ - ...f.select('name', 'accountNumber'), - f.subQuery('contacts', cf => ({ select: cf.select('id', 'firstName') })) - ], - where: [{ field: 'id', val: acc.id }] - })))[0]; + acc2 = ( + await Account.retrieve((f) => ({ + select: [...f.select('name', 'accountNumber'), f.subQuery('contacts', (cf) => ({ select: cf.select('id', 'firstName') }))], + where: [{ field: 'id', val: acc.id }], + })) + )[0]; // if the lookup rel is blank, it should be null expect(acc2.contacts.length).to.eq(1); expect(acc2.contacts[0].firstName).to.eq(contact.firstName); @@ -95,7 +94,7 @@ describe('Generated Classes', () => { it('DML End-to-End', async () => { let acc = new Account({ - name: 'test account' + name: 'test account', }); await acc.insert(); expect(acc.id).to.not.be.null; @@ -114,9 +113,8 @@ describe('Generated Classes', () => { }); it('Stale Memory', async () => { - let acc = new Account({ - name: 'stale name' + name: 'stale name', }); await acc.insert(); expect(acc.id).to.not.be.null; @@ -141,7 +139,7 @@ describe('Generated Classes', () => { // test update let acc4 = new Account({ name: 'new name 2', - id: acc.id + id: acc.id, }); await acc4.update(); @@ -159,9 +157,8 @@ describe('Generated Classes', () => { }); it('prepareFor Update all', async () => { - let acc = new Account({ - name: 'account' + name: 'account', }); await acc.insert(); @@ -173,49 +170,63 @@ describe('Generated Classes', () => { expect(sob['Name']).to.equal(acc2.name); await acc.delete(); - }) - - it('handle ifModifiedSince', async () => { + }); + + it('handle multiple headers', async () => { const modifiedDate = new Date(); let acc = new Account({ - name: 'account' + name: 'account', }); await acc.insert(); //@ts-expect-error acc._client.request.interceptors.request.use((config) => { - if (config.method === 'patch') - expect(config.headers["If-Modified-Since"]).to.equal(modifiedDate.toUTCString()); - return config + if (config.method === 'patch') expect(config.headers['If-Modified-Since']).to.equal(modifiedDate.toUTCString()); + return config; + }); + acc.name = 'account2'; + await acc.update({ + refresh: false, + headers: [ + { + 'If-Unmodified-Since': new Date(), + 'If-Modified-Since': new Date('2021-12-12'), + 'Sforce-Auto-Assign': false, + 'Sforce-Mru': { updateMru: true }, + }, + ], }); - acc.name = "account2"; - await acc.update({ refresh: false, ifModifiedSince: new Date()}); await acc.delete(); - }) + }); - it('handle ifUmodifiedSince', async () => { + it('handle generic headers', async () => { const modifiedDate = new Date(); let acc = new Account({ - name: 'account' + name: 'account', }); await acc.insert(); //@ts-expect-error acc._client.request.interceptors.request.use((config) => { - if (config.method === 'patch') - expect(config.headers["If-Unmodified-Since"]).to.equal(modifiedDate.toUTCString()); - return config + if (config.method === 'patch') expect(config.headers['If-Unmodified-Since']).to.equal(modifiedDate.toUTCString()); + return config; + }); + acc.name = 'account2'; + await acc.update({ + refresh: false, + headers: [ + { + 'Generic-Header': { header: 'Sforce-Mru', value: 'updateMru=true' }, + }, + ], }); - acc.name = "account2"; - await acc.update({ refresh: false, ifUnmodifiedSince: new Date()}); await acc.delete(); - }) + }); it('multi-picklists', async () => { - const { multiPick } = Account.PICKLIST; let acc = new Account({ name: 'account', - multiPick: [multiPick.ONE, 'two'] + multiPick: [multiPick.ONE, 'two'], }); await acc.insert(); @@ -233,10 +244,9 @@ describe('Generated Classes', () => { }); it('refresh', async () => { - let acc = new Account({ name: 'test account', - website: 'www.facepamplet.com' + website: 'www.facepamplet.com', }); await acc.insert(); let acc2 = (await Account.retrieve(`SELECT Id, Name FROM Account WHERE Id = '${acc.id}'`))[0]; @@ -253,14 +263,14 @@ describe('Generated Classes', () => { let acc = new Account({ name: 'testing' }); await acc.insert(); - let acc2 = (await Account.retrieve(fields => { - return { - select: fields.select('lastModifiedDate', 'createdById'), - where: [ - { field: fields.select('id'), op: '=', val: acc.id } - ] - }; - }))[0]; + let acc2 = ( + await Account.retrieve((fields) => { + return { + select: fields.select('lastModifiedDate', 'createdById'), + where: [{ field: fields.select('id'), op: '=', val: acc.id }], + }; + }) + )[0]; expect(acc2.lastModifiedDate).not.null; expect(acc2.createdById).not.null; @@ -268,59 +278,73 @@ describe('Generated Classes', () => { it('Collections End-to-End', async () => { let acc = new Account({ - name: 'test account' + name: 'test account', }); await acc.insert(); let contacts = []; const contactSize = 50; for (let i = 0; i < contactSize; i++) { - contacts.push(new Contact({ - accountId: acc.id, - firstName: `test`, - lastName: `contact ${i}` - })); + contacts.push( + new Contact({ + accountId: acc.id, + firstName: `test`, + lastName: `contact ${i}`, + }) + ); } let bulk = new CompositeCollection(); let insertResults = await bulk.insert(contacts); expect(contacts[0]._modified.size).to.equal(0); - insertResults.forEach(r => { + insertResults.forEach((r) => { if (!r.success) { - throw r.errors.map(e => e.message).join(', '); + throw r.errors.map((e) => e.message).join(', '); } }); - acc = (await Account.retrieve(`SELECT Id, (SELECT ${Contact.FIELDS.name.apiName} FROM ${Account.FIELDS.contacts.apiName}) FROM Account WHERE Id = '${acc.id}'`))[0]; + acc = ( + await Account.retrieve( + `SELECT Id, (SELECT ${Contact.FIELDS.name.apiName} FROM ${Account.FIELDS.contacts.apiName}) FROM Account WHERE Id = '${acc.id}'` + ) + )[0]; expect(acc.contacts.length).to.equal(contactSize); - contacts.forEach(c => { + contacts.forEach((c) => { c.email = 'test@example.com'; }); let updateResults = await bulk.update(contacts); expect(contacts[0]._modified.size).to.equal(0); - updateResults.forEach(r => { + updateResults.forEach((r) => { if (!r.success) { - throw r.errors.map(e => e.message).join(', '); + throw r.errors.map((e) => e.message).join(', '); } }); - acc = (await Account.retrieve(`SELECT Id, (SELECT ${Contact.FIELDS.name.apiName}, ${Contact.FIELDS.email} FROM ${Account.FIELDS.contacts.apiName}) FROM Account WHERE Id = '${acc.id}'`))[0]; - acc.contacts.forEach(c => { + acc = ( + await Account.retrieve( + `SELECT Id, (SELECT ${Contact.FIELDS.name.apiName}, ${Contact.FIELDS.email} FROM ${Account.FIELDS.contacts.apiName}) FROM Account WHERE Id = '${acc.id}'` + ) + )[0]; + acc.contacts.forEach((c) => { expect(c.email).to.equal('test@example.com'); }); let delResults = await bulk.delete(contacts); - delResults.forEach(r => { + delResults.forEach((r) => { if (!r.success) { - throw r.errors.map(e => e.message).join(', '); + throw r.errors.map((e) => e.message).join(', '); } }); - acc = (await Account.retrieve(`SELECT Id, (SELECT ${Contact.FIELDS.name.apiName} FROM ${Account.FIELDS.contacts.apiName}) FROM Account WHERE Id = '${acc.id}'`))[0]; + acc = ( + await Account.retrieve( + `SELECT Id, (SELECT ${Contact.FIELDS.name.apiName} FROM ${Account.FIELDS.contacts.apiName}) FROM Account WHERE Id = '${acc.id}'` + ) + )[0]; expect(acc.contacts.length).to.equal(0); @@ -330,14 +354,10 @@ describe('Generated Classes', () => { it('should set relation by external id', async () => { const extId = '123abcd'; // setup account - let accs = await Account.retrieve(f => { + let accs = await Account.retrieve((f) => { return { - select: [ - ...f.select('id', 'testExternalId') - ], - where: [ - { field: f.select('testExternalId'), op: '=', val: extId } - ] + select: [...f.select('id', 'testExternalId')], + where: [{ field: f.select('testExternalId'), op: '=', val: extId }], }; }); @@ -347,7 +367,7 @@ describe('Generated Classes', () => { } else { acc = new Account({ name: 'test external id account', - testExternalId: extId + testExternalId: extId, }); await acc.insert(); } @@ -355,83 +375,74 @@ describe('Generated Classes', () => { let contact = new Contact({ firstName: 'john', lastName: 'doe', - account: new Account({ testExternalId: extId }) + account: new Account({ testExternalId: extId }), }); await contact.insert(); - let retCont = await Contact.retrieve(f => { + let retCont = await Contact.retrieve((f) => { return { - select: [ - f.select('accountId') - ], - where: [ - { field: f.select('id'), op: '=', val: contact.id } - ] + select: [f.select('accountId')], + where: [{ field: f.select('id'), op: '=', val: contact.id }], }; }); expect(retCont[0].accountId).to.equal(acc.id); - }); it('prepareFor Apex', async () => { - let c = new Contact({ id: '123', accountId: 'abc', firstName: `john`, lastName: `doe`, account: new Account({ - name: 'acme' - }) + name: 'acme', + }), }); - expect(c.toJson({ dmlMode: 'all', sendChildObj: true, sendParentObj: true, hideAttributes: true })).to.deep.equal( - { - Id: '123', - AccountId: 'abc', - FirstName: 'john', - LastName: 'doe', - Account: { - Name: 'acme' - } - } - ); + expect(c.toJson({ dmlMode: 'all', sendChildObj: true, sendParentObj: true, hideAttributes: true })).to.deep.equal({ + Id: '123', + AccountId: 'abc', + FirstName: 'john', + LastName: 'doe', + Account: { + Name: 'acme', + }, + }); let acc = new Account({ id: '123', - contacts: [new Contact({ - firstName: 'john', - lastName: 'doe' - })] + contacts: [ + new Contact({ + firstName: 'john', + lastName: 'doe', + }), + ], }); - expect(acc.toJson({ dmlMode: 'all', sendChildObj: true, sendParentObj: true, hideAttributes: true })).to.deep.equal( - { - Id: '123', - Contacts: { records: [{ FirstName: 'john', LastName: 'doe' }] } - } - ); + expect(acc.toJson({ dmlMode: 'all', sendChildObj: true, sendParentObj: true, hideAttributes: true })).to.deep.equal({ + Id: '123', + Contacts: { records: [{ FirstName: 'john', LastName: 'doe' }] }, + }); }); it('prepareFor Apex End To End', async () => { let acc = new Account({ id: '123', - contacts: [new Contact({ - firstName: 'john', - lastName: 'doe' - })], + contacts: [ + new Contact({ + firstName: 'john', + lastName: 'doe', + }), + ], owner: new User({ - 'email': 'example@gmai.com' - }) + email: 'example@gmai.com', + }), }); const sfSob = acc.toJson({ dmlMode: 'all', sendChildObj: true, sendParentObj: true, hideAttributes: true }); - let data = (await new Rest().request.post( - '/services/apexrest/myservice', - { acc: sfSob } - )).data; + let data = (await new Rest().request.post('/services/apexrest/myservice', { acc: sfSob })).data; const retAcc = Account.fromSFObject(data); expect(acc.id).to.deep.equal(retAcc.id); expect(acc.contacts[0].firstName).to.deep.equal(retAcc.contacts[0].firstName); @@ -441,8 +452,8 @@ describe('Generated Classes', () => { it('composite retrieve', async () => { let results = await compositeRetrieve(Contact, Account, User)( - f => ({ select: f.select('cleanStatus', 'email', 'accountId') }), - f => ({ select: f.select('numberOfEmployees', 'accountNumber', 'active') }), + (f) => ({ select: f.select('cleanStatus', 'email', 'accountId') }), + (f) => ({ select: f.select('numberOfEmployees', 'accountNumber', 'active') }), 'select x from blah' ); let contactResults = results[0]; @@ -474,19 +485,20 @@ describe('Generated Classes', () => { let c = new Contact({ firstName: 'test', lastName: 'contact', - birthdate: getCalendarDate() + birthdate: getCalendarDate(), }); await c.insert(); expect(c.id).to.not.be.null; - let c2 = (await Contact.retrieve(f => ({ - select: [f.select('birthdate')], - where: [{ field: 'id', val: c.id }] - })))[0]; + let c2 = ( + await Contact.retrieve((f) => ({ + select: [f.select('birthdate')], + where: [{ field: 'id', val: c.id }], + })) + )[0]; expect(c2.birthdate).to.deep.equal(c.birthdate); await c.delete(); }); - }); From ff6f4e0ba4a837436950ceddbc3b5e8a984f7eae Mon Sep 17 00:00:00 2001 From: NinaFan0824 Date: Thu, 22 Feb 2024 15:53:02 +1300 Subject: [PATCH 13/16] test update --- ts-force/src/rest/restObject.ts | 1 + ts-force/src/test/rest/restObjects.spec.ts | 31 +++++----------------- 2 files changed, 7 insertions(+), 25 deletions(-) diff --git a/ts-force/src/rest/restObject.ts b/ts-force/src/rest/restObject.ts index c82f97e..080c70d 100644 --- a/ts-force/src/rest/restObject.ts +++ b/ts-force/src/rest/restObject.ts @@ -526,6 +526,7 @@ export abstract class RestObject extends SObject { default: const defaultValue = value as GenericHeader; headers[defaultValue.header] = defaultValue.value; + break; } } } diff --git a/ts-force/src/test/rest/restObjects.spec.ts b/ts-force/src/test/rest/restObjects.spec.ts index be822d9..223cba6 100644 --- a/ts-force/src/test/rest/restObjects.spec.ts +++ b/ts-force/src/test/rest/restObjects.spec.ts @@ -173,14 +173,17 @@ describe('Generated Classes', () => { }); it('handle multiple headers', async () => { - const modifiedDate = new Date(); let acc = new Account({ name: 'account', }); await acc.insert(); //@ts-expect-error acc._client.request.interceptors.request.use((config) => { - if (config.method === 'patch') expect(config.headers['If-Modified-Since']).to.equal(modifiedDate.toUTCString()); + if (config.method === 'patch') expect(config.headers['Sforce-Auto-Assign']).to.equal('false'); + if (config.method === 'patch') expect(config.headers['Sforce-Mru']).to.equal('updateMru=true'); + if (config.method === 'patch') expect(config.headers['If-Unmodified-Since']).to.equal(new Date().toUTCString()); + if (config.method === 'patch') expect(config.headers['If-Modified-Since']).to.equal(new Date('2021-12-12').toUTCString()); + if (config.method === 'patch') expect(config.headers['Custom-Header']).to.equal('Custom-Value'); return config; }); acc.name = 'account2'; @@ -192,29 +195,7 @@ describe('Generated Classes', () => { 'If-Modified-Since': new Date('2021-12-12'), 'Sforce-Auto-Assign': false, 'Sforce-Mru': { updateMru: true }, - }, - ], - }); - await acc.delete(); - }); - - it('handle generic headers', async () => { - const modifiedDate = new Date(); - let acc = new Account({ - name: 'account', - }); - await acc.insert(); - //@ts-expect-error - acc._client.request.interceptors.request.use((config) => { - if (config.method === 'patch') expect(config.headers['If-Unmodified-Since']).to.equal(modifiedDate.toUTCString()); - return config; - }); - acc.name = 'account2'; - await acc.update({ - refresh: false, - headers: [ - { - 'Generic-Header': { header: 'Sforce-Mru', value: 'updateMru=true' }, + 'Generic-Header': { header: 'Custom-Header', value: 'Custom-Value' }, }, ], }); From b89812db9639a89addbb4243d1286eb5a8860794 Mon Sep 17 00:00:00 2001 From: NinaFan0824 Date: Mon, 26 Feb 2024 10:19:36 +1300 Subject: [PATCH 14/16] update --- ts-force/src/rest/restHeader.ts | 26 +---------- ts-force/src/rest/restObject.ts | 53 +++++++--------------- ts-force/src/test/rest/restObjects.spec.ts | 20 +++----- 3 files changed, 26 insertions(+), 73 deletions(-) diff --git a/ts-force/src/rest/restHeader.ts b/ts-force/src/rest/restHeader.ts index effc138..8beebf4 100644 --- a/ts-force/src/rest/restHeader.ts +++ b/ts-force/src/rest/restHeader.ts @@ -1,30 +1,8 @@ -export type SforceMru = { - updateMru: boolean; -}; -export type PackageVersion = { - package: string; - version: string; -}; - -export type GenericHeader = { - header: string; - value: string; -}; - export type RequestHeadersInput = { 'Sforce-Auto-Assign'?: boolean; - 'Sforce-Mru'?: SforceMru; - 'x-sfdc-packageversion'?: PackageVersion; 'If-Modified-Since'?: Date; 'If-Unmodified-Since'?: Date; - 'If-Match'?: string[]; - 'If-None-Match'?: string[]; - 'Generic-Header'?: GenericHeader; }; +export type GeneralRequestHeadersInput = RequestHeadersInput | Record; -export const ConditionalRequestHeaders: (keyof RequestHeadersInput)[] = [ - 'If-Match', - 'If-None-Match', - 'If-Modified-Since', - 'If-Unmodified-Since', -]; +export const ConditionalRequestHeaders = ['If-Match', 'If-None-Match', 'If-Modified-Since', 'If-Unmodified-Since']; diff --git a/ts-force/src/rest/restObject.ts b/ts-force/src/rest/restObject.ts index 080c70d..f30607b 100644 --- a/ts-force/src/rest/restObject.ts +++ b/ts-force/src/rest/restObject.ts @@ -13,7 +13,7 @@ import { SObject } from './sObject'; import { CompositeError, ConditionalError, StandardRestError } from './errors'; import { FieldProps } from '..'; import { CalendarDate, calendarToString, getCalendarDate } from '../utils/calendarDate'; -import { RequestHeadersInput, ConditionalRequestHeaders, SforceMru, PackageVersion, GenericHeader } from './restHeader'; +import { ConditionalRequestHeaders, GeneralRequestHeadersInput } from './restHeader'; export interface DMLResponse { id: string; @@ -228,7 +228,7 @@ export abstract class RestObject extends SObject { * @returns {Promise} * @memberof RestObject */ - public async update(opts?: { refresh?: boolean; sendAllFields?: boolean; headers?: RequestHeadersInput[] }): Promise { + public async update(opts?: { refresh?: boolean; sendAllFields?: boolean; headers?: GeneralRequestHeadersInput }): Promise { opts = opts || {}; if (this.id == null) { throw new Error('Must have Id to update!'); @@ -236,19 +236,20 @@ export abstract class RestObject extends SObject { if (opts.refresh === true) { return this.updateComposite(opts.sendAllFields); } else { - const headerOutput = this.setHeader(opts.headers); + const headers = this.convertValuesToStrings(opts.headers); const response = await this._client.request.patch( `${this.attributes.url}/${this.id}`, this.toJson({ dmlMode: opts.sendAllFields ? 'update' : 'update_modified_only' }), - { headers: headerOutput } + { headers } ); // Check if it contains Conditional Request Headers and deal with errors - const containsConditionalHeader = Object.keys(opts.headers).some((key) => - ConditionalRequestHeaders.includes(key as keyof RequestHeadersInput) - ); + const containsConditionalHeader = Object.keys(headers).some((key) => ConditionalRequestHeaders.includes(key)); if (containsConditionalHeader && (response.status === 304 || response.status === 412)) { - throw new ConditionalError('Conditions not met'); + const problematicHeaders = Object.keys(headers) + .filter((key) => ConditionalRequestHeaders.includes(key)) + .join(', '); + throw new ConditionalError(`Conditions not met. Headers: ${problematicHeaders}`); } this._modified.clear(); @@ -499,36 +500,16 @@ export abstract class RestObject extends SObject { } } - private setHeader(headersInput: RequestHeadersInput[]) { + private convertValuesToStrings(headerInput: GeneralRequestHeadersInput) { const headers = {}; - for (const header of headersInput) { - for (const [name, value] of Object.entries(header)) { - switch (name) { - case 'Sforce-Auto-Assign': - headers[name] = value.toString(); - break; - case 'If-Match': - case 'If-None-Match': - headers[name] = `"${(value as string[]).join('", "')}"`; - break; - case 'If-Modified-Since': - case 'If-Unmodified-Since': - headers[name] = (value as Date).toUTCString(); - break; - case 'Sforce-Mru': - headers[name] = `updateMru=${(value as SforceMru).updateMru}`; - break; - case 'x-sfdc-packageversion': - const packageDetail = value as PackageVersion; - headers[`x-sfdc-packageversion-${packageDetail.package}`] = packageDetail.version; - break; - case 'Generic-Header': - default: - const defaultValue = value as GenericHeader; - headers[defaultValue.header] = defaultValue.value; - break; - } + for (const key in headerInput) { + const value = headerInput[key]; + if (value instanceof Date) { + headerInput[key] = (value as Date).toUTCString(); + } else if (typeof value === 'boolean') { + headerInput[key] = value.toString(); } + headers[key] = headerInput[key]; } return headers; } diff --git a/ts-force/src/test/rest/restObjects.spec.ts b/ts-force/src/test/rest/restObjects.spec.ts index 223cba6..1b2c1e9 100644 --- a/ts-force/src/test/rest/restObjects.spec.ts +++ b/ts-force/src/test/rest/restObjects.spec.ts @@ -179,25 +179,19 @@ describe('Generated Classes', () => { await acc.insert(); //@ts-expect-error acc._client.request.interceptors.request.use((config) => { - if (config.method === 'patch') expect(config.headers['Sforce-Auto-Assign']).to.equal('false'); + if (config.method === 'patch') expect(config.headers['Sforce-Auto-Assign']).to.equal('true'); if (config.method === 'patch') expect(config.headers['Sforce-Mru']).to.equal('updateMru=true'); - if (config.method === 'patch') expect(config.headers['If-Unmodified-Since']).to.equal(new Date().toUTCString()); - if (config.method === 'patch') expect(config.headers['If-Modified-Since']).to.equal(new Date('2021-12-12').toUTCString()); - if (config.method === 'patch') expect(config.headers['Custom-Header']).to.equal('Custom-Value'); + if (config.method === 'patch') expect(config.headers['If-Modified-Since']).to.equal(new Date().toUTCString()); return config; }); acc.name = 'account2'; await acc.update({ refresh: false, - headers: [ - { - 'If-Unmodified-Since': new Date(), - 'If-Modified-Since': new Date('2021-12-12'), - 'Sforce-Auto-Assign': false, - 'Sforce-Mru': { updateMru: true }, - 'Generic-Header': { header: 'Custom-Header', value: 'Custom-Value' }, - }, - ], + headers: { + 'Sforce-Auto-Assign': true, + 'If-Modified-Since': new Date(), + 'Sforce-Mru': 'updateMru=true', + }, }); await acc.delete(); }); From ef2a7b076e583a953664221dc152fbaeec195433 Mon Sep 17 00:00:00 2001 From: NinaFan0824 Date: Tue, 27 Feb 2024 13:28:37 +1300 Subject: [PATCH 15/16] change format back --- ts-force/src/test/rest/restObjects.spec.ts | 256 +++++++++++---------- 1 file changed, 130 insertions(+), 126 deletions(-) diff --git a/ts-force/src/test/rest/restObjects.spec.ts b/ts-force/src/test/rest/restObjects.spec.ts index 1b2c1e9..6ddd330 100644 --- a/ts-force/src/test/rest/restObjects.spec.ts +++ b/ts-force/src/test/rest/restObjects.spec.ts @@ -6,6 +6,8 @@ import { getCalendarDate } from '../../utils/calendarDate'; import { Account, Contact, User } from '../assets/sobs'; import { createDefaultClient } from '../helper'; + + describe('Generated Classes', () => { before(async () => { await createDefaultClient(); @@ -14,7 +16,7 @@ describe('Generated Classes', () => { it('Parent Relationship', async () => { let acc = new Account({ name: 'test account', - website: 'example.com', + website: 'example.com' }); await acc.insert(); @@ -23,25 +25,22 @@ describe('Generated Classes', () => { let contact = new Contact({ accountId: acc.id, firstName: `test`, - lastName: `contact`, + lastName: `contact` }); await contact.insert(); - let contact2 = ( - await Contact.retrieve( - `SELECT ${Contact.FIELDS.name}, ${Contact.FIELDS.account}.${Account.FIELDS.website} FROM ${Contact.API_NAME} WHERE Id = '${contact.id}'` - ) - )[0]; + let contact2 = (await Contact.retrieve(`SELECT ${Contact.FIELDS.name}, ${Contact.FIELDS.account}.${Account.FIELDS.website} FROM ${Contact.API_NAME} WHERE Id = '${contact.id}'`))[0]; expect(contact2.account).not.to.be.empty; expect(contact2.account.website).to.equal(acc.website); - let acc2 = ( - await Account.retrieve((f) => ({ - select: [...f.select('name', 'accountNumber'), ...f.parent('parent').select('id', 'accountNumber')], - where: [{ field: 'id', val: acc.id }], - })) - )[0]; + let acc2 = (await Account.retrieve(f => ({ + select: [ + ...f.select('name', 'accountNumber'), + ...f.parent('parent').select('id', 'accountNumber') + ], + where: [{ field: 'id', val: acc.id }] + })))[0]; // if the lookup rel is blank, it should be null expect(acc2.parent).to.be.null; expect(acc2.name).to.not.be.null; @@ -57,18 +56,19 @@ describe('Generated Classes', () => { it('Child Relationship', async () => { let acc = new Account({ name: 'test account', - website: 'example.com', + website: 'example.com' }); await acc.insert(); expect(acc.id).to.not.be.null; - let acc2 = ( - await Account.retrieve((f) => ({ - select: [...f.select('name', 'accountNumber'), f.subQuery('contacts', (cf) => ({ select: cf.select('id', 'firstName') }))], - where: [{ field: 'id', val: acc.id }], - })) - )[0]; + let acc2 = (await Account.retrieve(f => ({ + select: [ + ...f.select('name', 'accountNumber'), + f.subQuery('contacts', cf => ({ select: cf.select('id', 'firstName') })) + ], + where: [{ field: 'id', val: acc.id }] + })))[0]; expect(acc2.contacts).to.not.be.null; expect(acc2.contacts.length).to.eq(0); @@ -76,16 +76,17 @@ describe('Generated Classes', () => { let contact = new Contact({ accountId: acc.id, firstName: `test`, - lastName: `contact`, + lastName: `contact` }); await contact.insert(); - acc2 = ( - await Account.retrieve((f) => ({ - select: [...f.select('name', 'accountNumber'), f.subQuery('contacts', (cf) => ({ select: cf.select('id', 'firstName') }))], - where: [{ field: 'id', val: acc.id }], - })) - )[0]; + acc2 = (await Account.retrieve(f => ({ + select: [ + ...f.select('name', 'accountNumber'), + f.subQuery('contacts', cf => ({ select: cf.select('id', 'firstName') })) + ], + where: [{ field: 'id', val: acc.id }] + })))[0]; // if the lookup rel is blank, it should be null expect(acc2.contacts.length).to.eq(1); expect(acc2.contacts[0].firstName).to.eq(contact.firstName); @@ -94,7 +95,7 @@ describe('Generated Classes', () => { it('DML End-to-End', async () => { let acc = new Account({ - name: 'test account', + name: 'test account' }); await acc.insert(); expect(acc.id).to.not.be.null; @@ -113,8 +114,9 @@ describe('Generated Classes', () => { }); it('Stale Memory', async () => { + let acc = new Account({ - name: 'stale name', + name: 'stale name' }); await acc.insert(); expect(acc.id).to.not.be.null; @@ -139,7 +141,7 @@ describe('Generated Classes', () => { // test update let acc4 = new Account({ name: 'new name 2', - id: acc.id, + id: acc.id }); await acc4.update(); @@ -157,8 +159,9 @@ describe('Generated Classes', () => { }); it('prepareFor Update all', async () => { + let acc = new Account({ - name: 'account', + name: 'account' }); await acc.insert(); @@ -170,11 +173,12 @@ describe('Generated Classes', () => { expect(sob['Name']).to.equal(acc2.name); await acc.delete(); - }); - + }) + it('handle multiple headers', async () => { + const modifiedDate = new Date(); let acc = new Account({ - name: 'account', + name: 'account' }); await acc.insert(); //@ts-expect-error @@ -182,9 +186,9 @@ describe('Generated Classes', () => { if (config.method === 'patch') expect(config.headers['Sforce-Auto-Assign']).to.equal('true'); if (config.method === 'patch') expect(config.headers['Sforce-Mru']).to.equal('updateMru=true'); if (config.method === 'patch') expect(config.headers['If-Modified-Since']).to.equal(new Date().toUTCString()); - return config; + return config }); - acc.name = 'account2'; + acc.name = "account2"; await acc.update({ refresh: false, headers: { @@ -194,14 +198,15 @@ describe('Generated Classes', () => { }, }); await acc.delete(); - }); + }) it('multi-picklists', async () => { + const { multiPick } = Account.PICKLIST; let acc = new Account({ name: 'account', - multiPick: [multiPick.ONE, 'two'], + multiPick: [multiPick.ONE, 'two'] }); await acc.insert(); @@ -219,9 +224,10 @@ describe('Generated Classes', () => { }); it('refresh', async () => { + let acc = new Account({ name: 'test account', - website: 'www.facepamplet.com', + website: 'www.facepamplet.com' }); await acc.insert(); let acc2 = (await Account.retrieve(`SELECT Id, Name FROM Account WHERE Id = '${acc.id}'`))[0]; @@ -238,14 +244,14 @@ describe('Generated Classes', () => { let acc = new Account({ name: 'testing' }); await acc.insert(); - let acc2 = ( - await Account.retrieve((fields) => { - return { - select: fields.select('lastModifiedDate', 'createdById'), - where: [{ field: fields.select('id'), op: '=', val: acc.id }], - }; - }) - )[0]; + let acc2 = (await Account.retrieve(fields => { + return { + select: fields.select('lastModifiedDate', 'createdById'), + where: [ + { field: fields.select('id'), op: '=', val: acc.id } + ] + }; + }))[0]; expect(acc2.lastModifiedDate).not.null; expect(acc2.createdById).not.null; @@ -253,73 +259,59 @@ describe('Generated Classes', () => { it('Collections End-to-End', async () => { let acc = new Account({ - name: 'test account', + name: 'test account' }); await acc.insert(); let contacts = []; const contactSize = 50; for (let i = 0; i < contactSize; i++) { - contacts.push( - new Contact({ - accountId: acc.id, - firstName: `test`, - lastName: `contact ${i}`, - }) - ); + contacts.push(new Contact({ + accountId: acc.id, + firstName: `test`, + lastName: `contact ${i}` + })); } let bulk = new CompositeCollection(); let insertResults = await bulk.insert(contacts); expect(contacts[0]._modified.size).to.equal(0); - insertResults.forEach((r) => { + insertResults.forEach(r => { if (!r.success) { - throw r.errors.map((e) => e.message).join(', '); + throw r.errors.map(e => e.message).join(', '); } }); - acc = ( - await Account.retrieve( - `SELECT Id, (SELECT ${Contact.FIELDS.name.apiName} FROM ${Account.FIELDS.contacts.apiName}) FROM Account WHERE Id = '${acc.id}'` - ) - )[0]; + acc = (await Account.retrieve(`SELECT Id, (SELECT ${Contact.FIELDS.name.apiName} FROM ${Account.FIELDS.contacts.apiName}) FROM Account WHERE Id = '${acc.id}'`))[0]; expect(acc.contacts.length).to.equal(contactSize); - contacts.forEach((c) => { + contacts.forEach(c => { c.email = 'test@example.com'; }); let updateResults = await bulk.update(contacts); expect(contacts[0]._modified.size).to.equal(0); - updateResults.forEach((r) => { + updateResults.forEach(r => { if (!r.success) { - throw r.errors.map((e) => e.message).join(', '); + throw r.errors.map(e => e.message).join(', '); } }); - acc = ( - await Account.retrieve( - `SELECT Id, (SELECT ${Contact.FIELDS.name.apiName}, ${Contact.FIELDS.email} FROM ${Account.FIELDS.contacts.apiName}) FROM Account WHERE Id = '${acc.id}'` - ) - )[0]; - acc.contacts.forEach((c) => { + acc = (await Account.retrieve(`SELECT Id, (SELECT ${Contact.FIELDS.name.apiName}, ${Contact.FIELDS.email} FROM ${Account.FIELDS.contacts.apiName}) FROM Account WHERE Id = '${acc.id}'`))[0]; + acc.contacts.forEach(c => { expect(c.email).to.equal('test@example.com'); }); let delResults = await bulk.delete(contacts); - delResults.forEach((r) => { + delResults.forEach(r => { if (!r.success) { - throw r.errors.map((e) => e.message).join(', '); + throw r.errors.map(e => e.message).join(', '); } }); - acc = ( - await Account.retrieve( - `SELECT Id, (SELECT ${Contact.FIELDS.name.apiName} FROM ${Account.FIELDS.contacts.apiName}) FROM Account WHERE Id = '${acc.id}'` - ) - )[0]; + acc = (await Account.retrieve(`SELECT Id, (SELECT ${Contact.FIELDS.name.apiName} FROM ${Account.FIELDS.contacts.apiName}) FROM Account WHERE Id = '${acc.id}'`))[0]; expect(acc.contacts.length).to.equal(0); @@ -329,10 +321,14 @@ describe('Generated Classes', () => { it('should set relation by external id', async () => { const extId = '123abcd'; // setup account - let accs = await Account.retrieve((f) => { + let accs = await Account.retrieve(f => { return { - select: [...f.select('id', 'testExternalId')], - where: [{ field: f.select('testExternalId'), op: '=', val: extId }], + select: [ + ...f.select('id', 'testExternalId') + ], + where: [ + { field: f.select('testExternalId'), op: '=', val: extId } + ] }; }); @@ -342,7 +338,7 @@ describe('Generated Classes', () => { } else { acc = new Account({ name: 'test external id account', - testExternalId: extId, + testExternalId: extId }); await acc.insert(); } @@ -350,74 +346,83 @@ describe('Generated Classes', () => { let contact = new Contact({ firstName: 'john', lastName: 'doe', - account: new Account({ testExternalId: extId }), + account: new Account({ testExternalId: extId }) }); await contact.insert(); - let retCont = await Contact.retrieve((f) => { + let retCont = await Contact.retrieve(f => { return { - select: [f.select('accountId')], - where: [{ field: f.select('id'), op: '=', val: contact.id }], + select: [ + f.select('accountId') + ], + where: [ + { field: f.select('id'), op: '=', val: contact.id } + ] }; }); expect(retCont[0].accountId).to.equal(acc.id); + }); it('prepareFor Apex', async () => { + let c = new Contact({ id: '123', accountId: 'abc', firstName: `john`, lastName: `doe`, account: new Account({ - name: 'acme', - }), + name: 'acme' + }) }); - expect(c.toJson({ dmlMode: 'all', sendChildObj: true, sendParentObj: true, hideAttributes: true })).to.deep.equal({ - Id: '123', - AccountId: 'abc', - FirstName: 'john', - LastName: 'doe', - Account: { - Name: 'acme', - }, - }); + expect(c.toJson({ dmlMode: 'all', sendChildObj: true, sendParentObj: true, hideAttributes: true })).to.deep.equal( + { + Id: '123', + AccountId: 'abc', + FirstName: 'john', + LastName: 'doe', + Account: { + Name: 'acme' + } + } + ); let acc = new Account({ id: '123', - contacts: [ - new Contact({ - firstName: 'john', - lastName: 'doe', - }), - ], + contacts: [new Contact({ + firstName: 'john', + lastName: 'doe' + })] }); - expect(acc.toJson({ dmlMode: 'all', sendChildObj: true, sendParentObj: true, hideAttributes: true })).to.deep.equal({ - Id: '123', - Contacts: { records: [{ FirstName: 'john', LastName: 'doe' }] }, - }); + expect(acc.toJson({ dmlMode: 'all', sendChildObj: true, sendParentObj: true, hideAttributes: true })).to.deep.equal( + { + Id: '123', + Contacts: { records: [{ FirstName: 'john', LastName: 'doe' }] } + } + ); }); it('prepareFor Apex End To End', async () => { let acc = new Account({ id: '123', - contacts: [ - new Contact({ - firstName: 'john', - lastName: 'doe', - }), - ], + contacts: [new Contact({ + firstName: 'john', + lastName: 'doe' + })], owner: new User({ - email: 'example@gmai.com', - }), + 'email': 'example@gmai.com' + }) }); const sfSob = acc.toJson({ dmlMode: 'all', sendChildObj: true, sendParentObj: true, hideAttributes: true }); - let data = (await new Rest().request.post('/services/apexrest/myservice', { acc: sfSob })).data; + let data = (await new Rest().request.post( + '/services/apexrest/myservice', + { acc: sfSob } + )).data; const retAcc = Account.fromSFObject(data); expect(acc.id).to.deep.equal(retAcc.id); expect(acc.contacts[0].firstName).to.deep.equal(retAcc.contacts[0].firstName); @@ -427,8 +432,8 @@ describe('Generated Classes', () => { it('composite retrieve', async () => { let results = await compositeRetrieve(Contact, Account, User)( - (f) => ({ select: f.select('cleanStatus', 'email', 'accountId') }), - (f) => ({ select: f.select('numberOfEmployees', 'accountNumber', 'active') }), + f => ({ select: f.select('cleanStatus', 'email', 'accountId') }), + f => ({ select: f.select('numberOfEmployees', 'accountNumber', 'active') }), 'select x from blah' ); let contactResults = results[0]; @@ -460,20 +465,19 @@ describe('Generated Classes', () => { let c = new Contact({ firstName: 'test', lastName: 'contact', - birthdate: getCalendarDate(), + birthdate: getCalendarDate() }); await c.insert(); expect(c.id).to.not.be.null; - let c2 = ( - await Contact.retrieve((f) => ({ - select: [f.select('birthdate')], - where: [{ field: 'id', val: c.id }], - })) - )[0]; + let c2 = (await Contact.retrieve(f => ({ + select: [f.select('birthdate')], + where: [{ field: 'id', val: c.id }] + })))[0]; expect(c2.birthdate).to.deep.equal(c.birthdate); await c.delete(); }); -}); + +}); \ No newline at end of file From 02f5a5eb014757f09bbc020f04b79d69dbf51b04 Mon Sep 17 00:00:00 2001 From: NinaFan0824 Date: Tue, 27 Feb 2024 13:29:47 +1300 Subject: [PATCH 16/16] 4.0.0 --- package-lock.json | 4 ++-- package.json | 4 ++-- ts-force-gen/package-lock.json | 4 ++-- ts-force-gen/package.json | 4 ++-- ts-force/package-lock.json | 4 ++-- ts-force/package.json | 4 ++-- 6 files changed, 12 insertions(+), 12 deletions(-) diff --git a/package-lock.json b/package-lock.json index 2aec52a..d14c150 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@vacasaoss/ts-force-project", - "version": "3.4.5", + "version": "4.0.0", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "@vacasaoss/ts-force-project", - "version": "3.4.5", + "version": "4.0.0", "hasInstallScript": true, "license": "ISC" } diff --git a/package.json b/package.json index 44fcdbb..adf1635 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@vacasaoss/ts-force-project", - "version": "3.4.5", + "version": "4.0.0", "description": "", "scripts": { "postinstall": "cd ts-force && npm install && npm run build && cd ../ts-force-gen && npm install", @@ -25,4 +25,4 @@ "url": "https://github.com/ChuckJonas/ts-force/issues" }, "homepage": "https://github.com/ChuckJonas/ts-force#readme" -} \ No newline at end of file +} diff --git a/ts-force-gen/package-lock.json b/ts-force-gen/package-lock.json index 2e894e4..f01c877 100644 --- a/ts-force-gen/package-lock.json +++ b/ts-force-gen/package-lock.json @@ -1,12 +1,12 @@ { "name": "@vacasaoss/ts-force-gen", - "version": "3.4.5", + "version": "4.0.0", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "@vacasaoss/ts-force-gen", - "version": "3.4.5", + "version": "4.0.0", "license": "BSD-3-Clause", "dependencies": { "@salesforce/core": "^2.12.3", diff --git a/ts-force-gen/package.json b/ts-force-gen/package.json index 4ed18df..9c379cc 100644 --- a/ts-force-gen/package.json +++ b/ts-force-gen/package.json @@ -1,6 +1,6 @@ { "name": "@vacasaoss/ts-force-gen", - "version": "3.4.5", + "version": "4.0.0", "description": "Code generation for ts-force", "main": "build/index.js", "typings": "build/index.d.ts", @@ -63,4 +63,4 @@ "ts-morph": "^2.1.0", "tslib": "^2.0.3" } -} \ No newline at end of file +} diff --git a/ts-force/package-lock.json b/ts-force/package-lock.json index 209a243..979260e 100644 --- a/ts-force/package-lock.json +++ b/ts-force/package-lock.json @@ -1,12 +1,12 @@ { "name": "@vacasaoss/ts-force", - "version": "3.4.5", + "version": "4.0.0", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "@vacasaoss/ts-force", - "version": "3.4.5", + "version": "4.0.0", "license": "BSD-3-Clause", "dependencies": { "@types/cometd": "^4.0.4", diff --git a/ts-force/package.json b/ts-force/package.json index 313ed87..c034952 100644 --- a/ts-force/package.json +++ b/ts-force/package.json @@ -1,6 +1,6 @@ { "name": "@vacasaoss/ts-force", - "version": "3.4.5", + "version": "4.0.0", "description": "a typescript client for connecting with salesforce APIs", "main": "build/index.js", "typings": "build/index.d.ts", @@ -57,4 +57,4 @@ "reflect-metadata": "^0.1.10", "tslib": "^1.6.0" } -} \ No newline at end of file +}