@memberjunction/connector-imis 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/IMISConnector.d.ts +170 -0
- package/dist/IMISConnector.js +695 -0
- package/dist/IMISConnector.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 +40 -0
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
import { type UserInfo } from '@memberjunction/core';
|
|
2
|
+
import type { MJCompanyIntegrationEntity, MJIntegrationObjectEntity } from '@memberjunction/core-entities';
|
|
3
|
+
import { BaseRESTIntegrationConnector, type RESTAuthContext, type RESTResponse, type PaginationState, type PaginationType, type ConnectionTestResult, type FetchContext, type FetchBatchResult, type ExternalObjectSchema, type ExternalFieldSchema, type DefaultFieldMapping, type DefaultIntegrationConfig } from '@memberjunction/integration-engine';
|
|
4
|
+
/**
|
|
5
|
+
* iMIS REST API connection configuration, parsed from the attached MJ Credential
|
|
6
|
+
* (preferred) or the CompanyIntegration.Configuration JSON. Field names are read
|
|
7
|
+
* case-insensitively. NONE of these values are read at build time — they are resolved
|
|
8
|
+
* from the bound credential at runtime.
|
|
9
|
+
*/
|
|
10
|
+
export interface IMISConnectionConfig {
|
|
11
|
+
/** Full base URL to the tenant's iMIS API (no trailing slash). */
|
|
12
|
+
BaseURL: string;
|
|
13
|
+
/** API username (must hold the iMIS RemoteService/IqaUser role) for the password grant. */
|
|
14
|
+
Username: string;
|
|
15
|
+
/** API password for the password grant. */
|
|
16
|
+
Password: string;
|
|
17
|
+
/** Optional token-endpoint override; defaults to `{base}/token` (legacy `/asiScheduler/token` fallback). */
|
|
18
|
+
TokenURL?: string;
|
|
19
|
+
/** Optional OAuth client identifier (iMIS password grant does not require it). */
|
|
20
|
+
ClientId?: string;
|
|
21
|
+
/** Optional OAuth client secret. */
|
|
22
|
+
ClientSecret?: string;
|
|
23
|
+
/** Maximum retries for rate-limited / transient failures. Default 3. */
|
|
24
|
+
MaxRetries?: number;
|
|
25
|
+
/** HTTP request timeout in ms. Default 60000. */
|
|
26
|
+
RequestTimeoutMs?: number;
|
|
27
|
+
/** Minimum ms between requests. Default 200. */
|
|
28
|
+
MinRequestIntervalMs?: number;
|
|
29
|
+
}
|
|
30
|
+
export declare class IMISConnector extends BaseRESTIntegrationConnector {
|
|
31
|
+
/** Cached auth context for the current sync run. */
|
|
32
|
+
private authCache;
|
|
33
|
+
/** Shared OAuth2 token manager — owns the token round-trip + cache. */
|
|
34
|
+
private readonly tokenManager;
|
|
35
|
+
/** Timestamp of the last API request, for client-side throttling. */
|
|
36
|
+
private lastRequestTime;
|
|
37
|
+
/** Current watermark value, emitted as the IO's IncrementalWatermarkField on the request. */
|
|
38
|
+
private currentWatermark;
|
|
39
|
+
get IntegrationName(): string;
|
|
40
|
+
get SupportsCreate(): boolean;
|
|
41
|
+
get SupportsUpdate(): boolean;
|
|
42
|
+
get SupportsDelete(): boolean;
|
|
43
|
+
get SupportsSearch(): boolean;
|
|
44
|
+
get SupportsListing(): boolean;
|
|
45
|
+
/** True when any cached IntegrationObject satisfies the predicate. []→false when the engine
|
|
46
|
+
* cache is unavailable (e.g. capability probed before configuration) — fail-safe read-only. */
|
|
47
|
+
private anyObjectDeclares;
|
|
48
|
+
/**
|
|
49
|
+
* OAuth 2.0 password-grant authentication. Mints/refreshes the access token via the shared
|
|
50
|
+
* {@link OAuth2TokenManager} (no inlined token/crypto logic). One token exchange per run is
|
|
51
|
+
* cached and reused across every request.
|
|
52
|
+
*/
|
|
53
|
+
protected Authenticate(companyIntegration: MJCompanyIntegrationEntity, contextUser: UserInfo): Promise<RESTAuthContext>;
|
|
54
|
+
/**
|
|
55
|
+
* Runs the password-grant token round-trip through OAuth2TokenManager. Tries the resolved
|
|
56
|
+
* token endpoint first, then the legacy `/asiScheduler/token` path on failure (older iMIS).
|
|
57
|
+
*/
|
|
58
|
+
private MintToken;
|
|
59
|
+
/** Builds the OAuth2 password-grant request. iMIS does not require client credentials. */
|
|
60
|
+
private BuildTokenRequest;
|
|
61
|
+
/**
|
|
62
|
+
* Extracts the new record's external ID from a create response. iMIS returns the created
|
|
63
|
+
* object with a vendor-specific ID field (Id / PartyId / EventId / InvoiceId / …); the base
|
|
64
|
+
* scanner only knows the generic `id` family, so widen it for iMIS's `{Object}Id` convention.
|
|
65
|
+
* The empty-ID case still routes through BuildCreatedResult, which fails loudly.
|
|
66
|
+
*/
|
|
67
|
+
protected ExtractIDFromResponse(response: RESTResponse, idLocation: string | null): string | undefined;
|
|
68
|
+
/** Sends the OAuth2 bearer token on every request. */
|
|
69
|
+
protected BuildHeaders(auth: RESTAuthContext): Record<string, string>;
|
|
70
|
+
protected GetBaseURL(_companyIntegration: MJCompanyIntegrationEntity, auth: RESTAuthContext): string;
|
|
71
|
+
/**
|
|
72
|
+
* Unwraps the iMIS PagedResult envelope to the Items collection. Handles the envelope,
|
|
73
|
+
* an explicit responseDataKey, a raw array, and a single-object detail response. The FULL
|
|
74
|
+
* source record passes through (no field filtering) so the framework's custom-column capture
|
|
75
|
+
* sees everything the source returned.
|
|
76
|
+
*/
|
|
77
|
+
protected NormalizeResponse(rawBody: unknown, responseDataKey: string | null): Record<string, unknown>[];
|
|
78
|
+
/**
|
|
79
|
+
* Derives Offset/Limit pagination state from the iMIS PagedResult envelope: advance Offset by
|
|
80
|
+
* the number of Items returned and continue while HasNext is true (terminate when HasNext is false).
|
|
81
|
+
*/
|
|
82
|
+
protected ExtractPaginationInfo(rawBody: unknown, paginationType: PaginationType, currentPage: number, currentOffset: number, _pageSize: number): PaginationState;
|
|
83
|
+
/**
|
|
84
|
+
* iMIS pagination uses capitalized `Offset` / `Limit` query parameters. Override the base
|
|
85
|
+
* helper to match iMIS's param names.
|
|
86
|
+
*/
|
|
87
|
+
protected BuildPaginatedURL(basePath: string, obj: MJIntegrationObjectEntity, page: number, offset: number, cursor?: string, effectivePageSize?: number): string;
|
|
88
|
+
/**
|
|
89
|
+
* Injects the incremental watermark filter when a sync is active. The param NAME is fully
|
|
90
|
+
* METADATA-DRIVEN: it comes from the IO's `IncrementalWatermarkField` and is emitted only when
|
|
91
|
+
* SupportsIncrementalSync=true AND a watermark value is in context. iMIS accepts operator-prefixed
|
|
92
|
+
* filter values (`gt:`) on list endpoints.
|
|
93
|
+
*/
|
|
94
|
+
protected AppendDefaultQueryParams(url: string, obj: MJIntegrationObjectEntity): string;
|
|
95
|
+
/** Executes an HTTP request with client-side throttling + retry for 401/429/5xx. */
|
|
96
|
+
protected MakeHTTPRequest(auth: RESTAuthContext, url: string, method: string, headers: Record<string, string>, body?: unknown): Promise<RESTResponse>;
|
|
97
|
+
/**
|
|
98
|
+
* Tests connectivity by minting a password-grant token and listing one Party record. A 2xx
|
|
99
|
+
* confirms the OAuth2 credentials + base URL are valid against the live API.
|
|
100
|
+
*/
|
|
101
|
+
TestConnection(companyIntegration: MJCompanyIntegrationEntity, contextUser: UserInfo): Promise<ConnectionTestResult>;
|
|
102
|
+
/**
|
|
103
|
+
* Discovers objects: the Declared core baseline from the IntegrationEngineBase cache, ADDITIVELY
|
|
104
|
+
* extended at runtime via the auth-gated /EntityDefinition endpoint (the tenant's customer
|
|
105
|
+
* Business-Object catalog). The catalog is NEVER baked into code — DiscoverObjects expresses only
|
|
106
|
+
* the discovery MECHANISM. Failures degrade gracefully to the Declared baseline.
|
|
107
|
+
*/
|
|
108
|
+
DiscoverObjects(companyIntegration: MJCompanyIntegrationEntity, contextUser: UserInfo): Promise<ExternalObjectSchema[]>;
|
|
109
|
+
/**
|
|
110
|
+
* Discovers fields: the Declared baseline from the cache, ADDITIVELY extended at runtime via the
|
|
111
|
+
* auth-gated /{entity}/metadata endpoint (the tenant's PropertyDefinitions). The field set is
|
|
112
|
+
* NEVER baked in code. Failures degrade gracefully to the Declared baseline.
|
|
113
|
+
*/
|
|
114
|
+
DiscoverFields(companyIntegration: MJCompanyIntegrationEntity, objectName: string, contextUser: UserInfo): Promise<ExternalFieldSchema[]>;
|
|
115
|
+
/**
|
|
116
|
+
* Sets the watermark context the URL builder needs, delegates the walk to the base (which handles
|
|
117
|
+
* pagination + nested template-var paths), then advances the watermark from the returned records
|
|
118
|
+
* on the FINAL batch only (partial-failure-safe — a mid-stream failure leaves the watermark unchanged).
|
|
119
|
+
*/
|
|
120
|
+
FetchChanges(ctx: FetchContext): Promise<FetchBatchResult>;
|
|
121
|
+
GetDefaultConfiguration(): DefaultIntegrationConfig;
|
|
122
|
+
GetDefaultFieldMappings(objectName: string, _entityName: string): DefaultFieldMapping[];
|
|
123
|
+
private DiscoverEntityDefinitions;
|
|
124
|
+
private DiscoverFieldsFromLiveAPI;
|
|
125
|
+
private PropertyDefinitionToFieldSchema;
|
|
126
|
+
/**
|
|
127
|
+
* Merge live-discovered fields with the Declared baseline. Live-only fields are appended;
|
|
128
|
+
* Declared fields win on matching names (the curated MJ schema is authoritative for the baseline).
|
|
129
|
+
*/
|
|
130
|
+
private MergeFieldSchemas;
|
|
131
|
+
/**
|
|
132
|
+
* Resolve an object name to its API path. Prefers the configured APIPath from IntegrationObject
|
|
133
|
+
* metadata; falls back to `/{ObjectName}` for runtime-discovered Business Objects.
|
|
134
|
+
*/
|
|
135
|
+
private ResolveObjectPath;
|
|
136
|
+
/**
|
|
137
|
+
* Parses the connection config, preferring the attached MJ Credential over the raw Configuration
|
|
138
|
+
* JSON. Credential bytes are resolved at runtime — never at build.
|
|
139
|
+
*/
|
|
140
|
+
private ParseConfig;
|
|
141
|
+
/** Loads the config from the MJ: Credentials entity Values JSON. */
|
|
142
|
+
private ParseConfigFromCredential;
|
|
143
|
+
/** Validates the parsed config + applies defaults. Field names are case-insensitive. */
|
|
144
|
+
private ValidateConfig;
|
|
145
|
+
/** Resolves the API base URL from the credential's BaseURL (never a hardcoded host). */
|
|
146
|
+
private ResolveBaseUrl;
|
|
147
|
+
/**
|
|
148
|
+
* Resolves the token endpoint: credential TokenURL override, else `{base}/token`. iMIS issues
|
|
149
|
+
* tokens at the host root (not under /api/), so a trailing /api segment is stripped.
|
|
150
|
+
*/
|
|
151
|
+
private ResolveTokenURL;
|
|
152
|
+
private ComputeNewWatermark;
|
|
153
|
+
/** Throttle to respect the configured minimum request interval. */
|
|
154
|
+
private ThrottleIfNeeded;
|
|
155
|
+
/** Parses a Retry-After header (seconds or http-date) into ms, if present. */
|
|
156
|
+
private RetryAfterMs;
|
|
157
|
+
/** Exponential backoff delay for retry attempts (capped at 15s). */
|
|
158
|
+
private BackoffDelay;
|
|
159
|
+
/** Checks whether an error is transient (network/timeout). */
|
|
160
|
+
private IsTransientNetworkError;
|
|
161
|
+
/** Builds the normalized RESTResponse from a fetch Response. */
|
|
162
|
+
private BuildRESTResponse;
|
|
163
|
+
private ExtractServerVersion;
|
|
164
|
+
/** True when a body is the iMIS PagedResult envelope (Items[] + Offset/Limit/HasNext). */
|
|
165
|
+
private IsPagedResult;
|
|
166
|
+
/** Promise-wrapped setTimeout. */
|
|
167
|
+
private Sleep;
|
|
168
|
+
}
|
|
169
|
+
/** Tree-shaking prevention — import and call from the package entry point. */
|
|
170
|
+
export declare function LoadIMISConnector(): void;
|
|
@@ -0,0 +1,695 @@
|
|
|
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
|
+
* IMISConnector — Integration connector for iMIS EMS (Advanced Solutions International)
|
|
9
|
+
* REST API.
|
|
10
|
+
*
|
|
11
|
+
* ── Auth: OAuth 2.0 PASSWORD grant (resource-owner) ─────────────────────────
|
|
12
|
+
* Mints/refreshes a bearer ACCESS TOKEN via the shared {@link OAuth2TokenManager}
|
|
13
|
+
* (no inlined token/crypto logic). iMIS issues tokens via the resource-owner
|
|
14
|
+
* `grant_type=password` flow — username + password POSTed to the token endpoint:
|
|
15
|
+
* - default = `{BaseURL}/token`
|
|
16
|
+
* - override = the credential's `TokenURL` (when supplied)
|
|
17
|
+
* - fallback = the legacy `{BaseURL}/asiScheduler/token` path (older iMIS builds)
|
|
18
|
+
* iMIS does NOT require client_id/client_secret for the password grant, so the
|
|
19
|
+
* OAuth2TokenManager request is configured with empty client credentials in the
|
|
20
|
+
* body. Every API request sends `Authorization: Bearer {accessToken}`.
|
|
21
|
+
*
|
|
22
|
+
* ── Base URL ────────────────────────────────────────────────────────────────
|
|
23
|
+
* Comes from the credential's `BaseURL` (the tenant's iMIS API endpoint) — never
|
|
24
|
+
* a hardcoded host. Credential bytes are resolved at runtime, never at build.
|
|
25
|
+
*
|
|
26
|
+
* ── Catalog (metadata-driven, NOT hardcoded) ───────────────────────────────
|
|
27
|
+
* The Declared core object/field set comes from the seeded metadata loaded into
|
|
28
|
+
* the IntegrationEngineBase cache. The connector NEVER bakes an object/field catalog
|
|
29
|
+
* into code. DiscoverObjects/DiscoverFields express only the MECHANISM: they call the
|
|
30
|
+
* auth-gated /EntityDefinition + /{entity}/metadata endpoints at runtime to enumerate
|
|
31
|
+
* the tenant's Business-Object catalog + per-object fields, ADDITIVE to the Declared
|
|
32
|
+
* baseline. DiscoveryIsAuthoritative stays false (default) — these scoped enumerations
|
|
33
|
+
* never deactivate Declared metadata on absence.
|
|
34
|
+
*
|
|
35
|
+
* ── Pagination & incremental ───────────────────────────────────────────────
|
|
36
|
+
* iMIS uses the PagedResult envelope (`{ Items, Offset, Limit, Count, HasNext }`) with
|
|
37
|
+
* capitalized `Offset`/`Limit` query params; ExtractPaginationInfo reads HasNext/Offset/Limit
|
|
38
|
+
* and advances Offset by the returned count until HasNext is false. NormalizeResponse
|
|
39
|
+
* unwraps the Items collection. Incremental sync is metadata-driven: an IO with
|
|
40
|
+
* SupportsIncrementalSync=true emits its `IncrementalWatermarkField` as a `gt:` filter.
|
|
41
|
+
*
|
|
42
|
+
* ── Write ───────────────────────────────────────────────────────────────────
|
|
43
|
+
* CRUD rides the generic BaseRESTIntegrationConnector per-operation path: Create/Update/Delete
|
|
44
|
+
* read the per-operation IO columns (CreateAPIPath/Method/BodyShape/BodyKey/IDLocation, Update*,
|
|
45
|
+
* Delete*), creates route through BuildCreatedResult (fail loudly on empty ID), and DeleteMethod
|
|
46
|
+
* is metadata-driven (not assumed DELETE). Capability flags follow the per-operation columns the
|
|
47
|
+
* metadata declares — no hardcoded `true`, no 501 stubs. Full-record pass-through (Fields=raw).
|
|
48
|
+
*
|
|
49
|
+
* Reference: https://developer.imis.com/docs/imis-rest-api-data-models-and-swagger-json-files
|
|
50
|
+
*/
|
|
51
|
+
import { RegisterClass } from '@memberjunction/global';
|
|
52
|
+
import { Metadata } from '@memberjunction/core';
|
|
53
|
+
import { BaseIntegrationConnector, BaseRESTIntegrationConnector, OAuth2TokenManager, } from '@memberjunction/integration-engine';
|
|
54
|
+
import { IntegrationEngineBase } from '@memberjunction/integration-engine-base';
|
|
55
|
+
// ─── Constants ───────────────────────────────────────────────────────
|
|
56
|
+
/** The canonical MJ: Integrations.Name — part of the three-way invariant. */
|
|
57
|
+
const INTEGRATION_NAME = 'iMIS';
|
|
58
|
+
/** Default token endpoint path appended to the base when no TokenURL override is supplied. */
|
|
59
|
+
const DEFAULT_TOKEN_PATH = '/token';
|
|
60
|
+
/** Legacy token endpoint path for older iMIS deployments (documented fallback). */
|
|
61
|
+
const LEGACY_TOKEN_PATH = '/asiScheduler/token';
|
|
62
|
+
const DEFAULT_MAX_RETRIES = 3;
|
|
63
|
+
const DEFAULT_REQUEST_TIMEOUT_MS = 60_000;
|
|
64
|
+
const DEFAULT_MIN_REQUEST_INTERVAL_MS = 200;
|
|
65
|
+
const DEFAULT_PAGE_SIZE = 100;
|
|
66
|
+
/** iMIS-to-generic type mapping used when merging live-discovered field metadata. */
|
|
67
|
+
const IMIS_TYPE_MAP = {
|
|
68
|
+
'string': 'string',
|
|
69
|
+
'datetime': 'datetime',
|
|
70
|
+
'date': 'date',
|
|
71
|
+
'boolean': 'boolean',
|
|
72
|
+
'int32': 'integer',
|
|
73
|
+
'int64': 'integer',
|
|
74
|
+
'integer': 'integer',
|
|
75
|
+
'long': 'integer',
|
|
76
|
+
'decimal': 'decimal',
|
|
77
|
+
'double': 'decimal',
|
|
78
|
+
'float': 'decimal',
|
|
79
|
+
'guid': 'uuid',
|
|
80
|
+
'uuid': 'uuid',
|
|
81
|
+
'monetaryamountdata': 'decimal',
|
|
82
|
+
};
|
|
83
|
+
// ─── Connector Implementation ────────────────────────────────────────
|
|
84
|
+
let IMISConnector = class IMISConnector extends BaseRESTIntegrationConnector {
|
|
85
|
+
constructor() {
|
|
86
|
+
super(...arguments);
|
|
87
|
+
/** Cached auth context for the current sync run. */
|
|
88
|
+
this.authCache = null;
|
|
89
|
+
/** Shared OAuth2 token manager — owns the token round-trip + cache. */
|
|
90
|
+
this.tokenManager = new OAuth2TokenManager();
|
|
91
|
+
/** Timestamp of the last API request, for client-side throttling. */
|
|
92
|
+
this.lastRequestTime = 0;
|
|
93
|
+
}
|
|
94
|
+
// Returns the EXACT MJ: Integrations.Name string LITERAL so the T1 ThreeWayName invariant can
|
|
95
|
+
// statically parse the getter's returned value from connector source.
|
|
96
|
+
get IntegrationName() { return 'iMIS'; }
|
|
97
|
+
// ── Capability getters: METADATA-DRIVEN (no hardcoded answer) ─────
|
|
98
|
+
//
|
|
99
|
+
// Write capability FOLLOWS the per-operation CRUD columns on the cached IntegrationObjects
|
|
100
|
+
// (Declared metadata). An object is create-capable when it declares both CreateAPIPath +
|
|
101
|
+
// CreateMethod; same for update/delete. With no write metadata authored the surface is
|
|
102
|
+
// read-only (returns false) — but the moment a writable object's per-operation columns are
|
|
103
|
+
// populated, the capability flips on and the base generic CRUD path executes it.
|
|
104
|
+
get SupportsCreate() {
|
|
105
|
+
return this.anyObjectDeclares(o => !!o.CreateAPIPath && !!o.CreateMethod);
|
|
106
|
+
}
|
|
107
|
+
get SupportsUpdate() {
|
|
108
|
+
return this.anyObjectDeclares(o => !!o.UpdateAPIPath && !!o.UpdateMethod);
|
|
109
|
+
}
|
|
110
|
+
get SupportsDelete() {
|
|
111
|
+
return this.anyObjectDeclares(o => !!o.DeleteAPIPath && !!o.DeleteMethod);
|
|
112
|
+
}
|
|
113
|
+
get SupportsSearch() { return false; }
|
|
114
|
+
get SupportsListing() { return false; }
|
|
115
|
+
/** True when any cached IntegrationObject satisfies the predicate. []→false when the engine
|
|
116
|
+
* cache is unavailable (e.g. capability probed before configuration) — fail-safe read-only. */
|
|
117
|
+
anyObjectDeclares(pred) {
|
|
118
|
+
const integration = IntegrationEngineBase.Instance.GetIntegrationByName(INTEGRATION_NAME);
|
|
119
|
+
if (!integration)
|
|
120
|
+
return false;
|
|
121
|
+
return IntegrationEngineBase.Instance.GetActiveIntegrationObjects(integration.ID).some(pred);
|
|
122
|
+
}
|
|
123
|
+
// ─── BaseRESTIntegrationConnector abstract methods ──────────────
|
|
124
|
+
/**
|
|
125
|
+
* OAuth 2.0 password-grant authentication. Mints/refreshes the access token via the shared
|
|
126
|
+
* {@link OAuth2TokenManager} (no inlined token/crypto logic). One token exchange per run is
|
|
127
|
+
* cached and reused across every request.
|
|
128
|
+
*/
|
|
129
|
+
async Authenticate(companyIntegration, contextUser) {
|
|
130
|
+
if (this.authCache)
|
|
131
|
+
return this.authCache;
|
|
132
|
+
const config = await this.ParseConfig(companyIntegration, contextUser);
|
|
133
|
+
const baseUrl = this.ResolveBaseUrl(config);
|
|
134
|
+
const token = await this.MintToken(config, baseUrl);
|
|
135
|
+
const auth = { Token: token, BaseUrl: baseUrl, Config: config };
|
|
136
|
+
this.authCache = auth;
|
|
137
|
+
return auth;
|
|
138
|
+
}
|
|
139
|
+
/**
|
|
140
|
+
* Runs the password-grant token round-trip through OAuth2TokenManager. Tries the resolved
|
|
141
|
+
* token endpoint first, then the legacy `/asiScheduler/token` path on failure (older iMIS).
|
|
142
|
+
*/
|
|
143
|
+
async MintToken(config, baseUrl) {
|
|
144
|
+
const primaryURL = this.ResolveTokenURL(config, baseUrl);
|
|
145
|
+
const req = this.BuildTokenRequest(config, primaryURL);
|
|
146
|
+
try {
|
|
147
|
+
const token = await this.tokenManager.GetAccessToken(req, 'password');
|
|
148
|
+
return token.AccessToken;
|
|
149
|
+
}
|
|
150
|
+
catch (primaryErr) {
|
|
151
|
+
// Legacy fallback only when no explicit override was given and the legacy path differs.
|
|
152
|
+
const legacyURL = `${baseUrl}${LEGACY_TOKEN_PATH}`;
|
|
153
|
+
if (config.TokenURL || legacyURL === primaryURL)
|
|
154
|
+
throw primaryErr;
|
|
155
|
+
this.tokenManager.Reset();
|
|
156
|
+
const token = await this.tokenManager.GetAccessToken({ ...req, TokenURL: legacyURL }, 'password');
|
|
157
|
+
return token.AccessToken;
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
/** Builds the OAuth2 password-grant request. iMIS does not require client credentials. */
|
|
161
|
+
BuildTokenRequest(config, tokenURL) {
|
|
162
|
+
return {
|
|
163
|
+
TokenURL: tokenURL,
|
|
164
|
+
ClientId: config.ClientId ?? '',
|
|
165
|
+
ClientSecret: config.ClientSecret ?? '',
|
|
166
|
+
Username: config.Username,
|
|
167
|
+
Password: config.Password,
|
|
168
|
+
TimeoutMs: config.RequestTimeoutMs ?? DEFAULT_REQUEST_TIMEOUT_MS,
|
|
169
|
+
};
|
|
170
|
+
}
|
|
171
|
+
/**
|
|
172
|
+
* Extracts the new record's external ID from a create response. iMIS returns the created
|
|
173
|
+
* object with a vendor-specific ID field (Id / PartyId / EventId / InvoiceId / …); the base
|
|
174
|
+
* scanner only knows the generic `id` family, so widen it for iMIS's `{Object}Id` convention.
|
|
175
|
+
* The empty-ID case still routes through BuildCreatedResult, which fails loudly.
|
|
176
|
+
*/
|
|
177
|
+
ExtractIDFromResponse(response, idLocation) {
|
|
178
|
+
if ((!idLocation || idLocation === 'body') && response.Body && typeof response.Body === 'object') {
|
|
179
|
+
const b = response.Body;
|
|
180
|
+
const candidates = ['Id', 'ID', 'PartyId', 'UniformId', 'EventId', 'InvoiceId', 'PaymentId', 'id', 'externalID', 'ExternalID'];
|
|
181
|
+
for (const k of candidates) {
|
|
182
|
+
const v = b[k];
|
|
183
|
+
if (typeof v === 'string' || typeof v === 'number') {
|
|
184
|
+
const s = String(v).trim();
|
|
185
|
+
if (s.length > 0)
|
|
186
|
+
return s;
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
return undefined;
|
|
190
|
+
}
|
|
191
|
+
return super.ExtractIDFromResponse(response, idLocation);
|
|
192
|
+
}
|
|
193
|
+
/** Sends the OAuth2 bearer token on every request. */
|
|
194
|
+
BuildHeaders(auth) {
|
|
195
|
+
const token = auth.Token ?? auth.Token ?? '';
|
|
196
|
+
return {
|
|
197
|
+
'Authorization': `Bearer ${token}`,
|
|
198
|
+
'Accept': 'application/json',
|
|
199
|
+
'Content-Type': 'application/json',
|
|
200
|
+
};
|
|
201
|
+
}
|
|
202
|
+
GetBaseURL(_companyIntegration, auth) {
|
|
203
|
+
return auth.BaseUrl;
|
|
204
|
+
}
|
|
205
|
+
/**
|
|
206
|
+
* Unwraps the iMIS PagedResult envelope to the Items collection. Handles the envelope,
|
|
207
|
+
* an explicit responseDataKey, a raw array, and a single-object detail response. The FULL
|
|
208
|
+
* source record passes through (no field filtering) so the framework's custom-column capture
|
|
209
|
+
* sees everything the source returned.
|
|
210
|
+
*/
|
|
211
|
+
NormalizeResponse(rawBody, responseDataKey) {
|
|
212
|
+
if (rawBody == null)
|
|
213
|
+
return [];
|
|
214
|
+
// PagedResult envelope → Items (ResponseDataKey is typically "Items")
|
|
215
|
+
if (this.IsPagedResult(rawBody)) {
|
|
216
|
+
return rawBody.Items ?? [];
|
|
217
|
+
}
|
|
218
|
+
if (responseDataKey) {
|
|
219
|
+
const record = rawBody;
|
|
220
|
+
const data = record[responseDataKey];
|
|
221
|
+
if (Array.isArray(data))
|
|
222
|
+
return data;
|
|
223
|
+
}
|
|
224
|
+
if (Array.isArray(rawBody))
|
|
225
|
+
return rawBody;
|
|
226
|
+
// Single-object detail response (GET /{Entity}/{id}).
|
|
227
|
+
return [rawBody];
|
|
228
|
+
}
|
|
229
|
+
/**
|
|
230
|
+
* Derives Offset/Limit pagination state from the iMIS PagedResult envelope: advance Offset by
|
|
231
|
+
* the number of Items returned and continue while HasNext is true (terminate when HasNext is false).
|
|
232
|
+
*/
|
|
233
|
+
ExtractPaginationInfo(rawBody, paginationType, currentPage, currentOffset, _pageSize) {
|
|
234
|
+
if (paginationType === 'None')
|
|
235
|
+
return { HasMore: false };
|
|
236
|
+
const envelope = this.IsPagedResult(rawBody) ? rawBody : null;
|
|
237
|
+
if (!envelope) {
|
|
238
|
+
// No PagedResult envelope → cannot advance → no more pages.
|
|
239
|
+
return { HasMore: false };
|
|
240
|
+
}
|
|
241
|
+
const returnedCount = envelope.Items?.length ?? 0;
|
|
242
|
+
const hasMore = (envelope.HasNext ?? false) && returnedCount > 0;
|
|
243
|
+
const nextOffset = (envelope.Offset ?? currentOffset) + returnedCount;
|
|
244
|
+
return {
|
|
245
|
+
HasMore: hasMore,
|
|
246
|
+
NextOffset: paginationType === 'Offset' ? nextOffset : undefined,
|
|
247
|
+
NextPage: paginationType === 'PageNumber' ? currentPage + 1 : undefined,
|
|
248
|
+
TotalRecords: envelope.Count,
|
|
249
|
+
};
|
|
250
|
+
}
|
|
251
|
+
/**
|
|
252
|
+
* iMIS pagination uses capitalized `Offset` / `Limit` query parameters. Override the base
|
|
253
|
+
* helper to match iMIS's param names.
|
|
254
|
+
*/
|
|
255
|
+
BuildPaginatedURL(basePath, obj, page, offset, cursor, effectivePageSize) {
|
|
256
|
+
const pageSize = effectivePageSize ?? obj.DefaultPageSize ?? DEFAULT_PAGE_SIZE;
|
|
257
|
+
const separator = basePath.includes('?') ? '&' : '?';
|
|
258
|
+
switch (obj.PaginationType) {
|
|
259
|
+
case 'PageNumber':
|
|
260
|
+
return `${basePath}${separator}Page=${page}&Limit=${pageSize}`;
|
|
261
|
+
case 'Offset':
|
|
262
|
+
return `${basePath}${separator}Offset=${offset}&Limit=${pageSize}`;
|
|
263
|
+
case 'Cursor':
|
|
264
|
+
return cursor
|
|
265
|
+
? `${basePath}${separator}cursor=${encodeURIComponent(cursor)}&Limit=${pageSize}`
|
|
266
|
+
: `${basePath}${separator}Limit=${pageSize}`;
|
|
267
|
+
default:
|
|
268
|
+
return basePath;
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
/**
|
|
272
|
+
* Injects the incremental watermark filter when a sync is active. The param NAME is fully
|
|
273
|
+
* METADATA-DRIVEN: it comes from the IO's `IncrementalWatermarkField` and is emitted only when
|
|
274
|
+
* SupportsIncrementalSync=true AND a watermark value is in context. iMIS accepts operator-prefixed
|
|
275
|
+
* filter values (`gt:`) on list endpoints.
|
|
276
|
+
*/
|
|
277
|
+
AppendDefaultQueryParams(url, obj) {
|
|
278
|
+
let result = super.AppendDefaultQueryParams(url, obj);
|
|
279
|
+
const watermarkField = obj.IncrementalWatermarkField;
|
|
280
|
+
if (obj.SupportsIncrementalSync && watermarkField && this.currentWatermark) {
|
|
281
|
+
const separator = result.includes('?') ? '&' : '?';
|
|
282
|
+
const param = `${encodeURIComponent(watermarkField)}=${encodeURIComponent('gt:' + this.currentWatermark)}`;
|
|
283
|
+
result = `${result}${separator}${param}`;
|
|
284
|
+
}
|
|
285
|
+
return result;
|
|
286
|
+
}
|
|
287
|
+
/** Executes an HTTP request with client-side throttling + retry for 401/429/5xx. */
|
|
288
|
+
async MakeHTTPRequest(auth, url, method, headers, body) {
|
|
289
|
+
const imisAuth = auth;
|
|
290
|
+
const maxRetries = imisAuth.Config.MaxRetries ?? DEFAULT_MAX_RETRIES;
|
|
291
|
+
const timeoutMs = imisAuth.Config.RequestTimeoutMs ?? DEFAULT_REQUEST_TIMEOUT_MS;
|
|
292
|
+
const minInterval = imisAuth.Config.MinRequestIntervalMs ?? DEFAULT_MIN_REQUEST_INTERVAL_MS;
|
|
293
|
+
const effectiveHeaders = { ...headers };
|
|
294
|
+
for (let attempt = 0; attempt <= maxRetries; attempt++) {
|
|
295
|
+
await this.ThrottleIfNeeded(minInterval);
|
|
296
|
+
const fetchOptions = {
|
|
297
|
+
method,
|
|
298
|
+
headers: effectiveHeaders,
|
|
299
|
+
signal: AbortSignal.timeout(timeoutMs),
|
|
300
|
+
};
|
|
301
|
+
if (body !== undefined && method !== 'GET') {
|
|
302
|
+
fetchOptions.body = typeof body === 'string' ? body : JSON.stringify(body);
|
|
303
|
+
}
|
|
304
|
+
let response;
|
|
305
|
+
try {
|
|
306
|
+
response = await fetch(url, fetchOptions);
|
|
307
|
+
}
|
|
308
|
+
catch (err) {
|
|
309
|
+
if (attempt < maxRetries && this.IsTransientNetworkError(err)) {
|
|
310
|
+
await this.Sleep(this.BackoffDelay(attempt));
|
|
311
|
+
continue;
|
|
312
|
+
}
|
|
313
|
+
throw err;
|
|
314
|
+
}
|
|
315
|
+
this.lastRequestTime = Date.now();
|
|
316
|
+
// Token expired mid-run — force a fresh mint and retry once with the new bearer.
|
|
317
|
+
if (response.status === 401 && attempt < maxRetries) {
|
|
318
|
+
this.tokenManager.Reset();
|
|
319
|
+
this.authCache = null;
|
|
320
|
+
const fresh = await this.MintToken(imisAuth.Config, imisAuth.BaseUrl);
|
|
321
|
+
imisAuth.Token = fresh;
|
|
322
|
+
this.authCache = imisAuth;
|
|
323
|
+
effectiveHeaders['Authorization'] = `Bearer ${fresh}`;
|
|
324
|
+
continue;
|
|
325
|
+
}
|
|
326
|
+
if ((response.status === 429 || response.status >= 500) && attempt < maxRetries) {
|
|
327
|
+
console.warn(`[iMIS] HTTP ${response.status} from ${url} — backing off`);
|
|
328
|
+
await this.Sleep(this.RetryAfterMs(response) ?? this.BackoffDelay(attempt));
|
|
329
|
+
continue;
|
|
330
|
+
}
|
|
331
|
+
return this.BuildRESTResponse(response);
|
|
332
|
+
}
|
|
333
|
+
throw new Error(`iMIS request failed after ${maxRetries + 1} attempts: ${url}`);
|
|
334
|
+
}
|
|
335
|
+
// ─── TestConnection ──────────────────────────────────────────────
|
|
336
|
+
/**
|
|
337
|
+
* Tests connectivity by minting a password-grant token and listing one Party record. A 2xx
|
|
338
|
+
* confirms the OAuth2 credentials + base URL are valid against the live API.
|
|
339
|
+
*/
|
|
340
|
+
async TestConnection(companyIntegration, contextUser) {
|
|
341
|
+
try {
|
|
342
|
+
const auth = (await this.Authenticate(companyIntegration, contextUser));
|
|
343
|
+
const headers = this.BuildHeaders(auth);
|
|
344
|
+
const url = `${auth.BaseUrl}/Party?Limit=1`;
|
|
345
|
+
const response = await this.MakeHTTPRequest(auth, url, 'GET', headers);
|
|
346
|
+
if (response.Status < 200 || response.Status >= 300) {
|
|
347
|
+
return { Success: false, Message: `iMIS returned HTTP ${response.Status} from ${url}` };
|
|
348
|
+
}
|
|
349
|
+
return {
|
|
350
|
+
Success: true,
|
|
351
|
+
Message: `Connected to iMIS EMS at ${auth.BaseUrl}`,
|
|
352
|
+
ServerVersion: this.ExtractServerVersion(response.Headers),
|
|
353
|
+
};
|
|
354
|
+
}
|
|
355
|
+
catch (err) {
|
|
356
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
357
|
+
return { Success: false, Message: `Connection failed: ${message}` };
|
|
358
|
+
}
|
|
359
|
+
}
|
|
360
|
+
// ─── Discovery (metadata-driven baseline + runtime mechanism) ────
|
|
361
|
+
/**
|
|
362
|
+
* Discovers objects: the Declared core baseline from the IntegrationEngineBase cache, ADDITIVELY
|
|
363
|
+
* extended at runtime via the auth-gated /EntityDefinition endpoint (the tenant's customer
|
|
364
|
+
* Business-Object catalog). The catalog is NEVER baked into code — DiscoverObjects expresses only
|
|
365
|
+
* the discovery MECHANISM. Failures degrade gracefully to the Declared baseline.
|
|
366
|
+
*/
|
|
367
|
+
async DiscoverObjects(companyIntegration, contextUser) {
|
|
368
|
+
const declared = await super.DiscoverObjects(companyIntegration, contextUser);
|
|
369
|
+
const declaredNames = new Set(declared.map(o => o.Name.toLowerCase()));
|
|
370
|
+
try {
|
|
371
|
+
const discovered = await this.DiscoverEntityDefinitions(companyIntegration, contextUser);
|
|
372
|
+
const additive = discovered.filter(d => !declaredNames.has(d.Name.toLowerCase()));
|
|
373
|
+
return [...declared, ...additive];
|
|
374
|
+
}
|
|
375
|
+
catch (err) {
|
|
376
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
377
|
+
console.warn(`[iMIS] EntityDefinition discovery failed, returning declared objects only: ${message}`);
|
|
378
|
+
return declared;
|
|
379
|
+
}
|
|
380
|
+
}
|
|
381
|
+
/**
|
|
382
|
+
* Discovers fields: the Declared baseline from the cache, ADDITIVELY extended at runtime via the
|
|
383
|
+
* auth-gated /{entity}/metadata endpoint (the tenant's PropertyDefinitions). The field set is
|
|
384
|
+
* NEVER baked in code. Failures degrade gracefully to the Declared baseline.
|
|
385
|
+
*/
|
|
386
|
+
async DiscoverFields(companyIntegration, objectName, contextUser) {
|
|
387
|
+
const declared = await super.DiscoverFields(companyIntegration, objectName, contextUser);
|
|
388
|
+
try {
|
|
389
|
+
const live = await this.DiscoverFieldsFromLiveAPI(companyIntegration, objectName, contextUser);
|
|
390
|
+
return this.MergeFieldSchemas(declared, live);
|
|
391
|
+
}
|
|
392
|
+
catch (err) {
|
|
393
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
394
|
+
console.warn(`[iMIS] Live field discovery failed for "${objectName}", returning declared fields: ${message}`);
|
|
395
|
+
return declared;
|
|
396
|
+
}
|
|
397
|
+
}
|
|
398
|
+
// ─── FetchChanges override (watermark wiring) ────────────────────
|
|
399
|
+
/**
|
|
400
|
+
* Sets the watermark context the URL builder needs, delegates the walk to the base (which handles
|
|
401
|
+
* pagination + nested template-var paths), then advances the watermark from the returned records
|
|
402
|
+
* on the FINAL batch only (partial-failure-safe — a mid-stream failure leaves the watermark unchanged).
|
|
403
|
+
*/
|
|
404
|
+
async FetchChanges(ctx) {
|
|
405
|
+
this.currentWatermark = ctx.WatermarkValue ?? undefined;
|
|
406
|
+
try {
|
|
407
|
+
const result = await super.FetchChanges(ctx);
|
|
408
|
+
const isFinal = !result.HasMore;
|
|
409
|
+
result.NewWatermarkValue = isFinal
|
|
410
|
+
? this.ComputeNewWatermark(result.Records, ctx.WatermarkValue)
|
|
411
|
+
: undefined;
|
|
412
|
+
return result;
|
|
413
|
+
}
|
|
414
|
+
finally {
|
|
415
|
+
this.currentWatermark = undefined;
|
|
416
|
+
}
|
|
417
|
+
}
|
|
418
|
+
// ── Default configuration / field mappings ──────────────────────
|
|
419
|
+
GetDefaultConfiguration() {
|
|
420
|
+
return {
|
|
421
|
+
DefaultSchemaName: 'iMIS',
|
|
422
|
+
DefaultObjects: [],
|
|
423
|
+
};
|
|
424
|
+
}
|
|
425
|
+
GetDefaultFieldMappings(objectName, _entityName) {
|
|
426
|
+
switch (objectName) {
|
|
427
|
+
case 'Party':
|
|
428
|
+
case 'Person':
|
|
429
|
+
return [
|
|
430
|
+
{ SourceFieldName: 'PartyId', DestinationFieldName: 'ExternalID', IsKeyField: true },
|
|
431
|
+
{ SourceFieldName: 'FullName', DestinationFieldName: 'Name' },
|
|
432
|
+
{ SourceFieldName: 'PrimaryEmail', DestinationFieldName: 'Email' },
|
|
433
|
+
{ SourceFieldName: 'PrimaryPhone', DestinationFieldName: 'Phone' },
|
|
434
|
+
];
|
|
435
|
+
case 'Event':
|
|
436
|
+
return [
|
|
437
|
+
{ SourceFieldName: 'EventId', DestinationFieldName: 'ExternalID', IsKeyField: true },
|
|
438
|
+
{ SourceFieldName: 'Name', DestinationFieldName: 'Name' },
|
|
439
|
+
{ SourceFieldName: 'StartDate', DestinationFieldName: 'StartDate' },
|
|
440
|
+
{ SourceFieldName: 'EndDate', DestinationFieldName: 'EndDate' },
|
|
441
|
+
];
|
|
442
|
+
default:
|
|
443
|
+
return [];
|
|
444
|
+
}
|
|
445
|
+
}
|
|
446
|
+
// ─────────────────────────────────────────────────────────────────
|
|
447
|
+
// Private helpers
|
|
448
|
+
// ─────────────────────────────────────────────────────────────────
|
|
449
|
+
// ── Runtime discovery (Business Objects + field metadata) ───────
|
|
450
|
+
async DiscoverEntityDefinitions(companyIntegration, contextUser) {
|
|
451
|
+
const auth = (await this.Authenticate(companyIntegration, contextUser));
|
|
452
|
+
const url = `${auth.BaseUrl}/EntityDefinition?Limit=500`;
|
|
453
|
+
const headers = this.BuildHeaders(auth);
|
|
454
|
+
const response = await this.MakeHTTPRequest(auth, url, 'GET', headers);
|
|
455
|
+
if (response.Status < 200 || response.Status >= 300)
|
|
456
|
+
return [];
|
|
457
|
+
const records = this.NormalizeResponse(response.Body, 'Items');
|
|
458
|
+
return records
|
|
459
|
+
.filter(d => !!d.Name)
|
|
460
|
+
.map(d => ({
|
|
461
|
+
Name: String(d.Name),
|
|
462
|
+
Label: d.DisplayName ?? String(d.Name),
|
|
463
|
+
Description: d.Description ?? 'Tenant-defined Business Object (runtime-discovered)',
|
|
464
|
+
SupportsIncrementalSync: true,
|
|
465
|
+
SupportsWrite: false,
|
|
466
|
+
}));
|
|
467
|
+
}
|
|
468
|
+
async DiscoverFieldsFromLiveAPI(companyIntegration, objectName, contextUser) {
|
|
469
|
+
const auth = (await this.Authenticate(companyIntegration, contextUser));
|
|
470
|
+
const path = this.ResolveObjectPath(companyIntegration.IntegrationID, objectName);
|
|
471
|
+
const url = `${auth.BaseUrl}${path}/metadata`;
|
|
472
|
+
const headers = this.BuildHeaders(auth);
|
|
473
|
+
const response = await this.MakeHTTPRequest(auth, url, 'GET', headers);
|
|
474
|
+
if (response.Status < 200 || response.Status >= 300)
|
|
475
|
+
return [];
|
|
476
|
+
const meta = response.Body;
|
|
477
|
+
const propDefs = meta?.PropertyDefinitions ?? [];
|
|
478
|
+
return propDefs
|
|
479
|
+
.filter(p => !!p.Name)
|
|
480
|
+
.map(p => this.PropertyDefinitionToFieldSchema(p));
|
|
481
|
+
}
|
|
482
|
+
PropertyDefinitionToFieldSchema(prop) {
|
|
483
|
+
const rawType = (prop.PropertyType ?? prop.PropertyTypeData?.Name ?? 'string').toLowerCase();
|
|
484
|
+
const genericType = IMIS_TYPE_MAP[rawType] ?? 'string';
|
|
485
|
+
return {
|
|
486
|
+
Name: String(prop.Name),
|
|
487
|
+
Label: prop.DisplayName ?? String(prop.Name),
|
|
488
|
+
Description: prop.Description,
|
|
489
|
+
DataType: genericType,
|
|
490
|
+
IsRequired: !!prop.IsRequired,
|
|
491
|
+
// Provable-only: the source explicitly distinguishes PK; only set PK when stated.
|
|
492
|
+
IsPrimaryKey: prop.IsPrimaryKey === true ? true : undefined,
|
|
493
|
+
IsUniqueKey: !!prop.IsPrimaryKey,
|
|
494
|
+
IsReadOnly: !!prop.IsReadOnly,
|
|
495
|
+
MaxLength: prop.MaxLength ?? undefined,
|
|
496
|
+
};
|
|
497
|
+
}
|
|
498
|
+
/**
|
|
499
|
+
* Merge live-discovered fields with the Declared baseline. Live-only fields are appended;
|
|
500
|
+
* Declared fields win on matching names (the curated MJ schema is authoritative for the baseline).
|
|
501
|
+
*/
|
|
502
|
+
MergeFieldSchemas(declared, live) {
|
|
503
|
+
const declaredNames = new Set(declared.map(f => f.Name.toLowerCase()));
|
|
504
|
+
const liveOnly = live.filter(f => !declaredNames.has(f.Name.toLowerCase()));
|
|
505
|
+
return [...declared, ...liveOnly];
|
|
506
|
+
}
|
|
507
|
+
/**
|
|
508
|
+
* Resolve an object name to its API path. Prefers the configured APIPath from IntegrationObject
|
|
509
|
+
* metadata; falls back to `/{ObjectName}` for runtime-discovered Business Objects.
|
|
510
|
+
*/
|
|
511
|
+
ResolveObjectPath(integrationID, objectName) {
|
|
512
|
+
try {
|
|
513
|
+
const obj = this.GetCachedObject(integrationID, objectName);
|
|
514
|
+
if (obj.APIPath) {
|
|
515
|
+
return obj.APIPath.startsWith('/') ? obj.APIPath : `/${obj.APIPath}`;
|
|
516
|
+
}
|
|
517
|
+
}
|
|
518
|
+
catch {
|
|
519
|
+
// Not in Declared metadata — a runtime-discovered Business Object.
|
|
520
|
+
}
|
|
521
|
+
return `/${objectName}`;
|
|
522
|
+
}
|
|
523
|
+
// ── Config parsing ──────────────────────────────────────────────
|
|
524
|
+
/**
|
|
525
|
+
* Parses the connection config, preferring the attached MJ Credential over the raw Configuration
|
|
526
|
+
* JSON. Credential bytes are resolved at runtime — never at build.
|
|
527
|
+
*/
|
|
528
|
+
async ParseConfig(companyIntegration, contextUser) {
|
|
529
|
+
if (companyIntegration.CredentialID) {
|
|
530
|
+
return this.ParseConfigFromCredential(companyIntegration.CredentialID, contextUser);
|
|
531
|
+
}
|
|
532
|
+
if (companyIntegration.Configuration) {
|
|
533
|
+
return this.ValidateConfig(JSON.parse(companyIntegration.Configuration));
|
|
534
|
+
}
|
|
535
|
+
throw new Error('iMIS connector requires either CredentialID or Configuration JSON');
|
|
536
|
+
}
|
|
537
|
+
/** Loads the config from the MJ: Credentials entity Values JSON. */
|
|
538
|
+
async ParseConfigFromCredential(credentialID, contextUser, provider) {
|
|
539
|
+
const md = provider ?? new Metadata();
|
|
540
|
+
const cred = await md.GetEntityObject('MJ: Credentials', contextUser);
|
|
541
|
+
const loaded = await cred.Load(credentialID);
|
|
542
|
+
if (!loaded || !cred.Values) {
|
|
543
|
+
throw new Error('iMIS credential could not be loaded or has no Values JSON');
|
|
544
|
+
}
|
|
545
|
+
return this.ValidateConfig(JSON.parse(cred.Values));
|
|
546
|
+
}
|
|
547
|
+
/** Validates the parsed config + applies defaults. Field names are case-insensitive. */
|
|
548
|
+
ValidateConfig(raw) {
|
|
549
|
+
if (!raw || typeof raw !== 'object') {
|
|
550
|
+
throw new Error('iMIS configuration is not a valid object');
|
|
551
|
+
}
|
|
552
|
+
const obj = raw;
|
|
553
|
+
const getStr = (...keys) => {
|
|
554
|
+
for (const key of keys) {
|
|
555
|
+
const lower = key.toLowerCase();
|
|
556
|
+
for (const [k, v] of Object.entries(obj)) {
|
|
557
|
+
if (k.toLowerCase() === lower && typeof v === 'string' && v.length > 0)
|
|
558
|
+
return v;
|
|
559
|
+
}
|
|
560
|
+
}
|
|
561
|
+
return undefined;
|
|
562
|
+
};
|
|
563
|
+
const getNum = (...keys) => {
|
|
564
|
+
for (const key of keys) {
|
|
565
|
+
const lower = key.toLowerCase();
|
|
566
|
+
for (const [k, v] of Object.entries(obj)) {
|
|
567
|
+
if (k.toLowerCase() === lower && typeof v === 'number')
|
|
568
|
+
return v;
|
|
569
|
+
}
|
|
570
|
+
}
|
|
571
|
+
return undefined;
|
|
572
|
+
};
|
|
573
|
+
const baseURL = getStr('baseurl', 'base_url');
|
|
574
|
+
if (!baseURL)
|
|
575
|
+
throw new Error('iMIS configuration missing required field: BaseURL');
|
|
576
|
+
const username = getStr('username', 'user');
|
|
577
|
+
const password = getStr('password', 'pass');
|
|
578
|
+
if (!username)
|
|
579
|
+
throw new Error('iMIS configuration missing required field: Username');
|
|
580
|
+
if (!password)
|
|
581
|
+
throw new Error('iMIS configuration missing required field: Password');
|
|
582
|
+
return {
|
|
583
|
+
BaseURL: baseURL,
|
|
584
|
+
Username: username,
|
|
585
|
+
Password: password,
|
|
586
|
+
TokenURL: getStr('tokenurl', 'token_url'),
|
|
587
|
+
ClientId: getStr('clientid', 'client_id'),
|
|
588
|
+
ClientSecret: getStr('clientsecret', 'client_secret'),
|
|
589
|
+
MaxRetries: getNum('maxretries') ?? DEFAULT_MAX_RETRIES,
|
|
590
|
+
RequestTimeoutMs: getNum('requesttimeoutms') ?? DEFAULT_REQUEST_TIMEOUT_MS,
|
|
591
|
+
MinRequestIntervalMs: getNum('minrequestintervalms') ?? DEFAULT_MIN_REQUEST_INTERVAL_MS,
|
|
592
|
+
};
|
|
593
|
+
}
|
|
594
|
+
/** Resolves the API base URL from the credential's BaseURL (never a hardcoded host). */
|
|
595
|
+
ResolveBaseUrl(config) {
|
|
596
|
+
return config.BaseURL.replace(/\/+$/, '');
|
|
597
|
+
}
|
|
598
|
+
/**
|
|
599
|
+
* Resolves the token endpoint: credential TokenURL override, else `{base}/token`. iMIS issues
|
|
600
|
+
* tokens at the host root (not under /api/), so a trailing /api segment is stripped.
|
|
601
|
+
*/
|
|
602
|
+
ResolveTokenURL(config, baseUrl) {
|
|
603
|
+
if (config.TokenURL && config.TokenURL.length > 0)
|
|
604
|
+
return config.TokenURL;
|
|
605
|
+
const root = baseUrl.replace(/\/api$/i, '');
|
|
606
|
+
return `${root}${DEFAULT_TOKEN_PATH}`;
|
|
607
|
+
}
|
|
608
|
+
// ── Watermark helper ────────────────────────────────────────────
|
|
609
|
+
ComputeNewWatermark(records, current) {
|
|
610
|
+
const candidateKeys = ['UpdatedOn', 'UpdateDate', 'ModifiedOn', 'LastUpdatedAt'];
|
|
611
|
+
let latest;
|
|
612
|
+
for (const record of records) {
|
|
613
|
+
for (const key of candidateKeys) {
|
|
614
|
+
const value = record.Fields[key];
|
|
615
|
+
if (typeof value === 'string' && (!latest || value > latest))
|
|
616
|
+
latest = value;
|
|
617
|
+
}
|
|
618
|
+
}
|
|
619
|
+
if (!latest)
|
|
620
|
+
return current ?? undefined;
|
|
621
|
+
if (current && latest <= current)
|
|
622
|
+
return current;
|
|
623
|
+
return latest;
|
|
624
|
+
}
|
|
625
|
+
// ── HTTP helpers ────────────────────────────────────────────────
|
|
626
|
+
/** Throttle to respect the configured minimum request interval. */
|
|
627
|
+
async ThrottleIfNeeded(minIntervalMs) {
|
|
628
|
+
const elapsed = Date.now() - this.lastRequestTime;
|
|
629
|
+
if (elapsed < minIntervalMs)
|
|
630
|
+
await this.Sleep(minIntervalMs - elapsed);
|
|
631
|
+
}
|
|
632
|
+
/** Parses a Retry-After header (seconds or http-date) into ms, if present. */
|
|
633
|
+
RetryAfterMs(response) {
|
|
634
|
+
const header = response.headers.get('retry-after');
|
|
635
|
+
if (!header)
|
|
636
|
+
return undefined;
|
|
637
|
+
const asSeconds = Number(header);
|
|
638
|
+
if (!isNaN(asSeconds))
|
|
639
|
+
return Math.max(0, asSeconds * 1_000);
|
|
640
|
+
const asDate = new Date(header).getTime();
|
|
641
|
+
if (!isNaN(asDate))
|
|
642
|
+
return Math.max(0, asDate - Date.now());
|
|
643
|
+
return undefined;
|
|
644
|
+
}
|
|
645
|
+
/** Exponential backoff delay for retry attempts (capped at 15s). */
|
|
646
|
+
BackoffDelay(attempt) {
|
|
647
|
+
return Math.min(1_000 * Math.pow(2, attempt), 15_000);
|
|
648
|
+
}
|
|
649
|
+
/** Checks whether an error is transient (network/timeout). */
|
|
650
|
+
IsTransientNetworkError(err) {
|
|
651
|
+
if (!(err instanceof Error))
|
|
652
|
+
return false;
|
|
653
|
+
const msg = err.message.toLowerCase();
|
|
654
|
+
return msg.includes('timeout') || msg.includes('abort') ||
|
|
655
|
+
msg.includes('econnreset') || msg.includes('econnrefused') ||
|
|
656
|
+
msg.includes('fetch failed');
|
|
657
|
+
}
|
|
658
|
+
/** Builds the normalized RESTResponse from a fetch Response. */
|
|
659
|
+
async BuildRESTResponse(response) {
|
|
660
|
+
const headers = {};
|
|
661
|
+
response.headers.forEach((v, k) => { headers[k.toLowerCase()] = v; });
|
|
662
|
+
const text = await response.text();
|
|
663
|
+
let body = null;
|
|
664
|
+
if (text.length > 0) {
|
|
665
|
+
try {
|
|
666
|
+
body = JSON.parse(text);
|
|
667
|
+
}
|
|
668
|
+
catch {
|
|
669
|
+
body = text;
|
|
670
|
+
}
|
|
671
|
+
}
|
|
672
|
+
return { Status: response.status, Body: body, Headers: headers };
|
|
673
|
+
}
|
|
674
|
+
ExtractServerVersion(headers) {
|
|
675
|
+
return headers['x-imis-version'] ?? headers['server'] ?? 'iMIS REST API';
|
|
676
|
+
}
|
|
677
|
+
/** True when a body is the iMIS PagedResult envelope (Items[] + Offset/Limit/HasNext). */
|
|
678
|
+
IsPagedResult(body) {
|
|
679
|
+
if (body == null || typeof body !== 'object' || Array.isArray(body))
|
|
680
|
+
return false;
|
|
681
|
+
const record = body;
|
|
682
|
+
return Array.isArray(record['Items']) && ('Offset' in record || 'Limit' in record || 'HasNext' in record);
|
|
683
|
+
}
|
|
684
|
+
/** Promise-wrapped setTimeout. */
|
|
685
|
+
Sleep(ms) {
|
|
686
|
+
return new Promise(resolve => setTimeout(resolve, ms));
|
|
687
|
+
}
|
|
688
|
+
};
|
|
689
|
+
IMISConnector = __decorate([
|
|
690
|
+
RegisterClass(BaseIntegrationConnector, '@memberjunction/connector-imis')
|
|
691
|
+
], IMISConnector);
|
|
692
|
+
export { IMISConnector };
|
|
693
|
+
/** Tree-shaking prevention — import and call from the package entry point. */
|
|
694
|
+
export function LoadIMISConnector() { }
|
|
695
|
+
//# sourceMappingURL=IMISConnector.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"IMISConnector.js","sourceRoot":"","sources":["../src/IMISConnector.ts"],"names":[],"mappings":"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"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
export * from './IMISConnector.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 './IMISConnector.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,oBAAoB,CAAC;AAEnC;oGACoG;AACpG,MAAM,UAAU,iBAAiB,KAAiD,CAAC"}
|
package/package.json
ADDED
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@memberjunction/connector-imis",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "MemberJunction IMIS 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
|
+
"@memberjunction/integration-engine-base": ">=5.42.0 <6.0.0"
|
|
23
|
+
},
|
|
24
|
+
"dependencies": {},
|
|
25
|
+
"devDependencies": {
|
|
26
|
+
"@types/node": "24.10.11",
|
|
27
|
+
"tsc-alias": "^1.8.16",
|
|
28
|
+
"typescript": "^5.9.3",
|
|
29
|
+
"vitest": "^4.0.18",
|
|
30
|
+
"@memberjunction/core": "^5.42.0",
|
|
31
|
+
"@memberjunction/core-entities": "^5.42.0",
|
|
32
|
+
"@memberjunction/global": "^5.42.0",
|
|
33
|
+
"@memberjunction/integration-engine": "^5.42.0",
|
|
34
|
+
"@memberjunction/integration-engine-base": "^5.42.0"
|
|
35
|
+
},
|
|
36
|
+
"repository": {
|
|
37
|
+
"type": "git",
|
|
38
|
+
"url": "https://github.com/MemberJunction/Integrations"
|
|
39
|
+
}
|
|
40
|
+
}
|