@memberjunction/connector-nimble-ams 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/NimbleAMSConnector.d.ts +141 -0
- package/dist/NimbleAMSConnector.js +703 -0
- package/dist/NimbleAMSConnector.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 +38 -0
|
@@ -0,0 +1,141 @@
|
|
|
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 FetchContext, type FetchBatchResult, type CreateRecordContext, type UpdateRecordContext, type DeleteRecordContext, type CRUDResult, type RateLimitPolicy, type ExternalObjectSchema, type ExternalFieldSchema, type IntrospectSchemaOptions, type SourceSchemaInfo } from '@memberjunction/integration-engine';
|
|
4
|
+
/** Connection configuration parsed from the Credential entity / CompanyIntegration.Configuration JSON. */
|
|
5
|
+
export interface NimbleAMSConnectionConfig {
|
|
6
|
+
/** Salesforce org instance URL, e.g. https://myorg.my.salesforce.com (from OAuth `instance_url`). */
|
|
7
|
+
InstanceURL: string;
|
|
8
|
+
ClientID: string;
|
|
9
|
+
ClientSecret?: string;
|
|
10
|
+
AccessToken?: string;
|
|
11
|
+
RefreshToken?: string;
|
|
12
|
+
/** Salesforce login host for the token exchange. */
|
|
13
|
+
LoginURL: string;
|
|
14
|
+
/** Salesforce REST API version (e.g. "59.0"), from Configuration.SalesforceAPIVersion. */
|
|
15
|
+
ApiVersion: string;
|
|
16
|
+
}
|
|
17
|
+
export declare class NimbleAMSConnector extends BaseRESTIntegrationConnector {
|
|
18
|
+
private tokenCache;
|
|
19
|
+
private lastRequestTime;
|
|
20
|
+
get IntegrationName(): string;
|
|
21
|
+
get SupportsCreate(): boolean;
|
|
22
|
+
get SupportsUpdate(): boolean;
|
|
23
|
+
get SupportsDelete(): boolean;
|
|
24
|
+
/**
|
|
25
|
+
* AUTHORITATIVE discovery (§ deactivation gate): the live SF global describe returns the FULL set
|
|
26
|
+
* of NU__/NUINT__ + standard objects the credential can see, so absence on a later refresh genuinely
|
|
27
|
+
* means the source dropped it. The Declared baseline plus the describe extension are the complete
|
|
28
|
+
* gamut for the scoped namespace.
|
|
29
|
+
*/
|
|
30
|
+
get DiscoveryIsAuthoritative(): boolean;
|
|
31
|
+
/**
|
|
32
|
+
* Route schema introspection through the connector's LIVE SF global-describe discovery.
|
|
33
|
+
*
|
|
34
|
+
* `BaseRESTIntegrationConnector` (our parent) overrides the grandparent's real discover loop with a
|
|
35
|
+
* CACHE-DRIVEN `IntrospectSchema` that re-reads persisted ACTIVE metadata and is hard-coded
|
|
36
|
+
* non-authoritative — so it never calls our `DiscoverObjects`/`DiscoverFields` and can never drive
|
|
37
|
+
* the authoritative deactivation overlay. Nimble HAS live describe-backed discovery (and declares
|
|
38
|
+
* `DiscoveryIsAuthoritative = true`), so we invoke the grandparent `BaseIntegrationConnector`
|
|
39
|
+
* implementation directly. It calls `this.DiscoverObjects` → `this.DiscoverFields` per object and
|
|
40
|
+
* sets `IsAuthoritative` from our getter, so the full field set is discovered and absent
|
|
41
|
+
* objects/fields deactivate (reversibly) on a comprehensive refresh.
|
|
42
|
+
*/
|
|
43
|
+
IntrospectSchema(companyIntegration: MJCompanyIntegrationEntity, contextUser: UserInfo, options?: IntrospectSchemaOptions): Promise<SourceSchemaInfo>;
|
|
44
|
+
/** Salesforce REST is monotonic when we ORDER BY the watermark column — narrow the next incremental. */
|
|
45
|
+
get MonotonicWatermark(): boolean;
|
|
46
|
+
/** Every Nimble sObject carries the immutable 18-char `Id`; the LMS REST objects carry `id`. */
|
|
47
|
+
StableOrderingKey(objectName: string): string | null;
|
|
48
|
+
/**
|
|
49
|
+
* Salesforce enforces a per-org rolling 24h API-call cap (Enterprise ~100k/day) rather than a
|
|
50
|
+
* tokens/sec rate — a conservative sustained rate keeps the connector well clear of the daily cap.
|
|
51
|
+
*/
|
|
52
|
+
get RateLimitPolicy(): RateLimitPolicy | null;
|
|
53
|
+
/** Salesforce 429 / REQUEST_LIMIT_EXCEEDED → honor the standard `Retry-After` header (seconds). */
|
|
54
|
+
ExtractRetryAfterMs(error: unknown): number | undefined;
|
|
55
|
+
DiscoverObjects(companyIntegration: MJCompanyIntegrationEntity, contextUser: UserInfo): Promise<ExternalObjectSchema[]>;
|
|
56
|
+
DiscoverFields(companyIntegration: MJCompanyIntegrationEntity, objectName: string, contextUser: UserInfo): Promise<ExternalFieldSchema[]>;
|
|
57
|
+
/** A SF API name is in Nimble scope iff it is a managed-package object or a standard AMS object. */
|
|
58
|
+
private IsNimbleScopedObject;
|
|
59
|
+
private MapSFType;
|
|
60
|
+
protected Authenticate(companyIntegration: MJCompanyIntegrationEntity, contextUser: UserInfo): Promise<RESTAuthContext>;
|
|
61
|
+
private BuildAuthContext;
|
|
62
|
+
private ObtainToken;
|
|
63
|
+
private ParseConfig;
|
|
64
|
+
private ParseConfigJSON;
|
|
65
|
+
private BuildConfig;
|
|
66
|
+
/** Accept "59.0" or "v59.0"; the SF data path embeds it as `v{version}`. Store the bare number. */
|
|
67
|
+
private NormalizeApiVersion;
|
|
68
|
+
TestConnection(companyIntegration: MJCompanyIntegrationEntity, contextUser: UserInfo): Promise<ConnectionTestResult>;
|
|
69
|
+
protected GetBaseURL(_ci: MJCompanyIntegrationEntity, auth: RESTAuthContext): string;
|
|
70
|
+
/** The instance origin for all REST calls. Routes through GetBaseURL so a test harness can redirect. */
|
|
71
|
+
private Origin;
|
|
72
|
+
/** The `/services/data/v{version}` base for all Salesforce REST calls. */
|
|
73
|
+
private SFDataBase;
|
|
74
|
+
/** Strip the SF `attributes` envelope blob from every record (sanctioned, declared removal). */
|
|
75
|
+
protected TransformRecord(raw: Record<string, unknown>, _obj: MJIntegrationObjectEntity, _fields: MJIntegrationObjectFieldEntity[]): Record<string, unknown>;
|
|
76
|
+
protected ExcludedSourceKeys(_objectName: string): string[];
|
|
77
|
+
/**
|
|
78
|
+
* Strip the response envelope to expose individual records. Handles all three doors:
|
|
79
|
+
* - SF SOQL → `body.records`
|
|
80
|
+
* - Fuse out → `body.Records` (note: PascalCase; null fields omitted per record)
|
|
81
|
+
* - LMS REST → root array or single object
|
|
82
|
+
*/
|
|
83
|
+
protected NormalizeResponse(rawBody: unknown, _key: string | null): Record<string, unknown>[];
|
|
84
|
+
protected ExtractPaginationInfo(rawBody: unknown, _pt: PaginationType, _cp: number, _co: number, _ps: number): PaginationState;
|
|
85
|
+
FetchChanges(ctx: FetchContext): Promise<FetchBatchResult>;
|
|
86
|
+
/** Salesforce REST SOQL fetch — cursor pagination via nextRecordsUrl + watermark-ordered incremental. */
|
|
87
|
+
private FetchSOQL;
|
|
88
|
+
/**
|
|
89
|
+
* Build the SOQL for the first page. CRITICAL: NO `LIMIT` — Salesforce natively paginates via
|
|
90
|
+
* done/nextRecordsUrl, so a SOQL LIMIT would cap the ENTIRE result set and SF would report
|
|
91
|
+
* done=true at that count, silently dropping every record past the limit AND advancing the
|
|
92
|
+
* watermark past them next sync. Use `>=` (not `>`) so records at the exact watermark instant
|
|
93
|
+
* aren't lost; engine dedupe absorbs the boundary re-fetch. ORDER BY the watermark for monotonicity.
|
|
94
|
+
*/
|
|
95
|
+
private BuildSOQL;
|
|
96
|
+
/** SF SOQL datetime literals are unquoted ISO-8601 (e.g. 2026-01-01T00:00:00Z). */
|
|
97
|
+
private FormatSOQLDateTime;
|
|
98
|
+
/** NAMS LMS REST fetch — GET with an optional `lastUpdated` incremental filter (no pagination). */
|
|
99
|
+
private FetchLMS;
|
|
100
|
+
CreateRecord(ctx: CreateRecordContext): Promise<CRUDResult>;
|
|
101
|
+
UpdateRecord(ctx: UpdateRecordContext): Promise<CRUDResult>;
|
|
102
|
+
DeleteRecord(ctx: DeleteRecordContext): Promise<CRUDResult>;
|
|
103
|
+
/** Salesforce REST sObject create — POST `/sobjects/{ObjectName}` flat body, ID in response `id`. */
|
|
104
|
+
private SFRestCreate;
|
|
105
|
+
/** Salesforce REST sObject update — PATCH `/sobjects/{ObjectName}/{Id}` flat body. */
|
|
106
|
+
private SFRestUpdate;
|
|
107
|
+
/**
|
|
108
|
+
* Nimble Fuse INBOUND upsert — POST `/services/apexrest/NUINT/NUIntegrationService` with the
|
|
109
|
+
* `{Request: {Name, "Authentication Key", InboundRecords:[{Fields:{...}}]}}` envelope. Parses the
|
|
110
|
+
* per-record `InboundResults` for partial success, routes the single-record create through
|
|
111
|
+
* BuildCreatedResult, and fails LOUDLY on an empty SalesforceId.
|
|
112
|
+
*/
|
|
113
|
+
private FuseUpsert;
|
|
114
|
+
private JoinErrors;
|
|
115
|
+
/** True when the object's write door is the Fuse inbound upsert (vs Salesforce REST sObject CRUD). */
|
|
116
|
+
private IsFuseWritePath;
|
|
117
|
+
/**
|
|
118
|
+
* Resolve a Salesforce-REST path template, substituting `{apiVersion}` (bare version), `{ObjectName}`,
|
|
119
|
+
* and `{Id}`/`{ExternalID}`. The contract's SF-REST paths are root-relative (start at /services/...),
|
|
120
|
+
* so we prefix the instance origin only.
|
|
121
|
+
*/
|
|
122
|
+
private ResolveSFRestPath;
|
|
123
|
+
/**
|
|
124
|
+
* Build an ExternalRecord from a raw vendor record. The FULL record flows into `Fields` (custom-column
|
|
125
|
+
* pass-through contract M1–M4) — the ONLY removed key is the sanctioned SF `attributes` envelope blob
|
|
126
|
+
* declared in {@link ExcludedSourceKeys}. The PK field supplies ExternalID; the watermark field supplies
|
|
127
|
+
* ModifiedAt. (FetchSOQL/FetchLMS override the base fetch, so we hand-build records here.)
|
|
128
|
+
*/
|
|
129
|
+
private RawToExternalRecord;
|
|
130
|
+
/** Parse the per-IO access Configuration (Family / SObjectType / AccessPath / Fuse setting names). */
|
|
131
|
+
private ReadAccessConfig;
|
|
132
|
+
protected BuildHeaders(auth: RESTAuthContext): Record<string, string>;
|
|
133
|
+
protected MakeHTTPRequest(_auth: RESTAuthContext, url: string, method: string, headers: Record<string, string>, body?: unknown): Promise<RESTResponse>;
|
|
134
|
+
private FetchWithTimeout;
|
|
135
|
+
private ParseBody;
|
|
136
|
+
private ToRESTResponse;
|
|
137
|
+
private SafeBody;
|
|
138
|
+
private Throttle;
|
|
139
|
+
private Sleep;
|
|
140
|
+
}
|
|
141
|
+
export declare function LoadNimbleAMSConnector(): void;
|
|
@@ -0,0 +1,703 @@
|
|
|
1
|
+
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
|
|
2
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
3
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
4
|
+
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
5
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
6
|
+
};
|
|
7
|
+
/**
|
|
8
|
+
* NimbleAMSConnector — Integration connector for Nimble AMS (Salesforce-native association management).
|
|
9
|
+
*
|
|
10
|
+
* Nimble AMS is a Salesforce-native AMS: data lives as Salesforce sObjects — standard SF objects
|
|
11
|
+
* (Account, Contact) and NU__/NUINT__-prefixed managed-package custom objects (`__c` suffix). The
|
|
12
|
+
* connector surfaces THREE access doors, each carried on the per-IO `Configuration` from the frozen
|
|
13
|
+
* contract — never hardcoded here:
|
|
14
|
+
*
|
|
15
|
+
* 1. Salesforce REST SOQL — `/services/data/v{apiVersion}/query` with `nextRecordsUrl` cursor
|
|
16
|
+
* pagination + `WHERE LastModifiedDate >= :watermark` incremental. The default read door, and
|
|
17
|
+
* the write door for sObject CRUD (`/sobjects/{ObjectName}` flat body, path-delete).
|
|
18
|
+
* 2. Nimble Fuse — POST `/services/apexrest/NUINT/NUIntegrationService`. OUTBOUND reads run a
|
|
19
|
+
* named Integration-Setting SOQL and return `{Records, RecordCount, Message}` (null fields are
|
|
20
|
+
* OMITTED — missing fields are nullable, NOT a schema error). INBOUND writes are an
|
|
21
|
+
* External-ID-keyed JSON upsert returning `{RecordCount, InboundResults:[{...,SalesforceId,
|
|
22
|
+
* ErrorMessages}], ErrorMessages}` with per-record partial success.
|
|
23
|
+
* 3. NAMS LMS REST — `/services/apexrest/nams/api/lms/v1/...` GET with a `lastUpdated` incremental.
|
|
24
|
+
*
|
|
25
|
+
* API Documentation:
|
|
26
|
+
* - Nimble AMS API: https://help.nimbleams.com/help/live/api
|
|
27
|
+
* - Call the Integration API: https://help.nimbleams.com/help/live/call-the-integration-api
|
|
28
|
+
* - Object Reference: https://nimbleuser.github.io/nams-api-docs/
|
|
29
|
+
*
|
|
30
|
+
* Auth: Salesforce OAuth 2.0 bearer (Connected App; client_credentials or refresh_token grant).
|
|
31
|
+
* Base URL: the per-org Salesforce instance URL (from the OAuth token response `instance_url`).
|
|
32
|
+
*
|
|
33
|
+
* NO BAKED CATALOG. The object/field universe is Declared (credential-free docs → metadata file) +
|
|
34
|
+
* Discovered (runtime SF global-describe MECHANISM, scoped to the NU__/NUINT__ managed-package
|
|
35
|
+
* namespace). This file is pure mechanism: auth, HTTP, discover, fetch, normalize, transform, write.
|
|
36
|
+
*/
|
|
37
|
+
import { RegisterClass } from '@memberjunction/global';
|
|
38
|
+
import { Metadata } from '@memberjunction/core';
|
|
39
|
+
import { BaseIntegrationConnector, BaseRESTIntegrationConnector, } from '@memberjunction/integration-engine';
|
|
40
|
+
// ─── Constants ──────────────────────────────────────────────────────────────
|
|
41
|
+
const DEFAULT_SF_API_VERSION = '59.0';
|
|
42
|
+
const FUSE_PATH = '/services/apexrest/NUINT/NUIntegrationService';
|
|
43
|
+
/** SF managed-package namespaces that scope the global describe to Nimble objects. */
|
|
44
|
+
const NIMBLE_NAMESPACES = ['NU__', 'NUINT__'];
|
|
45
|
+
/** Standard SF objects used by Nimble AMS alongside the managed package. */
|
|
46
|
+
const NIMBLE_STANDARD_OBJECTS = new Set(['Account', 'Contact']);
|
|
47
|
+
/** Nimble Fuse OUTBOUND hard limits per call — 50,000 records OR 3MB, whichever is reached first. */
|
|
48
|
+
const FUSE_MAX_RECORDS_PER_CALL = 50_000;
|
|
49
|
+
const FUSE_MAX_BYTES_PER_CALL = 3 * 1024 * 1024; // 3MB
|
|
50
|
+
const TOKEN_REFRESH_BUFFER_MS = 60_000;
|
|
51
|
+
const TOKEN_LIFETIME_MS = 3_600_000;
|
|
52
|
+
const MAX_RETRIES = 3;
|
|
53
|
+
const REQUEST_TIMEOUT_MS = 30_000;
|
|
54
|
+
const MIN_REQUEST_INTERVAL_MS = 50;
|
|
55
|
+
// ─── Connector ──────────────────────────────────────────────────────────────
|
|
56
|
+
let NimbleAMSConnector = class NimbleAMSConnector extends BaseRESTIntegrationConnector {
|
|
57
|
+
constructor() {
|
|
58
|
+
super(...arguments);
|
|
59
|
+
this.tokenCache = null;
|
|
60
|
+
this.lastRequestTime = 0;
|
|
61
|
+
}
|
|
62
|
+
get IntegrationName() { return 'Nimble AMS'; }
|
|
63
|
+
// Capability getters reflect the contract: writeable objects expose SF-REST sObject CRUD
|
|
64
|
+
// (Create/Update/Delete) and Fuse upsert (Create/Update). The per-IO write columns gate which
|
|
65
|
+
// verb actually fires; these getters declare the connector CAN write where metadata says so.
|
|
66
|
+
get SupportsCreate() { return true; }
|
|
67
|
+
get SupportsUpdate() { return true; }
|
|
68
|
+
get SupportsDelete() { return true; }
|
|
69
|
+
/**
|
|
70
|
+
* AUTHORITATIVE discovery (§ deactivation gate): the live SF global describe returns the FULL set
|
|
71
|
+
* of NU__/NUINT__ + standard objects the credential can see, so absence on a later refresh genuinely
|
|
72
|
+
* means the source dropped it. The Declared baseline plus the describe extension are the complete
|
|
73
|
+
* gamut for the scoped namespace.
|
|
74
|
+
*/
|
|
75
|
+
get DiscoveryIsAuthoritative() { return true; }
|
|
76
|
+
/**
|
|
77
|
+
* Route schema introspection through the connector's LIVE SF global-describe discovery.
|
|
78
|
+
*
|
|
79
|
+
* `BaseRESTIntegrationConnector` (our parent) overrides the grandparent's real discover loop with a
|
|
80
|
+
* CACHE-DRIVEN `IntrospectSchema` that re-reads persisted ACTIVE metadata and is hard-coded
|
|
81
|
+
* non-authoritative — so it never calls our `DiscoverObjects`/`DiscoverFields` and can never drive
|
|
82
|
+
* the authoritative deactivation overlay. Nimble HAS live describe-backed discovery (and declares
|
|
83
|
+
* `DiscoveryIsAuthoritative = true`), so we invoke the grandparent `BaseIntegrationConnector`
|
|
84
|
+
* implementation directly. It calls `this.DiscoverObjects` → `this.DiscoverFields` per object and
|
|
85
|
+
* sets `IsAuthoritative` from our getter, so the full field set is discovered and absent
|
|
86
|
+
* objects/fields deactivate (reversibly) on a comprehensive refresh.
|
|
87
|
+
*/
|
|
88
|
+
async IntrospectSchema(companyIntegration, contextUser, options) {
|
|
89
|
+
return BaseIntegrationConnector.prototype.IntrospectSchema.call(this, companyIntegration, contextUser, options);
|
|
90
|
+
}
|
|
91
|
+
// ── §7 sync-efficiency hooks ─────────────────────────────────────────────
|
|
92
|
+
/** Salesforce REST is monotonic when we ORDER BY the watermark column — narrow the next incremental. */
|
|
93
|
+
get MonotonicWatermark() { return true; }
|
|
94
|
+
/** Every Nimble sObject carries the immutable 18-char `Id`; the LMS REST objects carry `id`. */
|
|
95
|
+
StableOrderingKey(objectName) {
|
|
96
|
+
return objectName.startsWith('Lms') ? 'id' : 'Id';
|
|
97
|
+
}
|
|
98
|
+
/**
|
|
99
|
+
* Salesforce enforces a per-org rolling 24h API-call cap (Enterprise ~100k/day) rather than a
|
|
100
|
+
* tokens/sec rate — a conservative sustained rate keeps the connector well clear of the daily cap.
|
|
101
|
+
*/
|
|
102
|
+
get RateLimitPolicy() {
|
|
103
|
+
return { TokensPerSec: 20, Burst: 25, ThrottleBackoffFactor: 0.5 };
|
|
104
|
+
}
|
|
105
|
+
/** Salesforce 429 / REQUEST_LIMIT_EXCEEDED → honor the standard `Retry-After` header (seconds). */
|
|
106
|
+
ExtractRetryAfterMs(error) {
|
|
107
|
+
if (!error || typeof error !== 'object')
|
|
108
|
+
return undefined;
|
|
109
|
+
const headers = error.Headers;
|
|
110
|
+
const ra = headers?.['retry-after'];
|
|
111
|
+
if (ra) {
|
|
112
|
+
const secs = Number(ra);
|
|
113
|
+
if (!isNaN(secs))
|
|
114
|
+
return secs * 1000;
|
|
115
|
+
}
|
|
116
|
+
return undefined;
|
|
117
|
+
}
|
|
118
|
+
// ── Discovery (runtime SF global-describe MECHANISM, namespace-scoped) ────
|
|
119
|
+
async DiscoverObjects(companyIntegration, contextUser) {
|
|
120
|
+
const auth = await this.Authenticate(companyIntegration, contextUser);
|
|
121
|
+
const url = `${this.SFDataBase(auth)}/sobjects/`;
|
|
122
|
+
const response = await this.MakeHTTPRequest(auth, url, 'GET', this.BuildHeaders(auth));
|
|
123
|
+
if (response.Status < 200 || response.Status >= 300) {
|
|
124
|
+
throw new Error(`Nimble AMS DiscoverObjects failed: HTTP ${response.Status} at ${url}`);
|
|
125
|
+
}
|
|
126
|
+
const body = response.Body;
|
|
127
|
+
const results = [];
|
|
128
|
+
for (const obj of (body.sobjects ?? [])) {
|
|
129
|
+
if (!obj.queryable)
|
|
130
|
+
continue;
|
|
131
|
+
if (!this.IsNimbleScopedObject(obj.name))
|
|
132
|
+
continue;
|
|
133
|
+
results.push({
|
|
134
|
+
Name: obj.name,
|
|
135
|
+
Label: obj.label,
|
|
136
|
+
Description: `${obj.label} (Nimble AMS)`,
|
|
137
|
+
SupportsIncrementalSync: true,
|
|
138
|
+
SupportsWrite: obj.createable || obj.updateable,
|
|
139
|
+
});
|
|
140
|
+
}
|
|
141
|
+
return results;
|
|
142
|
+
}
|
|
143
|
+
async DiscoverFields(companyIntegration, objectName, contextUser) {
|
|
144
|
+
const auth = await this.Authenticate(companyIntegration, contextUser);
|
|
145
|
+
const url = `${this.SFDataBase(auth)}/sobjects/${encodeURIComponent(objectName)}/describe`;
|
|
146
|
+
const response = await this.MakeHTTPRequest(auth, url, 'GET', this.BuildHeaders(auth));
|
|
147
|
+
if (response.Status < 200 || response.Status >= 300) {
|
|
148
|
+
throw new Error(`Nimble AMS DiscoverFields failed for "${objectName}": HTTP ${response.Status}`);
|
|
149
|
+
}
|
|
150
|
+
const body = response.Body;
|
|
151
|
+
return (body.fields ?? []).map(f => {
|
|
152
|
+
const schema = {
|
|
153
|
+
Name: f.name,
|
|
154
|
+
Label: f.label,
|
|
155
|
+
Description: '',
|
|
156
|
+
DataType: this.MapSFType(f.type),
|
|
157
|
+
// SF describe states create-time required via `nillable=false` (provable from the source).
|
|
158
|
+
IsRequired: f.nillable === false,
|
|
159
|
+
// Provable-only: the SF `id` system field is the PK; uniqueness alone is NOT PK.
|
|
160
|
+
IsPrimaryKey: f.type === 'id',
|
|
161
|
+
IsUniqueKey: f.type === 'id' || f.unique === true,
|
|
162
|
+
IsReadOnly: !f.createable && !f.updateable,
|
|
163
|
+
};
|
|
164
|
+
// Bounded typing — propagate the source's length/precision/scale when present.
|
|
165
|
+
if (typeof f.length === 'number' && f.length > 0)
|
|
166
|
+
schema.MaxLength = f.length;
|
|
167
|
+
if (typeof f.precision === 'number' && f.precision > 0)
|
|
168
|
+
schema.Precision = f.precision;
|
|
169
|
+
if (typeof f.scale === 'number' && f.scale >= 0)
|
|
170
|
+
schema.Scale = f.scale;
|
|
171
|
+
return schema;
|
|
172
|
+
});
|
|
173
|
+
}
|
|
174
|
+
/** A SF API name is in Nimble scope iff it is a managed-package object or a standard AMS object. */
|
|
175
|
+
IsNimbleScopedObject(name) {
|
|
176
|
+
if (NIMBLE_STANDARD_OBJECTS.has(name))
|
|
177
|
+
return true;
|
|
178
|
+
return NIMBLE_NAMESPACES.some(ns => name.startsWith(ns));
|
|
179
|
+
}
|
|
180
|
+
MapSFType(sfType) {
|
|
181
|
+
const map = {
|
|
182
|
+
id: 'string', string: 'string', textarea: 'string', url: 'string', email: 'string', phone: 'string',
|
|
183
|
+
boolean: 'boolean', int: 'number', double: 'decimal', currency: 'decimal', percent: 'decimal',
|
|
184
|
+
date: 'datetime', datetime: 'datetime', time: 'string', reference: 'string',
|
|
185
|
+
picklist: 'string', multipicklist: 'string', base64: 'string', anyType: 'string', address: 'string',
|
|
186
|
+
};
|
|
187
|
+
return map[sfType] ?? 'string';
|
|
188
|
+
}
|
|
189
|
+
// ── Auth (Salesforce OAuth2 bearer) ──────────────────────────────────────
|
|
190
|
+
async Authenticate(companyIntegration, contextUser) {
|
|
191
|
+
const config = await this.ParseConfig(companyIntegration, contextUser);
|
|
192
|
+
if (this.tokenCache && this.tokenCache.ExpiresAt > Date.now() + TOKEN_REFRESH_BUFFER_MS) {
|
|
193
|
+
return this.BuildAuthContext(config, this.tokenCache.AccessToken, this.tokenCache.InstanceURL);
|
|
194
|
+
}
|
|
195
|
+
const token = await this.ObtainToken(config);
|
|
196
|
+
this.tokenCache = token;
|
|
197
|
+
return this.BuildAuthContext(config, token.AccessToken, token.InstanceURL);
|
|
198
|
+
}
|
|
199
|
+
BuildAuthContext(config, accessToken, instanceURL) {
|
|
200
|
+
return {
|
|
201
|
+
Token: accessToken,
|
|
202
|
+
TokenType: 'Bearer',
|
|
203
|
+
Config: config,
|
|
204
|
+
InstanceURL: instanceURL,
|
|
205
|
+
ApiVersion: config.ApiVersion,
|
|
206
|
+
};
|
|
207
|
+
}
|
|
208
|
+
async ObtainToken(config) {
|
|
209
|
+
// Pre-provisioned access token (e.g. test/broker injection) — use as-is, no exchange.
|
|
210
|
+
if (config.AccessToken && config.InstanceURL) {
|
|
211
|
+
return { AccessToken: config.AccessToken, InstanceURL: config.InstanceURL, ExpiresAt: Date.now() + TOKEN_LIFETIME_MS };
|
|
212
|
+
}
|
|
213
|
+
const tokenURL = `${config.LoginURL.replace(/\/+$/, '')}/services/oauth2/token`;
|
|
214
|
+
const params = { client_id: config.ClientID };
|
|
215
|
+
if (config.RefreshToken) {
|
|
216
|
+
params['grant_type'] = 'refresh_token';
|
|
217
|
+
params['refresh_token'] = config.RefreshToken;
|
|
218
|
+
if (config.ClientSecret)
|
|
219
|
+
params['client_secret'] = config.ClientSecret;
|
|
220
|
+
}
|
|
221
|
+
else {
|
|
222
|
+
if (!config.ClientSecret)
|
|
223
|
+
throw new Error('Nimble AMS auth requires either a refresh_token or a client_secret.');
|
|
224
|
+
params['grant_type'] = 'client_credentials';
|
|
225
|
+
params['client_secret'] = config.ClientSecret;
|
|
226
|
+
}
|
|
227
|
+
const body = new URLSearchParams(params);
|
|
228
|
+
const response = await fetch(tokenURL, {
|
|
229
|
+
method: 'POST',
|
|
230
|
+
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
|
|
231
|
+
body: body.toString(),
|
|
232
|
+
signal: AbortSignal.timeout(REQUEST_TIMEOUT_MS),
|
|
233
|
+
});
|
|
234
|
+
if (!response.ok)
|
|
235
|
+
throw new Error(`Nimble AMS auth failed: HTTP ${response.status}`);
|
|
236
|
+
const data = await response.json();
|
|
237
|
+
// Salesforce returns the org instance_url in the token response — it is the authoritative origin.
|
|
238
|
+
const instanceURL = data.instance_url ?? config.InstanceURL;
|
|
239
|
+
if (!instanceURL)
|
|
240
|
+
throw new Error('Nimble AMS auth response carried no instance_url and none was configured.');
|
|
241
|
+
return { AccessToken: data.access_token, InstanceURL: instanceURL, ExpiresAt: Date.now() + TOKEN_LIFETIME_MS };
|
|
242
|
+
}
|
|
243
|
+
async ParseConfig(companyIntegration, contextUser, provider) {
|
|
244
|
+
const fromConfigJSON = this.ParseConfigJSON(companyIntegration.Configuration);
|
|
245
|
+
const credentialID = companyIntegration.CredentialID;
|
|
246
|
+
if (credentialID) {
|
|
247
|
+
const md = provider ?? new Metadata();
|
|
248
|
+
const credential = await md.GetEntityObject('MJ: Credentials', contextUser);
|
|
249
|
+
const loaded = await credential.Load(credentialID);
|
|
250
|
+
if (loaded && credential.Values) {
|
|
251
|
+
const parsed = JSON.parse(credential.Values);
|
|
252
|
+
return this.BuildConfig(parsed, fromConfigJSON);
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
if (fromConfigJSON)
|
|
256
|
+
return this.BuildConfig(fromConfigJSON, null);
|
|
257
|
+
throw new Error('No Nimble AMS credentials found — set the Credential entity or Configuration JSON.');
|
|
258
|
+
}
|
|
259
|
+
ParseConfigJSON(json) {
|
|
260
|
+
if (!json)
|
|
261
|
+
return null;
|
|
262
|
+
try {
|
|
263
|
+
return JSON.parse(json);
|
|
264
|
+
}
|
|
265
|
+
catch {
|
|
266
|
+
return null;
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
BuildConfig(creds, configJSON) {
|
|
270
|
+
const pick = (...keys) => {
|
|
271
|
+
for (const k of keys) {
|
|
272
|
+
if (creds[k])
|
|
273
|
+
return creds[k];
|
|
274
|
+
if (configJSON && configJSON[k])
|
|
275
|
+
return configJSON[k];
|
|
276
|
+
}
|
|
277
|
+
return undefined;
|
|
278
|
+
};
|
|
279
|
+
return {
|
|
280
|
+
InstanceURL: pick('InstanceURL', 'instanceUrl', 'instance_url') ?? '',
|
|
281
|
+
ClientID: pick('ClientID', 'clientId', 'client_id') ?? '',
|
|
282
|
+
ClientSecret: pick('ClientSecret', 'clientSecret', 'client_secret'),
|
|
283
|
+
AccessToken: pick('AccessToken', 'accessToken', 'access_token'),
|
|
284
|
+
RefreshToken: pick('RefreshToken', 'refreshToken', 'refresh_token'),
|
|
285
|
+
LoginURL: pick('LoginURL', 'loginUrl', 'login_url') ?? 'https://login.salesforce.com',
|
|
286
|
+
ApiVersion: this.NormalizeApiVersion(pick('SalesforceAPIVersion', 'ApiVersion', 'apiVersion')),
|
|
287
|
+
};
|
|
288
|
+
}
|
|
289
|
+
/** Accept "59.0" or "v59.0"; the SF data path embeds it as `v{version}`. Store the bare number. */
|
|
290
|
+
NormalizeApiVersion(raw) {
|
|
291
|
+
if (!raw)
|
|
292
|
+
return DEFAULT_SF_API_VERSION;
|
|
293
|
+
return raw.replace(/^v/i, '');
|
|
294
|
+
}
|
|
295
|
+
// ── TestConnection ───────────────────────────────────────────────────────
|
|
296
|
+
async TestConnection(companyIntegration, contextUser) {
|
|
297
|
+
try {
|
|
298
|
+
const auth = await this.Authenticate(companyIntegration, contextUser);
|
|
299
|
+
const response = await this.MakeHTTPRequest(auth, `${this.SFDataBase(auth)}/sobjects/`, 'GET', this.BuildHeaders(auth));
|
|
300
|
+
if (response.Status >= 200 && response.Status < 300)
|
|
301
|
+
return { Success: true, Message: 'Connected to Nimble AMS (Salesforce)' };
|
|
302
|
+
return { Success: false, Message: `Nimble AMS API returned HTTP ${response.Status}` };
|
|
303
|
+
}
|
|
304
|
+
catch (err) {
|
|
305
|
+
return { Success: false, Message: err instanceof Error ? err.message : String(err) };
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
// ── URL / Response / Pagination ──────────────────────────────────────────
|
|
309
|
+
GetBaseURL(_ci, auth) {
|
|
310
|
+
return auth.InstanceURL;
|
|
311
|
+
}
|
|
312
|
+
/** The instance origin for all REST calls. Routes through GetBaseURL so a test harness can redirect. */
|
|
313
|
+
Origin(auth) {
|
|
314
|
+
return this.GetBaseURL(undefined, auth);
|
|
315
|
+
}
|
|
316
|
+
/** The `/services/data/v{version}` base for all Salesforce REST calls. */
|
|
317
|
+
SFDataBase(auth) {
|
|
318
|
+
return `${this.Origin(auth)}/services/data/v${auth.ApiVersion}`;
|
|
319
|
+
}
|
|
320
|
+
/** Strip the SF `attributes` envelope blob from every record (sanctioned, declared removal). */
|
|
321
|
+
TransformRecord(raw, _obj, _fields) {
|
|
322
|
+
if (!('attributes' in raw))
|
|
323
|
+
return raw;
|
|
324
|
+
const out = { ...raw };
|
|
325
|
+
delete out['attributes'];
|
|
326
|
+
return out;
|
|
327
|
+
}
|
|
328
|
+
ExcludedSourceKeys(_objectName) {
|
|
329
|
+
return ['attributes'];
|
|
330
|
+
}
|
|
331
|
+
/**
|
|
332
|
+
* Strip the response envelope to expose individual records. Handles all three doors:
|
|
333
|
+
* - SF SOQL → `body.records`
|
|
334
|
+
* - Fuse out → `body.Records` (note: PascalCase; null fields omitted per record)
|
|
335
|
+
* - LMS REST → root array or single object
|
|
336
|
+
*/
|
|
337
|
+
NormalizeResponse(rawBody, _key) {
|
|
338
|
+
if (Array.isArray(rawBody))
|
|
339
|
+
return rawBody;
|
|
340
|
+
if (!rawBody || typeof rawBody !== 'object')
|
|
341
|
+
return [];
|
|
342
|
+
const body = rawBody;
|
|
343
|
+
if (Array.isArray(body['records']))
|
|
344
|
+
return body['records'];
|
|
345
|
+
if (Array.isArray(body['Records']))
|
|
346
|
+
return body['Records'];
|
|
347
|
+
// A single non-enveloped record (e.g. LMS get-one) — pass through as one record.
|
|
348
|
+
return [body];
|
|
349
|
+
}
|
|
350
|
+
ExtractPaginationInfo(rawBody, _pt, _cp, _co, _ps) {
|
|
351
|
+
const body = rawBody;
|
|
352
|
+
return { HasMore: body?.done === false, NextCursor: body?.nextRecordsUrl };
|
|
353
|
+
}
|
|
354
|
+
// ── FetchChanges (door-aware) ────────────────────────────────────────────
|
|
355
|
+
async FetchChanges(ctx) {
|
|
356
|
+
const obj = this.GetCachedObject(ctx.CompanyIntegration.IntegrationID, ctx.ObjectName);
|
|
357
|
+
const cfg = this.ReadAccessConfig(obj);
|
|
358
|
+
const family = cfg.Family ?? '';
|
|
359
|
+
if (family === 'FuseEndpoint') {
|
|
360
|
+
// The Fuse endpoint IO is a contract descriptor, not a queryable record type — nothing to pull.
|
|
361
|
+
return { Records: [], HasMore: false, Warnings: [{ Code: 'NON_QUERYABLE', Message: `"${ctx.ObjectName}" is the Fuse contract descriptor; it carries no syncable records.` }] };
|
|
362
|
+
}
|
|
363
|
+
if (family === 'LmsFamily') {
|
|
364
|
+
return this.FetchLMS(ctx, obj);
|
|
365
|
+
}
|
|
366
|
+
// Default door: Salesforce REST SOQL (also used by the Fuse outbound named-SOQL setting when one
|
|
367
|
+
// is configured — the SOQL path is the credential-free-verifiable common case).
|
|
368
|
+
return this.FetchSOQL(ctx, obj);
|
|
369
|
+
}
|
|
370
|
+
/** Salesforce REST SOQL fetch — cursor pagination via nextRecordsUrl + watermark-ordered incremental. */
|
|
371
|
+
async FetchSOQL(ctx, obj) {
|
|
372
|
+
const auth = await this.Authenticate(ctx.CompanyIntegration, ctx.ContextUser);
|
|
373
|
+
const headers = this.BuildHeaders(auth);
|
|
374
|
+
const watermarkField = obj.IncrementalWatermarkField ?? 'LastModifiedDate';
|
|
375
|
+
// Follow the SF cursor when present; otherwise build the first SOQL page.
|
|
376
|
+
const url = ctx.CurrentCursor
|
|
377
|
+
? `${this.GetBaseURL(ctx.CompanyIntegration, auth)}${ctx.CurrentCursor}`
|
|
378
|
+
: `${this.SFDataBase(auth)}/query?q=${encodeURIComponent(this.BuildSOQL(ctx.ObjectName, watermarkField, ctx.WatermarkValue ?? null))}`;
|
|
379
|
+
const response = await this.MakeHTTPRequest(auth, url, 'GET', headers);
|
|
380
|
+
if (response.Status < 200 || response.Status >= 300) {
|
|
381
|
+
throw new Error(`Nimble AMS SOQL error for "${ctx.ObjectName}": HTTP ${response.Status} — ${this.SafeBody(response)}`);
|
|
382
|
+
}
|
|
383
|
+
const body = response.Body;
|
|
384
|
+
const raw = body.records ?? [];
|
|
385
|
+
// Defensive: the Fuse OUTBOUND door caps a single call at 50,000 records; SF native paging
|
|
386
|
+
// (≤2000/page) keeps SOQL pages well under it, so a page at/over the cap signals a misconfig.
|
|
387
|
+
const warnings = raw.length >= FUSE_MAX_RECORDS_PER_CALL
|
|
388
|
+
? [{ Code: 'PAGE_AT_RECORD_CAP', Message: `"${ctx.ObjectName}" returned ${raw.length} records in one page — at/over the ${FUSE_MAX_RECORDS_PER_CALL}-record Fuse per-call cap.` }]
|
|
389
|
+
: undefined;
|
|
390
|
+
const records = raw.map(r => this.RawToExternalRecord(r, obj, ctx.ObjectName, watermarkField, 'Id'));
|
|
391
|
+
// Watermark advances ONLY on the final page (full-batch success), and only to the max seen.
|
|
392
|
+
let newWatermark;
|
|
393
|
+
if (body.done) {
|
|
394
|
+
for (const r of raw) {
|
|
395
|
+
const mod = r[watermarkField];
|
|
396
|
+
if (typeof mod === 'string' && (!newWatermark || mod > newWatermark))
|
|
397
|
+
newWatermark = mod;
|
|
398
|
+
}
|
|
399
|
+
}
|
|
400
|
+
return {
|
|
401
|
+
Records: records,
|
|
402
|
+
HasMore: body.done === false,
|
|
403
|
+
NextCursor: body.nextRecordsUrl,
|
|
404
|
+
NewWatermarkValue: body.done ? newWatermark : undefined,
|
|
405
|
+
Warnings: warnings,
|
|
406
|
+
};
|
|
407
|
+
}
|
|
408
|
+
/**
|
|
409
|
+
* Build the SOQL for the first page. CRITICAL: NO `LIMIT` — Salesforce natively paginates via
|
|
410
|
+
* done/nextRecordsUrl, so a SOQL LIMIT would cap the ENTIRE result set and SF would report
|
|
411
|
+
* done=true at that count, silently dropping every record past the limit AND advancing the
|
|
412
|
+
* watermark past them next sync. Use `>=` (not `>`) so records at the exact watermark instant
|
|
413
|
+
* aren't lost; engine dedupe absorbs the boundary re-fetch. ORDER BY the watermark for monotonicity.
|
|
414
|
+
*/
|
|
415
|
+
BuildSOQL(objectName, watermarkField, watermarkValue) {
|
|
416
|
+
let soql = `SELECT FIELDS(ALL) FROM ${objectName}`;
|
|
417
|
+
if (watermarkValue)
|
|
418
|
+
soql += ` WHERE ${watermarkField} >= ${this.FormatSOQLDateTime(watermarkValue)}`;
|
|
419
|
+
soql += ` ORDER BY ${watermarkField} ASC`;
|
|
420
|
+
return soql;
|
|
421
|
+
}
|
|
422
|
+
/** SF SOQL datetime literals are unquoted ISO-8601 (e.g. 2026-01-01T00:00:00Z). */
|
|
423
|
+
FormatSOQLDateTime(value) {
|
|
424
|
+
// Already an unquoted ISO literal → use verbatim; otherwise emit a UTC ISO string.
|
|
425
|
+
if (/^\d{4}-\d{2}-\d{2}T/.test(value))
|
|
426
|
+
return value.endsWith('Z') || /[+-]\d{2}:\d{2}$/.test(value) ? value : `${value}Z`;
|
|
427
|
+
const d = new Date(value);
|
|
428
|
+
return isNaN(d.getTime()) ? value : d.toISOString();
|
|
429
|
+
}
|
|
430
|
+
/** NAMS LMS REST fetch — GET with an optional `lastUpdated` incremental filter (no pagination). */
|
|
431
|
+
async FetchLMS(ctx, obj) {
|
|
432
|
+
const auth = await this.Authenticate(ctx.CompanyIntegration, ctx.ContextUser);
|
|
433
|
+
const headers = this.BuildHeaders(auth);
|
|
434
|
+
const watermarkField = obj.IncrementalWatermarkField ?? 'lastUpdated';
|
|
435
|
+
const base = `${this.GetBaseURL(ctx.CompanyIntegration, auth)}${obj.APIPath}`;
|
|
436
|
+
const url = ctx.WatermarkValue
|
|
437
|
+
? `${base}?${encodeURIComponent(watermarkField)}=${encodeURIComponent(ctx.WatermarkValue)}`
|
|
438
|
+
: base;
|
|
439
|
+
const response = await this.MakeHTTPRequest(auth, url, 'GET', headers);
|
|
440
|
+
if (response.Status < 200 || response.Status >= 300) {
|
|
441
|
+
throw new Error(`Nimble AMS LMS error for "${ctx.ObjectName}": HTTP ${response.Status} — ${this.SafeBody(response)}`);
|
|
442
|
+
}
|
|
443
|
+
const raw = this.NormalizeResponse(response.Body, null);
|
|
444
|
+
const records = raw.map(r => this.RawToExternalRecord(r, obj, ctx.ObjectName, watermarkField, 'id'));
|
|
445
|
+
let newWatermark;
|
|
446
|
+
for (const r of raw) {
|
|
447
|
+
const mod = r[watermarkField];
|
|
448
|
+
if (typeof mod === 'string' && (!newWatermark || mod > newWatermark))
|
|
449
|
+
newWatermark = mod;
|
|
450
|
+
}
|
|
451
|
+
// LMS REST returns the full set in one response (no cursor) → batch is always complete.
|
|
452
|
+
return { Records: records, HasMore: false, NewWatermarkValue: newWatermark };
|
|
453
|
+
}
|
|
454
|
+
// ── CRUD (door-aware: Fuse inbound upsert vs Salesforce REST sObject) ─────
|
|
455
|
+
async CreateRecord(ctx) {
|
|
456
|
+
const ci = ctx.CompanyIntegration;
|
|
457
|
+
const obj = this.GetCachedObject(ci.IntegrationID, ctx.ObjectName);
|
|
458
|
+
if (this.IsFuseWritePath(obj)) {
|
|
459
|
+
return this.FuseUpsert(ctx.ContextUser, ci, obj, ctx.ObjectName, ctx.Attributes, /*forUpdate*/ false);
|
|
460
|
+
}
|
|
461
|
+
return this.SFRestCreate(ctx.ContextUser, ci, ctx.ObjectName, ctx.Attributes, obj);
|
|
462
|
+
}
|
|
463
|
+
async UpdateRecord(ctx) {
|
|
464
|
+
const ci = ctx.CompanyIntegration;
|
|
465
|
+
const obj = this.GetCachedObject(ci.IntegrationID, ctx.ObjectName);
|
|
466
|
+
if (this.IsFuseWritePath(obj)) {
|
|
467
|
+
// Fuse is upsert-keyed by External ID — the External ID rides in the record body, not the path.
|
|
468
|
+
const result = await this.FuseUpsert(ctx.ContextUser, ci, obj, ctx.ObjectName, ctx.Attributes, /*forUpdate*/ true);
|
|
469
|
+
// For update we report the supplied ExternalID on success (Fuse returns the SalesforceId).
|
|
470
|
+
return result.Success ? { Success: true, StatusCode: result.StatusCode, ExternalID: result.ExternalID ?? ctx.ExternalID } : result;
|
|
471
|
+
}
|
|
472
|
+
return this.SFRestUpdate(ctx.ContextUser, ci, ctx.ObjectName, ctx.ExternalID, ctx.Attributes, obj);
|
|
473
|
+
}
|
|
474
|
+
async DeleteRecord(ctx) {
|
|
475
|
+
const ci = ctx.CompanyIntegration;
|
|
476
|
+
const obj = this.GetCachedObject(ci.IntegrationID, ctx.ObjectName);
|
|
477
|
+
if (!obj.DeleteAPIPath || !obj.DeleteMethod) {
|
|
478
|
+
return { Success: false, StatusCode: 0, ErrorMessage: `DeleteRecord not supported for "${ctx.ObjectName}": Fuse is upsert-only; hard delete requires Salesforce REST sObject DELETE, which this object does not declare.` };
|
|
479
|
+
}
|
|
480
|
+
const auth = await this.Authenticate(ci, ctx.ContextUser);
|
|
481
|
+
const url = this.ResolveSFRestPath(auth, obj.DeleteAPIPath, ctx.ObjectName, ctx.ExternalID);
|
|
482
|
+
const response = await this.MakeHTTPRequest(auth, url, obj.DeleteMethod, this.BuildHeaders(auth));
|
|
483
|
+
if (response.Status === 204 || (response.Status >= 200 && response.Status < 300)) {
|
|
484
|
+
return { Success: true, StatusCode: response.Status, ExternalID: ctx.ExternalID };
|
|
485
|
+
}
|
|
486
|
+
return { Success: false, StatusCode: response.Status, ExternalID: '', ErrorMessage: `DeleteRecord failed for "${ctx.ObjectName}": HTTP ${response.Status} — ${this.SafeBody(response)}` };
|
|
487
|
+
}
|
|
488
|
+
/** Salesforce REST sObject create — POST `/sobjects/{ObjectName}` flat body, ID in response `id`. */
|
|
489
|
+
async SFRestCreate(contextUser, ci, objectName, attributes, obj) {
|
|
490
|
+
const auth = await this.Authenticate(ci, contextUser);
|
|
491
|
+
const createPath = obj.CreateAPIPath ?? `/services/data/v{apiVersion}/sobjects/{ObjectName}/`;
|
|
492
|
+
const url = this.ResolveSFRestPath(auth, createPath, objectName, undefined);
|
|
493
|
+
const headers = { ...this.BuildHeaders(auth), 'Content-Type': 'application/json' };
|
|
494
|
+
const response = await this.MakeHTTPRequest(auth, url, obj.CreateMethod ?? 'POST', headers, attributes);
|
|
495
|
+
if (response.Status >= 200 && response.Status < 300) {
|
|
496
|
+
const body = response.Body;
|
|
497
|
+
const newID = body?.['id'] == null ? undefined : String(body['id']);
|
|
498
|
+
return this.BuildCreatedResult(newID, response.Status, objectName); // fail LOUDLY on empty id
|
|
499
|
+
}
|
|
500
|
+
return { Success: false, StatusCode: response.Status, ErrorMessage: `CreateRecord failed for "${objectName}": HTTP ${response.Status} — ${this.SafeBody(response)}` };
|
|
501
|
+
}
|
|
502
|
+
/** Salesforce REST sObject update — PATCH `/sobjects/{ObjectName}/{Id}` flat body. */
|
|
503
|
+
async SFRestUpdate(contextUser, ci, objectName, externalID, attributes, obj) {
|
|
504
|
+
const auth = await this.Authenticate(ci, contextUser);
|
|
505
|
+
const updatePath = obj.UpdateAPIPath ?? `/services/data/v{apiVersion}/sobjects/{ObjectName}/{Id}`;
|
|
506
|
+
const url = this.ResolveSFRestPath(auth, updatePath, objectName, externalID);
|
|
507
|
+
const headers = { ...this.BuildHeaders(auth), 'Content-Type': 'application/json' };
|
|
508
|
+
const response = await this.MakeHTTPRequest(auth, url, obj.UpdateMethod ?? 'PATCH', headers, attributes);
|
|
509
|
+
if (response.Status >= 200 && response.Status < 300) {
|
|
510
|
+
return { Success: true, StatusCode: response.Status, ExternalID: externalID };
|
|
511
|
+
}
|
|
512
|
+
return { Success: false, StatusCode: response.Status, ExternalID: '', ErrorMessage: `UpdateRecord failed for "${objectName}": HTTP ${response.Status} — ${this.SafeBody(response)}` };
|
|
513
|
+
}
|
|
514
|
+
/**
|
|
515
|
+
* Nimble Fuse INBOUND upsert — POST `/services/apexrest/NUINT/NUIntegrationService` with the
|
|
516
|
+
* `{Request: {Name, "Authentication Key", InboundRecords:[{Fields:{...}}]}}` envelope. Parses the
|
|
517
|
+
* per-record `InboundResults` for partial success, routes the single-record create through
|
|
518
|
+
* BuildCreatedResult, and fails LOUDLY on an empty SalesforceId.
|
|
519
|
+
*/
|
|
520
|
+
async FuseUpsert(contextUser, ci, obj, objectName, attributes, forUpdate) {
|
|
521
|
+
const auth = await this.Authenticate(ci, contextUser);
|
|
522
|
+
const cfg = this.ReadAccessConfig(obj);
|
|
523
|
+
const settingName = cfg.InboundSettingName;
|
|
524
|
+
if (!settingName) {
|
|
525
|
+
return { Success: false, StatusCode: 0, ErrorMessage: `Fuse upsert for "${objectName}" requires Configuration.InboundSettingName (the admin-configured Integration-Setting name); none is set.` };
|
|
526
|
+
}
|
|
527
|
+
const authKey = (auth.Config.AccessToken ? undefined : auth.Config.ClientSecret) ?? auth.Token ?? '';
|
|
528
|
+
const envelope = {
|
|
529
|
+
Request: {
|
|
530
|
+
Name: settingName,
|
|
531
|
+
'Authentication Key': authKey,
|
|
532
|
+
InboundRecords: [{ Fields: attributes }],
|
|
533
|
+
},
|
|
534
|
+
};
|
|
535
|
+
const url = `${this.GetBaseURL(ci, auth)}${FUSE_PATH}`;
|
|
536
|
+
const headers = { ...this.BuildHeaders(auth), 'Content-Type': 'application/json' };
|
|
537
|
+
const response = await this.MakeHTTPRequest(auth, url, 'POST', headers, envelope);
|
|
538
|
+
if (response.Status < 200 || response.Status >= 300) {
|
|
539
|
+
return { Success: false, StatusCode: response.Status, ErrorMessage: `Fuse upsert failed for "${objectName}": HTTP ${response.Status} — ${this.SafeBody(response)}` };
|
|
540
|
+
}
|
|
541
|
+
const body = response.Body;
|
|
542
|
+
const result = (body.InboundResults ?? [])[0];
|
|
543
|
+
const envelopeError = this.JoinErrors(body.ErrorMessages);
|
|
544
|
+
if (!result) {
|
|
545
|
+
return { Success: false, StatusCode: response.Status, ErrorMessage: `Fuse upsert for "${objectName}" returned no InboundResults${envelopeError ? ` — ${envelopeError}` : ''}.` };
|
|
546
|
+
}
|
|
547
|
+
const recordError = this.JoinErrors(result.ErrorMessages ?? result.ErrorMessage);
|
|
548
|
+
if (result.Success === false || recordError) {
|
|
549
|
+
return { Success: false, StatusCode: response.Status, ExternalID: '', ErrorMessage: `Fuse upsert for "${objectName}" failed for ExternalId="${result.ExternalId ?? ''}": ${recordError || envelopeError || body.Message || 'unknown error'}` };
|
|
550
|
+
}
|
|
551
|
+
// A 2xx with no SalesforceId is a silent loss — fail LOUDLY via BuildCreatedResult.
|
|
552
|
+
if (!forUpdate) {
|
|
553
|
+
return this.BuildCreatedResult(result.SalesforceId, response.Status, objectName);
|
|
554
|
+
}
|
|
555
|
+
const sfid = result.SalesforceId == null ? '' : String(result.SalesforceId).trim();
|
|
556
|
+
if (sfid.length === 0) {
|
|
557
|
+
return { Success: false, StatusCode: response.Status, ErrorMessage: `Fuse upsert (update) for "${objectName}" returned HTTP ${response.Status} but no SalesforceId — treating as a failure.` };
|
|
558
|
+
}
|
|
559
|
+
return { Success: true, StatusCode: response.Status, ExternalID: sfid };
|
|
560
|
+
}
|
|
561
|
+
JoinErrors(errs) {
|
|
562
|
+
if (!errs)
|
|
563
|
+
return '';
|
|
564
|
+
return Array.isArray(errs) ? errs.filter(Boolean).join('; ') : errs;
|
|
565
|
+
}
|
|
566
|
+
/** True when the object's write door is the Fuse inbound upsert (vs Salesforce REST sObject CRUD). */
|
|
567
|
+
IsFuseWritePath(obj) {
|
|
568
|
+
return (obj.CreateAPIPath ?? '').includes(FUSE_PATH) || (obj.UpdateAPIPath ?? '').includes(FUSE_PATH);
|
|
569
|
+
}
|
|
570
|
+
/**
|
|
571
|
+
* Resolve a Salesforce-REST path template, substituting `{apiVersion}` (bare version), `{ObjectName}`,
|
|
572
|
+
* and `{Id}`/`{ExternalID}`. The contract's SF-REST paths are root-relative (start at /services/...),
|
|
573
|
+
* so we prefix the instance origin only.
|
|
574
|
+
*/
|
|
575
|
+
ResolveSFRestPath(auth, pathTemplate, objectName, externalID) {
|
|
576
|
+
let path = pathTemplate
|
|
577
|
+
.replace(/\{apiVersion\}/g, auth.ApiVersion)
|
|
578
|
+
.replace(/\{ObjectName\}/g, encodeURIComponent(objectName));
|
|
579
|
+
if (externalID !== undefined) {
|
|
580
|
+
const enc = encodeURIComponent(externalID);
|
|
581
|
+
path = path.replace(/\{Id\}/g, enc).replace(/\{ID\}/g, enc).replace(/\{id\}/g, enc).replace(/\{ExternalID\}/g, enc);
|
|
582
|
+
}
|
|
583
|
+
return `${this.Origin(auth)}${path}`;
|
|
584
|
+
}
|
|
585
|
+
/**
|
|
586
|
+
* Build an ExternalRecord from a raw vendor record. The FULL record flows into `Fields` (custom-column
|
|
587
|
+
* pass-through contract M1–M4) — the ONLY removed key is the sanctioned SF `attributes` envelope blob
|
|
588
|
+
* declared in {@link ExcludedSourceKeys}. The PK field supplies ExternalID; the watermark field supplies
|
|
589
|
+
* ModifiedAt. (FetchSOQL/FetchLMS override the base fetch, so we hand-build records here.)
|
|
590
|
+
*/
|
|
591
|
+
RawToExternalRecord(raw, obj, objectType, watermarkField, pkField) {
|
|
592
|
+
const transformed = this.TransformRecord(raw, obj, this.GetCachedFields(obj.ID));
|
|
593
|
+
const excluded = new Set(this.ExcludedSourceKeys(objectType));
|
|
594
|
+
const fields = {};
|
|
595
|
+
for (const [key, value] of Object.entries(transformed)) {
|
|
596
|
+
if (excluded.has(key))
|
|
597
|
+
continue;
|
|
598
|
+
fields[key] = value;
|
|
599
|
+
}
|
|
600
|
+
const modRaw = raw[watermarkField];
|
|
601
|
+
return {
|
|
602
|
+
ExternalID: String(raw[pkField] ?? ''),
|
|
603
|
+
ObjectType: objectType,
|
|
604
|
+
Fields: fields,
|
|
605
|
+
ModifiedAt: typeof modRaw === 'string' ? new Date(modRaw) : undefined,
|
|
606
|
+
IsDeleted: raw['IsDeleted'] === true,
|
|
607
|
+
};
|
|
608
|
+
}
|
|
609
|
+
/** Parse the per-IO access Configuration (Family / SObjectType / AccessPath / Fuse setting names). */
|
|
610
|
+
ReadAccessConfig(obj) {
|
|
611
|
+
const raw = obj.Configuration;
|
|
612
|
+
if (!raw)
|
|
613
|
+
return {};
|
|
614
|
+
try {
|
|
615
|
+
return JSON.parse(raw);
|
|
616
|
+
}
|
|
617
|
+
catch {
|
|
618
|
+
return {};
|
|
619
|
+
}
|
|
620
|
+
}
|
|
621
|
+
// ── Headers + HTTP transport ─────────────────────────────────────────────
|
|
622
|
+
BuildHeaders(auth) {
|
|
623
|
+
return {
|
|
624
|
+
Authorization: `Bearer ${auth.Token}`,
|
|
625
|
+
Accept: 'application/json',
|
|
626
|
+
'User-Agent': 'MemberJunction-Integration/1.0',
|
|
627
|
+
};
|
|
628
|
+
}
|
|
629
|
+
async MakeHTTPRequest(_auth, url, method, headers, body) {
|
|
630
|
+
await this.Throttle();
|
|
631
|
+
for (let attempt = 0; attempt <= MAX_RETRIES; attempt++) {
|
|
632
|
+
const response = await this.FetchWithTimeout(url, method, headers, body);
|
|
633
|
+
this.lastRequestTime = Date.now();
|
|
634
|
+
if (response.status === 401 && attempt === 0) {
|
|
635
|
+
this.tokenCache = null;
|
|
636
|
+
continue;
|
|
637
|
+
}
|
|
638
|
+
if (response.status === 429 && attempt < MAX_RETRIES) {
|
|
639
|
+
const ra = Number(response.headers.get('retry-after'));
|
|
640
|
+
const wait = !isNaN(ra) && ra > 0 ? ra * 1000 : Math.min(1000 * Math.pow(2, attempt) + Math.random() * 500, 60_000);
|
|
641
|
+
await this.Sleep(wait);
|
|
642
|
+
continue;
|
|
643
|
+
}
|
|
644
|
+
if (response.status >= 500 && attempt < MAX_RETRIES) {
|
|
645
|
+
await this.Sleep(Math.min(1000 * Math.pow(2, attempt), 30_000));
|
|
646
|
+
continue;
|
|
647
|
+
}
|
|
648
|
+
const parsed = await this.ParseBody(response);
|
|
649
|
+
return this.ToRESTResponse(response, parsed);
|
|
650
|
+
}
|
|
651
|
+
throw new Error(`Nimble AMS request failed after ${MAX_RETRIES} retries: ${url}`);
|
|
652
|
+
}
|
|
653
|
+
async FetchWithTimeout(url, method, headers, body) {
|
|
654
|
+
const controller = new AbortController();
|
|
655
|
+
const tid = setTimeout(() => controller.abort(), REQUEST_TIMEOUT_MS);
|
|
656
|
+
try {
|
|
657
|
+
const opts = { method, headers, signal: controller.signal };
|
|
658
|
+
if (body !== undefined && (method === 'POST' || method === 'PUT' || method === 'PATCH')) {
|
|
659
|
+
const serialized = JSON.stringify(body);
|
|
660
|
+
// Fuse OUTBOUND enforces a 3MB-per-call ceiling — guard the request body up front.
|
|
661
|
+
if (url.includes(FUSE_PATH) && Buffer.byteLength(serialized, 'utf8') > FUSE_MAX_BYTES_PER_CALL) {
|
|
662
|
+
throw new Error(`Nimble Fuse request body exceeds the ${FUSE_MAX_BYTES_PER_CALL}-byte (3MB) per-call limit.`);
|
|
663
|
+
}
|
|
664
|
+
opts.body = serialized;
|
|
665
|
+
}
|
|
666
|
+
return await fetch(url, opts);
|
|
667
|
+
}
|
|
668
|
+
catch (err) {
|
|
669
|
+
if (err instanceof Error && err.name === 'AbortError')
|
|
670
|
+
throw new Error(`Request timed out: ${url}`);
|
|
671
|
+
throw err;
|
|
672
|
+
}
|
|
673
|
+
finally {
|
|
674
|
+
clearTimeout(tid);
|
|
675
|
+
}
|
|
676
|
+
}
|
|
677
|
+
async ParseBody(r) {
|
|
678
|
+
const ct = r.headers.get('content-type') ?? '';
|
|
679
|
+
return ct.includes('json') ? r.json().catch(() => null) : r.text();
|
|
680
|
+
}
|
|
681
|
+
ToRESTResponse(r, body) {
|
|
682
|
+
const h = {};
|
|
683
|
+
r.headers.forEach((v, k) => { h[k.toLowerCase()] = v; });
|
|
684
|
+
return { Status: r.status, Body: body, Headers: h };
|
|
685
|
+
}
|
|
686
|
+
SafeBody(r) {
|
|
687
|
+
const s = typeof r.Body === 'string' ? r.Body : JSON.stringify(r.Body);
|
|
688
|
+
return (s ?? '').substring(0, 300);
|
|
689
|
+
}
|
|
690
|
+
async Throttle() {
|
|
691
|
+
const elapsed = Date.now() - this.lastRequestTime;
|
|
692
|
+
if (elapsed < MIN_REQUEST_INTERVAL_MS)
|
|
693
|
+
await this.Sleep(MIN_REQUEST_INTERVAL_MS - elapsed);
|
|
694
|
+
}
|
|
695
|
+
Sleep(ms) { return new Promise(resolve => setTimeout(resolve, ms)); }
|
|
696
|
+
};
|
|
697
|
+
NimbleAMSConnector = __decorate([
|
|
698
|
+
RegisterClass(BaseIntegrationConnector, '@memberjunction/connector-nimble-ams')
|
|
699
|
+
], NimbleAMSConnector);
|
|
700
|
+
export { NimbleAMSConnector };
|
|
701
|
+
// Tree-shaking prevention — REQUIRED so @RegisterClass survives bundling.
|
|
702
|
+
export function LoadNimbleAMSConnector() { }
|
|
703
|
+
//# sourceMappingURL=NimbleAMSConnector.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"NimbleAMSConnector.js","sourceRoot":"","sources":["../src/NimbleAMSConnector.ts"],"names":[],"mappings":"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"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
export * from './NimbleAMSConnector.js';
|
|
2
|
+
/** Open App bootstrap entry: importing this module ran the connector's @RegisterClass decorator;
|
|
3
|
+
* this no-op satisfies the loader's required startupExport and forces the import at MJAPI boot. */
|
|
4
|
+
export declare function registerConnector(): void;
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
export * from './NimbleAMSConnector.js';
|
|
2
|
+
/** Open App bootstrap entry: importing this module ran the connector's @RegisterClass decorator;
|
|
3
|
+
* this no-op satisfies the loader's required startupExport and forces the import at MJAPI boot. */
|
|
4
|
+
export function registerConnector() { }
|
|
5
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,yBAAyB,CAAC;AAExC;oGACoG;AACpG,MAAM,UAAU,iBAAiB,KAAiD,CAAC"}
|
package/package.json
ADDED
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@memberjunction/connector-nimble-ams",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "MemberJunction NimbleAMS connector.",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "dist/index.js",
|
|
7
|
+
"types": "dist/index.d.ts",
|
|
8
|
+
"files": [
|
|
9
|
+
"/dist"
|
|
10
|
+
],
|
|
11
|
+
"scripts": {
|
|
12
|
+
"build": "tsc && tsc-alias -f",
|
|
13
|
+
"test": "vitest run --passWithNoTests"
|
|
14
|
+
},
|
|
15
|
+
"author": "MemberJunction.com",
|
|
16
|
+
"license": "ISC",
|
|
17
|
+
"peerDependencies": {
|
|
18
|
+
"@memberjunction/core": ">=5.42.0 <6.0.0",
|
|
19
|
+
"@memberjunction/core-entities": ">=5.42.0 <6.0.0",
|
|
20
|
+
"@memberjunction/global": ">=5.42.0 <6.0.0",
|
|
21
|
+
"@memberjunction/integration-engine": ">=5.42.0 <6.0.0"
|
|
22
|
+
},
|
|
23
|
+
"dependencies": {},
|
|
24
|
+
"devDependencies": {
|
|
25
|
+
"@types/node": "24.10.11",
|
|
26
|
+
"tsc-alias": "^1.8.16",
|
|
27
|
+
"typescript": "^5.9.3",
|
|
28
|
+
"vitest": "^4.0.18",
|
|
29
|
+
"@memberjunction/core": "^5.42.0",
|
|
30
|
+
"@memberjunction/core-entities": "^5.42.0",
|
|
31
|
+
"@memberjunction/global": "^5.42.0",
|
|
32
|
+
"@memberjunction/integration-engine": "^5.42.0"
|
|
33
|
+
},
|
|
34
|
+
"repository": {
|
|
35
|
+
"type": "git",
|
|
36
|
+
"url": "https://github.com/MemberJunction/Integrations"
|
|
37
|
+
}
|
|
38
|
+
}
|