@memberjunction/integration-engine 0.0.1 → 5.8.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/README.md +142 -28
- package/dist/BaseIntegrationConnector.d.ts +142 -0
- package/dist/BaseIntegrationConnector.d.ts.map +1 -0
- package/dist/BaseIntegrationConnector.js +91 -0
- package/dist/BaseIntegrationConnector.js.map +1 -0
- package/dist/ConnectorFactory.d.ts +30 -0
- package/dist/ConnectorFactory.d.ts.map +1 -0
- package/dist/ConnectorFactory.js +50 -0
- package/dist/ConnectorFactory.js.map +1 -0
- package/dist/FieldMappingEngine.d.ts +94 -0
- package/dist/FieldMappingEngine.d.ts.map +1 -0
- package/dist/FieldMappingEngine.js +250 -0
- package/dist/FieldMappingEngine.js.map +1 -0
- package/dist/IntegrationOrchestrator.d.ts +126 -0
- package/dist/IntegrationOrchestrator.d.ts.map +1 -0
- package/dist/IntegrationOrchestrator.js +548 -0
- package/dist/IntegrationOrchestrator.js.map +1 -0
- package/dist/MatchEngine.d.ts +50 -0
- package/dist/MatchEngine.d.ts.map +1 -0
- package/dist/MatchEngine.js +124 -0
- package/dist/MatchEngine.js.map +1 -0
- package/dist/RetryRunner.d.ts +27 -0
- package/dist/RetryRunner.d.ts.map +1 -0
- package/dist/RetryRunner.js +59 -0
- package/dist/RetryRunner.js.map +1 -0
- package/dist/WatermarkService.d.ts +44 -0
- package/dist/WatermarkService.d.ts.map +1 -0
- package/dist/WatermarkService.js +97 -0
- package/dist/WatermarkService.js.map +1 -0
- package/dist/entity-types.d.ts +90 -0
- package/dist/entity-types.d.ts.map +1 -0
- package/dist/entity-types.js +13 -0
- package/dist/entity-types.js.map +1 -0
- package/dist/index.d.ts +14 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +16 -0
- package/dist/index.js.map +1 -0
- package/dist/transforms.d.ts +75 -0
- package/dist/transforms.d.ts.map +1 -0
- package/dist/transforms.js +2 -0
- package/dist/transforms.js.map +1 -0
- package/dist/types.d.ts +192 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +44 -0
- package/dist/types.js.map +1 -0
- package/package.json +29 -7
|
@@ -0,0 +1,548 @@
|
|
|
1
|
+
import { CompositeKey, Metadata, RunView } from '@memberjunction/core';
|
|
2
|
+
import { ClassifyError } from './types.js';
|
|
3
|
+
import { ConnectorFactory } from './ConnectorFactory.js';
|
|
4
|
+
import { FieldMappingEngine } from './FieldMappingEngine.js';
|
|
5
|
+
import { MatchEngine } from './MatchEngine.js';
|
|
6
|
+
import { WatermarkService } from './WatermarkService.js';
|
|
7
|
+
/** Default batch size for fetching records from external systems */
|
|
8
|
+
const DEFAULT_BATCH_SIZE = 200;
|
|
9
|
+
/**
|
|
10
|
+
* Top-level orchestrator that runs an end-to-end integration sync.
|
|
11
|
+
* Coordinates connector, field mapping, match resolution, and entity persistence.
|
|
12
|
+
*/
|
|
13
|
+
export class IntegrationOrchestrator {
|
|
14
|
+
constructor() {
|
|
15
|
+
this.fieldMappingEngine = new FieldMappingEngine();
|
|
16
|
+
this.matchEngine = new MatchEngine();
|
|
17
|
+
this.watermarkService = new WatermarkService();
|
|
18
|
+
/** Configurable maximum batch size. Connector batches exceeding this are truncated. */
|
|
19
|
+
this.MaxBatchSize = DEFAULT_BATCH_SIZE;
|
|
20
|
+
}
|
|
21
|
+
/** In-process lock map to prevent concurrent syncs for the same CompanyIntegration */
|
|
22
|
+
static { this.activeSyncs = new Map(); }
|
|
23
|
+
/**
|
|
24
|
+
* Executes a full sync run for a company integration.
|
|
25
|
+
* If a sync is already running for the same integration, waits for it to finish
|
|
26
|
+
* and returns the existing result (concurrency lock).
|
|
27
|
+
*
|
|
28
|
+
* @param companyIntegrationID - ID of the CompanyIntegration to sync
|
|
29
|
+
* @param contextUser - User context for all data operations
|
|
30
|
+
* @param triggerType - What triggered this sync (defaults to 'Manual')
|
|
31
|
+
* @param onProgress - Optional callback invoked with progress updates during sync
|
|
32
|
+
* @param onNotification - Optional callback invoked with a notification when the sync completes or fails
|
|
33
|
+
* @returns Aggregate sync result with record counts and errors
|
|
34
|
+
*/
|
|
35
|
+
async RunSync(companyIntegrationID, contextUser, triggerType = 'Manual', onProgress, onNotification) {
|
|
36
|
+
const lockKey = companyIntegrationID.toLowerCase();
|
|
37
|
+
const existing = IntegrationOrchestrator.activeSyncs.get(lockKey);
|
|
38
|
+
if (existing) {
|
|
39
|
+
console.warn(`[IntegrationOrchestrator] Sync already running for ${lockKey}, waiting...`);
|
|
40
|
+
return existing;
|
|
41
|
+
}
|
|
42
|
+
const syncPromise = this.executeSyncInternal(companyIntegrationID, contextUser, triggerType, onProgress, onNotification);
|
|
43
|
+
IntegrationOrchestrator.activeSyncs.set(lockKey, syncPromise);
|
|
44
|
+
try {
|
|
45
|
+
return await syncPromise;
|
|
46
|
+
}
|
|
47
|
+
finally {
|
|
48
|
+
IntegrationOrchestrator.activeSyncs.delete(lockKey);
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* Internal sync execution method. Contains the full orchestration logic.
|
|
53
|
+
*/
|
|
54
|
+
async executeSyncInternal(companyIntegrationID, contextUser, triggerType, onProgress, onNotification) {
|
|
55
|
+
const config = await this.LoadRunConfiguration(companyIntegrationID, contextUser);
|
|
56
|
+
const run = await this.CreateRunRecord(config.companyIntegration, triggerType, contextUser);
|
|
57
|
+
try {
|
|
58
|
+
const result = await this.ExecuteEntityMaps(config, run, contextUser, onProgress);
|
|
59
|
+
await this.FinalizeRun(run, result, contextUser, onNotification);
|
|
60
|
+
return result;
|
|
61
|
+
}
|
|
62
|
+
catch (err) {
|
|
63
|
+
await this.FailRun(run, err, contextUser, onNotification);
|
|
64
|
+
throw err;
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* Loads all configuration needed for a sync run.
|
|
69
|
+
*/
|
|
70
|
+
async LoadRunConfiguration(companyIntegrationID, contextUser) {
|
|
71
|
+
const rv = new RunView();
|
|
72
|
+
const [ciResult, entityMapsResult, integrationsResult, sourceTypesResult] = await rv.RunViews([
|
|
73
|
+
{
|
|
74
|
+
EntityName: 'MJ: Company Integrations',
|
|
75
|
+
ExtraFilter: `ID='${companyIntegrationID}'`,
|
|
76
|
+
MaxRows: 1,
|
|
77
|
+
ResultType: 'entity_object',
|
|
78
|
+
},
|
|
79
|
+
{
|
|
80
|
+
EntityName: 'MJ: Company Integration Entity Maps',
|
|
81
|
+
ExtraFilter: `CompanyIntegrationID='${companyIntegrationID}' AND SyncEnabled=1 AND Status='Active'`,
|
|
82
|
+
OrderBy: 'Priority ASC',
|
|
83
|
+
ResultType: 'entity_object',
|
|
84
|
+
},
|
|
85
|
+
{
|
|
86
|
+
EntityName: 'MJ: Integrations',
|
|
87
|
+
ExtraFilter: '',
|
|
88
|
+
ResultType: 'entity_object',
|
|
89
|
+
},
|
|
90
|
+
{
|
|
91
|
+
EntityName: 'MJ: Integration Source Types',
|
|
92
|
+
ExtraFilter: `Status='Active'`,
|
|
93
|
+
ResultType: 'entity_object',
|
|
94
|
+
},
|
|
95
|
+
], contextUser);
|
|
96
|
+
const companyIntegration = ciResult.Results[0];
|
|
97
|
+
if (!companyIntegration) {
|
|
98
|
+
throw new Error(`CompanyIntegration not found: ${companyIntegrationID}`);
|
|
99
|
+
}
|
|
100
|
+
const integration = integrationsResult.Results
|
|
101
|
+
.find(i => i.Get('ID') === companyIntegration.IntegrationID);
|
|
102
|
+
if (!integration) {
|
|
103
|
+
throw new Error(`Integration not found for CompanyIntegration: ${companyIntegrationID}`);
|
|
104
|
+
}
|
|
105
|
+
const connector = ConnectorFactory.Resolve(integration, sourceTypesResult.Results);
|
|
106
|
+
return {
|
|
107
|
+
companyIntegration,
|
|
108
|
+
entityMaps: entityMapsResult.Results,
|
|
109
|
+
integration,
|
|
110
|
+
connector,
|
|
111
|
+
};
|
|
112
|
+
}
|
|
113
|
+
/**
|
|
114
|
+
* Creates a new CompanyIntegrationRun record to track this sync.
|
|
115
|
+
*/
|
|
116
|
+
async CreateRunRecord(companyIntegration, triggerType, contextUser) {
|
|
117
|
+
const md = new Metadata();
|
|
118
|
+
const run = await md.GetEntityObject('MJ: Company Integration Runs', contextUser);
|
|
119
|
+
run.NewRecord();
|
|
120
|
+
run.CompanyIntegrationID = companyIntegration.Get('ID');
|
|
121
|
+
run.RunByUserID = contextUser.ID;
|
|
122
|
+
run.StartedAt = new Date();
|
|
123
|
+
run.Status = 'In Progress';
|
|
124
|
+
run.TotalRecords = 0;
|
|
125
|
+
run.ConfigData = JSON.stringify({ triggerType });
|
|
126
|
+
const saved = await run.Save();
|
|
127
|
+
if (!saved) {
|
|
128
|
+
throw new Error('Failed to create CompanyIntegrationRun record');
|
|
129
|
+
}
|
|
130
|
+
return run;
|
|
131
|
+
}
|
|
132
|
+
/**
|
|
133
|
+
* Processes all entity maps, aggregating results with progress tracking.
|
|
134
|
+
*/
|
|
135
|
+
async ExecuteEntityMaps(config, run, contextUser, onProgress) {
|
|
136
|
+
const aggregate = {
|
|
137
|
+
Success: true,
|
|
138
|
+
RecordsProcessed: 0,
|
|
139
|
+
RecordsCreated: 0,
|
|
140
|
+
RecordsUpdated: 0,
|
|
141
|
+
RecordsDeleted: 0,
|
|
142
|
+
RecordsErrored: 0,
|
|
143
|
+
RecordsSkipped: 0,
|
|
144
|
+
Errors: [],
|
|
145
|
+
};
|
|
146
|
+
const totalMaps = config.entityMaps.length;
|
|
147
|
+
for (let i = 0; i < totalMaps; i++) {
|
|
148
|
+
const entityMap = config.entityMaps[i];
|
|
149
|
+
const mapResult = await this.ProcessSingleEntityMap(config, entityMap, run, contextUser, i, totalMaps, onProgress);
|
|
150
|
+
this.MergeResult(aggregate, mapResult);
|
|
151
|
+
}
|
|
152
|
+
return aggregate;
|
|
153
|
+
}
|
|
154
|
+
/**
|
|
155
|
+
* Processes a single entity map: fetch → map → match → validate → apply.
|
|
156
|
+
*/
|
|
157
|
+
async ProcessSingleEntityMap(config, entityMap, run, contextUser, entityMapIndex, totalEntityMaps, onProgress) {
|
|
158
|
+
const entityMapID = entityMap.Get('ID');
|
|
159
|
+
const fieldMaps = await this.LoadFieldMaps(entityMapID, contextUser);
|
|
160
|
+
const watermark = await this.watermarkService.Load(entityMapID, contextUser);
|
|
161
|
+
// A6: Validate watermark before using it
|
|
162
|
+
let initialWatermark = watermark?.WatermarkValue ?? null;
|
|
163
|
+
if (initialWatermark && watermark) {
|
|
164
|
+
const watermarkType = (watermark.WatermarkType ?? 'Timestamp');
|
|
165
|
+
if (!this.watermarkService.ValidateWatermark(initialWatermark, watermarkType)) {
|
|
166
|
+
console.warn(`[IntegrationOrchestrator] Invalid watermark '${initialWatermark}' for EntityMap ${entityMap.Get('ID')}, resetting to full fetch`);
|
|
167
|
+
initialWatermark = null;
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
const result = {
|
|
171
|
+
Success: true,
|
|
172
|
+
RecordsProcessed: 0,
|
|
173
|
+
RecordsCreated: 0,
|
|
174
|
+
RecordsUpdated: 0,
|
|
175
|
+
RecordsDeleted: 0,
|
|
176
|
+
RecordsErrored: 0,
|
|
177
|
+
RecordsSkipped: 0,
|
|
178
|
+
Errors: [],
|
|
179
|
+
};
|
|
180
|
+
let hasMore = true;
|
|
181
|
+
let currentWatermark = initialWatermark;
|
|
182
|
+
let recordsInMap = 0;
|
|
183
|
+
while (hasMore) {
|
|
184
|
+
const ctx = {
|
|
185
|
+
CompanyIntegration: config.companyIntegration,
|
|
186
|
+
ObjectName: entityMap.ExternalObjectName,
|
|
187
|
+
WatermarkValue: currentWatermark,
|
|
188
|
+
BatchSize: this.MaxBatchSize,
|
|
189
|
+
ContextUser: contextUser,
|
|
190
|
+
};
|
|
191
|
+
const batch = await config.connector.FetchChanges(ctx);
|
|
192
|
+
// A7: Batch size enforcement — truncate if connector returns more than MaxBatchSize
|
|
193
|
+
let batchRecords = batch.Records;
|
|
194
|
+
if (batchRecords.length > this.MaxBatchSize) {
|
|
195
|
+
console.warn(`[IntegrationOrchestrator] Connector returned ${batchRecords.length} records, exceeding MaxBatchSize of ${this.MaxBatchSize}. Truncating.`);
|
|
196
|
+
batchRecords = batchRecords.slice(0, this.MaxBatchSize);
|
|
197
|
+
}
|
|
198
|
+
const mapped = this.fieldMappingEngine.Apply(batchRecords, fieldMaps, entityMap.Entity);
|
|
199
|
+
const resolved = await this.matchEngine.Resolve(mapped, entityMap, fieldMaps, contextUser);
|
|
200
|
+
await this.ApplyRecords(resolved, config.companyIntegration, entityMap, result, contextUser);
|
|
201
|
+
recordsInMap += batchRecords.length;
|
|
202
|
+
// A8: Progress tracking
|
|
203
|
+
if (onProgress) {
|
|
204
|
+
this.emitProgress(onProgress, entityMapIndex, totalEntityMaps, recordsInMap, recordsInMap);
|
|
205
|
+
}
|
|
206
|
+
if (batch.NewWatermarkValue) {
|
|
207
|
+
currentWatermark = batch.NewWatermarkValue;
|
|
208
|
+
}
|
|
209
|
+
hasMore = batch.HasMore;
|
|
210
|
+
}
|
|
211
|
+
if (currentWatermark) {
|
|
212
|
+
await this.watermarkService.Update(entityMapID, currentWatermark, contextUser);
|
|
213
|
+
result.WatermarkAfter = currentWatermark;
|
|
214
|
+
}
|
|
215
|
+
await this.CreateRunDetail(run, entityMap, result, contextUser);
|
|
216
|
+
return result;
|
|
217
|
+
}
|
|
218
|
+
/**
|
|
219
|
+
* Emits a progress update via the callback.
|
|
220
|
+
*/
|
|
221
|
+
emitProgress(onProgress, entityMapIndex, totalEntityMaps, processedInMap, totalInMap) {
|
|
222
|
+
const mapProgress = totalEntityMaps > 0 ? entityMapIndex / totalEntityMaps : 0;
|
|
223
|
+
const inMapProgress = totalInMap > 0 ? processedInMap / totalInMap : 0;
|
|
224
|
+
const overallPercent = Math.round((mapProgress + inMapProgress / totalEntityMaps) * 100);
|
|
225
|
+
const progress = {
|
|
226
|
+
EntityMapIndex: entityMapIndex,
|
|
227
|
+
TotalEntityMaps: totalEntityMaps,
|
|
228
|
+
RecordsProcessedInCurrentMap: processedInMap,
|
|
229
|
+
TotalRecordsInCurrentMap: totalInMap,
|
|
230
|
+
PercentComplete: Math.min(overallPercent, 100),
|
|
231
|
+
};
|
|
232
|
+
onProgress(progress);
|
|
233
|
+
}
|
|
234
|
+
/**
|
|
235
|
+
* Loads active field maps for a given entity map.
|
|
236
|
+
*/
|
|
237
|
+
async LoadFieldMaps(entityMapID, contextUser) {
|
|
238
|
+
const rv = new RunView();
|
|
239
|
+
const result = await rv.RunView({
|
|
240
|
+
EntityName: 'MJ: Company Integration Field Maps',
|
|
241
|
+
ExtraFilter: `EntityMapID='${entityMapID}' AND Status='Active'`,
|
|
242
|
+
OrderBy: 'Priority ASC',
|
|
243
|
+
ResultType: 'entity_object',
|
|
244
|
+
}, contextUser);
|
|
245
|
+
return result.Success ? result.Results : [];
|
|
246
|
+
}
|
|
247
|
+
/**
|
|
248
|
+
* Applies resolved records to MJ, handling each individually for error isolation.
|
|
249
|
+
*/
|
|
250
|
+
async ApplyRecords(records, companyIntegration, entityMap, result, contextUser) {
|
|
251
|
+
for (const record of records) {
|
|
252
|
+
result.RecordsProcessed++;
|
|
253
|
+
try {
|
|
254
|
+
await this.ApplySingleRecord(record, companyIntegration, entityMap, result, contextUser);
|
|
255
|
+
}
|
|
256
|
+
catch (err) {
|
|
257
|
+
const classified = ClassifyError(err);
|
|
258
|
+
result.RecordsErrored++;
|
|
259
|
+
result.Errors.push({
|
|
260
|
+
ExternalID: record.ExternalRecord.ExternalID,
|
|
261
|
+
ChangeType: record.ChangeType,
|
|
262
|
+
ErrorMessage: err instanceof Error ? err.message : String(err),
|
|
263
|
+
ErrorCode: classified.Code,
|
|
264
|
+
Severity: classified.Severity,
|
|
265
|
+
ExternalRecord: record.ExternalRecord,
|
|
266
|
+
});
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
/**
|
|
271
|
+
* Applies a single record change (Create, Update, Delete, or Skip).
|
|
272
|
+
*/
|
|
273
|
+
async ApplySingleRecord(record, companyIntegration, entityMap, result, contextUser) {
|
|
274
|
+
switch (record.ChangeType) {
|
|
275
|
+
case 'Create':
|
|
276
|
+
await this.CreateRecord(record, companyIntegration, entityMap, contextUser);
|
|
277
|
+
result.RecordsCreated++;
|
|
278
|
+
break;
|
|
279
|
+
case 'Update':
|
|
280
|
+
await this.UpdateRecord(record, contextUser);
|
|
281
|
+
result.RecordsUpdated++;
|
|
282
|
+
break;
|
|
283
|
+
case 'Delete':
|
|
284
|
+
await this.DeleteRecord(record, entityMap, contextUser);
|
|
285
|
+
result.RecordsDeleted++;
|
|
286
|
+
break;
|
|
287
|
+
case 'Skip':
|
|
288
|
+
result.RecordsSkipped++;
|
|
289
|
+
break;
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
/**
|
|
293
|
+
* Creates a new MJ record with pre-write validation and saves a record map entry.
|
|
294
|
+
*/
|
|
295
|
+
async CreateRecord(record, companyIntegration, entityMap, contextUser) {
|
|
296
|
+
const md = new Metadata();
|
|
297
|
+
const entity = await md.GetEntityObject(record.MJEntityName, contextUser);
|
|
298
|
+
entity.NewRecord();
|
|
299
|
+
this.SetEntityFields(entity, record.MappedFields);
|
|
300
|
+
// A5: Pre-write validation
|
|
301
|
+
this.validateEntity(entity, record.MJEntityName);
|
|
302
|
+
const saved = await entity.Save();
|
|
303
|
+
if (!saved) {
|
|
304
|
+
throw new Error(`Failed to create ${record.MJEntityName} record`);
|
|
305
|
+
}
|
|
306
|
+
await this.SaveRecordMap(companyIntegration.Get('ID'), record.ExternalRecord.ExternalID, entityMap.EntityID, entity.Get('ID'), contextUser);
|
|
307
|
+
}
|
|
308
|
+
/**
|
|
309
|
+
* Updates an existing MJ record with pre-write validation.
|
|
310
|
+
*/
|
|
311
|
+
async UpdateRecord(record, contextUser) {
|
|
312
|
+
if (!record.MatchedMJRecordID) {
|
|
313
|
+
throw new Error('Cannot update record without MatchedMJRecordID');
|
|
314
|
+
}
|
|
315
|
+
const md = new Metadata();
|
|
316
|
+
const entity = await md.GetEntityObject(record.MJEntityName, contextUser);
|
|
317
|
+
const loaded = await entity.InnerLoad(this.BuildCompositeKey(record.MatchedMJRecordID));
|
|
318
|
+
if (!loaded) {
|
|
319
|
+
throw new Error(`Failed to load ${record.MJEntityName} record ${record.MatchedMJRecordID}`);
|
|
320
|
+
}
|
|
321
|
+
this.SetEntityFields(entity, record.MappedFields);
|
|
322
|
+
// A5: Pre-write validation
|
|
323
|
+
this.validateEntity(entity, record.MJEntityName);
|
|
324
|
+
const saved = await entity.Save();
|
|
325
|
+
if (!saved) {
|
|
326
|
+
throw new Error(`Failed to update ${record.MJEntityName} record ${record.MatchedMJRecordID}`);
|
|
327
|
+
}
|
|
328
|
+
}
|
|
329
|
+
/**
|
|
330
|
+
* Runs Validate() on an entity if the method exists.
|
|
331
|
+
* Throws a validation error with details if validation fails.
|
|
332
|
+
*/
|
|
333
|
+
validateEntity(entity, entityName) {
|
|
334
|
+
if (typeof entity.Validate !== 'function')
|
|
335
|
+
return;
|
|
336
|
+
const validationResult = entity.Validate();
|
|
337
|
+
if (validationResult && !validationResult.Success) {
|
|
338
|
+
const messages = validationResult.Errors?.map(e => e.Message).join('; ') ?? 'Validation failed';
|
|
339
|
+
throw new Error(`Validation failed for ${entityName}: ${messages}`);
|
|
340
|
+
}
|
|
341
|
+
}
|
|
342
|
+
/**
|
|
343
|
+
* Deletes (or soft-deletes) an MJ record based on the entity map's DeleteBehavior.
|
|
344
|
+
*/
|
|
345
|
+
async DeleteRecord(record, entityMap, contextUser) {
|
|
346
|
+
if (!record.MatchedMJRecordID) {
|
|
347
|
+
throw new Error('Cannot delete record without MatchedMJRecordID');
|
|
348
|
+
}
|
|
349
|
+
if (entityMap.DeleteBehavior === 'DoNothing')
|
|
350
|
+
return;
|
|
351
|
+
const md = new Metadata();
|
|
352
|
+
const entity = await md.GetEntityObject(record.MJEntityName, contextUser);
|
|
353
|
+
const loaded = await entity.InnerLoad(this.BuildCompositeKey(record.MatchedMJRecordID));
|
|
354
|
+
if (!loaded) {
|
|
355
|
+
throw new Error(`Failed to load ${record.MJEntityName} for deletion: ${record.MatchedMJRecordID}`);
|
|
356
|
+
}
|
|
357
|
+
const deleted = await entity.Delete();
|
|
358
|
+
if (!deleted) {
|
|
359
|
+
throw new Error(`Failed to delete ${record.MJEntityName} record ${record.MatchedMJRecordID}`);
|
|
360
|
+
}
|
|
361
|
+
}
|
|
362
|
+
/**
|
|
363
|
+
* Builds a CompositeKey for a single ID field.
|
|
364
|
+
*/
|
|
365
|
+
BuildCompositeKey(id) {
|
|
366
|
+
const key = new CompositeKey();
|
|
367
|
+
key.KeyValuePairs.push({ FieldName: 'ID', Value: id });
|
|
368
|
+
return key;
|
|
369
|
+
}
|
|
370
|
+
/**
|
|
371
|
+
* Sets fields on a BaseEntity instance from a field value map.
|
|
372
|
+
*/
|
|
373
|
+
SetEntityFields(entity, fields) {
|
|
374
|
+
for (const [fieldName, value] of Object.entries(fields)) {
|
|
375
|
+
entity.Set(fieldName, value);
|
|
376
|
+
}
|
|
377
|
+
}
|
|
378
|
+
/**
|
|
379
|
+
* Creates or updates a CompanyIntegrationRecordMap entry to track the external↔MJ mapping.
|
|
380
|
+
*/
|
|
381
|
+
async SaveRecordMap(companyIntegrationID, externalID, entityID, entityRecordID, contextUser) {
|
|
382
|
+
const md = new Metadata();
|
|
383
|
+
const recordMap = await md.GetEntityObject('MJ: Company Integration Record Maps', contextUser);
|
|
384
|
+
recordMap.NewRecord();
|
|
385
|
+
recordMap.CompanyIntegrationID = companyIntegrationID;
|
|
386
|
+
recordMap.ExternalSystemRecordID = externalID;
|
|
387
|
+
recordMap.EntityID = entityID;
|
|
388
|
+
recordMap.EntityRecordID = entityRecordID;
|
|
389
|
+
const saved = await recordMap.Save();
|
|
390
|
+
if (!saved) {
|
|
391
|
+
throw new Error(`Failed to save record map for external ID: ${externalID}`);
|
|
392
|
+
}
|
|
393
|
+
}
|
|
394
|
+
/**
|
|
395
|
+
* Creates a CompanyIntegrationRunDetail record for audit/reporting.
|
|
396
|
+
*/
|
|
397
|
+
async CreateRunDetail(run, entityMap, result, contextUser) {
|
|
398
|
+
const md = new Metadata();
|
|
399
|
+
const detail = await md.GetEntityObject('MJ: Company Integration Run Details', contextUser);
|
|
400
|
+
detail.NewRecord();
|
|
401
|
+
detail.CompanyIntegrationRunID = run.Get('ID');
|
|
402
|
+
detail.EntityID = entityMap.EntityID;
|
|
403
|
+
detail.RecordID = `Processed:${result.RecordsProcessed}`;
|
|
404
|
+
detail.Action = result.RecordsCreated > 0 ? 'INSERT' : 'UPDATE';
|
|
405
|
+
detail.IsSuccess = result.RecordsErrored === 0;
|
|
406
|
+
await detail.Save();
|
|
407
|
+
}
|
|
408
|
+
/**
|
|
409
|
+
* Merges an entity-map-level result into the aggregate result.
|
|
410
|
+
*/
|
|
411
|
+
MergeResult(aggregate, mapResult) {
|
|
412
|
+
aggregate.RecordsProcessed += mapResult.RecordsProcessed;
|
|
413
|
+
aggregate.RecordsCreated += mapResult.RecordsCreated;
|
|
414
|
+
aggregate.RecordsUpdated += mapResult.RecordsUpdated;
|
|
415
|
+
aggregate.RecordsDeleted += mapResult.RecordsDeleted;
|
|
416
|
+
aggregate.RecordsErrored += mapResult.RecordsErrored;
|
|
417
|
+
aggregate.RecordsSkipped += mapResult.RecordsSkipped;
|
|
418
|
+
aggregate.Errors.push(...mapResult.Errors);
|
|
419
|
+
if (mapResult.RecordsErrored > 0) {
|
|
420
|
+
aggregate.Success = false;
|
|
421
|
+
}
|
|
422
|
+
}
|
|
423
|
+
/**
|
|
424
|
+
* Finalizes a successful run with aggregate totals and emits a completion notification.
|
|
425
|
+
*/
|
|
426
|
+
async FinalizeRun(run, result, _contextUser, onNotification) {
|
|
427
|
+
run.EndedAt = new Date();
|
|
428
|
+
run.TotalRecords = result.RecordsProcessed;
|
|
429
|
+
run.Status = result.RecordsErrored > 0 ? 'Failed' : 'Success';
|
|
430
|
+
if (result.Errors.length > 0) {
|
|
431
|
+
run.ErrorLog = JSON.stringify(result.Errors.slice(0, 100));
|
|
432
|
+
}
|
|
433
|
+
await run.Save();
|
|
434
|
+
if (onNotification) {
|
|
435
|
+
const notification = this.buildCompletionNotification(run, result);
|
|
436
|
+
this.safeNotify(onNotification, notification);
|
|
437
|
+
}
|
|
438
|
+
}
|
|
439
|
+
/**
|
|
440
|
+
* Marks a run as failed after an unrecoverable error and emits a failure notification.
|
|
441
|
+
*/
|
|
442
|
+
async FailRun(run, err, _contextUser, onNotification) {
|
|
443
|
+
run.EndedAt = new Date();
|
|
444
|
+
run.Status = 'Failed';
|
|
445
|
+
run.ErrorLog = err instanceof Error ? err.message : String(err);
|
|
446
|
+
await run.Save();
|
|
447
|
+
if (onNotification) {
|
|
448
|
+
const failResult = {
|
|
449
|
+
Success: false,
|
|
450
|
+
RecordsProcessed: 0,
|
|
451
|
+
RecordsCreated: 0,
|
|
452
|
+
RecordsUpdated: 0,
|
|
453
|
+
RecordsDeleted: 0,
|
|
454
|
+
RecordsErrored: 0,
|
|
455
|
+
RecordsSkipped: 0,
|
|
456
|
+
Errors: [],
|
|
457
|
+
};
|
|
458
|
+
const notification = this.buildFailureNotification(run, err, failResult);
|
|
459
|
+
this.safeNotify(onNotification, notification);
|
|
460
|
+
}
|
|
461
|
+
}
|
|
462
|
+
/**
|
|
463
|
+
* Builds a SyncNotification for a completed run (success or completed-with-errors).
|
|
464
|
+
*/
|
|
465
|
+
buildCompletionNotification(run, result) {
|
|
466
|
+
const hasErrors = result.RecordsErrored > 0;
|
|
467
|
+
const event = hasErrors ? 'SyncCompletedWithErrors' : 'SyncCompleted';
|
|
468
|
+
const severity = hasErrors ? 'Warning' : 'Info';
|
|
469
|
+
const integrationName = run.Get('Integration') ?? 'Unknown Integration';
|
|
470
|
+
const subject = hasErrors
|
|
471
|
+
? `Integration Sync Completed with Errors: ${integrationName}`
|
|
472
|
+
: `Integration Sync Completed: ${integrationName}`;
|
|
473
|
+
const body = this.buildSyncResultBody(integrationName, result);
|
|
474
|
+
return {
|
|
475
|
+
Event: event,
|
|
476
|
+
Severity: severity,
|
|
477
|
+
CompanyIntegrationID: run.CompanyIntegrationID,
|
|
478
|
+
RunID: run.Get('ID'),
|
|
479
|
+
Subject: subject,
|
|
480
|
+
Body: body,
|
|
481
|
+
Result: result,
|
|
482
|
+
OccurredAt: new Date(),
|
|
483
|
+
};
|
|
484
|
+
}
|
|
485
|
+
/**
|
|
486
|
+
* Builds a SyncNotification for a catastrophically failed run.
|
|
487
|
+
*/
|
|
488
|
+
buildFailureNotification(run, err, result) {
|
|
489
|
+
const integrationName = run.Get('Integration') ?? 'Unknown Integration';
|
|
490
|
+
const errorMessage = err instanceof Error ? err.message : String(err);
|
|
491
|
+
const subject = `Integration Sync Failed: ${integrationName}`;
|
|
492
|
+
const body = [
|
|
493
|
+
`Integration: ${integrationName}`,
|
|
494
|
+
`Status: Failed`,
|
|
495
|
+
`Error: ${errorMessage}`,
|
|
496
|
+
`Time: ${new Date().toISOString()}`,
|
|
497
|
+
].join('\n');
|
|
498
|
+
return {
|
|
499
|
+
Event: 'SyncFailed',
|
|
500
|
+
Severity: 'Error',
|
|
501
|
+
CompanyIntegrationID: run.CompanyIntegrationID,
|
|
502
|
+
RunID: run.Get('ID'),
|
|
503
|
+
Subject: subject,
|
|
504
|
+
Body: body,
|
|
505
|
+
Result: result,
|
|
506
|
+
OccurredAt: new Date(),
|
|
507
|
+
};
|
|
508
|
+
}
|
|
509
|
+
/**
|
|
510
|
+
* Formats a human-readable sync result body for notification messages.
|
|
511
|
+
*/
|
|
512
|
+
buildSyncResultBody(integrationName, result) {
|
|
513
|
+
const lines = [
|
|
514
|
+
`Integration: ${integrationName}`,
|
|
515
|
+
`Status: ${result.RecordsErrored > 0 ? 'Completed with errors' : 'Success'}`,
|
|
516
|
+
'',
|
|
517
|
+
'Record Counts:',
|
|
518
|
+
` Processed: ${result.RecordsProcessed}`,
|
|
519
|
+
` Created: ${result.RecordsCreated}`,
|
|
520
|
+
` Updated: ${result.RecordsUpdated}`,
|
|
521
|
+
` Deleted: ${result.RecordsDeleted}`,
|
|
522
|
+
` Skipped: ${result.RecordsSkipped}`,
|
|
523
|
+
];
|
|
524
|
+
if (result.RecordsErrored > 0) {
|
|
525
|
+
lines.push(` Errors: ${result.RecordsErrored}`);
|
|
526
|
+
if (result.Errors.length > 0) {
|
|
527
|
+
lines.push('', 'Error Summary (first 5):');
|
|
528
|
+
for (const e of result.Errors.slice(0, 5)) {
|
|
529
|
+
lines.push(` [${e.ErrorCode}] ${e.ExternalID}: ${e.ErrorMessage}`);
|
|
530
|
+
}
|
|
531
|
+
}
|
|
532
|
+
}
|
|
533
|
+
lines.push('', `Completed at: ${new Date().toISOString()}`);
|
|
534
|
+
return lines.join('\n');
|
|
535
|
+
}
|
|
536
|
+
/**
|
|
537
|
+
* Invokes the notification callback without propagating exceptions.
|
|
538
|
+
*/
|
|
539
|
+
safeNotify(callback, notification) {
|
|
540
|
+
try {
|
|
541
|
+
callback(notification);
|
|
542
|
+
}
|
|
543
|
+
catch (notifyErr) {
|
|
544
|
+
console.warn('[IntegrationOrchestrator] Notification callback threw:', notifyErr);
|
|
545
|
+
}
|
|
546
|
+
}
|
|
547
|
+
}
|
|
548
|
+
//# sourceMappingURL=IntegrationOrchestrator.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"IntegrationOrchestrator.js","sourceRoot":"","sources":["../src/IntegrationOrchestrator.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import { type UserInfo } from '@memberjunction/core';
|
|
2
|
+
import type { ICompanyIntegrationFieldMap, ICompanyIntegrationEntityMap } from './entity-types.js';
|
|
3
|
+
import type { MappedRecord } from './types.js';
|
|
4
|
+
/**
|
|
5
|
+
* Resolves mapped records against existing MJ data to determine
|
|
6
|
+
* whether each record should be Created, Updated, Skipped, or Deleted.
|
|
7
|
+
*/
|
|
8
|
+
export declare class MatchEngine {
|
|
9
|
+
/**
|
|
10
|
+
* Resolves change types for a batch of mapped records by checking for existing
|
|
11
|
+
* MJ records via key field matching and record map lookups.
|
|
12
|
+
*
|
|
13
|
+
* @param records - Mapped records with preliminary change types
|
|
14
|
+
* @param entityMap - The entity map configuration for this object
|
|
15
|
+
* @param fieldMaps - Active field maps for identifying key fields
|
|
16
|
+
* @param contextUser - User context for data access
|
|
17
|
+
* @returns Updated mapped records with resolved ChangeType and MatchedMJRecordID
|
|
18
|
+
*/
|
|
19
|
+
Resolve(records: MappedRecord[], entityMap: ICompanyIntegrationEntityMap, fieldMaps: ICompanyIntegrationFieldMap[], contextUser: UserInfo): Promise<MappedRecord[]>;
|
|
20
|
+
/**
|
|
21
|
+
* Resolves a single record by checking for an existing MJ match.
|
|
22
|
+
*/
|
|
23
|
+
private ResolveSingleRecord;
|
|
24
|
+
/**
|
|
25
|
+
* Handles records marked as deleted in the external system.
|
|
26
|
+
*/
|
|
27
|
+
private ResolveDeletedRecord;
|
|
28
|
+
/**
|
|
29
|
+
* Determines change type for a record that matches an existing MJ record.
|
|
30
|
+
*/
|
|
31
|
+
private ResolveExistingRecord;
|
|
32
|
+
/**
|
|
33
|
+
* Attempts to find an existing MJ record by key field matching, then falls back
|
|
34
|
+
* to the CompanyIntegrationRecordMap.
|
|
35
|
+
*/
|
|
36
|
+
private FindExistingRecord;
|
|
37
|
+
/**
|
|
38
|
+
* Searches for an existing MJ record using key field values.
|
|
39
|
+
*/
|
|
40
|
+
private FindByKeyFields;
|
|
41
|
+
/**
|
|
42
|
+
* Builds a SQL filter clause from key field values on a mapped record.
|
|
43
|
+
*/
|
|
44
|
+
private BuildKeyFieldFilter;
|
|
45
|
+
/**
|
|
46
|
+
* Checks the CompanyIntegrationRecordMap for a previous external↔MJ mapping.
|
|
47
|
+
*/
|
|
48
|
+
private FindRecordMapEntry;
|
|
49
|
+
}
|
|
50
|
+
//# sourceMappingURL=MatchEngine.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"MatchEngine.d.ts","sourceRoot":"","sources":["../src/MatchEngine.ts"],"names":[],"mappings":"AAAA,OAAO,EAAW,KAAK,QAAQ,EAAE,MAAM,sBAAsB,CAAC;AAC9D,OAAO,KAAK,EAAE,2BAA2B,EAAE,4BAA4B,EAAE,MAAM,mBAAmB,CAAC;AACnG,OAAO,KAAK,EAAE,YAAY,EAAsB,MAAM,YAAY,CAAC;AAEnE;;;GAGG;AACH,qBAAa,WAAW;IACpB;;;;;;;;;OASG;IACU,OAAO,CAChB,OAAO,EAAE,YAAY,EAAE,EACvB,SAAS,EAAE,4BAA4B,EACvC,SAAS,EAAE,2BAA2B,EAAE,EACxC,WAAW,EAAE,QAAQ,GACtB,OAAO,CAAC,YAAY,EAAE,CAAC;IAkB1B;;OAEG;YACW,mBAAmB;IAsBjC;;OAEG;YACW,oBAAoB;IAmBlC;;OAEG;IACH,OAAO,CAAC,qBAAqB;IAW7B;;;OAGG;YACW,kBAAkB;IAmBhC;;OAEG;YACW,eAAe;IAqB7B;;OAEG;IACH,OAAO,CAAC,mBAAmB;IAc3B;;OAEG;YACW,kBAAkB;CAsBnC"}
|