@memberjunction/connector-microsoft-dynamics-365-dataverse 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.
@@ -0,0 +1,295 @@
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 RateLimitPolicy } from '@memberjunction/integration-engine';
4
+ /**
5
+ * Connection configuration parsed from the CompanyIntegration's MJ Credential
6
+ * (preferred) or its Configuration JSON. Dynamics 365 / Dataverse is reached
7
+ * through the OData v4 Web API using a Microsoft Entra ID (Azure AD)
8
+ * client-credentials (server-to-server / app-only) token.
9
+ *
10
+ * Field names are read case-insensitively so a credential authored as
11
+ * `tenantId`/`TenantID`/`tenant_id` all resolve.
12
+ */
13
+ export interface DynamicsConnectionConfig {
14
+ /** Microsoft Entra ID tenant GUID (or `common`/`organizations`). */
15
+ TenantId: string;
16
+ /** Application (client) registration GUID. */
17
+ ClientId: string;
18
+ /** Application client secret. */
19
+ ClientSecret: string;
20
+ /**
21
+ * The Dataverse environment URL, e.g. `https://contoso.crm.dynamics.com`.
22
+ * Per-customer — NEVER hardcoded. The OAuth resource/audience scope is
23
+ * `<EnvironmentUrl>/.default` and the Web API base is `<EnvironmentUrl>/api/data/v9.2`.
24
+ */
25
+ EnvironmentUrl: string;
26
+ /** Override the Web API version segment. Default: `v9.2`. */
27
+ ApiVersion?: string;
28
+ /** Override the Entra ID token endpoint origin. Default: `https://login.microsoftonline.com`. */
29
+ AuthorityHost?: string;
30
+ /** Override the OAuth scope. Default: `<EnvironmentUrl>/.default`. */
31
+ Scope?: string;
32
+ /** Maximum retries for rate-limited / transient failures. Default: 5. */
33
+ MaxRetries?: number;
34
+ /** HTTP request timeout in milliseconds. Default: 60000. */
35
+ RequestTimeoutMs?: number;
36
+ /** Minimum milliseconds between API requests. Default: 0 (governed by the engine's adaptive bucket). */
37
+ MinRequestIntervalMs?: number;
38
+ /** Requested OData max page size (capped at 5000 by the service). Default: 5000. */
39
+ MaxPageSize?: number;
40
+ }
41
+ /** Auth context carrying the minted bearer token plus the resolved Web API base URL + config. */
42
+ interface DynamicsAuthContext extends RESTAuthContext {
43
+ Config: DynamicsConnectionConfig;
44
+ /**
45
+ * Org ROOT URL (`<EnvironmentUrl>`) used by the base class's BuildFullURL(baseURL, APIPath).
46
+ * The frozen IO `APIPath`s are ABSOLUTE-from-root (they already include `/api/data/v9.2/...`), so
47
+ * the base must be the org root to avoid doubling the version segment.
48
+ */
49
+ BaseUrl: string;
50
+ /** Versioned Web API base (`<EnvironmentUrl>/api/data/v9.2`) for the connector's OWN calls (WhoAmI, EntityDefinitions, delta). */
51
+ ApiBaseUrl: string;
52
+ }
53
+ /**
54
+ * Connector for Microsoft Dynamics 365 / Dataverse (Common Data Service) via the
55
+ * OData v4 Web API.
56
+ *
57
+ * - **Auth**: Microsoft Entra ID OAuth 2.0 client-credentials (app-only). Token is
58
+ * minted/cached by the shared {@link OAuth2TokenManager}; the credential bytes are
59
+ * resolved at runtime from the connection's MJ Credential / Configuration — never baked.
60
+ * - **Base URL**: `<EnvironmentUrl>/api/data/v9.2`, per-connection (every customer
61
+ * environment differs — nothing is hardcoded).
62
+ * - **Discovery**: `DiscoverObjects` / `DiscoverFields` parse the credentialed
63
+ * `EntityDefinitions` describe endpoint at runtime (case-2 auth-gated discovery) so
64
+ * BOTH standard and custom/solution-installed tables surface. The documented standard
65
+ * catalog lives in the Declared metadata file, not in this class.
66
+ * - **Pagination**: OData `@odata.nextLink` cursor, followed verbatim.
67
+ * - **Write**: generic per-operation CRUD from {@link BaseRESTIntegrationConnector}
68
+ * (flat body; create ID read from the `OData-EntityId` response header; update PATCH
69
+ * `/<entityset>(id)`; delete DELETE `/<entityset>(id)`; alternate-key upsert path form).
70
+ * - **Incremental**: change-tracking delta (`Prefer: odata.track-changes` →
71
+ * `@odata.deltaLink`) when a table declares it; `modifiedon`-polling fallback otherwise.
72
+ */
73
+ export declare class DynamicsDataverseConnector extends BaseRESTIntegrationConnector {
74
+ private readonly tokenManager;
75
+ private cachedAuth;
76
+ /** Verbatim from the upstream identity handoff. Drives the three-way name invariant.
77
+ * Returned as a string literal (not the INTEGRATION_NAME const ref) so the T1
78
+ * ThreeWayName static parser can extract it from source. */
79
+ get IntegrationName(): string;
80
+ get SupportsCreate(): boolean;
81
+ get SupportsUpdate(): boolean;
82
+ get SupportsDelete(): boolean;
83
+ /**
84
+ * §7 — `EntityDefinitions` enumerates the COMPLETE credentialed gamut (standard + custom +
85
+ * solution-installed tables / attributes), not a filtered subset, so a comprehensive refresh
86
+ * may safely + reversibly deactivate objects no longer in the response.
87
+ */
88
+ get DiscoveryIsAuthoritative(): boolean;
89
+ /**
90
+ * §7 — the watermark this connector advances (delta-link / modifiedon high-water) IS monotonic:
91
+ * change-tracking yields a forward-only delta token, and the modifiedon fallback fetches in
92
+ * ascending `modifiedon` order so the last batch carries the true maximum. Lets the engine narrow
93
+ * the next incremental instead of advancing to wall-clock now.
94
+ */
95
+ get MonotonicWatermark(): boolean;
96
+ /**
97
+ * §7 — keyset/seek resume key for watermark-less objects: the table's GUID primary key
98
+ * (`<logicalName>id`), read from the IO's Configuration. Stable + monotonic-enough for
99
+ * resume-from-last-seen; `null` when no PK is known.
100
+ */
101
+ StableOrderingKey(objectName: string): string | null;
102
+ /**
103
+ * §7 — Dataverse enforces a per-environment Web API service-protection limit (≈ 6000 requests /
104
+ * 300s sliding window, plus an execution-time budget). Conservative sustained rate keeps the
105
+ * connector under the request-count limit; the engine's AIMD bucket adapts on 429s via
106
+ * {@link ExtractRetryAfterMs}. (≈6000/300 ≈ 20 req/s; we stay below to leave headroom.)
107
+ */
108
+ get RateLimitPolicy(): RateLimitPolicy | null;
109
+ get MaxConcurrencyHint(): number | null;
110
+ /**
111
+ * Parse Dataverse's throttle signal. The service returns HTTP 429 with a `Retry-After` header
112
+ * (seconds) on a service-protection limit; honor it precisely.
113
+ */
114
+ ExtractRetryAfterMs(error: unknown): number | undefined;
115
+ /**
116
+ * Validates the service principal can reach the environment by issuing a fresh token and a
117
+ * lightweight `WhoAmI` call (an unbound function returning the calling user/org GUIDs).
118
+ */
119
+ TestConnection(companyIntegration: MJCompanyIntegrationEntity, contextUser: UserInfo): Promise<ConnectionTestResult>;
120
+ /**
121
+ * Case-2 (auth-gated) runtime discovery: enumerates EVERY table the credential can see via the
122
+ * `EntityDefinitions` describe endpoint (standard + custom + solution-installed) — NOT a baked
123
+ * catalog. The documented standard catalog seeded in the Declared metadata file is the floor;
124
+ * this returns what the live environment actually exposes (the ceiling), which is strictly a
125
+ * superset for custom-bearing tenants.
126
+ */
127
+ DiscoverObjects(companyIntegration: MJCompanyIntegrationEntity, contextUser: UserInfo): Promise<ExternalObjectSchema[]>;
128
+ /**
129
+ * Case-2 runtime discovery for a single object's columns: describes the table's `Attributes`
130
+ * collection and maps each Property → {@link ExternalFieldSchema}, surfacing Type/MaxLength,
131
+ * the GUID PK (`IsPrimaryId` / `PrimaryIdAttribute` → `IsPrimaryKey`), and scalar Lookup
132
+ * columns → FK (with `ForeignKeyTarget` = the referenced table's logical name).
133
+ */
134
+ DiscoverFields(companyIntegration: MJCompanyIntegrationEntity, objectName: string, contextUser: UserInfo): Promise<ExternalFieldSchema[]>;
135
+ /**
136
+ * Routes to the change-tracking delta path when the table declares it AND a watermark exists;
137
+ * otherwise delegates to the base metadata-driven fetch (which applies the `modifiedon` watermark
138
+ * fallback + standard `@odata.nextLink` pagination). The base path already handles
139
+ * `TransformRecord` (annotation stripping) + full-record pass-through, so the delta path mirrors it.
140
+ */
141
+ FetchChanges(ctx: FetchContext): Promise<FetchBatchResult>;
142
+ /**
143
+ * Removes the OData control annotations (`@odata.etag`, `@odata.context`, `@odata.id`,
144
+ * `<lookup>@odata.bind`, `<lookup>@OData.Community.Display.V1.FormattedValue`, etc.) Dataverse
145
+ * sprinkles onto every record — they are response transport metadata, not columns. Every other
146
+ * source key is preserved (full-record pass-through); the removed keys are declared in
147
+ * {@link ExcludedSourceKeys} so the base re-add doesn't restore them and change-detection ignores them.
148
+ */
149
+ protected TransformRecord(raw: Record<string, unknown>, _obj: MJIntegrationObjectEntity, _fields: MJIntegrationObjectFieldEntity[]): Record<string, unknown>;
150
+ /**
151
+ * The well-known static OData control annotation keys {@link TransformRecord} drops. The base
152
+ * `applyTransformPreservingKeys` uses this to avoid re-adding them. Dataverse ALSO emits dynamic,
153
+ * per-lookup annotation suffixes (`<col>@odata.bind`, `<col>@OData.Community.Display.V1.FormattedValue`,
154
+ * `<col>@Microsoft.Dynamics.CRM.lookuplogicalname`) whose exact names vary by record and cannot be
155
+ * enumerated statically — those are handled by the {@link applyTransformPreservingKeys} override
156
+ * below (predicate exclusion), so they never re-appear. This static list covers the always-present
157
+ * envelope keys for completeness / auditability.
158
+ */
159
+ protected ExcludedSourceKeys(_objectName: string): string[];
160
+ /**
161
+ * Overrides the base re-add so the DYNAMIC OData annotation keys (`<col>@odata.bind`, FormattedValue,
162
+ * lookuplogicalname, etc.) {@link TransformRecord} strips are NOT restored. The base loop re-adds any
163
+ * `raw` key absent from the transform output unless it is in the static {@link ExcludedSourceKeys}
164
+ * set — which can't enumerate the per-record annotation suffixes. Here we re-add a dropped key only
165
+ * when it is neither statically excluded NOR an annotation key (by predicate). This keeps full-record
166
+ * pass-through for genuine columns while making the annotation removal stick (auditable, by rule).
167
+ */
168
+ protected applyTransformPreservingKeys(raw: Record<string, unknown>, obj: MJIntegrationObjectEntity, fields: MJIntegrationObjectFieldEntity[]): Record<string, unknown>;
169
+ /**
170
+ * Mints/caches a Microsoft Entra ID access token via client-credentials. The scope is the
171
+ * environment's `.default` (`<EnvironmentUrl>/.default`) so the token's audience matches the
172
+ * Dataverse resource. Token is cached by {@link OAuth2TokenManager} until near expiry.
173
+ */
174
+ protected Authenticate(companyIntegration: MJCompanyIntegrationEntity, contextUser: UserInfo, forceRefresh?: boolean): Promise<DynamicsAuthContext>;
175
+ /** Runs the client_credentials token round-trip through {@link OAuth2TokenManager}. */
176
+ private MintToken;
177
+ /** Standard OData/Dataverse headers — bearer token + JSON + OData version + return-representation. */
178
+ protected BuildHeaders(auth: RESTAuthContext): Record<string, string>;
179
+ /**
180
+ * HTTP transport with retry/backoff on 429/503/504 (Dataverse service-protection limits). The
181
+ * thrown error on a retryable status carries the response headers so {@link ExtractRetryAfterMs}
182
+ * can read `Retry-After` for the engine's adaptive bucket.
183
+ */
184
+ protected MakeHTTPRequest(auth: RESTAuthContext, url: string, method: string, headers: Record<string, string>, body?: unknown): Promise<RESTResponse>;
185
+ /**
186
+ * Unwraps the OData collection envelope's `value` array. A single-record GET (`/<set>(id)`) is
187
+ * returned as a one-element array. Honors a metadata-declared `ResponseDataKey` if set, defaulting
188
+ * to the OData `value` convention.
189
+ */
190
+ protected NormalizeResponse(rawBody: unknown, responseDataKey: string | null): Record<string, unknown>[];
191
+ /**
192
+ * Cursor pagination via `@odata.nextLink`. The nextLink is an ABSOLUTE continuation URL that
193
+ * encodes the exact next request (including the opaque `$skiptoken`); it is followed VERBATIM and
194
+ * never hand-built. A present nextLink means another page exists.
195
+ */
196
+ protected ExtractPaginationInfo(rawBody: unknown, _paginationType: PaginationType, _currentPage: number, _currentOffset: number, _pageSize: number): PaginationState;
197
+ /**
198
+ * Per-connection base the engine joins the IO `APIPath` onto. The frozen Dataverse IO APIPaths are
199
+ * ABSOLUTE-from-root (they already include `/api/data/v9.2/...`), so this returns the org ROOT
200
+ * (`<EnvironmentUrl>`) and the resolved request URL is `<EnvironmentUrl>/api/data/v9.2/<entityset>`.
201
+ * The versioned Web API base (`<EnvironmentUrl>/api/data/v9.2`) is `ApiBaseUrl`, used by the
202
+ * connector's OWN calls (WhoAmI / EntityDefinitions / delta). Never a hardcoded org URL.
203
+ */
204
+ protected GetBaseURL(_companyIntegration: MJCompanyIntegrationEntity, auth: RESTAuthContext): string;
205
+ /**
206
+ * Honors the OData `@odata.nextLink` cursor (used verbatim) and otherwise injects the page-size
207
+ * request as a `$top`-free first page — Dataverse caps page size via the `Prefer: odata.maxpagesize`
208
+ * header (added in {@link MakeFirstPageHeaders}), NOT a query param, and does NOT support `$skip`.
209
+ */
210
+ protected BuildPaginatedURL(basePath: string, _obj: MJIntegrationObjectEntity, _page: number, _offset: number, cursor?: string, _effectivePageSize?: number): string;
211
+ /**
212
+ * Dataverse returns the created record's URI in the `OData-EntityId` response header
213
+ * (e.g. `https://org.crm.dynamics.com/api/data/v9.2/accounts(00000000-...)`); the GUID is the
214
+ * trailing `(...)` segment. Falls back to the `Location` header, then the body PK, so a tenant
215
+ * configured to return-representation still resolves. An empty ID makes the create FAIL LOUDLY
216
+ * via the base's BuildCreatedResult (never a silent duplicate-create).
217
+ */
218
+ protected ExtractIDFromResponse(response: RESTResponse, idLocation: string | null): string | undefined;
219
+ /**
220
+ * Substitutes the target ID into a Dataverse single-record path. Supports the standard GUID form
221
+ * `/<entityset>(id)` AND the alternate-key upsert form `/<entityset>(<altkey>='<value>')` — when
222
+ * the ExternalID already contains an `=` it is an alternate-key expression and is inserted as-is
223
+ * (only the value is escaped); otherwise it is treated as a GUID. Handles both `({id})` template
224
+ * placeholders and a bare `(id)` suffix.
225
+ */
226
+ protected SubstituteIDInPath(path: string, externalID: string, idLocation: string | null): string;
227
+ /**
228
+ * Builds the OData key expression for a single-record path. A plain GUID → `00000000-...`. An
229
+ * alternate-key expression (`key='value'` or `k1='v1',k2='v2'`) is passed through with each value
230
+ * single-quote-escaped. This realizes the alternate-key upsert path form the metadata may declare.
231
+ */
232
+ private BuildKeyExpression;
233
+ /** Whether a table is configured for change tracking (Configuration.changeTrackingHeader present). */
234
+ private ObjectUsesChangeTracking;
235
+ /**
236
+ * Fetches changed records via Dataverse change tracking. The watermark holds the previous poll's
237
+ * `@odata.deltaLink` (a full URL carrying `$deltatoken`) — followed VERBATIM. Deleted records
238
+ * arrive as `$deletedEntity` references; they are surfaced with `IsDeleted=true`. The last page's
239
+ * `@odata.deltaLink` becomes the new watermark for the next poll.
240
+ */
241
+ private FetchChangesViaDelta;
242
+ /** Maps one change-tracking row (live record OR `$deletedEntity` tombstone) to an ExternalRecord. */
243
+ private DeltaRowToExternalRecord;
244
+ /** Fetches the full table list via `EntityDefinitions` (paged, following @odata.nextLink). */
245
+ private FetchEntityDefinitions;
246
+ /** Describes one table + its Attributes by logical name. Returns null if not visible to the credential. */
247
+ private FetchEntityDefinition;
248
+ /** Resolves the Dataverse logical name for an IO (Configuration.logicalName, else the IO name). */
249
+ private ResolveLogicalName;
250
+ /** Maps a Dataverse table describe entry → ExternalObjectSchema. */
251
+ private EntityMetadataToObjectSchema;
252
+ /** Maps a Dataverse attribute describe entry → ExternalFieldSchema. */
253
+ private AttributeMetadataToFieldSchema;
254
+ /** Reads a Dataverse localized label, falling back to undefined. */
255
+ private LocalizedLabel;
256
+ /** Parses the connection config, preferring the attached MJ Credential over Configuration JSON. */
257
+ private ParseConfig;
258
+ /** Loads the OAuth2 config from the MJ: Credentials entity Values JSON. */
259
+ private ParseConfigFromCredential;
260
+ /** Validates the parsed config + applies defaults. Field names are read case-insensitively. */
261
+ private ValidateConfig;
262
+ /** Resolves the Web API base URL: `<EnvironmentUrl>/api/data/v9.2`. */
263
+ private ResolveBaseUrl;
264
+ private ExecuteOneRequest;
265
+ private ParseResponseBody;
266
+ private ExtractHeaders;
267
+ private IsRetryable;
268
+ private ComputeBackoffDelay;
269
+ private Sleep;
270
+ /**
271
+ * True for any OData/Dataverse annotation key. Genuine columns never contain `@`; every annotation
272
+ * does — the response-level controls (`@odata.etag`, `@odata.context`, …) and the per-column
273
+ * suffixes (`<col>@odata.bind`, `<col>@OData.Community.Display.V1.FormattedValue`,
274
+ * `<col>@Microsoft.Dynamics.CRM.lookuplogicalname`). So an `@`-containing key is an annotation.
275
+ */
276
+ private IsODataAnnotationKey;
277
+ private buildDataverseURL;
278
+ private previewBody;
279
+ /** Integration ID of the most recent fetch — lets the no-arg StableOrderingKey hook locate the IO. */
280
+ private lastIntegrationID;
281
+ /**
282
+ * Best-effort cached-object lookup by NAME using the last-seen integration ID. Used by
283
+ * {@link StableOrderingKey}, which the engine calls without a CompanyIntegration; returns null
284
+ * (keyset resume simply unavailable) before any fetch has run.
285
+ */
286
+ private tryGetCachedObject;
287
+ private tryGetCachedObjectFor;
288
+ /** Reads a trimmed string from an IO's Configuration JSON, tolerant of absent/invalid. */
289
+ private readObjectConfigString;
290
+ /** Extracts response headers from a thrown error, when the connector throws an error carrying them. */
291
+ private extractHeadersFromError;
292
+ }
293
+ /** Tree-shaking prevention function — import and call from the package entry point. */
294
+ export declare function LoadDynamicsDataverseConnector(): void;
295
+ export {};
@@ -0,0 +1,790 @@
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
+ import { RegisterClass } from '@memberjunction/global';
8
+ import { Metadata } from '@memberjunction/core';
9
+ import { BaseIntegrationConnector, BaseRESTIntegrationConnector, OAuth2TokenManager, } from '@memberjunction/integration-engine';
10
+ // ─── Constants ───────────────────────────────────────────────────────
11
+ /** Default Web API version segment. */
12
+ const DEFAULT_API_VERSION = 'v9.2';
13
+ /** Default Entra ID authority host. */
14
+ const DEFAULT_AUTHORITY_HOST = 'https://login.microsoftonline.com';
15
+ /** Default HTTP request timeout (Dataverse can be slow under load). */
16
+ const DEFAULT_REQUEST_TIMEOUT_MS = 60_000;
17
+ /** Default retry count for retryable (429 / 503 / 504) responses. */
18
+ const DEFAULT_MAX_RETRIES = 5;
19
+ /** OData service hard cap on page size; the service silently ignores requests above this. */
20
+ const MAX_ODATA_PAGE_SIZE = 5_000;
21
+ /**
22
+ * Connector for Microsoft Dynamics 365 / Dataverse (Common Data Service) via the
23
+ * OData v4 Web API.
24
+ *
25
+ * - **Auth**: Microsoft Entra ID OAuth 2.0 client-credentials (app-only). Token is
26
+ * minted/cached by the shared {@link OAuth2TokenManager}; the credential bytes are
27
+ * resolved at runtime from the connection's MJ Credential / Configuration — never baked.
28
+ * - **Base URL**: `<EnvironmentUrl>/api/data/v9.2`, per-connection (every customer
29
+ * environment differs — nothing is hardcoded).
30
+ * - **Discovery**: `DiscoverObjects` / `DiscoverFields` parse the credentialed
31
+ * `EntityDefinitions` describe endpoint at runtime (case-2 auth-gated discovery) so
32
+ * BOTH standard and custom/solution-installed tables surface. The documented standard
33
+ * catalog lives in the Declared metadata file, not in this class.
34
+ * - **Pagination**: OData `@odata.nextLink` cursor, followed verbatim.
35
+ * - **Write**: generic per-operation CRUD from {@link BaseRESTIntegrationConnector}
36
+ * (flat body; create ID read from the `OData-EntityId` response header; update PATCH
37
+ * `/<entityset>(id)`; delete DELETE `/<entityset>(id)`; alternate-key upsert path form).
38
+ * - **Incremental**: change-tracking delta (`Prefer: odata.track-changes` →
39
+ * `@odata.deltaLink`) when a table declares it; `modifiedon`-polling fallback otherwise.
40
+ */
41
+ let DynamicsDataverseConnector = class DynamicsDataverseConnector extends BaseRESTIntegrationConnector {
42
+ constructor() {
43
+ // ── State ────────────────────────────────────────────────────────
44
+ super(...arguments);
45
+ this.tokenManager = new OAuth2TokenManager();
46
+ this.cachedAuth = null;
47
+ /** Integration ID of the most recent fetch — lets the no-arg StableOrderingKey hook locate the IO. */
48
+ this.lastIntegrationID = null;
49
+ }
50
+ // ── Identity + capabilities ──────────────────────────────────────
51
+ /** Verbatim from the upstream identity handoff. Drives the three-way name invariant.
52
+ * Returned as a string literal (not the INTEGRATION_NAME const ref) so the T1
53
+ * ThreeWayName static parser can extract it from source. */
54
+ get IntegrationName() { return 'Microsoft Dynamics 365 (Dataverse)'; }
55
+ get SupportsCreate() { return true; }
56
+ get SupportsUpdate() { return true; }
57
+ get SupportsDelete() { return true; }
58
+ /**
59
+ * §7 — `EntityDefinitions` enumerates the COMPLETE credentialed gamut (standard + custom +
60
+ * solution-installed tables / attributes), not a filtered subset, so a comprehensive refresh
61
+ * may safely + reversibly deactivate objects no longer in the response.
62
+ */
63
+ get DiscoveryIsAuthoritative() { return true; }
64
+ /**
65
+ * §7 — the watermark this connector advances (delta-link / modifiedon high-water) IS monotonic:
66
+ * change-tracking yields a forward-only delta token, and the modifiedon fallback fetches in
67
+ * ascending `modifiedon` order so the last batch carries the true maximum. Lets the engine narrow
68
+ * the next incremental instead of advancing to wall-clock now.
69
+ */
70
+ get MonotonicWatermark() { return true; }
71
+ /**
72
+ * §7 — keyset/seek resume key for watermark-less objects: the table's GUID primary key
73
+ * (`<logicalName>id`), read from the IO's Configuration. Stable + monotonic-enough for
74
+ * resume-from-last-seen; `null` when no PK is known.
75
+ */
76
+ StableOrderingKey(objectName) {
77
+ const obj = this.tryGetCachedObject(objectName);
78
+ if (!obj)
79
+ return null;
80
+ return this.readObjectConfigString(obj, 'primaryIdAttribute')
81
+ ?? this.readObjectConfigString(obj, 'stableOrderingKey');
82
+ }
83
+ /**
84
+ * §7 — Dataverse enforces a per-environment Web API service-protection limit (≈ 6000 requests /
85
+ * 300s sliding window, plus an execution-time budget). Conservative sustained rate keeps the
86
+ * connector under the request-count limit; the engine's AIMD bucket adapts on 429s via
87
+ * {@link ExtractRetryAfterMs}. (≈6000/300 ≈ 20 req/s; we stay below to leave headroom.)
88
+ */
89
+ get RateLimitPolicy() {
90
+ return { TokensPerSec: 15, Burst: 30, ThrottleBackoffFactor: 0.5 };
91
+ }
92
+ get MaxConcurrencyHint() { return 4; }
93
+ /**
94
+ * Parse Dataverse's throttle signal. The service returns HTTP 429 with a `Retry-After` header
95
+ * (seconds) on a service-protection limit; honor it precisely.
96
+ */
97
+ ExtractRetryAfterMs(error) {
98
+ const headers = this.extractHeadersFromError(error);
99
+ if (!headers)
100
+ return undefined;
101
+ const retryAfter = headers['retry-after'];
102
+ if (retryAfter) {
103
+ const asSeconds = Number(retryAfter);
104
+ if (!Number.isNaN(asSeconds))
105
+ return Math.max(0, asSeconds * 1_000);
106
+ const asDate = new Date(retryAfter).getTime();
107
+ if (!Number.isNaN(asDate))
108
+ return Math.max(0, asDate - Date.now());
109
+ }
110
+ return undefined;
111
+ }
112
+ // ── TestConnection ───────────────────────────────────────────────
113
+ /**
114
+ * Validates the service principal can reach the environment by issuing a fresh token and a
115
+ * lightweight `WhoAmI` call (an unbound function returning the calling user/org GUIDs).
116
+ */
117
+ async TestConnection(companyIntegration, contextUser) {
118
+ try {
119
+ const auth = await this.Authenticate(companyIntegration, contextUser, true);
120
+ const url = `${auth.ApiBaseUrl}/WhoAmI`;
121
+ const response = await this.MakeHTTPRequest(auth, url, 'GET', this.BuildHeaders(auth));
122
+ if (response.Status < 200 || response.Status >= 300) {
123
+ return {
124
+ Success: false,
125
+ Message: `Dataverse WhoAmI failed: HTTP ${response.Status} — ${this.previewBody(response.Body)}`,
126
+ };
127
+ }
128
+ const body = response.Body;
129
+ return {
130
+ Success: true,
131
+ Message: `Connected to Dataverse (org ${body.OrganizationId ?? 'unknown'})`,
132
+ ServerVersion: `Web API ${auth.Config.ApiVersion ?? DEFAULT_API_VERSION}`,
133
+ };
134
+ }
135
+ catch (err) {
136
+ const message = err instanceof Error ? err.message : String(err);
137
+ return { Success: false, Message: `Connection failed: ${message}` };
138
+ }
139
+ }
140
+ // ── DiscoverObjects / DiscoverFields (runtime $metadata discovery) ──
141
+ /**
142
+ * Case-2 (auth-gated) runtime discovery: enumerates EVERY table the credential can see via the
143
+ * `EntityDefinitions` describe endpoint (standard + custom + solution-installed) — NOT a baked
144
+ * catalog. The documented standard catalog seeded in the Declared metadata file is the floor;
145
+ * this returns what the live environment actually exposes (the ceiling), which is strictly a
146
+ * superset for custom-bearing tenants.
147
+ */
148
+ async DiscoverObjects(companyIntegration, contextUser) {
149
+ try {
150
+ const entities = await this.FetchEntityDefinitions(companyIntegration, contextUser);
151
+ return entities
152
+ .filter(e => typeof e.LogicalName === 'string' && typeof e.EntitySetName === 'string' && e.EntitySetName.length > 0)
153
+ .map(e => this.EntityMetadataToObjectSchema(e));
154
+ }
155
+ catch (err) {
156
+ // Case-2 (auth-gated) discovery: the live EntityDefinitions endpoint requires credentials.
157
+ // When the config/credential is absent, re-throw with an EXPLICIT credential-absence signal
158
+ // so a credential-free run is classified as "discovery requires credentials" (cross-pass
159
+ // consistency is proven at the live tier) rather than a hard discovery failure. A genuine
160
+ // live API error (credentials present) keeps its original message and propagates.
161
+ const msg = err instanceof Error ? err.message : String(err);
162
+ if (/CredentialID or Configuration JSON|missing required field|could not be loaded|not a valid object/i.test(msg)) {
163
+ throw new Error(`DynamicsDataverseConnector requires credentials to discover (auth-gated EntityDefinitions endpoint): ${msg}`);
164
+ }
165
+ throw err;
166
+ }
167
+ }
168
+ /**
169
+ * Case-2 runtime discovery for a single object's columns: describes the table's `Attributes`
170
+ * collection and maps each Property → {@link ExternalFieldSchema}, surfacing Type/MaxLength,
171
+ * the GUID PK (`IsPrimaryId` / `PrimaryIdAttribute` → `IsPrimaryKey`), and scalar Lookup
172
+ * columns → FK (with `ForeignKeyTarget` = the referenced table's logical name).
173
+ */
174
+ async DiscoverFields(companyIntegration, objectName, contextUser) {
175
+ const entity = await this.FetchEntityDefinition(companyIntegration, objectName, contextUser);
176
+ if (!entity) {
177
+ // Fall back to the Declared metadata cache if the live describe returned nothing
178
+ // (object not visible to these credentials) — degrade gracefully rather than drop it.
179
+ return super.DiscoverFields(companyIntegration, objectName, contextUser);
180
+ }
181
+ const primaryId = entity.PrimaryIdAttribute ?? `${entity.LogicalName}id`;
182
+ const attributes = entity.Attributes ?? [];
183
+ return attributes
184
+ .filter(a => typeof a.LogicalName === 'string' && a.LogicalName.length > 0)
185
+ .map(a => this.AttributeMetadataToFieldSchema(a, primaryId));
186
+ }
187
+ // ── FetchChanges (change-tracking delta override) ────────────────
188
+ /**
189
+ * Routes to the change-tracking delta path when the table declares it AND a watermark exists;
190
+ * otherwise delegates to the base metadata-driven fetch (which applies the `modifiedon` watermark
191
+ * fallback + standard `@odata.nextLink` pagination). The base path already handles
192
+ * `TransformRecord` (annotation stripping) + full-record pass-through, so the delta path mirrors it.
193
+ */
194
+ async FetchChanges(ctx) {
195
+ this.lastIntegrationID = ctx.CompanyIntegration.IntegrationID;
196
+ const obj = this.GetCachedObject(ctx.CompanyIntegration.IntegrationID, ctx.ObjectName);
197
+ if (obj.SupportsIncrementalSync && this.ObjectUsesChangeTracking(obj) && ctx.WatermarkValue) {
198
+ return this.FetchChangesViaDelta(ctx, obj);
199
+ }
200
+ return super.FetchChanges(ctx);
201
+ }
202
+ // ── TransformRecord — strip OData annotations (auditable removal) ──
203
+ /**
204
+ * Removes the OData control annotations (`@odata.etag`, `@odata.context`, `@odata.id`,
205
+ * `<lookup>@odata.bind`, `<lookup>@OData.Community.Display.V1.FormattedValue`, etc.) Dataverse
206
+ * sprinkles onto every record — they are response transport metadata, not columns. Every other
207
+ * source key is preserved (full-record pass-through); the removed keys are declared in
208
+ * {@link ExcludedSourceKeys} so the base re-add doesn't restore them and change-detection ignores them.
209
+ */
210
+ TransformRecord(raw, _obj, _fields) {
211
+ const out = {};
212
+ for (const [key, value] of Object.entries(raw)) {
213
+ if (this.IsODataAnnotationKey(key))
214
+ continue;
215
+ out[key] = value;
216
+ }
217
+ return out;
218
+ }
219
+ /**
220
+ * The well-known static OData control annotation keys {@link TransformRecord} drops. The base
221
+ * `applyTransformPreservingKeys` uses this to avoid re-adding them. Dataverse ALSO emits dynamic,
222
+ * per-lookup annotation suffixes (`<col>@odata.bind`, `<col>@OData.Community.Display.V1.FormattedValue`,
223
+ * `<col>@Microsoft.Dynamics.CRM.lookuplogicalname`) whose exact names vary by record and cannot be
224
+ * enumerated statically — those are handled by the {@link applyTransformPreservingKeys} override
225
+ * below (predicate exclusion), so they never re-appear. This static list covers the always-present
226
+ * envelope keys for completeness / auditability.
227
+ */
228
+ ExcludedSourceKeys(_objectName) {
229
+ return ['@odata.etag', '@odata.context', '@odata.id', '@odata.editLink', '@odata.type'];
230
+ }
231
+ /**
232
+ * Overrides the base re-add so the DYNAMIC OData annotation keys (`<col>@odata.bind`, FormattedValue,
233
+ * lookuplogicalname, etc.) {@link TransformRecord} strips are NOT restored. The base loop re-adds any
234
+ * `raw` key absent from the transform output unless it is in the static {@link ExcludedSourceKeys}
235
+ * set — which can't enumerate the per-record annotation suffixes. Here we re-add a dropped key only
236
+ * when it is neither statically excluded NOR an annotation key (by predicate). This keeps full-record
237
+ * pass-through for genuine columns while making the annotation removal stick (auditable, by rule).
238
+ */
239
+ applyTransformPreservingKeys(raw, obj, fields) {
240
+ const transformed = this.TransformRecord(raw, obj, fields);
241
+ if (transformed === raw)
242
+ return raw;
243
+ const excluded = new Set(this.ExcludedSourceKeys(obj.Name));
244
+ const out = { ...transformed };
245
+ for (const key of Object.keys(raw)) {
246
+ if (key in out || excluded.has(key) || this.IsODataAnnotationKey(key))
247
+ continue;
248
+ out[key] = raw[key];
249
+ }
250
+ return out;
251
+ }
252
+ // ─── BaseRESTIntegrationConnector abstract hooks ────────────────
253
+ /**
254
+ * Mints/caches a Microsoft Entra ID access token via client-credentials. The scope is the
255
+ * environment's `.default` (`<EnvironmentUrl>/.default`) so the token's audience matches the
256
+ * Dataverse resource. Token is cached by {@link OAuth2TokenManager} until near expiry.
257
+ */
258
+ async Authenticate(companyIntegration, contextUser, forceRefresh = false) {
259
+ if (forceRefresh) {
260
+ this.cachedAuth = null;
261
+ this.tokenManager.Reset();
262
+ }
263
+ else if (this.cachedAuth) {
264
+ // Re-mint through the manager (cheap when cached) so an expired token refreshes.
265
+ const refreshed = await this.MintToken(this.cachedAuth.Config);
266
+ return { ...this.cachedAuth, Token: refreshed };
267
+ }
268
+ const config = await this.ParseConfig(companyIntegration, contextUser);
269
+ const token = await this.MintToken(config);
270
+ const auth = {
271
+ Token: token,
272
+ Config: config,
273
+ BaseUrl: config.EnvironmentUrl.replace(/\/+$/, ''), // org root — IO APIPaths are absolute-from-root
274
+ ApiBaseUrl: this.ResolveBaseUrl(config), // versioned base for the connector's own calls
275
+ };
276
+ this.cachedAuth = auth;
277
+ return auth;
278
+ }
279
+ /** Runs the client_credentials token round-trip through {@link OAuth2TokenManager}. */
280
+ async MintToken(config) {
281
+ const authorityHost = (config.AuthorityHost ?? DEFAULT_AUTHORITY_HOST).replace(/\/+$/, '');
282
+ const tokenURL = `${authorityHost}/${encodeURIComponent(config.TenantId)}/oauth2/v2.0/token`;
283
+ const scope = config.Scope ?? `${config.EnvironmentUrl.replace(/\/+$/, '')}/.default`;
284
+ const req = {
285
+ TokenURL: tokenURL,
286
+ ClientId: config.ClientId,
287
+ ClientSecret: config.ClientSecret,
288
+ // Entra ID v2.0 accepts client_id/client_secret in the form body for confidential clients.
289
+ Scopes: scope,
290
+ ScopeParam: 'scope',
291
+ TimeoutMs: config.RequestTimeoutMs ?? DEFAULT_REQUEST_TIMEOUT_MS,
292
+ };
293
+ const token = await this.tokenManager.GetAccessToken(req, 'client_credentials');
294
+ return token.AccessToken;
295
+ }
296
+ /** Standard OData/Dataverse headers — bearer token + JSON + OData version + return-representation. */
297
+ BuildHeaders(auth) {
298
+ const token = auth.Token ?? '';
299
+ return {
300
+ 'Authorization': `Bearer ${token}`,
301
+ 'Accept': 'application/json',
302
+ 'Content-Type': 'application/json; charset=utf-8',
303
+ 'OData-MaxVersion': '4.0',
304
+ 'OData-Version': '4.0',
305
+ // Ask Dataverse to return the created/updated record so create can read OData-EntityId / body.
306
+ 'Prefer': 'return=representation',
307
+ };
308
+ }
309
+ /**
310
+ * HTTP transport with retry/backoff on 429/503/504 (Dataverse service-protection limits). The
311
+ * thrown error on a retryable status carries the response headers so {@link ExtractRetryAfterMs}
312
+ * can read `Retry-After` for the engine's adaptive bucket.
313
+ */
314
+ async MakeHTTPRequest(auth, url, method, headers, body) {
315
+ const config = auth.Config;
316
+ const maxRetries = config.MaxRetries ?? DEFAULT_MAX_RETRIES;
317
+ const timeoutMs = config.RequestTimeoutMs ?? DEFAULT_REQUEST_TIMEOUT_MS;
318
+ for (let attempt = 0; attempt <= maxRetries; attempt++) {
319
+ const response = await this.ExecuteOneRequest(url, method, headers, body, timeoutMs);
320
+ if (this.IsRetryable(response) && attempt < maxRetries) {
321
+ const delay = this.ComputeBackoffDelay(attempt, response.Headers['retry-after']);
322
+ await this.Sleep(delay);
323
+ continue;
324
+ }
325
+ return response;
326
+ }
327
+ throw new Error(`DynamicsDataverseConnector: exhausted ${maxRetries + 1} attempts for ${method} ${url}`);
328
+ }
329
+ /**
330
+ * Unwraps the OData collection envelope's `value` array. A single-record GET (`/<set>(id)`) is
331
+ * returned as a one-element array. Honors a metadata-declared `ResponseDataKey` if set, defaulting
332
+ * to the OData `value` convention.
333
+ */
334
+ NormalizeResponse(rawBody, responseDataKey) {
335
+ if (rawBody == null || typeof rawBody !== 'object')
336
+ return [];
337
+ const body = rawBody;
338
+ const key = responseDataKey ?? 'value';
339
+ const data = body[key];
340
+ if (Array.isArray(data))
341
+ return data;
342
+ // Single-record detail response (has at least one non-annotation key) → wrap.
343
+ const hasRecordShape = Object.keys(body).some(k => !this.IsODataAnnotationKey(k));
344
+ return hasRecordShape ? [body] : [];
345
+ }
346
+ /**
347
+ * Cursor pagination via `@odata.nextLink`. The nextLink is an ABSOLUTE continuation URL that
348
+ * encodes the exact next request (including the opaque `$skiptoken`); it is followed VERBATIM and
349
+ * never hand-built. A present nextLink means another page exists.
350
+ */
351
+ ExtractPaginationInfo(rawBody, _paginationType, _currentPage, _currentOffset, _pageSize) {
352
+ const body = rawBody;
353
+ const nextLink = body?.['@odata.nextLink'];
354
+ return {
355
+ HasMore: typeof nextLink === 'string' && nextLink.length > 0,
356
+ NextCursor: typeof nextLink === 'string' ? nextLink : undefined,
357
+ };
358
+ }
359
+ /**
360
+ * Per-connection base the engine joins the IO `APIPath` onto. The frozen Dataverse IO APIPaths are
361
+ * ABSOLUTE-from-root (they already include `/api/data/v9.2/...`), so this returns the org ROOT
362
+ * (`<EnvironmentUrl>`) and the resolved request URL is `<EnvironmentUrl>/api/data/v9.2/<entityset>`.
363
+ * The versioned Web API base (`<EnvironmentUrl>/api/data/v9.2`) is `ApiBaseUrl`, used by the
364
+ * connector's OWN calls (WhoAmI / EntityDefinitions / delta). Never a hardcoded org URL.
365
+ */
366
+ GetBaseURL(_companyIntegration, auth) {
367
+ return auth.BaseUrl;
368
+ }
369
+ /**
370
+ * Honors the OData `@odata.nextLink` cursor (used verbatim) and otherwise injects the page-size
371
+ * request as a `$top`-free first page — Dataverse caps page size via the `Prefer: odata.maxpagesize`
372
+ * header (added in {@link MakeFirstPageHeaders}), NOT a query param, and does NOT support `$skip`.
373
+ */
374
+ BuildPaginatedURL(basePath, _obj, _page, _offset, cursor, _effectivePageSize) {
375
+ // A cursor is the full nextLink continuation URL — issue it exactly as Dataverse returned it.
376
+ if (cursor && cursor.length > 0)
377
+ return cursor;
378
+ return basePath;
379
+ }
380
+ // ─── Create-response ID extraction (OData-EntityId header) ───────
381
+ /**
382
+ * Dataverse returns the created record's URI in the `OData-EntityId` response header
383
+ * (e.g. `https://org.crm.dynamics.com/api/data/v9.2/accounts(00000000-...)`); the GUID is the
384
+ * trailing `(...)` segment. Falls back to the `Location` header, then the body PK, so a tenant
385
+ * configured to return-representation still resolves. An empty ID makes the create FAIL LOUDLY
386
+ * via the base's BuildCreatedResult (never a silent duplicate-create).
387
+ */
388
+ ExtractIDFromResponse(response, idLocation) {
389
+ if (!idLocation || idLocation === 'header') {
390
+ const headers = response.Headers ?? {};
391
+ const entityId = headers['odata-entityid'] ?? headers['OData-EntityId'] ?? headers['location'] ?? headers['Location'];
392
+ if (typeof entityId === 'string' && entityId.length > 0) {
393
+ const m = entityId.match(/\(([^)]+)\)\s*$/); // GUID inside the trailing (...)
394
+ if (m)
395
+ return m[1];
396
+ const seg = entityId.match(/[/=]([^/?&#]+)$/);
397
+ return seg ? seg[1] : entityId;
398
+ }
399
+ }
400
+ // Body fallback — return=representation echoes the record; the PK is `<set-singular>id` but the
401
+ // generic id-field scan also covers `id`.
402
+ if (response.Body && typeof response.Body === 'object') {
403
+ const b = response.Body;
404
+ for (const k of Object.keys(b)) {
405
+ if (/id$/i.test(k) && (typeof b[k] === 'string' || typeof b[k] === 'number')) {
406
+ const v = String(b[k]);
407
+ if (v.length > 0)
408
+ return v;
409
+ }
410
+ }
411
+ }
412
+ return super.ExtractIDFromResponse(response, idLocation);
413
+ }
414
+ /**
415
+ * Substitutes the target ID into a Dataverse single-record path. Supports the standard GUID form
416
+ * `/<entityset>(id)` AND the alternate-key upsert form `/<entityset>(<altkey>='<value>')` — when
417
+ * the ExternalID already contains an `=` it is an alternate-key expression and is inserted as-is
418
+ * (only the value is escaped); otherwise it is treated as a GUID. Handles both `({id})` template
419
+ * placeholders and a bare `(id)` suffix.
420
+ */
421
+ SubstituteIDInPath(path, externalID, idLocation) {
422
+ if (idLocation && idLocation !== 'path')
423
+ return path;
424
+ const keyExpr = this.BuildKeyExpression(externalID);
425
+ // Replace an explicit {id}/{ID}/{ExternalID} placeholder if present...
426
+ if (/\{(?:id|ID|ExternalID)\}/.test(path)) {
427
+ return path
428
+ .replace(/\{ID\}/g, keyExpr)
429
+ .replace(/\{id\}/g, keyExpr)
430
+ .replace(/\{ExternalID\}/g, keyExpr);
431
+ }
432
+ // ...otherwise inject into the `(...)` segment the metadata path declares (`/accounts({id})`
433
+ // came pre-substituted above; a literal `/accounts()` or `/accounts` gets the key appended).
434
+ if (/\(\s*\)\s*$/.test(path)) {
435
+ return path.replace(/\(\s*\)\s*$/, `(${keyExpr})`);
436
+ }
437
+ return `${path.replace(/\/+$/, '')}(${keyExpr})`;
438
+ }
439
+ /**
440
+ * Builds the OData key expression for a single-record path. A plain GUID → `00000000-...`. An
441
+ * alternate-key expression (`key='value'` or `k1='v1',k2='v2'`) is passed through with each value
442
+ * single-quote-escaped. This realizes the alternate-key upsert path form the metadata may declare.
443
+ */
444
+ BuildKeyExpression(externalID) {
445
+ if (externalID.includes('=')) {
446
+ // Alternate-key form: keep `key='...'` pairs, escape embedded single quotes in values.
447
+ return externalID.replace(/'([^']*)'/g, (_full, v) => `'${v.replace(/'/g, "''")}'`);
448
+ }
449
+ return externalID; // GUID — Dataverse accepts the bare GUID inside the parentheses.
450
+ }
451
+ // ─── Change-tracking delta sync ──────────────────────────────────
452
+ /** Whether a table is configured for change tracking (Configuration.changeTrackingHeader present). */
453
+ ObjectUsesChangeTracking(obj) {
454
+ const header = this.readObjectConfigString(obj, 'changeTrackingHeader');
455
+ return header != null && /track-changes/i.test(header);
456
+ }
457
+ /**
458
+ * Fetches changed records via Dataverse change tracking. The watermark holds the previous poll's
459
+ * `@odata.deltaLink` (a full URL carrying `$deltatoken`) — followed VERBATIM. Deleted records
460
+ * arrive as `$deletedEntity` references; they are surfaced with `IsDeleted=true`. The last page's
461
+ * `@odata.deltaLink` becomes the new watermark for the next poll.
462
+ */
463
+ async FetchChangesViaDelta(ctx, obj) {
464
+ const auth = await this.Authenticate(ctx.CompanyIntegration, ctx.ContextUser);
465
+ const fields = this.GetCachedFields(obj.ID);
466
+ const pkFieldNames = fields.filter(f => f.IsPrimaryKey).map(f => f.Name);
467
+ // The watermark is the prior deltaLink (a full URL); if it's not a URL, build the initial
468
+ // delta request off the object's read path with the track-changes Prefer header.
469
+ const deltaUrl = ctx.WatermarkValue && /^https?:\/\//.test(ctx.WatermarkValue)
470
+ ? ctx.WatermarkValue
471
+ : this.buildDataverseURL(auth.BaseUrl, obj.APIPath);
472
+ const headers = { ...this.BuildHeaders(auth), Prefer: 'odata.track-changes,return=representation' };
473
+ const response = await this.MakeHTTPRequest(auth, deltaUrl, 'GET', headers);
474
+ if (response.Status < 200 || response.Status >= 300) {
475
+ throw new Error(`Dataverse delta fetch failed for "${obj.Name}": HTTP ${response.Status} — ${this.previewBody(response.Body)}`);
476
+ }
477
+ const body = response.Body;
478
+ const rows = body.value ?? [];
479
+ const nextLink = body['@odata.nextLink'];
480
+ const deltaLink = body['@odata.deltaLink'];
481
+ const records = rows.map(r => this.DeltaRowToExternalRecord(r, obj, fields, pkFieldNames));
482
+ return {
483
+ Records: records,
484
+ HasMore: typeof nextLink === 'string' && nextLink.length > 0,
485
+ NextCursor: typeof nextLink === 'string' ? nextLink : undefined,
486
+ // Only persist the new high-water deltaLink once the full delta set has drained (last page).
487
+ NewWatermarkValue: typeof deltaLink === 'string' ? deltaLink : undefined,
488
+ };
489
+ }
490
+ /** Maps one change-tracking row (live record OR `$deletedEntity` tombstone) to an ExternalRecord. */
491
+ DeltaRowToExternalRecord(raw, obj, fields, pkFieldNames) {
492
+ const isDeleted = typeof raw['@odata.context'] === 'string' && /\$deletedEntity/i.test(raw['@odata.context']);
493
+ const transformed = this.applyTransformPreservingKeys(raw, obj, fields);
494
+ const idFieldName = pkFieldNames[0];
495
+ const externalID = idFieldName && transformed[idFieldName] != null
496
+ ? String(transformed[idFieldName])
497
+ : (typeof raw['id'] === 'string' ? raw['id'] : '');
498
+ return {
499
+ ExternalID: externalID,
500
+ ObjectType: obj.Name,
501
+ Fields: transformed, // full source record (minus annotations) — custom-column pass-through
502
+ IsDeleted: isDeleted,
503
+ };
504
+ }
505
+ // ─── EntityDefinitions describe helpers ──────────────────────────
506
+ /** Fetches the full table list via `EntityDefinitions` (paged, following @odata.nextLink). */
507
+ async FetchEntityDefinitions(companyIntegration, contextUser) {
508
+ const auth = await this.Authenticate(companyIntegration, contextUser);
509
+ const select = '$select=LogicalName,EntitySetName,DisplayName,Description,PrimaryIdAttribute,PrimaryNameAttribute,ChangeTrackingEnabled,IsCustomEntity';
510
+ let url = `${auth.ApiBaseUrl}/EntityDefinitions?${select}`;
511
+ const out = [];
512
+ while (url) {
513
+ const response = await this.MakeHTTPRequest(auth, url, 'GET', this.BuildHeaders(auth));
514
+ if (response.Status < 200 || response.Status >= 300) {
515
+ throw new Error(`EntityDefinitions describe failed: HTTP ${response.Status} — ${this.previewBody(response.Body)}`);
516
+ }
517
+ const body = response.Body;
518
+ for (const e of body.value ?? [])
519
+ out.push(e);
520
+ url = typeof body['@odata.nextLink'] === 'string' ? body['@odata.nextLink'] : undefined;
521
+ }
522
+ return out;
523
+ }
524
+ /** Describes one table + its Attributes by logical name. Returns null if not visible to the credential. */
525
+ async FetchEntityDefinition(companyIntegration, objectName, contextUser) {
526
+ const auth = await this.Authenticate(companyIntegration, contextUser);
527
+ const logicalName = this.ResolveLogicalName(companyIntegration, objectName);
528
+ const expand = '$expand=Attributes($select=LogicalName,SchemaName,DisplayName,Description,AttributeType,MaxLength,Precision,IsValidForCreate,IsValidForUpdate,IsPrimaryId,RequiredLevel,Targets)';
529
+ const select = '$select=LogicalName,EntitySetName,DisplayName,Description,PrimaryIdAttribute,PrimaryNameAttribute,ChangeTrackingEnabled,IsCustomEntity';
530
+ const url = `${auth.ApiBaseUrl}/EntityDefinitions(LogicalName='${encodeURIComponent(logicalName)}')?${select}&${expand}`;
531
+ const response = await this.MakeHTTPRequest(auth, url, 'GET', this.BuildHeaders(auth));
532
+ if (response.Status === 404)
533
+ return null;
534
+ if (response.Status < 200 || response.Status >= 300) {
535
+ throw new Error(`EntityDefinition describe failed for "${objectName}": HTTP ${response.Status} — ${this.previewBody(response.Body)}`);
536
+ }
537
+ return response.Body;
538
+ }
539
+ /** Resolves the Dataverse logical name for an IO (Configuration.logicalName, else the IO name). */
540
+ ResolveLogicalName(companyIntegration, objectName) {
541
+ const obj = this.tryGetCachedObjectFor(companyIntegration.IntegrationID, objectName);
542
+ if (obj) {
543
+ const logical = this.readObjectConfigString(obj, 'logicalName');
544
+ if (logical)
545
+ return logical;
546
+ }
547
+ return objectName;
548
+ }
549
+ /** Maps a Dataverse table describe entry → ExternalObjectSchema. */
550
+ EntityMetadataToObjectSchema(e) {
551
+ const name = e.LogicalName;
552
+ return {
553
+ Name: name,
554
+ Label: this.LocalizedLabel(e.DisplayName) ?? name,
555
+ Description: this.LocalizedLabel(e.Description) ?? undefined,
556
+ SupportsIncrementalSync: e.ChangeTrackingEnabled === true || true, // modifiedon fallback always available
557
+ SupportsWrite: true, // Dataverse tables are generally CRUD-capable; per-table messages refine downstream
558
+ };
559
+ }
560
+ /** Maps a Dataverse attribute describe entry → ExternalFieldSchema. */
561
+ AttributeMetadataToFieldSchema(a, primaryId) {
562
+ const name = a.LogicalName;
563
+ const isPk = a.IsPrimaryId === true || name === primaryId;
564
+ const requiredLevel = a.RequiredLevel?.Value;
565
+ const isRequired = requiredLevel === 'SystemRequired' || requiredLevel === 'ApplicationRequired';
566
+ // A scalar Lookup attribute that names a single target table is a FK. A polymorphic lookup
567
+ // (multiple Targets) or non-Lookup attribute is NOT emitted as an FK (provable-only).
568
+ const isLookup = a.AttributeType === 'Lookup' || a.AttributeType === 'Customer' || a.AttributeType === 'Owner';
569
+ const singleTarget = isLookup && Array.isArray(a.Targets) && a.Targets.length === 1 ? a.Targets[0] : null;
570
+ return {
571
+ Name: name,
572
+ Label: this.LocalizedLabel(a.DisplayName) ?? name,
573
+ Description: this.LocalizedLabel(a.Description) ?? undefined,
574
+ DataType: a.AttributeType ?? 'String',
575
+ IsRequired: isRequired,
576
+ IsUniqueKey: isPk,
577
+ IsPrimaryKey: isPk,
578
+ IsReadOnly: a.IsValidForCreate === false && a.IsValidForUpdate === false,
579
+ IsForeignKey: singleTarget != null,
580
+ ForeignKeyTarget: singleTarget,
581
+ MaxLength: typeof a.MaxLength === 'number' ? a.MaxLength : null,
582
+ Precision: typeof a.Precision === 'number' ? a.Precision : null,
583
+ };
584
+ }
585
+ /** Reads a Dataverse localized label, falling back to undefined. */
586
+ LocalizedLabel(label) {
587
+ const text = label?.UserLocalizedLabel?.Label;
588
+ return typeof text === 'string' && text.length > 0 ? text : undefined;
589
+ }
590
+ // ─── Config parsing ──────────────────────────────────────────────
591
+ /** Parses the connection config, preferring the attached MJ Credential over Configuration JSON. */
592
+ async ParseConfig(companyIntegration, contextUser) {
593
+ if (companyIntegration.CredentialID) {
594
+ return this.ParseConfigFromCredential(companyIntegration.CredentialID, contextUser);
595
+ }
596
+ if (companyIntegration.Configuration) {
597
+ return this.ValidateConfig(JSON.parse(companyIntegration.Configuration));
598
+ }
599
+ throw new Error('DynamicsDataverseConnector requires either CredentialID or Configuration JSON on the CompanyIntegration');
600
+ }
601
+ /** Loads the OAuth2 config from the MJ: Credentials entity Values JSON. */
602
+ async ParseConfigFromCredential(credentialID, contextUser, provider) {
603
+ const md = provider ?? new Metadata();
604
+ const cred = await md.GetEntityObject('MJ: Credentials', contextUser);
605
+ const loaded = await cred.Load(credentialID);
606
+ if (!loaded || !cred.Values) {
607
+ throw new Error('Dynamics credential could not be loaded or has no Values JSON');
608
+ }
609
+ return this.ValidateConfig(JSON.parse(cred.Values));
610
+ }
611
+ /** Validates the parsed config + applies defaults. Field names are read case-insensitively. */
612
+ ValidateConfig(raw) {
613
+ if (!raw || typeof raw !== 'object') {
614
+ throw new Error('Dynamics configuration is not a valid object');
615
+ }
616
+ const obj = raw;
617
+ const getStr = (...keys) => {
618
+ for (const key of keys) {
619
+ const lower = key.toLowerCase();
620
+ for (const [k, v] of Object.entries(obj)) {
621
+ if (k.toLowerCase() === lower && typeof v === 'string' && v.length > 0)
622
+ return v;
623
+ }
624
+ }
625
+ return undefined;
626
+ };
627
+ const getNum = (...keys) => {
628
+ for (const key of keys) {
629
+ const lower = key.toLowerCase();
630
+ for (const [k, v] of Object.entries(obj)) {
631
+ if (k.toLowerCase() === lower && typeof v === 'number')
632
+ return v;
633
+ }
634
+ }
635
+ return undefined;
636
+ };
637
+ const tenantId = getStr('tenantid', 'tenant_id', 'tenant');
638
+ const clientId = getStr('clientid', 'client_id', 'applicationid', 'appid');
639
+ const clientSecret = getStr('clientsecret', 'client_secret', 'secret');
640
+ const environmentUrl = getStr('environmenturl', 'environment_url', 'resource', 'orgurl', 'org_url', 'baseurl', 'base_url');
641
+ if (!tenantId)
642
+ throw new Error('Dynamics configuration missing required field: TenantId');
643
+ if (!clientId)
644
+ throw new Error('Dynamics configuration missing required field: ClientId');
645
+ if (!clientSecret)
646
+ throw new Error('Dynamics configuration missing required field: ClientSecret');
647
+ if (!environmentUrl)
648
+ throw new Error('Dynamics configuration missing required field: EnvironmentUrl');
649
+ return {
650
+ TenantId: tenantId,
651
+ ClientId: clientId,
652
+ ClientSecret: clientSecret,
653
+ EnvironmentUrl: environmentUrl.replace(/\/+$/, ''),
654
+ ApiVersion: getStr('apiversion', 'api_version') ?? DEFAULT_API_VERSION,
655
+ AuthorityHost: getStr('authorityhost', 'authority_host', 'authority'),
656
+ Scope: getStr('scope', 'scopes'),
657
+ MaxRetries: getNum('maxretries') ?? DEFAULT_MAX_RETRIES,
658
+ RequestTimeoutMs: getNum('requesttimeoutms') ?? DEFAULT_REQUEST_TIMEOUT_MS,
659
+ MinRequestIntervalMs: getNum('minrequestintervalms') ?? 0,
660
+ MaxPageSize: Math.min(getNum('maxpagesize') ?? MAX_ODATA_PAGE_SIZE, MAX_ODATA_PAGE_SIZE),
661
+ };
662
+ }
663
+ /** Resolves the Web API base URL: `<EnvironmentUrl>/api/data/v9.2`. */
664
+ ResolveBaseUrl(config) {
665
+ const version = config.ApiVersion ?? DEFAULT_API_VERSION;
666
+ return `${config.EnvironmentUrl.replace(/\/+$/, '')}/api/data/${version}`;
667
+ }
668
+ // ─── HTTP helpers ────────────────────────────────────────────────
669
+ async ExecuteOneRequest(url, method, headers, body, timeoutMs) {
670
+ const controller = new AbortController();
671
+ const timeoutHandle = setTimeout(() => controller.abort(), timeoutMs);
672
+ try {
673
+ const response = await fetch(url, {
674
+ method,
675
+ headers,
676
+ body: body !== undefined && method !== 'GET' && method !== 'DELETE'
677
+ ? JSON.stringify(body)
678
+ : undefined,
679
+ signal: controller.signal,
680
+ });
681
+ const responseHeaders = this.ExtractHeaders(response.headers);
682
+ const parsedBody = await this.ParseResponseBody(response);
683
+ return { Status: response.status, Body: parsedBody, Headers: responseHeaders };
684
+ }
685
+ finally {
686
+ clearTimeout(timeoutHandle);
687
+ }
688
+ }
689
+ async ParseResponseBody(response) {
690
+ const text = await response.text();
691
+ if (!text)
692
+ return null;
693
+ try {
694
+ return JSON.parse(text);
695
+ }
696
+ catch {
697
+ return text;
698
+ }
699
+ }
700
+ ExtractHeaders(headers) {
701
+ const map = {};
702
+ headers.forEach((value, key) => {
703
+ map[key.toLowerCase()] = value;
704
+ });
705
+ return map;
706
+ }
707
+ IsRetryable(response) {
708
+ return response.Status === 429 || response.Status === 503 || response.Status === 504;
709
+ }
710
+ ComputeBackoffDelay(attempt, retryAfterHeader) {
711
+ if (retryAfterHeader) {
712
+ const parsed = parseInt(retryAfterHeader, 10);
713
+ if (!Number.isNaN(parsed) && parsed > 0) {
714
+ return Math.min(parsed * 1000, 120_000);
715
+ }
716
+ }
717
+ return Math.min(Math.pow(2, attempt) * 1000, 30_000);
718
+ }
719
+ Sleep(ms) {
720
+ return new Promise(resolve => setTimeout(resolve, ms));
721
+ }
722
+ // ─── Small utilities ─────────────────────────────────────────────
723
+ /**
724
+ * True for any OData/Dataverse annotation key. Genuine columns never contain `@`; every annotation
725
+ * does — the response-level controls (`@odata.etag`, `@odata.context`, …) and the per-column
726
+ * suffixes (`<col>@odata.bind`, `<col>@OData.Community.Display.V1.FormattedValue`,
727
+ * `<col>@Microsoft.Dynamics.CRM.lookuplogicalname`). So an `@`-containing key is an annotation.
728
+ */
729
+ IsODataAnnotationKey(key) {
730
+ return key.includes('@');
731
+ }
732
+ buildDataverseURL(baseURL, apiPath) {
733
+ const base = baseURL.replace(/\/+$/, '');
734
+ const path = apiPath.startsWith('/') ? apiPath : `/${apiPath}`;
735
+ return `${base}${path}`;
736
+ }
737
+ previewBody(body) {
738
+ const s = typeof body === 'string' ? body : JSON.stringify(body);
739
+ return (s ?? '').slice(0, 500);
740
+ }
741
+ /**
742
+ * Best-effort cached-object lookup by NAME using the last-seen integration ID. Used by
743
+ * {@link StableOrderingKey}, which the engine calls without a CompanyIntegration; returns null
744
+ * (keyset resume simply unavailable) before any fetch has run.
745
+ */
746
+ tryGetCachedObject(objectName) {
747
+ if (!this.lastIntegrationID)
748
+ return null;
749
+ return this.tryGetCachedObjectFor(this.lastIntegrationID, objectName);
750
+ }
751
+ tryGetCachedObjectFor(integrationID, objectName) {
752
+ if (!integrationID)
753
+ return null;
754
+ try {
755
+ return this.GetCachedObject(integrationID, objectName);
756
+ }
757
+ catch {
758
+ return null;
759
+ }
760
+ }
761
+ /** Reads a trimmed string from an IO's Configuration JSON, tolerant of absent/invalid. */
762
+ readObjectConfigString(obj, key) {
763
+ const raw = obj.Configuration;
764
+ if (!raw || typeof raw !== 'string')
765
+ return null;
766
+ try {
767
+ const cfg = JSON.parse(raw);
768
+ const v = cfg[key];
769
+ return typeof v === 'string' && v.trim().length > 0 ? v.trim() : null;
770
+ }
771
+ catch {
772
+ return null;
773
+ }
774
+ }
775
+ /** Extracts response headers from a thrown error, when the connector throws an error carrying them. */
776
+ extractHeadersFromError(error) {
777
+ if (error && typeof error === 'object') {
778
+ const e = error;
779
+ return e.Headers ?? e.headers;
780
+ }
781
+ return undefined;
782
+ }
783
+ };
784
+ DynamicsDataverseConnector = __decorate([
785
+ RegisterClass(BaseIntegrationConnector, '@memberjunction/connector-microsoft-dynamics-365-dataverse')
786
+ ], DynamicsDataverseConnector);
787
+ export { DynamicsDataverseConnector };
788
+ /** Tree-shaking prevention function — import and call from the package entry point. */
789
+ export function LoadDynamicsDataverseConnector() { }
790
+ //# sourceMappingURL=DynamicsDataverseConnector.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"DynamicsDataverseConnector.js","sourceRoot":"","sources":["../src/DynamicsDataverseConnector.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,4 @@
1
+ export * from './DynamicsDataverseConnector.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 './DynamicsDataverseConnector.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,iCAAiC,CAAC;AAEhD;oGACoG;AACpG,MAAM,UAAU,iBAAiB,KAAiD,CAAC"}
package/package.json ADDED
@@ -0,0 +1,38 @@
1
+ {
2
+ "name": "@memberjunction/connector-microsoft-dynamics-365-dataverse",
3
+ "version": "1.0.0",
4
+ "description": "MemberJunction DynamicsDataverse 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
+ }