@memberjunction/connector-salesforce 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/SalesforceConnector.d.ts +460 -0
- package/dist/SalesforceConnector.js +1865 -0
- package/dist/SalesforceConnector.js.map +1 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.js +5 -0
- package/dist/index.js.map +1 -0
- package/package.json +43 -0
|
@@ -0,0 +1,460 @@
|
|
|
1
|
+
import { type UserInfo } from '@memberjunction/core';
|
|
2
|
+
import type { MJCompanyIntegrationEntity, MJIntegrationObjectEntity, MJIntegrationObjectFieldEntity } from '@memberjunction/core-entities';
|
|
3
|
+
import { BaseRESTIntegrationConnector, type RESTAuthContext, type RESTResponse, type PaginationState, type PaginationType, type ConnectionTestResult, type ExternalObjectSchema, type ExternalFieldSchema, type FetchContext, type FetchBatchResult, type DefaultFieldMapping, type DefaultIntegrationConfig, type ExternalRecord, type CRUDResult, type CreateRecordContext, type UpdateRecordContext, type DeleteRecordContext, type GetRecordContext, type SearchContext, type SearchResult, type SourceSchemaInfo, type IntegrationObjectInfo, type ActionGeneratorConfig, type IntrospectSchemaOptions } from '@memberjunction/integration-engine';
|
|
4
|
+
/** Supported Salesforce auth flows */
|
|
5
|
+
type SalesforceAuthFlow = 'jwt_bearer' | 'client_credentials';
|
|
6
|
+
/** Connection configuration parsed from CompanyIntegration.Configuration JSON */
|
|
7
|
+
export interface SalesforceConnectionConfig {
|
|
8
|
+
/** Auth flow to use */
|
|
9
|
+
AuthFlow: SalesforceAuthFlow;
|
|
10
|
+
/** Salesforce login URL (e.g. https://login.salesforce.com or https://myorg.my.salesforce.com) */
|
|
11
|
+
LoginUrl: string;
|
|
12
|
+
/** Connected App Consumer Key */
|
|
13
|
+
ClientId: string;
|
|
14
|
+
/** Salesforce REST API version. Default: '61.0' */
|
|
15
|
+
ApiVersion: string;
|
|
16
|
+
/** JWT Bearer only — Integration user email */
|
|
17
|
+
Username?: string;
|
|
18
|
+
/** JWT Bearer only — PEM-encoded RSA private key */
|
|
19
|
+
PrivateKey?: string;
|
|
20
|
+
/** Client Credentials only — Connected App client secret */
|
|
21
|
+
ClientSecret?: string;
|
|
22
|
+
/** Optional — token endpoint URL if different from LoginUrl (e.g. My Domain URL for JWT flow) */
|
|
23
|
+
TokenUrl?: string;
|
|
24
|
+
/** Maximum retries for rate-limited or failed requests. Default: 5 */
|
|
25
|
+
MaxRetries?: number;
|
|
26
|
+
/** HTTP request timeout in milliseconds. Default: 30000 */
|
|
27
|
+
RequestTimeoutMs?: number;
|
|
28
|
+
/** Minimum milliseconds between API requests. Default: 100 */
|
|
29
|
+
MinRequestIntervalMs?: number;
|
|
30
|
+
/** Batch size for SOQL queries. Default: 2000 */
|
|
31
|
+
DefaultBatchSize?: number;
|
|
32
|
+
}
|
|
33
|
+
/** Extended auth context carrying Salesforce credentials and cached token */
|
|
34
|
+
interface SalesforceAuthContext extends RESTAuthContext {
|
|
35
|
+
/** Salesforce instance URL (pod-specific, e.g., https://na1.salesforce.com) */
|
|
36
|
+
InstanceUrl: string;
|
|
37
|
+
/** API version string */
|
|
38
|
+
ApiVersion: string;
|
|
39
|
+
/** Full config for reference */
|
|
40
|
+
Config: SalesforceConnectionConfig;
|
|
41
|
+
/** The CompanyIntegration this auth was built for — lets API URLs route through the
|
|
42
|
+
* overridable GetBaseURL() (production returns InstanceUrl; test harnesses redirect to a mock). */
|
|
43
|
+
CompanyIntegration: MJCompanyIntegrationEntity;
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* Production Salesforce CRM connector using the Salesforce REST API v61.0.
|
|
47
|
+
*
|
|
48
|
+
* Supports:
|
|
49
|
+
* - OAuth 2.0 JWT Bearer Token authentication (RS256)
|
|
50
|
+
* - Live schema discovery via SF Describe API
|
|
51
|
+
* - SOQL-based incremental sync with SystemModstamp watermarks
|
|
52
|
+
* - Full CRUD (Create, Update, Delete) via SObject REST API
|
|
53
|
+
* - SOQL/SOSL search support
|
|
54
|
+
* - Comprehensive SF error code handling and governor limit management
|
|
55
|
+
*
|
|
56
|
+
* Extends BaseRESTIntegrationConnector but overrides FetchChanges entirely
|
|
57
|
+
* because Salesforce uses SOQL queries instead of standard REST list endpoints.
|
|
58
|
+
*/
|
|
59
|
+
export declare class SalesforceConnector extends BaseRESTIntegrationConnector {
|
|
60
|
+
private cachedAuth;
|
|
61
|
+
private tokenObtainedAt;
|
|
62
|
+
private lastRequestTime;
|
|
63
|
+
private governorState;
|
|
64
|
+
private _config;
|
|
65
|
+
private get effectiveMaxRetries();
|
|
66
|
+
private get effectiveRequestTimeoutMs();
|
|
67
|
+
private get effectiveMinRequestIntervalMs();
|
|
68
|
+
private get effectiveBatchSize();
|
|
69
|
+
get SupportsCreate(): boolean;
|
|
70
|
+
get SupportsUpdate(): boolean;
|
|
71
|
+
get SupportsDelete(): boolean;
|
|
72
|
+
get SupportsSearch(): boolean;
|
|
73
|
+
get IntegrationName(): string;
|
|
74
|
+
/**
|
|
75
|
+
* CORRECTION (IMPROVE build): affirm AUTHORITATIVE discovery. The global describe
|
|
76
|
+
* (`/sobjects/`) returns the COMPLETE credentialed gamut of queryable objects this
|
|
77
|
+
* org exposes (standard + custom `__c`), and `DoIntrospectSchema` describes that whole
|
|
78
|
+
* set. So an object/field absent from a comprehensive refresh genuinely means the
|
|
79
|
+
* source dropped it, and the engine's refresh path may safely DEACTIVATE it
|
|
80
|
+
* (`Status='Disabled'` — reversible; it flips back to Active if the object reappears on
|
|
81
|
+
* a later discovery). This is what makes comprehensive-refresh deactivation correct
|
|
82
|
+
* for Salesforce rather than wrongly wiping Declared metadata.
|
|
83
|
+
*/
|
|
84
|
+
get DiscoveryIsAuthoritative(): boolean;
|
|
85
|
+
/**
|
|
86
|
+
* Action-generation hint set. CORRECTION (IMPROVE build): this used to return
|
|
87
|
+
* a baked famous-subset catalog (~9 objects hardcoded in this file). That is a
|
|
88
|
+
* `catalog-in-code` defect — it froze the object universe to a famous subset.
|
|
89
|
+
*
|
|
90
|
+
* It now derives the hint set ENTIRELY from the runtime-cached IntegrationObject /
|
|
91
|
+
* IntegrationObjectField metadata for the Salesforce integration — the FULL
|
|
92
|
+
* Declared (credential-free catalog) + Discovered (live `DiscoverObjects` describe,
|
|
93
|
+
* custom `__c` included) gamut. There is NO baked catalog. If the metadata cache is
|
|
94
|
+
* not yet populated (e.g. action generation runs before the integration is seeded),
|
|
95
|
+
* it returns an empty array — the connector NEVER falls back to a hardcoded list.
|
|
96
|
+
*
|
|
97
|
+
* The per-tenant object UNIVERSE for sync comes exclusively from `DiscoverObjects`
|
|
98
|
+
* (live global describe / sObjects endpoint); this method only shapes the cached
|
|
99
|
+
* catalog into the ActionMetadataGenerator's hint structure.
|
|
100
|
+
*/
|
|
101
|
+
GetIntegrationObjects(): IntegrationObjectInfo[];
|
|
102
|
+
GetActionGeneratorConfig(): ActionGeneratorConfig | null;
|
|
103
|
+
TestConnection(companyIntegration: MJCompanyIntegrationEntity, contextUser: UserInfo): Promise<ConnectionTestResult>;
|
|
104
|
+
/**
|
|
105
|
+
* Discovers available objects by calling the SF SObjects API.
|
|
106
|
+
* Returns user-relevant queryable objects: standard CRM objects + custom
|
|
107
|
+
* (__c). Filters out audit/system noise (~1,866 → ~150-300 typically).
|
|
108
|
+
* Set MJ_SALESFORCE_INCLUDE_ALL_SOBJECTS=true to bypass the filter and
|
|
109
|
+
* return the full catalog (useful for debugging or unusual integrations).
|
|
110
|
+
*/
|
|
111
|
+
DiscoverObjects(companyIntegration: MJCompanyIntegrationEntity, contextUser: UserInfo): Promise<ExternalObjectSchema[]>;
|
|
112
|
+
/**
|
|
113
|
+
* Heuristic for "user-relevant" SF object: customer data (custom objects
|
|
114
|
+
* with __c suffix) OR createable standard CRM objects, excluding audit
|
|
115
|
+
* tables, system metadata, and managed-package telemetry.
|
|
116
|
+
*
|
|
117
|
+
* Standard SF orgs return ~1,866 sobjects from describeGlobal — most are
|
|
118
|
+
* audit/internal noise that don't represent business data the user wants
|
|
119
|
+
* to sync into MJ. This drops them to ~150-300.
|
|
120
|
+
*/
|
|
121
|
+
private isUserRelevantSObject;
|
|
122
|
+
/**
|
|
123
|
+
* Discovers fields on a specific SF object via the Describe API.
|
|
124
|
+
* Skips compound fields (address, location) — their component fields are included individually.
|
|
125
|
+
*/
|
|
126
|
+
DiscoverFields(companyIntegration: MJCompanyIntegrationEntity, objectName: string, contextUser: UserInfo): Promise<ExternalFieldSchema[]>;
|
|
127
|
+
/**
|
|
128
|
+
* Full schema introspection — builds object graph with relationships.
|
|
129
|
+
*
|
|
130
|
+
* If `options.ObjectNames` is provided, only those objects are described
|
|
131
|
+
* (fast path for user-selected subsets). Without a filter, every
|
|
132
|
+
* queryable sobject is described — ~70s even with parallelism on a
|
|
133
|
+
* large org — and the result is cached per-org for 5 minutes so
|
|
134
|
+
* back-to-back resolver calls don't re-describe.
|
|
135
|
+
*
|
|
136
|
+
* Filtered calls bypass the cache on purpose: the subset is typically
|
|
137
|
+
* small and cheap, and the full-schema cache would shadow newer results
|
|
138
|
+
* if a user selects a previously-unknown object.
|
|
139
|
+
*/
|
|
140
|
+
IntrospectSchema(companyIntegration: MJCompanyIntegrationEntity, contextUser: UserInfo, options?: IntrospectSchemaOptions): Promise<SourceSchemaInfo>;
|
|
141
|
+
private DoIntrospectSchema;
|
|
142
|
+
private static readonly INTROSPECT_CACHE_TTL_MS;
|
|
143
|
+
private static readonly introspectCache;
|
|
144
|
+
/**
|
|
145
|
+
* Fetches changed records using SOQL queries with SystemModstamp watermarks.
|
|
146
|
+
* Completely overrides the base class REST pagination because SF uses
|
|
147
|
+
* SOQL + queryMore, not standard REST list endpoints.
|
|
148
|
+
*
|
|
149
|
+
* Dispatches to family-specific fetch routines based on the
|
|
150
|
+
* IntegrationObject metadata's `DefaultQueryParams.api_family` hint:
|
|
151
|
+
* tooling → Tooling API, analytics_report/dashboard → Analytics API,
|
|
152
|
+
* bulk_* / composite → not intended for listing (returns empty batch),
|
|
153
|
+
* everything else → standard SObject SOQL flow.
|
|
154
|
+
*/
|
|
155
|
+
FetchChanges(ctx: FetchContext): Promise<FetchBatchResult>;
|
|
156
|
+
/**
|
|
157
|
+
* Resolves the API family for a given IntegrationObject by reading its
|
|
158
|
+
* `DefaultQueryParams.api_family` flag from the engine cache. Defaults to
|
|
159
|
+
* `sobject` when no metadata is available (common in unit tests).
|
|
160
|
+
*/
|
|
161
|
+
private ResolveAPIFamily;
|
|
162
|
+
private IsValidFamily;
|
|
163
|
+
/**
|
|
164
|
+
* Returns the REST path prefix for an SObject in the given API family.
|
|
165
|
+
* Used by CRUD operations and field discovery.
|
|
166
|
+
*/
|
|
167
|
+
private SObjectBasePath;
|
|
168
|
+
/**
|
|
169
|
+
* Returns the SOQL query endpoint for the given API family.
|
|
170
|
+
* Standard uses /query + /queryAll; Tooling uses /tooling/query.
|
|
171
|
+
*/
|
|
172
|
+
private SOQLEndpoint;
|
|
173
|
+
/**
|
|
174
|
+
* Creates a new record in Salesforce. Dispatches to the appropriate API
|
|
175
|
+
* family endpoint (standard SObjects, Tooling, Bulk Ingest/Query jobs, or
|
|
176
|
+
* Composite) based on the IntegrationObject metadata.
|
|
177
|
+
*/
|
|
178
|
+
CreateRecord(ctx: CreateRecordContext): Promise<CRUDResult>;
|
|
179
|
+
/**
|
|
180
|
+
* Updates an existing record in Salesforce (PATCH — only changed fields).
|
|
181
|
+
* Honors API family routing; Tooling uses `/tooling/sobjects/`, everything
|
|
182
|
+
* else uses standard `/sobjects/`.
|
|
183
|
+
*/
|
|
184
|
+
UpdateRecord(ctx: UpdateRecordContext): Promise<CRUDResult>;
|
|
185
|
+
/**
|
|
186
|
+
* Deletes a record from Salesforce. For Bulk Job families, delete aborts
|
|
187
|
+
* the job. For standard and Tooling SObjects, performs a soft delete
|
|
188
|
+
* (Recycle Bin, 15-day retention). Analytics/Composite/Knowledge do not
|
|
189
|
+
* support deletion through this path.
|
|
190
|
+
*/
|
|
191
|
+
DeleteRecord(ctx: DeleteRecordContext): Promise<CRUDResult>;
|
|
192
|
+
/**
|
|
193
|
+
* Retrieves a single record by its Salesforce ID. Routes to the API family
|
|
194
|
+
* endpoint indicated by IntegrationObject metadata.
|
|
195
|
+
*/
|
|
196
|
+
GetRecord(ctx: GetRecordContext): Promise<ExternalRecord | null>;
|
|
197
|
+
/**
|
|
198
|
+
* Searches records using SOQL WHERE clauses built from the provided filters.
|
|
199
|
+
* Search is supported for Standard SObjects and Tooling SObjects; other API
|
|
200
|
+
* families return an empty result.
|
|
201
|
+
*/
|
|
202
|
+
SearchRecords(ctx: SearchContext): Promise<SearchResult>;
|
|
203
|
+
GetDefaultFieldMappings(objectName: string, _entityName: string): DefaultFieldMapping[];
|
|
204
|
+
GetDefaultConfiguration(): DefaultIntegrationConfig;
|
|
205
|
+
protected Authenticate(companyIntegration: MJCompanyIntegrationEntity, contextUser: UserInfo): Promise<SalesforceAuthContext>;
|
|
206
|
+
protected BuildHeaders(auth: RESTAuthContext): Record<string, string>;
|
|
207
|
+
protected MakeHTTPRequest(auth: RESTAuthContext, url: string, method: string, headers: Record<string, string>, body?: unknown): Promise<RESTResponse>;
|
|
208
|
+
protected NormalizeResponse(rawBody: unknown, _responseDataKey: string | null): Record<string, unknown>[];
|
|
209
|
+
/**
|
|
210
|
+
* CORRECTION (IMPROVE build): per-record transform that strips Salesforce's
|
|
211
|
+
* `attributes` metadata blob (e.g. `{ type, url }`) from every record. This is a
|
|
212
|
+
* SANCTIONED removal declared in {@link ExcludedSourceKeys}, NOT a silent drop — the
|
|
213
|
+
* `attributes` key is vendor envelope noise, not customer data, so dropping it can
|
|
214
|
+
* never lose a custom column. Every OTHER source field flows through untouched, so
|
|
215
|
+
* the full-record pass-through contract (custom-column capture) is preserved.
|
|
216
|
+
*
|
|
217
|
+
* Used by both the base-fetch path (GetRecord via applyTransformPreservingKeys) and
|
|
218
|
+
* the SOQL override path (RawToExternalRecord routes through StripVendorAttributes,
|
|
219
|
+
* which reuses ExcludedSourceKeys so the two paths agree on what is removed).
|
|
220
|
+
*/
|
|
221
|
+
protected TransformRecord(raw: Record<string, unknown>, _obj: MJIntegrationObjectEntity, _fields: MJIntegrationObjectFieldEntity[]): Record<string, unknown>;
|
|
222
|
+
/**
|
|
223
|
+
* The Salesforce `attributes` blob is the ONLY key any object's TransformRecord
|
|
224
|
+
* removes. Declaring it here makes the removal auditable and excludes it from the
|
|
225
|
+
* base's re-add-dropped-keys safety net (and from change-detection).
|
|
226
|
+
*/
|
|
227
|
+
protected ExcludedSourceKeys(_objectName: string): string[];
|
|
228
|
+
protected ExtractPaginationInfo(rawBody: unknown, _paginationType: PaginationType, _currentPage: number, _currentOffset: number, _pageSize: number): PaginationState;
|
|
229
|
+
protected GetBaseURL(_companyIntegration: MJCompanyIntegrationEntity, auth: RESTAuthContext): string;
|
|
230
|
+
/**
|
|
231
|
+
* The API origin for all REST calls. Routes through GetBaseURL() so a test harness can
|
|
232
|
+
* redirect the connector to a mock server (the base class hooks GetBaseURL); in production
|
|
233
|
+
* this returns the authenticated InstanceUrl unchanged. ALL URL construction must go through
|
|
234
|
+
* this rather than reading auth.InstanceUrl directly, or the connector is not mock-testable.
|
|
235
|
+
*/
|
|
236
|
+
private ApiBase;
|
|
237
|
+
/**
|
|
238
|
+
* Builds a SOQL query string with optional watermark filter.
|
|
239
|
+
*/
|
|
240
|
+
private BuildSOQLQuery;
|
|
241
|
+
/**
|
|
242
|
+
* Executes a SOQL query and returns records as a FetchBatchResult.
|
|
243
|
+
* For the standard SObject family this uses `queryAll` so that soft-deleted
|
|
244
|
+
* records are returned (IsDeleted=true). For the Tooling family, the
|
|
245
|
+
* Tooling API's `/tooling/query` endpoint is used — `queryAll` is not
|
|
246
|
+
* supported there.
|
|
247
|
+
*/
|
|
248
|
+
private ExecuteSOQLQuery;
|
|
249
|
+
/**
|
|
250
|
+
* Fetches the next page of results using a queryMore locator.
|
|
251
|
+
*/
|
|
252
|
+
private FetchNextPage;
|
|
253
|
+
/**
|
|
254
|
+
* Parses a SOQL query response into a FetchBatchResult.
|
|
255
|
+
*/
|
|
256
|
+
private ParseSOQLResponse;
|
|
257
|
+
/**
|
|
258
|
+
* Extracts the maximum SystemModstamp value from a batch of records and
|
|
259
|
+
* advances it by 1ms so the next sync's `WHERE SystemModstamp >= <wm>`
|
|
260
|
+
* filter excludes the boundary cluster.
|
|
261
|
+
*
|
|
262
|
+
* Why advance: the SOQL filter uses `>=` (not `>`) on purpose so we don't
|
|
263
|
+
* drop records that share the modstamp instant of the previous max. But
|
|
264
|
+
* if the saved watermark equals the new max, every subsequent incremental
|
|
265
|
+
* re-pulls the same boundary cluster forever and the watermark plateaus.
|
|
266
|
+
* Observed in the wild: 1,800 EmailMessage records bulk-imported with
|
|
267
|
+
* identical SystemModstamp re-fetched on every run with `Updated` count
|
|
268
|
+
* never decreasing.
|
|
269
|
+
*
|
|
270
|
+
* SF's SystemModstamp is millisecond-precision, so adding 1ms cannot
|
|
271
|
+
* skip a real record — there is nothing scheduled between `max` and
|
|
272
|
+
* `max + 1ms`. The connector returns the advanced value as
|
|
273
|
+
* `NewWatermarkValue`; the engine persists it; the next run picks up
|
|
274
|
+
* cleanly past the cluster.
|
|
275
|
+
*/
|
|
276
|
+
private ExtractMaxWatermark;
|
|
277
|
+
/**
|
|
278
|
+
* Gets queryable field names for a SF object via the Describe API. When
|
|
279
|
+
* `family === 'tooling'`, calls the Tooling describe endpoint instead of
|
|
280
|
+
* the standard one.
|
|
281
|
+
*/
|
|
282
|
+
private GetQueryableFieldNames;
|
|
283
|
+
/**
|
|
284
|
+
* Builds a signed JWT assertion for the SF JWT Bearer Token flow.
|
|
285
|
+
*/
|
|
286
|
+
private BuildJWTAssertion;
|
|
287
|
+
/**
|
|
288
|
+
* Normalize a PEM private key that may have had its newlines stripped or
|
|
289
|
+
* replaced with spaces / literal "\n" sequences. Rebuilds the standard
|
|
290
|
+
* BEGIN header / 64-char body / END footer layout that OpenSSL requires.
|
|
291
|
+
*/
|
|
292
|
+
private NormalizePem;
|
|
293
|
+
/**
|
|
294
|
+
* Exchanges a JWT assertion for an access token at the SF token endpoint.
|
|
295
|
+
*/
|
|
296
|
+
private ExchangeJWTForToken;
|
|
297
|
+
/**
|
|
298
|
+
* Exchanges Client Credentials for an access token.
|
|
299
|
+
* Used with Connected Apps configured for the OAuth 2.0 Client Credentials flow.
|
|
300
|
+
*/
|
|
301
|
+
private ExchangeClientCredentialsForToken;
|
|
302
|
+
/**
|
|
303
|
+
* Checks if the cached token is still valid (within 80% of lifetime).
|
|
304
|
+
*/
|
|
305
|
+
private IsTokenValid;
|
|
306
|
+
/**
|
|
307
|
+
* Loads credentials from the Credential entity or Configuration JSON.
|
|
308
|
+
*/
|
|
309
|
+
private LoadCredentials;
|
|
310
|
+
private LoadFromCredentialEntity;
|
|
311
|
+
private ParseCredentialJson;
|
|
312
|
+
private BuildConnectionConfig;
|
|
313
|
+
private ApplyConfigOverrides;
|
|
314
|
+
/**
|
|
315
|
+
* Throttles requests to respect minimum interval between API calls.
|
|
316
|
+
*/
|
|
317
|
+
private ThrottleIfNeeded;
|
|
318
|
+
/**
|
|
319
|
+
* Checks governor limits and pauses if approaching the daily API limit.
|
|
320
|
+
*/
|
|
321
|
+
private CheckGovernorLimits;
|
|
322
|
+
/**
|
|
323
|
+
* Parses the Sforce-Limit-Info header from every SF response.
|
|
324
|
+
*/
|
|
325
|
+
private ParseGovernorLimits;
|
|
326
|
+
private FetchWithTimeout;
|
|
327
|
+
private BuildRESTResponse;
|
|
328
|
+
private ExtractHeaders;
|
|
329
|
+
private SafeParseJSON;
|
|
330
|
+
private CalculateRetryDelay;
|
|
331
|
+
/**
|
|
332
|
+
* Parses the vendor's stated retry wait from a 429/503 response into milliseconds, so the connector
|
|
333
|
+
* honors `Retry-After` (delta-seconds OR an HTTP-date) and Salesforce's `Sforce-Limit-Info` rather
|
|
334
|
+
* than blindly using exponential backoff. Returns null when no usable signal is present (caller falls
|
|
335
|
+
* back to {@link CalculateRetryDelay}). Capped at 60s so a hostile/garbage header can't stall a sync.
|
|
336
|
+
*/
|
|
337
|
+
private RetryAfterMs;
|
|
338
|
+
private Sleep;
|
|
339
|
+
/**
|
|
340
|
+
* Maps a Salesforce field describe to an ExternalFieldSchema.
|
|
341
|
+
* Custom fields can be detected by callers via the `__c` suffix in the
|
|
342
|
+
* field name — Salesforce standardizes this convention for all custom
|
|
343
|
+
* fields on both standard and custom SObjects.
|
|
344
|
+
*/
|
|
345
|
+
private MapSFFieldToSchema;
|
|
346
|
+
/**
|
|
347
|
+
* Maps a Salesforce field type to a generic integration type.
|
|
348
|
+
*/
|
|
349
|
+
private MapSalesforceType;
|
|
350
|
+
/**
|
|
351
|
+
* Formats a watermark value as a SOQL datetime literal.
|
|
352
|
+
*/
|
|
353
|
+
private FormatSOQLDateTime;
|
|
354
|
+
/**
|
|
355
|
+
* Builds a SOQL WHERE clause from a Filters map.
|
|
356
|
+
*/
|
|
357
|
+
private BuildWhereClauseFromFilters;
|
|
358
|
+
/**
|
|
359
|
+
* Checks if a response contains a specific SF error code.
|
|
360
|
+
*/
|
|
361
|
+
private HasSFErrorCode;
|
|
362
|
+
/**
|
|
363
|
+
* Extracts SF error objects from a response.
|
|
364
|
+
*/
|
|
365
|
+
private ExtractSFErrors;
|
|
366
|
+
/**
|
|
367
|
+
* Checks if a response body contains an UNABLE_TO_LOCK_ROW error.
|
|
368
|
+
*/
|
|
369
|
+
private IsLockRowError;
|
|
370
|
+
/**
|
|
371
|
+
* Builds a CRUDResult for error responses.
|
|
372
|
+
*/
|
|
373
|
+
private BuildCRUDError;
|
|
374
|
+
/**
|
|
375
|
+
* Handles write errors with special retry logic for UNABLE_TO_LOCK_ROW.
|
|
376
|
+
*/
|
|
377
|
+
private HandleWriteError;
|
|
378
|
+
/**
|
|
379
|
+
* Converts a raw SF API record to an ExternalRecord. The SOQL override path
|
|
380
|
+
* hand-builds records, so it routes through the SAME sanctioned strip the base path
|
|
381
|
+
* uses: every key flows through to `Fields` EXCEPT those declared in
|
|
382
|
+
* {@link ExcludedSourceKeys} (just `attributes`). This preserves full-record
|
|
383
|
+
* pass-through for custom-column capture while removing only the vendor envelope blob.
|
|
384
|
+
*/
|
|
385
|
+
private RawToExternalRecord;
|
|
386
|
+
/**
|
|
387
|
+
* Strips read-only and system fields from an attributes map before write operations.
|
|
388
|
+
*/
|
|
389
|
+
private StripReadOnlyFields;
|
|
390
|
+
private BuildSourceObjectFromDescribe;
|
|
391
|
+
private GetContactMappings;
|
|
392
|
+
private GetAccountMappings;
|
|
393
|
+
private GetLeadMappings;
|
|
394
|
+
private ValidateResponse;
|
|
395
|
+
private PreviewBody;
|
|
396
|
+
/**
|
|
397
|
+
* Lists Analytics Reports or Dashboards via the Analytics API. Read-only;
|
|
398
|
+
* no incremental-sync support at this endpoint.
|
|
399
|
+
*/
|
|
400
|
+
private FetchAnalyticsList;
|
|
401
|
+
private AnalyticsItemToRecord;
|
|
402
|
+
private GetAnalyticsReport;
|
|
403
|
+
private GetAnalyticsDashboard;
|
|
404
|
+
/**
|
|
405
|
+
* Fetches published Knowledge article versions. This endpoint is
|
|
406
|
+
* paginated via `pageNumber`/`pageSize` (not cursor). The connector
|
|
407
|
+
* returns a single batch; callers that need pagination can issue
|
|
408
|
+
* additional requests via SearchRecords.
|
|
409
|
+
*/
|
|
410
|
+
private FetchKnowledgeArticles;
|
|
411
|
+
/**
|
|
412
|
+
* Lists in-flight Bulk API 2.0 jobs. Useful for monitoring background
|
|
413
|
+
* imports/queries the integration previously started.
|
|
414
|
+
*/
|
|
415
|
+
private FetchBulkJobs;
|
|
416
|
+
/**
|
|
417
|
+
* Creates (starts) a new Bulk API 2.0 ingest or query job using the
|
|
418
|
+
* provided attributes. Required fields differ by operation; we pass
|
|
419
|
+
* attributes straight through to Salesforce after stripping any readonly
|
|
420
|
+
* fields. For ingest, CSV data is uploaded in a separate request (PUT) —
|
|
421
|
+
* callers must issue that themselves via UploadBulkData once the job ID
|
|
422
|
+
* is known.
|
|
423
|
+
*/
|
|
424
|
+
private CreateBulkJob;
|
|
425
|
+
/**
|
|
426
|
+
* Aborts a running Bulk API 2.0 job by PATCHing state=Aborted.
|
|
427
|
+
*/
|
|
428
|
+
private AbortBulkJob;
|
|
429
|
+
private GetBulkJob;
|
|
430
|
+
/**
|
|
431
|
+
* Executes a composite request — up to 25 sub-requests in one HTTP call.
|
|
432
|
+
* The `attrs` payload is expected to be the full composite body or at
|
|
433
|
+
* least a `compositeRequest` array. Returns the overall composite
|
|
434
|
+
* response as the created record's ExternalID (Salesforce returns
|
|
435
|
+
* per-sub-request results; callers should inspect Fields for details).
|
|
436
|
+
*
|
|
437
|
+
* Salesforce returns HTTP 200 for the OVERALL composite call even when
|
|
438
|
+
* individual sub-requests fail (the composite envelope carries a
|
|
439
|
+
* per-sub-request `httpStatusCode`). Blanket-returning Success:true on the
|
|
440
|
+
* envelope status swallows those sub-request failures. We inspect the
|
|
441
|
+
* `compositeResponse` array and fail loudly if any sub-request returned a
|
|
442
|
+
* 4xx/5xx, summarizing the sub-request errors so the caller sees them.
|
|
443
|
+
*/
|
|
444
|
+
private ExecuteCompositeRequest;
|
|
445
|
+
/**
|
|
446
|
+
* Inspects a composite response body for per-sub-request failures. A
|
|
447
|
+
* sub-request failed if its `httpStatusCode` is >= 400. Returns a
|
|
448
|
+
* human-readable summary string per failed sub-request (empty array when
|
|
449
|
+
* all sub-requests succeeded or the body has no compositeResponse array).
|
|
450
|
+
*/
|
|
451
|
+
private CollectCompositeSubErrors;
|
|
452
|
+
/** Formats a single failed composite sub-response into a readable error string. */
|
|
453
|
+
private SummarizeCompositeSubError;
|
|
454
|
+
/**
|
|
455
|
+
* Salesforce sub-request error bodies are typically an array of
|
|
456
|
+
* `{ errorCode, message }`. Pull a concise detail string out of that shape.
|
|
457
|
+
*/
|
|
458
|
+
private ExtractCompositeSubErrorDetail;
|
|
459
|
+
}
|
|
460
|
+
export {};
|