@open-mercato/core 0.6.2-develop.3330.1.8ad2b3b84c → 0.6.2-develop.3366.1.4692954e44
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/.turbo/turbo-build.log +2 -2
- package/dist/generated/entities/sync_excel_upload/index.js +37 -0
- package/dist/generated/entities/sync_excel_upload/index.js.map +7 -0
- package/dist/generated/entities.ids.generated.js +4 -0
- package/dist/generated/entities.ids.generated.js.map +2 -2
- package/dist/generated/entity-fields-registry.js +19 -0
- package/dist/generated/entity-fields-registry.js.map +2 -2
- package/dist/modules/customers/search.js +3 -2
- package/dist/modules/customers/search.js.map +2 -2
- package/dist/modules/data_sync/api/runs/[id]/cancel.js +16 -0
- package/dist/modules/data_sync/api/runs/[id]/cancel.js.map +2 -2
- package/dist/modules/data_sync/di.js +2 -1
- package/dist/modules/data_sync/di.js.map +2 -2
- package/dist/modules/data_sync/lib/sync-engine.js +178 -54
- package/dist/modules/data_sync/lib/sync-engine.js.map +2 -2
- package/dist/modules/data_sync/lib/sync-run-service.js +23 -0
- package/dist/modules/data_sync/lib/sync-run-service.js.map +3 -3
- package/dist/modules/integrations/backend/integrations/[id]/page.js +241 -55
- package/dist/modules/integrations/backend/integrations/[id]/page.js.map +3 -3
- package/dist/modules/sync_excel/acl.js +10 -0
- package/dist/modules/sync_excel/acl.js.map +7 -0
- package/dist/modules/sync_excel/api/import/route.js +164 -0
- package/dist/modules/sync_excel/api/import/route.js.map +7 -0
- package/dist/modules/sync_excel/api/preview/route.js +84 -0
- package/dist/modules/sync_excel/api/preview/route.js.map +7 -0
- package/dist/modules/sync_excel/api/upload/route.js +131 -0
- package/dist/modules/sync_excel/api/upload/route.js.map +7 -0
- package/dist/modules/sync_excel/data/entities.js +79 -0
- package/dist/modules/sync_excel/data/entities.js.map +7 -0
- package/dist/modules/sync_excel/data/validators.js +58 -0
- package/dist/modules/sync_excel/data/validators.js.map +7 -0
- package/dist/modules/sync_excel/di.js +11 -0
- package/dist/modules/sync_excel/di.js.map +7 -0
- package/dist/modules/sync_excel/index.js +11 -0
- package/dist/modules/sync_excel/index.js.map +7 -0
- package/dist/modules/sync_excel/integration.js +36 -0
- package/dist/modules/sync_excel/integration.js.map +7 -0
- package/dist/modules/sync_excel/lib/adapters/customers.js +852 -0
- package/dist/modules/sync_excel/lib/adapters/customers.js.map +7 -0
- package/dist/modules/sync_excel/lib/column-detector.js +77 -0
- package/dist/modules/sync_excel/lib/column-detector.js.map +7 -0
- package/dist/modules/sync_excel/lib/mapping.js +16 -0
- package/dist/modules/sync_excel/lib/mapping.js.map +7 -0
- package/dist/modules/sync_excel/lib/parser.js +125 -0
- package/dist/modules/sync_excel/lib/parser.js.map +7 -0
- package/dist/modules/sync_excel/lib/scope.js +44 -0
- package/dist/modules/sync_excel/lib/scope.js.map +7 -0
- package/dist/modules/sync_excel/lib/upload-storage.js +63 -0
- package/dist/modules/sync_excel/lib/upload-storage.js.map +7 -0
- package/dist/modules/sync_excel/migrations/Migration20260329181120_sync_excel.js +11 -0
- package/dist/modules/sync_excel/migrations/Migration20260329181120_sync_excel.js.map +7 -0
- package/dist/modules/sync_excel/setup.js +19 -0
- package/dist/modules/sync_excel/setup.js.map +7 -0
- package/dist/modules/sync_excel/widgets/injection/upload-config/target-options.js +293 -0
- package/dist/modules/sync_excel/widgets/injection/upload-config/target-options.js.map +7 -0
- package/dist/modules/sync_excel/widgets/injection/upload-config/widget.client.js +934 -0
- package/dist/modules/sync_excel/widgets/injection/upload-config/widget.client.js.map +7 -0
- package/dist/modules/sync_excel/widgets/injection/upload-config/widget.js +15 -0
- package/dist/modules/sync_excel/widgets/injection/upload-config/widget.js.map +7 -0
- package/dist/modules/sync_excel/widgets/injection-table.js +17 -0
- package/dist/modules/sync_excel/widgets/injection-table.js.map +7 -0
- package/generated/entities/sync_excel_upload/index.ts +17 -0
- package/generated/entities.ids.generated.ts +4 -0
- package/generated/entity-fields-registry.ts +19 -0
- package/package.json +7 -7
- package/src/modules/customers/search.ts +3 -2
- package/src/modules/data_sync/api/runs/[id]/cancel.ts +18 -0
- package/src/modules/data_sync/di.ts +4 -1
- package/src/modules/data_sync/lib/adapter.ts +15 -0
- package/src/modules/data_sync/lib/sync-engine.ts +205 -52
- package/src/modules/data_sync/lib/sync-run-service.ts +24 -0
- package/src/modules/integrations/backend/integrations/[id]/page.tsx +220 -15
- package/src/modules/integrations/i18n/de.json +9 -0
- package/src/modules/integrations/i18n/en.json +9 -0
- package/src/modules/integrations/i18n/es.json +9 -0
- package/src/modules/integrations/i18n/pl.json +9 -0
- package/src/modules/sync_excel/acl.ts +6 -0
- package/src/modules/sync_excel/api/import/route.ts +182 -0
- package/src/modules/sync_excel/api/preview/route.ts +89 -0
- package/src/modules/sync_excel/api/upload/route.ts +144 -0
- package/src/modules/sync_excel/data/entities.ts +61 -0
- package/src/modules/sync_excel/data/validators.ts +58 -0
- package/src/modules/sync_excel/di.ts +9 -0
- package/src/modules/sync_excel/i18n/de.json +107 -0
- package/src/modules/sync_excel/i18n/en.json +107 -0
- package/src/modules/sync_excel/i18n/es.json +107 -0
- package/src/modules/sync_excel/i18n/pl.json +107 -0
- package/src/modules/sync_excel/index.ts +7 -0
- package/src/modules/sync_excel/integration.ts +31 -0
- package/src/modules/sync_excel/lib/adapters/customers.ts +1097 -0
- package/src/modules/sync_excel/lib/column-detector.ts +91 -0
- package/src/modules/sync_excel/lib/mapping.ts +19 -0
- package/src/modules/sync_excel/lib/parser.ts +165 -0
- package/src/modules/sync_excel/lib/scope.ts +63 -0
- package/src/modules/sync_excel/lib/upload-storage.ts +79 -0
- package/src/modules/sync_excel/migrations/.snapshot-open-mercato.json +199 -0
- package/src/modules/sync_excel/migrations/Migration20260329181120_sync_excel.ts +10 -0
- package/src/modules/sync_excel/setup.ts +20 -0
- package/src/modules/sync_excel/widgets/injection/upload-config/target-options.ts +351 -0
- package/src/modules/sync_excel/widgets/injection/upload-config/widget.client.tsx +1224 -0
- package/src/modules/sync_excel/widgets/injection/upload-config/widget.ts +30 -0
- package/src/modules/sync_excel/widgets/injection-table.ts +15 -0
|
@@ -2,6 +2,7 @@ import type { EntityManager } from '@mikro-orm/postgresql'
|
|
|
2
2
|
import { getIntegration } from '@open-mercato/shared/modules/integrations/types'
|
|
3
3
|
import type { CredentialsService } from '../../integrations/lib/credentials-service'
|
|
4
4
|
import type { IntegrationLogService } from '../../integrations/lib/log-service'
|
|
5
|
+
import type { IntegrationStateService } from '../../integrations/lib/state-service'
|
|
5
6
|
import type { ProgressService } from '../../progress/lib/progressService'
|
|
6
7
|
import { refreshCoverageSnapshot } from '../../query_index/lib/coverage'
|
|
7
8
|
import { emitDataSyncEvent } from '../events'
|
|
@@ -20,6 +21,7 @@ type EngineDeps = {
|
|
|
20
21
|
syncRunService: SyncRunService
|
|
21
22
|
integrationCredentialsService: CredentialsService
|
|
22
23
|
integrationLogService: IntegrationLogService
|
|
24
|
+
integrationStateService?: IntegrationStateService
|
|
23
25
|
progressService: ProgressService
|
|
24
26
|
}
|
|
25
27
|
|
|
@@ -71,7 +73,7 @@ function applyExportCounters(batch: ExportBatch): SyncCounterDelta {
|
|
|
71
73
|
}
|
|
72
74
|
|
|
73
75
|
export function createSyncEngine(deps: EngineDeps) {
|
|
74
|
-
const { syncRunService, integrationCredentialsService, integrationLogService, progressService } = deps
|
|
76
|
+
const { syncRunService, integrationCredentialsService, integrationLogService, integrationStateService, progressService } = deps
|
|
75
77
|
|
|
76
78
|
async function resolveMapping(adapter: DataSyncAdapter, entityType: string, scope: SyncScope): Promise<DataMapping> {
|
|
77
79
|
return adapter.getMapping({
|
|
@@ -128,7 +130,7 @@ export function createSyncEngine(deps: EngineDeps) {
|
|
|
128
130
|
? item.data.sourceIdentifier.trim()
|
|
129
131
|
: null
|
|
130
132
|
const message = [
|
|
131
|
-
`Failed to import
|
|
133
|
+
`Failed to import item ${item.externalId}`,
|
|
132
134
|
sourceProductUuid ? `(uuid: ${sourceProductUuid})` : null,
|
|
133
135
|
sourceIdentifier ? `(identifier: ${sourceIdentifier})` : null,
|
|
134
136
|
`: ${errorMessage}`,
|
|
@@ -147,10 +149,65 @@ export function createSyncEngine(deps: EngineDeps) {
|
|
|
147
149
|
}
|
|
148
150
|
}
|
|
149
151
|
|
|
150
|
-
async function
|
|
152
|
+
async function writeOperationalLog(params: {
|
|
153
|
+
integrationId: string
|
|
154
|
+
runId: string
|
|
155
|
+
level: 'info' | 'warn' | 'error'
|
|
156
|
+
message: string
|
|
157
|
+
scope: SyncScope
|
|
158
|
+
enabled: boolean
|
|
159
|
+
payload?: Record<string, unknown>
|
|
160
|
+
}): Promise<void> {
|
|
161
|
+
if (!params.enabled) return
|
|
162
|
+
|
|
163
|
+
await integrationLogService.write(
|
|
164
|
+
{
|
|
165
|
+
integrationId: params.integrationId,
|
|
166
|
+
runId: params.runId,
|
|
167
|
+
level: params.level,
|
|
168
|
+
message: params.message,
|
|
169
|
+
payload: params.payload,
|
|
170
|
+
},
|
|
171
|
+
params.scope,
|
|
172
|
+
)
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
async function updateOperationalState(params: {
|
|
176
|
+
integrationId: string
|
|
177
|
+
status: 'healthy' | 'degraded' | 'unhealthy'
|
|
178
|
+
scope: SyncScope
|
|
179
|
+
enabled: boolean
|
|
180
|
+
}): Promise<void> {
|
|
181
|
+
if (!params.enabled || !integrationStateService) return
|
|
182
|
+
|
|
183
|
+
await integrationStateService.upsert(
|
|
184
|
+
params.integrationId,
|
|
185
|
+
{
|
|
186
|
+
lastHealthStatus: params.status,
|
|
187
|
+
lastHealthCheckedAt: new Date(),
|
|
188
|
+
},
|
|
189
|
+
params.scope,
|
|
190
|
+
)
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
async function finalizeRun(
|
|
194
|
+
runId: string,
|
|
195
|
+
status: 'completed' | 'failed' | 'cancelled',
|
|
196
|
+
scope: SyncScope,
|
|
197
|
+
error?: string,
|
|
198
|
+
operationalTelemetry = false,
|
|
199
|
+
): Promise<void> {
|
|
200
|
+
const existingRun = await syncRunService.getRun(runId, scope)
|
|
201
|
+
const alreadyFinalizedWithSameStatus = existingRun?.status === status
|
|
202
|
+
&& (status === 'completed' || status === 'failed' || status === 'cancelled')
|
|
203
|
+
|
|
151
204
|
const run = await syncRunService.markStatus(runId, status, scope, error)
|
|
152
205
|
if (!run) return
|
|
153
206
|
|
|
207
|
+
if (alreadyFinalizedWithSameStatus) {
|
|
208
|
+
return
|
|
209
|
+
}
|
|
210
|
+
|
|
154
211
|
if (run.progressJobId) {
|
|
155
212
|
if (status === 'completed') {
|
|
156
213
|
await progressService.completeJob(
|
|
@@ -194,6 +251,70 @@ export function createSyncEngine(deps: EngineDeps) {
|
|
|
194
251
|
}
|
|
195
252
|
}
|
|
196
253
|
|
|
254
|
+
if (status === 'completed') {
|
|
255
|
+
await updateOperationalState({
|
|
256
|
+
integrationId: run.integrationId,
|
|
257
|
+
status: 'healthy',
|
|
258
|
+
scope,
|
|
259
|
+
enabled: operationalTelemetry,
|
|
260
|
+
})
|
|
261
|
+
await writeOperationalLog({
|
|
262
|
+
integrationId: run.integrationId,
|
|
263
|
+
runId: run.id,
|
|
264
|
+
level: 'info',
|
|
265
|
+
message: 'Sync run completed',
|
|
266
|
+
scope,
|
|
267
|
+
enabled: operationalTelemetry,
|
|
268
|
+
payload: {
|
|
269
|
+
operationalStatus: 'completed',
|
|
270
|
+
summary: `Sync completed with ${run.createdCount} created, ${run.updatedCount} updated, ${run.failedCount} failed.`,
|
|
271
|
+
createdCount: run.createdCount,
|
|
272
|
+
updatedCount: run.updatedCount,
|
|
273
|
+
skippedCount: run.skippedCount,
|
|
274
|
+
failedCount: run.failedCount,
|
|
275
|
+
batchesCompleted: run.batchesCompleted,
|
|
276
|
+
},
|
|
277
|
+
})
|
|
278
|
+
} else if (status === 'cancelled') {
|
|
279
|
+
await updateOperationalState({
|
|
280
|
+
integrationId: run.integrationId,
|
|
281
|
+
status: 'degraded',
|
|
282
|
+
scope,
|
|
283
|
+
enabled: operationalTelemetry,
|
|
284
|
+
})
|
|
285
|
+
await writeOperationalLog({
|
|
286
|
+
integrationId: run.integrationId,
|
|
287
|
+
runId: run.id,
|
|
288
|
+
level: 'warn',
|
|
289
|
+
message: 'Sync run cancelled',
|
|
290
|
+
scope,
|
|
291
|
+
enabled: operationalTelemetry,
|
|
292
|
+
payload: {
|
|
293
|
+
operationalStatus: 'cancelled',
|
|
294
|
+
summary: 'The sync run was cancelled before completion.',
|
|
295
|
+
},
|
|
296
|
+
})
|
|
297
|
+
} else {
|
|
298
|
+
await updateOperationalState({
|
|
299
|
+
integrationId: run.integrationId,
|
|
300
|
+
status: 'unhealthy',
|
|
301
|
+
scope,
|
|
302
|
+
enabled: operationalTelemetry,
|
|
303
|
+
})
|
|
304
|
+
await writeOperationalLog({
|
|
305
|
+
integrationId: run.integrationId,
|
|
306
|
+
runId: run.id,
|
|
307
|
+
level: 'error',
|
|
308
|
+
message: error ?? 'Sync run failed',
|
|
309
|
+
scope,
|
|
310
|
+
enabled: operationalTelemetry,
|
|
311
|
+
payload: {
|
|
312
|
+
operationalStatus: 'failed',
|
|
313
|
+
summary: error ?? 'The sync run failed.',
|
|
314
|
+
},
|
|
315
|
+
})
|
|
316
|
+
}
|
|
317
|
+
|
|
197
318
|
if (status === 'completed') {
|
|
198
319
|
await emitDataSyncEvent('data_sync.run.completed', {
|
|
199
320
|
runId,
|
|
@@ -252,6 +373,7 @@ export function createSyncEngine(deps: EngineDeps) {
|
|
|
252
373
|
if (!adapter?.streamImport) {
|
|
253
374
|
throw new Error(`No import adapter registered for provider ${providerKey}`)
|
|
254
375
|
}
|
|
376
|
+
const operationalTelemetry = adapter.operationalTelemetry === true
|
|
255
377
|
|
|
256
378
|
const credentials = await integrationCredentialsService.resolve(run.integrationId, scope)
|
|
257
379
|
if (!credentials) {
|
|
@@ -260,13 +382,6 @@ export function createSyncEngine(deps: EngineDeps) {
|
|
|
260
382
|
|
|
261
383
|
const activeRun = await syncRunService.markStatus(run.id, 'running', scope)
|
|
262
384
|
if (!activeRun || activeRun.status !== 'running') {
|
|
263
|
-
if (run.progressJobId) {
|
|
264
|
-
await progressService.markCancelled(run.progressJobId, {
|
|
265
|
-
tenantId: scope.tenantId,
|
|
266
|
-
organizationId: scope.organizationId,
|
|
267
|
-
userId: scope.userId,
|
|
268
|
-
})
|
|
269
|
-
}
|
|
270
385
|
return
|
|
271
386
|
}
|
|
272
387
|
await emitDataSyncEvent('data_sync.run.started', {
|
|
@@ -277,6 +392,26 @@ export function createSyncEngine(deps: EngineDeps) {
|
|
|
277
392
|
tenantId: scope.tenantId,
|
|
278
393
|
organizationId: scope.organizationId,
|
|
279
394
|
})
|
|
395
|
+
await updateOperationalState({
|
|
396
|
+
integrationId: run.integrationId,
|
|
397
|
+
status: 'degraded',
|
|
398
|
+
scope,
|
|
399
|
+
enabled: operationalTelemetry,
|
|
400
|
+
})
|
|
401
|
+
await writeOperationalLog({
|
|
402
|
+
integrationId: run.integrationId,
|
|
403
|
+
runId: run.id,
|
|
404
|
+
level: 'info',
|
|
405
|
+
message: 'Sync run started',
|
|
406
|
+
scope,
|
|
407
|
+
enabled: operationalTelemetry,
|
|
408
|
+
payload: {
|
|
409
|
+
operationalStatus: 'running',
|
|
410
|
+
summary: `Import run started for ${run.entityType}.`,
|
|
411
|
+
entityType: run.entityType,
|
|
412
|
+
direction: run.direction,
|
|
413
|
+
},
|
|
414
|
+
})
|
|
280
415
|
|
|
281
416
|
if (run.progressJobId) {
|
|
282
417
|
await progressService.startJob(run.progressJobId, {
|
|
@@ -298,9 +433,10 @@ export function createSyncEngine(deps: EngineDeps) {
|
|
|
298
433
|
credentials,
|
|
299
434
|
mapping,
|
|
300
435
|
scope: { organizationId: scope.organizationId, tenantId: scope.tenantId },
|
|
436
|
+
runId: run.id,
|
|
301
437
|
})) {
|
|
302
438
|
if (run.progressJobId && await progressService.isCancellationRequested(run.progressJobId, scope.tenantId)) {
|
|
303
|
-
await finalizeRun(run.id, 'cancelled', scope)
|
|
439
|
+
await finalizeRun(run.id, 'cancelled', scope, undefined, operationalTelemetry)
|
|
304
440
|
return
|
|
305
441
|
}
|
|
306
442
|
|
|
@@ -323,23 +459,24 @@ export function createSyncEngine(deps: EngineDeps) {
|
|
|
323
459
|
await refreshCoverageSnapshots(batch.refreshCoverageEntityTypes, scope)
|
|
324
460
|
await logImportItemFailures(run.id, run.integrationId, batch.items, scope)
|
|
325
461
|
|
|
326
|
-
await
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
: `Processed import batch ${batch.batchIndex}`,
|
|
334
|
-
payload: {
|
|
335
|
-
processedCount,
|
|
336
|
-
batchSize: batch.items.length,
|
|
337
|
-
processedBatchCount,
|
|
338
|
-
cursor: batch.cursor,
|
|
339
|
-
},
|
|
340
|
-
},
|
|
462
|
+
await writeOperationalLog({
|
|
463
|
+
integrationId: run.integrationId,
|
|
464
|
+
runId: run.id,
|
|
465
|
+
level: 'info',
|
|
466
|
+
message: batch.message?.trim().length
|
|
467
|
+
? batch.message.trim()
|
|
468
|
+
: `Processed import batch ${batch.batchIndex}`,
|
|
341
469
|
scope,
|
|
342
|
-
|
|
470
|
+
enabled: operationalTelemetry,
|
|
471
|
+
payload: {
|
|
472
|
+
operationalStatus: 'running',
|
|
473
|
+
summary: `Processed ${processedCount}${totalCount ? ` of ${totalCount}` : ''} rows so far.`,
|
|
474
|
+
processedCount,
|
|
475
|
+
batchSize: batch.items.length,
|
|
476
|
+
processedBatchCount,
|
|
477
|
+
cursor: batch.cursor,
|
|
478
|
+
},
|
|
479
|
+
})
|
|
343
480
|
}
|
|
344
481
|
} catch (error) {
|
|
345
482
|
const message = error instanceof Error ? error.message : 'Sync import failed'
|
|
@@ -352,11 +489,11 @@ export function createSyncEngine(deps: EngineDeps) {
|
|
|
352
489
|
},
|
|
353
490
|
scope,
|
|
354
491
|
)
|
|
355
|
-
await finalizeRun(run.id, 'failed', scope, message)
|
|
492
|
+
await finalizeRun(run.id, 'failed', scope, message, operationalTelemetry)
|
|
356
493
|
return
|
|
357
494
|
}
|
|
358
495
|
|
|
359
|
-
await finalizeRun(run.id, 'completed', scope)
|
|
496
|
+
await finalizeRun(run.id, 'completed', scope, undefined, operationalTelemetry)
|
|
360
497
|
},
|
|
361
498
|
|
|
362
499
|
async runExport(runId: string, batchSize: number, scope: SyncScope): Promise<void> {
|
|
@@ -381,6 +518,7 @@ export function createSyncEngine(deps: EngineDeps) {
|
|
|
381
518
|
if (!adapter?.streamExport) {
|
|
382
519
|
throw new Error(`No export adapter registered for provider ${providerKey}`)
|
|
383
520
|
}
|
|
521
|
+
const operationalTelemetry = adapter.operationalTelemetry === true
|
|
384
522
|
|
|
385
523
|
const credentials = await integrationCredentialsService.resolve(run.integrationId, scope)
|
|
386
524
|
if (!credentials) {
|
|
@@ -389,13 +527,6 @@ export function createSyncEngine(deps: EngineDeps) {
|
|
|
389
527
|
|
|
390
528
|
const activeRun = await syncRunService.markStatus(run.id, 'running', scope)
|
|
391
529
|
if (!activeRun || activeRun.status !== 'running') {
|
|
392
|
-
if (run.progressJobId) {
|
|
393
|
-
await progressService.markCancelled(run.progressJobId, {
|
|
394
|
-
tenantId: scope.tenantId,
|
|
395
|
-
organizationId: scope.organizationId,
|
|
396
|
-
userId: scope.userId,
|
|
397
|
-
})
|
|
398
|
-
}
|
|
399
530
|
return
|
|
400
531
|
}
|
|
401
532
|
await emitDataSyncEvent('data_sync.run.started', {
|
|
@@ -406,6 +537,26 @@ export function createSyncEngine(deps: EngineDeps) {
|
|
|
406
537
|
tenantId: scope.tenantId,
|
|
407
538
|
organizationId: scope.organizationId,
|
|
408
539
|
})
|
|
540
|
+
await updateOperationalState({
|
|
541
|
+
integrationId: run.integrationId,
|
|
542
|
+
status: 'degraded',
|
|
543
|
+
scope,
|
|
544
|
+
enabled: operationalTelemetry,
|
|
545
|
+
})
|
|
546
|
+
await writeOperationalLog({
|
|
547
|
+
integrationId: run.integrationId,
|
|
548
|
+
runId: run.id,
|
|
549
|
+
level: 'info',
|
|
550
|
+
message: 'Sync run started',
|
|
551
|
+
scope,
|
|
552
|
+
enabled: operationalTelemetry,
|
|
553
|
+
payload: {
|
|
554
|
+
operationalStatus: 'running',
|
|
555
|
+
summary: `Export run started for ${run.entityType}.`,
|
|
556
|
+
entityType: run.entityType,
|
|
557
|
+
direction: run.direction,
|
|
558
|
+
},
|
|
559
|
+
})
|
|
409
560
|
|
|
410
561
|
if (run.progressJobId) {
|
|
411
562
|
await progressService.startJob(run.progressJobId, {
|
|
@@ -426,9 +577,10 @@ export function createSyncEngine(deps: EngineDeps) {
|
|
|
426
577
|
credentials,
|
|
427
578
|
mapping,
|
|
428
579
|
scope: { organizationId: scope.organizationId, tenantId: scope.tenantId },
|
|
580
|
+
runId: run.id,
|
|
429
581
|
})) {
|
|
430
582
|
if (run.progressJobId && await progressService.isCancellationRequested(run.progressJobId, scope.tenantId)) {
|
|
431
|
-
await finalizeRun(run.id, 'cancelled', scope)
|
|
583
|
+
await finalizeRun(run.id, 'cancelled', scope, undefined, operationalTelemetry)
|
|
432
584
|
return
|
|
433
585
|
}
|
|
434
586
|
|
|
@@ -450,20 +602,21 @@ export function createSyncEngine(deps: EngineDeps) {
|
|
|
450
602
|
await syncRunService.updateCursor(run.id, batch.cursor, scope)
|
|
451
603
|
await updateProgress(run.progressJobId, processedCount, null, scope)
|
|
452
604
|
|
|
453
|
-
await
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
message: `Processed export batch ${batch.batchIndex}`,
|
|
459
|
-
payload: {
|
|
460
|
-
processedCount,
|
|
461
|
-
batchSize: batch.results.length,
|
|
462
|
-
cursor: batch.cursor,
|
|
463
|
-
},
|
|
464
|
-
},
|
|
605
|
+
await writeOperationalLog({
|
|
606
|
+
integrationId: run.integrationId,
|
|
607
|
+
runId: run.id,
|
|
608
|
+
level: 'info',
|
|
609
|
+
message: `Processed export batch ${batch.batchIndex}`,
|
|
465
610
|
scope,
|
|
466
|
-
|
|
611
|
+
enabled: operationalTelemetry,
|
|
612
|
+
payload: {
|
|
613
|
+
operationalStatus: 'running',
|
|
614
|
+
summary: `Processed ${processedCount} export items so far.`,
|
|
615
|
+
processedCount,
|
|
616
|
+
batchSize: batch.results.length,
|
|
617
|
+
cursor: batch.cursor,
|
|
618
|
+
},
|
|
619
|
+
})
|
|
467
620
|
}
|
|
468
621
|
} catch (error) {
|
|
469
622
|
const message = error instanceof Error ? error.message : 'Sync export failed'
|
|
@@ -476,11 +629,11 @@ export function createSyncEngine(deps: EngineDeps) {
|
|
|
476
629
|
},
|
|
477
630
|
scope,
|
|
478
631
|
)
|
|
479
|
-
await finalizeRun(run.id, 'failed', scope, message)
|
|
632
|
+
await finalizeRun(run.id, 'failed', scope, message, operationalTelemetry)
|
|
480
633
|
return
|
|
481
634
|
}
|
|
482
635
|
|
|
483
|
-
await finalizeRun(run.id, 'completed', scope)
|
|
636
|
+
await finalizeRun(run.id, 'completed', scope, undefined, operationalTelemetry)
|
|
484
637
|
},
|
|
485
638
|
}
|
|
486
639
|
}
|
|
@@ -86,6 +86,30 @@ export function createSyncRunService(em: EntityManager) {
|
|
|
86
86
|
},
|
|
87
87
|
|
|
88
88
|
async markStatus(runId: string, status: SyncRun['status'], scope: SyncScope, error?: string): Promise<SyncRun | null> {
|
|
89
|
+
if (status === 'running') {
|
|
90
|
+
const updated = await em.nativeUpdate(
|
|
91
|
+
SyncRun,
|
|
92
|
+
{
|
|
93
|
+
id: runId,
|
|
94
|
+
organizationId: scope.organizationId,
|
|
95
|
+
tenantId: scope.tenantId,
|
|
96
|
+
deletedAt: null,
|
|
97
|
+
status: 'pending',
|
|
98
|
+
},
|
|
99
|
+
{
|
|
100
|
+
status,
|
|
101
|
+
...(error !== undefined ? { lastError: error } : {}),
|
|
102
|
+
updatedAt: new Date(),
|
|
103
|
+
},
|
|
104
|
+
)
|
|
105
|
+
if (updated === 0) return null
|
|
106
|
+
const row = await this.getRun(runId, scope)
|
|
107
|
+
if (row && typeof em.refresh === 'function') {
|
|
108
|
+
await em.refresh(row)
|
|
109
|
+
}
|
|
110
|
+
return row
|
|
111
|
+
}
|
|
112
|
+
|
|
89
113
|
const row = await this.getRun(runId, scope)
|
|
90
114
|
if (!row) return null
|
|
91
115
|
const isTerminal = row.status === 'completed' || row.status === 'failed' || row.status === 'cancelled'
|