@open-mercato/core 0.4.7-develop-78d7541539 → 0.4.7-develop-74069040de
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/AGENTS.md +1 -0
- package/dist/modules/catalog/api/bulk-delete/route.js +86 -0
- package/dist/modules/catalog/api/bulk-delete/route.js.map +7 -0
- package/dist/modules/catalog/api/prices/route.js +39 -6
- package/dist/modules/catalog/api/prices/route.js.map +2 -2
- package/dist/modules/catalog/api/products/route.js +6 -11
- package/dist/modules/catalog/api/products/route.js.map +2 -2
- package/dist/modules/catalog/commands/products.js +2 -0
- package/dist/modules/catalog/commands/products.js.map +2 -2
- package/dist/modules/catalog/components/products/ProductsDataTable.js +9 -1
- package/dist/modules/catalog/components/products/ProductsDataTable.js.map +2 -2
- package/dist/modules/catalog/lib/bulkDelete.js +70 -0
- package/dist/modules/catalog/lib/bulkDelete.js.map +7 -0
- package/dist/modules/catalog/widgets/injection/product-bulk-delete/widget.js +185 -0
- package/dist/modules/catalog/widgets/injection/product-bulk-delete/widget.js.map +7 -0
- package/dist/modules/catalog/widgets/injection-table.js +9 -1
- package/dist/modules/catalog/widgets/injection-table.js.map +2 -2
- package/dist/modules/catalog/workers/catalog-product-bulk-delete.js +40 -0
- package/dist/modules/catalog/workers/catalog-product-bulk-delete.js.map +7 -0
- package/dist/modules/data_sync/api/options.js +52 -0
- package/dist/modules/data_sync/api/options.js.map +7 -0
- package/dist/modules/data_sync/api/run.js +30 -35
- package/dist/modules/data_sync/api/run.js.map +2 -2
- package/dist/modules/data_sync/api/runs/[id]/cancel.js +2 -2
- package/dist/modules/data_sync/api/runs/[id]/cancel.js.map +2 -2
- package/dist/modules/data_sync/api/runs/[id]/retry.js +15 -30
- package/dist/modules/data_sync/api/runs/[id]/retry.js.map +2 -2
- package/dist/modules/data_sync/api/schedules/[id]/route.js +109 -0
- package/dist/modules/data_sync/api/schedules/[id]/route.js.map +7 -0
- package/dist/modules/data_sync/api/schedules/route.js +72 -0
- package/dist/modules/data_sync/api/schedules/route.js.map +7 -0
- package/dist/modules/data_sync/api/schedules/serialize.js +21 -0
- package/dist/modules/data_sync/api/schedules/serialize.js.map +7 -0
- package/dist/modules/data_sync/backend/data-sync/page.js +656 -47
- package/dist/modules/data_sync/backend/data-sync/page.js.map +2 -2
- package/dist/modules/data_sync/backend/data-sync/runs/[id]/page.js +116 -34
- package/dist/modules/data_sync/backend/data-sync/runs/[id]/page.js.map +2 -2
- package/dist/modules/data_sync/components/IntegrationScheduleTab.js +394 -0
- package/dist/modules/data_sync/components/IntegrationScheduleTab.js.map +7 -0
- package/dist/modules/data_sync/data/validators.js +32 -0
- package/dist/modules/data_sync/data/validators.js.map +2 -2
- package/dist/modules/data_sync/di.js +2 -0
- package/dist/modules/data_sync/di.js.map +2 -2
- package/dist/modules/data_sync/lib/id-mapping.js +24 -2
- package/dist/modules/data_sync/lib/id-mapping.js.map +2 -2
- package/dist/modules/data_sync/lib/start-run.js +57 -0
- package/dist/modules/data_sync/lib/start-run.js.map +7 -0
- package/dist/modules/data_sync/lib/sync-engine.js +93 -4
- package/dist/modules/data_sync/lib/sync-engine.js.map +2 -2
- package/dist/modules/data_sync/lib/sync-run-service.js +5 -1
- package/dist/modules/data_sync/lib/sync-run-service.js.map +2 -2
- package/dist/modules/data_sync/lib/sync-schedule-service.js +138 -0
- package/dist/modules/data_sync/lib/sync-schedule-service.js.map +7 -0
- package/dist/modules/data_sync/workers/sync-export.js +28 -2
- package/dist/modules/data_sync/workers/sync-export.js.map +2 -2
- package/dist/modules/data_sync/workers/sync-import.js +28 -2
- package/dist/modules/data_sync/workers/sync-import.js.map +2 -2
- package/dist/modules/data_sync/workers/sync-scheduled.js +5 -0
- package/dist/modules/data_sync/workers/sync-scheduled.js.map +2 -2
- package/dist/modules/entities/api/definitions.js +5 -2
- package/dist/modules/entities/api/definitions.js.map +2 -2
- package/dist/modules/entities/lib/field-definitions.js +3 -1
- package/dist/modules/entities/lib/field-definitions.js.map +2 -2
- package/dist/modules/integrations/api/[id]/route.js +14 -15
- package/dist/modules/integrations/api/[id]/route.js.map +2 -2
- package/dist/modules/integrations/api/route.js +3 -3
- package/dist/modules/integrations/api/route.js.map +2 -2
- package/dist/modules/integrations/backend/integrations/[id]/page.js +148 -33
- package/dist/modules/integrations/backend/integrations/[id]/page.js.map +2 -2
- package/dist/modules/integrations/lib/state-service.js +15 -1
- package/dist/modules/integrations/lib/state-service.js.map +2 -2
- package/dist/modules/messages/api/[id]/route.js +24 -22
- package/dist/modules/messages/api/[id]/route.js.map +2 -2
- package/dist/modules/payment_gateways/api/webhook/[provider]/route.js.map +2 -2
- package/dist/modules/progress/api/active/route.js +3 -1
- package/dist/modules/progress/api/active/route.js.map +2 -2
- package/dist/modules/progress/api/jobs/[id]/route.js +1 -1
- package/dist/modules/progress/api/jobs/[id]/route.js.map +2 -2
- package/dist/modules/progress/api/jobs/route.js +1 -1
- package/dist/modules/progress/api/jobs/route.js.map +2 -2
- package/dist/modules/progress/lib/events.js.map +1 -1
- package/dist/modules/progress/lib/progressService.js.map +2 -2
- package/dist/modules/progress/lib/progressServiceImpl.js +42 -1
- package/dist/modules/progress/lib/progressServiceImpl.js.map +2 -2
- package/dist/modules/query_index/lib/document.js +35 -1
- package/dist/modules/query_index/lib/document.js.map +2 -2
- package/dist/modules/query_index/lib/engine.js +91 -4
- package/dist/modules/query_index/lib/engine.js.map +2 -2
- package/dist/modules/query_index/lib/indexer.js +2 -0
- package/dist/modules/query_index/lib/indexer.js.map +2 -2
- package/dist/modules/sales/api/adjustment-kinds/route.js +3 -9
- package/dist/modules/sales/api/adjustment-kinds/route.js.map +2 -2
- package/dist/modules/sales/api/channels/route.js +3 -10
- package/dist/modules/sales/api/channels/route.js.map +2 -2
- package/dist/modules/sales/api/delivery-windows/route.js +3 -10
- package/dist/modules/sales/api/delivery-windows/route.js.map +2 -2
- package/dist/modules/sales/api/payment-methods/route.js +3 -11
- package/dist/modules/sales/api/payment-methods/route.js.map +2 -2
- package/dist/modules/sales/api/price-kinds/route.js +3 -5
- package/dist/modules/sales/api/price-kinds/route.js.map +2 -2
- package/dist/modules/sales/api/shipping-methods/route.js +3 -11
- package/dist/modules/sales/api/shipping-methods/route.js.map +2 -2
- package/dist/modules/sales/api/tags/route.js +3 -9
- package/dist/modules/sales/api/tags/route.js.map +2 -2
- package/dist/modules/sales/api/tax-rates/route.js +3 -13
- package/dist/modules/sales/api/tax-rates/route.js.map +2 -2
- package/dist/modules/sales/api/utils.js +9 -0
- package/dist/modules/sales/api/utils.js.map +2 -2
- package/dist/modules/sales/lib/makeStatusDictionaryRoute.js +3 -9
- package/dist/modules/sales/lib/makeStatusDictionaryRoute.js.map +2 -2
- package/dist/modules/workflows/api/definitions/[id]/route.js +3 -2
- package/dist/modules/workflows/api/definitions/[id]/route.js.map +2 -2
- package/dist/modules/workflows/api/definitions/route.js +4 -3
- package/dist/modules/workflows/api/definitions/route.js.map +2 -2
- package/dist/modules/workflows/api/definitions/serialize.js +25 -0
- package/dist/modules/workflows/api/definitions/serialize.js.map +7 -0
- package/package.json +3 -3
- package/src/modules/catalog/api/bulk-delete/route.ts +93 -0
- package/src/modules/catalog/api/prices/route.ts +53 -6
- package/src/modules/catalog/api/products/route.ts +6 -11
- package/src/modules/catalog/commands/products.ts +2 -0
- package/src/modules/catalog/components/products/ProductsDataTable.tsx +8 -0
- package/src/modules/catalog/i18n/de.json +10 -0
- package/src/modules/catalog/i18n/en.json +10 -0
- package/src/modules/catalog/i18n/es.json +10 -0
- package/src/modules/catalog/i18n/pl.json +10 -0
- package/src/modules/catalog/lib/bulkDelete.ts +106 -0
- package/src/modules/catalog/widgets/injection/product-bulk-delete/widget.ts +242 -0
- package/src/modules/catalog/widgets/injection-table.ts +8 -0
- package/src/modules/catalog/workers/catalog-product-bulk-delete.ts +48 -0
- package/src/modules/data_sync/AGENTS.md +11 -3
- package/src/modules/data_sync/api/options.ts +58 -0
- package/src/modules/data_sync/api/run.ts +34 -36
- package/src/modules/data_sync/api/runs/[id]/cancel.ts +2 -2
- package/src/modules/data_sync/api/runs/[id]/retry.ts +14 -31
- package/src/modules/data_sync/api/schedules/[id]/route.ts +130 -0
- package/src/modules/data_sync/api/schedules/route.ts +77 -0
- package/src/modules/data_sync/api/schedules/serialize.ts +31 -0
- package/src/modules/data_sync/backend/data-sync/page.tsx +756 -2
- package/src/modules/data_sync/backend/data-sync/runs/[id]/page.tsx +179 -53
- package/src/modules/data_sync/components/IntegrationScheduleTab.tsx +512 -0
- package/src/modules/data_sync/data/validators.ts +35 -0
- package/src/modules/data_sync/di.ts +6 -0
- package/src/modules/data_sync/i18n/de.json +72 -0
- package/src/modules/data_sync/i18n/en.json +72 -0
- package/src/modules/data_sync/i18n/es.json +72 -0
- package/src/modules/data_sync/i18n/pl.json +72 -0
- package/src/modules/data_sync/lib/adapter.ts +4 -1
- package/src/modules/data_sync/lib/id-mapping.ts +32 -2
- package/src/modules/data_sync/lib/start-run.ts +90 -0
- package/src/modules/data_sync/lib/sync-engine.ts +111 -4
- package/src/modules/data_sync/lib/sync-run-service.ts +5 -1
- package/src/modules/data_sync/lib/sync-schedule-service.ts +207 -0
- package/src/modules/data_sync/workers/sync-export.ts +33 -2
- package/src/modules/data_sync/workers/sync-import.ts +33 -2
- package/src/modules/data_sync/workers/sync-scheduled.ts +7 -0
- package/src/modules/entities/api/definitions.ts +12 -2
- package/src/modules/entities/lib/field-definitions.ts +2 -0
- package/src/modules/integrations/AGENTS.md +16 -3
- package/src/modules/integrations/api/[id]/route.ts +14 -15
- package/src/modules/integrations/api/route.ts +3 -3
- package/src/modules/integrations/backend/integrations/[id]/page.tsx +176 -54
- package/src/modules/integrations/lib/state-service.ts +25 -1
- package/src/modules/messages/api/[id]/route.ts +25 -22
- package/src/modules/payment_gateways/api/webhook/[provider]/route.ts +3 -3
- package/src/modules/progress/api/active/route.ts +4 -1
- package/src/modules/progress/api/jobs/[id]/route.ts +1 -1
- package/src/modules/progress/api/jobs/route.ts +1 -1
- package/src/modules/progress/lib/events.ts +6 -0
- package/src/modules/progress/lib/progressService.ts +1 -0
- package/src/modules/progress/lib/progressServiceImpl.ts +47 -1
- package/src/modules/query_index/lib/document.ts +52 -1
- package/src/modules/query_index/lib/engine.ts +104 -4
- package/src/modules/query_index/lib/indexer.ts +2 -0
- package/src/modules/sales/api/adjustment-kinds/route.ts +3 -9
- package/src/modules/sales/api/channels/route.ts +3 -10
- package/src/modules/sales/api/delivery-windows/route.ts +3 -10
- package/src/modules/sales/api/payment-methods/route.ts +3 -11
- package/src/modules/sales/api/price-kinds/route.ts +3 -5
- package/src/modules/sales/api/shipping-methods/route.ts +3 -11
- package/src/modules/sales/api/tags/route.ts +3 -9
- package/src/modules/sales/api/tax-rates/route.ts +3 -13
- package/src/modules/sales/api/utils.ts +9 -0
- package/src/modules/sales/lib/makeStatusDictionaryRoute.ts +3 -9
- package/src/modules/workflows/api/definitions/[id]/route.ts +3 -2
- package/src/modules/workflows/api/definitions/route.ts +4 -3
- package/src/modules/workflows/api/definitions/serialize.ts +23 -0
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../../src/modules/data_sync/lib/id-mapping.ts"],
|
|
4
|
-
"sourcesContent": ["import type { EntityManager } from '@mikro-orm/postgresql'\nimport { findOneWithDecryption } from '@open-mercato/shared/lib/encryption/find'\nimport { SyncExternalIdMapping } from '../../integrations/data/entities'\n\ntype MappingScope = {\n organizationId: string\n tenantId: string\n}\n\nexport function createExternalIdMappingService(em: EntityManager) {\n return {\n async lookupLocalId(integrationId: string, entityType: string, externalId: string, scope: MappingScope): Promise<string | null> {\n const row = await findOneWithDecryption(\n em,\n SyncExternalIdMapping,\n {\n integrationId,\n internalEntityType: entityType,\n externalId,\n organizationId: scope.organizationId,\n tenantId: scope.tenantId,\n deletedAt: null,\n },\n undefined,\n scope,\n )\n return row?.internalEntityId ?? null\n },\n\n async lookupExternalId(integrationId: string, entityType: string, localId: string, scope: MappingScope): Promise<string | null> {\n const row = await findOneWithDecryption(\n em,\n SyncExternalIdMapping,\n {\n integrationId,\n internalEntityType: entityType,\n internalEntityId: localId,\n organizationId: scope.organizationId,\n tenantId: scope.tenantId,\n deletedAt: null,\n },\n undefined,\n scope,\n )\n return row?.externalId ?? null\n },\n\n async storeExternalIdMapping(\n integrationId: string,\n entityType: string,\n localId: string,\n externalId: string,\n scope: MappingScope,\n ): Promise<SyncExternalIdMapping> {\n const
|
|
5
|
-
"mappings": "AACA,SAAS,6BAA6B;AACtC,SAAS,6BAA6B;AAO/B,SAAS,+BAA+B,IAAmB;AAChE,SAAO;AAAA,IACL,MAAM,cAAc,eAAuB,YAAoB,YAAoB,OAA6C;AAC9H,YAAM,MAAM,MAAM;AAAA,QAChB;AAAA,QACA;AAAA,QACA;AAAA,UACA;AAAA,UACA,oBAAoB;AAAA,UACpB;AAAA,UACA,gBAAgB,MAAM;AAAA,UACtB,UAAU,MAAM;AAAA,UAChB,WAAW;AAAA,QACX;AAAA,QACA;AAAA,QACA;AAAA,MACF;AACA,aAAO,KAAK,oBAAoB;AAAA,IAClC;AAAA,IAEA,MAAM,iBAAiB,eAAuB,YAAoB,SAAiB,OAA6C;AAC9H,YAAM,MAAM,MAAM;AAAA,QAChB;AAAA,QACA;AAAA,QACA;AAAA,UACA;AAAA,UACA,oBAAoB;AAAA,UACpB,kBAAkB;AAAA,UAClB,gBAAgB,MAAM;AAAA,UACtB,UAAU,MAAM;AAAA,UAChB,WAAW;AAAA,QACX;AAAA,QACA;AAAA,QACA;AAAA,MACF;AACA,aAAO,KAAK,cAAc;AAAA,IAC5B;AAAA,IAEA,MAAM,uBACJ,eACA,YACA,SACA,YACA,OACgC;AAChC,YAAM,
|
|
4
|
+
"sourcesContent": ["import type { EntityManager } from '@mikro-orm/postgresql'\nimport { findOneWithDecryption } from '@open-mercato/shared/lib/encryption/find'\nimport { SyncExternalIdMapping } from '../../integrations/data/entities'\n\ntype MappingScope = {\n organizationId: string\n tenantId: string\n}\n\nexport function createExternalIdMappingService(em: EntityManager) {\n return {\n async lookupLocalId(integrationId: string, entityType: string, externalId: string, scope: MappingScope): Promise<string | null> {\n const row = await findOneWithDecryption(\n em,\n SyncExternalIdMapping,\n {\n integrationId,\n internalEntityType: entityType,\n externalId,\n organizationId: scope.organizationId,\n tenantId: scope.tenantId,\n deletedAt: null,\n },\n undefined,\n scope,\n )\n return row?.internalEntityId ?? null\n },\n\n async lookupExternalId(integrationId: string, entityType: string, localId: string, scope: MappingScope): Promise<string | null> {\n const row = await findOneWithDecryption(\n em,\n SyncExternalIdMapping,\n {\n integrationId,\n internalEntityType: entityType,\n internalEntityId: localId,\n organizationId: scope.organizationId,\n tenantId: scope.tenantId,\n deletedAt: null,\n },\n undefined,\n scope,\n )\n return row?.externalId ?? null\n },\n\n async storeExternalIdMapping(\n integrationId: string,\n entityType: string,\n localId: string,\n externalId: string,\n scope: MappingScope,\n ): Promise<SyncExternalIdMapping> {\n const existingByLocalId = await findOneWithDecryption(\n em,\n SyncExternalIdMapping,\n {\n integrationId,\n internalEntityType: entityType,\n internalEntityId: localId,\n organizationId: scope.organizationId,\n tenantId: scope.tenantId,\n deletedAt: null,\n },\n undefined,\n scope,\n )\n\n const existingByExternalId = await findOneWithDecryption(\n em,\n SyncExternalIdMapping,\n {\n integrationId,\n internalEntityType: entityType,\n externalId,\n organizationId: scope.organizationId,\n tenantId: scope.tenantId,\n deletedAt: null,\n },\n undefined,\n scope,\n )\n\n const existing = existingByExternalId ?? existingByLocalId\n\n if (existing) {\n const now = new Date()\n existing.internalEntityId = localId\n existing.externalId = externalId\n existing.syncStatus = 'synced'\n existing.lastSyncedAt = now\n existing.deletedAt = null\n if (\n existingByExternalId &&\n existingByLocalId &&\n existingByExternalId.id !== existingByLocalId.id\n ) {\n const duplicate = existing.id === existingByExternalId.id\n ? existingByLocalId\n : existingByExternalId\n duplicate.deletedAt = now\n }\n await em.flush()\n return existing\n }\n\n const created = em.create(SyncExternalIdMapping, {\n integrationId,\n internalEntityType: entityType,\n internalEntityId: localId,\n externalId,\n syncStatus: 'synced',\n lastSyncedAt: new Date(),\n organizationId: scope.organizationId,\n tenantId: scope.tenantId,\n })\n\n await em.persistAndFlush(created)\n return created\n },\n }\n}\n\nexport type ExternalIdMappingService = ReturnType<typeof createExternalIdMappingService>\n"],
|
|
5
|
+
"mappings": "AACA,SAAS,6BAA6B;AACtC,SAAS,6BAA6B;AAO/B,SAAS,+BAA+B,IAAmB;AAChE,SAAO;AAAA,IACL,MAAM,cAAc,eAAuB,YAAoB,YAAoB,OAA6C;AAC9H,YAAM,MAAM,MAAM;AAAA,QAChB;AAAA,QACA;AAAA,QACA;AAAA,UACA;AAAA,UACA,oBAAoB;AAAA,UACpB;AAAA,UACA,gBAAgB,MAAM;AAAA,UACtB,UAAU,MAAM;AAAA,UAChB,WAAW;AAAA,QACX;AAAA,QACA;AAAA,QACA;AAAA,MACF;AACA,aAAO,KAAK,oBAAoB;AAAA,IAClC;AAAA,IAEA,MAAM,iBAAiB,eAAuB,YAAoB,SAAiB,OAA6C;AAC9H,YAAM,MAAM,MAAM;AAAA,QAChB;AAAA,QACA;AAAA,QACA;AAAA,UACA;AAAA,UACA,oBAAoB;AAAA,UACpB,kBAAkB;AAAA,UAClB,gBAAgB,MAAM;AAAA,UACtB,UAAU,MAAM;AAAA,UAChB,WAAW;AAAA,QACX;AAAA,QACA;AAAA,QACA;AAAA,MACF;AACA,aAAO,KAAK,cAAc;AAAA,IAC5B;AAAA,IAEA,MAAM,uBACJ,eACA,YACA,SACA,YACA,OACgC;AAChC,YAAM,oBAAoB,MAAM;AAAA,QAC9B;AAAA,QACA;AAAA,QACA;AAAA,UACA;AAAA,UACA,oBAAoB;AAAA,UACpB,kBAAkB;AAAA,UAClB,gBAAgB,MAAM;AAAA,UACtB,UAAU,MAAM;AAAA,UAChB,WAAW;AAAA,QACX;AAAA,QACA;AAAA,QACA;AAAA,MACF;AAEA,YAAM,uBAAuB,MAAM;AAAA,QACjC;AAAA,QACA;AAAA,QACA;AAAA,UACA;AAAA,UACA,oBAAoB;AAAA,UACpB;AAAA,UACA,gBAAgB,MAAM;AAAA,UACtB,UAAU,MAAM;AAAA,UAChB,WAAW;AAAA,QACX;AAAA,QACA;AAAA,QACA;AAAA,MACF;AAEA,YAAM,WAAW,wBAAwB;AAEzC,UAAI,UAAU;AACZ,cAAM,MAAM,oBAAI,KAAK;AACrB,iBAAS,mBAAmB;AAC5B,iBAAS,aAAa;AACtB,iBAAS,aAAa;AACtB,iBAAS,eAAe;AACxB,iBAAS,YAAY;AACrB,YACE,wBACA,qBACA,qBAAqB,OAAO,kBAAkB,IAC9C;AACA,gBAAM,YAAY,SAAS,OAAO,qBAAqB,KACnD,oBACA;AACJ,oBAAU,YAAY;AAAA,QACxB;AACA,cAAM,GAAG,MAAM;AACf,eAAO;AAAA,MACT;AAEA,YAAM,UAAU,GAAG,OAAO,uBAAuB;AAAA,QAC/C;AAAA,QACA,oBAAoB;AAAA,QACpB,kBAAkB;AAAA,QAClB;AAAA,QACA,YAAY;AAAA,QACZ,cAAc,oBAAI,KAAK;AAAA,QACvB,gBAAgB,MAAM;AAAA,QACtB,UAAU,MAAM;AAAA,MAClB,CAAC;AAED,YAAM,GAAG,gBAAgB,OAAO;AAChC,aAAO;AAAA,IACT;AAAA,EACF;AACF;",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import { getSyncQueue } from "./queue.js";
|
|
2
|
+
async function startDataSyncRun(params) {
|
|
3
|
+
const { syncRunService, progressService, scope, input } = params;
|
|
4
|
+
const createProgressJob = input.createProgressJob !== false;
|
|
5
|
+
const progressJob = createProgressJob ? await progressService.createJob(
|
|
6
|
+
{
|
|
7
|
+
jobType: input.progressJob?.jobType ?? `data_sync:${input.direction}`,
|
|
8
|
+
name: input.progressJob?.name ?? `Data sync ${input.integrationId} \u2014 ${input.entityType}`,
|
|
9
|
+
description: input.progressJob?.description ?? `${input.entityType} ${input.direction}`,
|
|
10
|
+
cancellable: input.progressJob?.cancellable ?? true,
|
|
11
|
+
meta: {
|
|
12
|
+
integrationId: input.integrationId,
|
|
13
|
+
entityType: input.entityType,
|
|
14
|
+
direction: input.direction,
|
|
15
|
+
...input.progressJob?.meta ?? {}
|
|
16
|
+
}
|
|
17
|
+
},
|
|
18
|
+
{
|
|
19
|
+
tenantId: scope.tenantId,
|
|
20
|
+
organizationId: scope.organizationId,
|
|
21
|
+
userId: scope.userId
|
|
22
|
+
}
|
|
23
|
+
) : null;
|
|
24
|
+
const run = await syncRunService.createRun(
|
|
25
|
+
{
|
|
26
|
+
integrationId: input.integrationId,
|
|
27
|
+
entityType: input.entityType,
|
|
28
|
+
direction: input.direction,
|
|
29
|
+
cursor: input.cursor ?? null,
|
|
30
|
+
triggeredBy: input.triggeredBy ?? scope.userId ?? null,
|
|
31
|
+
progressJobId: progressJob?.id ?? null
|
|
32
|
+
},
|
|
33
|
+
{
|
|
34
|
+
organizationId: scope.organizationId,
|
|
35
|
+
tenantId: scope.tenantId
|
|
36
|
+
}
|
|
37
|
+
);
|
|
38
|
+
const queueName = input.direction === "import" ? "data-sync-import" : "data-sync-export";
|
|
39
|
+
const queue = getSyncQueue(queueName);
|
|
40
|
+
await queue.enqueue({
|
|
41
|
+
runId: run.id,
|
|
42
|
+
batchSize: input.batchSize ?? 100,
|
|
43
|
+
scope: {
|
|
44
|
+
organizationId: scope.organizationId,
|
|
45
|
+
tenantId: scope.tenantId,
|
|
46
|
+
userId: scope.userId ?? null
|
|
47
|
+
}
|
|
48
|
+
});
|
|
49
|
+
return {
|
|
50
|
+
run,
|
|
51
|
+
progressJob
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
export {
|
|
55
|
+
startDataSyncRun
|
|
56
|
+
};
|
|
57
|
+
//# sourceMappingURL=start-run.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../../../src/modules/data_sync/lib/start-run.ts"],
|
|
4
|
+
"sourcesContent": ["import type { ProgressService } from '../../progress/lib/progressService'\nimport type { SyncRunService } from './sync-run-service'\nimport { getSyncQueue } from './queue'\n\nexport type DataSyncStartScope = {\n organizationId: string\n tenantId: string\n userId?: string | null\n}\n\nexport type StartDataSyncRunInput = {\n integrationId: string\n entityType: string\n direction: 'import' | 'export'\n cursor?: string | null\n triggeredBy?: string | null\n batchSize?: number\n createProgressJob?: boolean\n progressJob?: {\n jobType?: string\n name?: string\n description?: string\n cancellable?: boolean\n meta?: Record<string, unknown>\n }\n}\n\nexport async function startDataSyncRun(params: {\n syncRunService: SyncRunService\n progressService: ProgressService\n scope: DataSyncStartScope\n input: StartDataSyncRunInput\n}) {\n const { syncRunService, progressService, scope, input } = params\n const createProgressJob = input.createProgressJob !== false\n\n const progressJob = createProgressJob\n ? await progressService.createJob(\n {\n jobType: input.progressJob?.jobType ?? `data_sync:${input.direction}`,\n name: input.progressJob?.name ?? `Data sync ${input.integrationId} \u2014 ${input.entityType}`,\n description: input.progressJob?.description ?? `${input.entityType} ${input.direction}`,\n cancellable: input.progressJob?.cancellable ?? true,\n meta: {\n integrationId: input.integrationId,\n entityType: input.entityType,\n direction: input.direction,\n ...(input.progressJob?.meta ?? {}),\n },\n },\n {\n tenantId: scope.tenantId,\n organizationId: scope.organizationId,\n userId: scope.userId,\n },\n )\n : null\n\n const run = await syncRunService.createRun(\n {\n integrationId: input.integrationId,\n entityType: input.entityType,\n direction: input.direction,\n cursor: input.cursor ?? null,\n triggeredBy: input.triggeredBy ?? scope.userId ?? null,\n progressJobId: progressJob?.id ?? null,\n },\n {\n organizationId: scope.organizationId,\n tenantId: scope.tenantId,\n },\n )\n\n const queueName = input.direction === 'import' ? 'data-sync-import' : 'data-sync-export'\n const queue = getSyncQueue(queueName)\n await queue.enqueue({\n runId: run.id,\n batchSize: input.batchSize ?? 100,\n scope: {\n organizationId: scope.organizationId,\n tenantId: scope.tenantId,\n userId: scope.userId ?? null,\n },\n })\n\n return {\n run,\n progressJob,\n }\n}\n"],
|
|
5
|
+
"mappings": "AAEA,SAAS,oBAAoB;AAyB7B,eAAsB,iBAAiB,QAKpC;AACD,QAAM,EAAE,gBAAgB,iBAAiB,OAAO,MAAM,IAAI;AAC1D,QAAM,oBAAoB,MAAM,sBAAsB;AAEtD,QAAM,cAAc,oBAChB,MAAM,gBAAgB;AAAA,IACtB;AAAA,MACE,SAAS,MAAM,aAAa,WAAW,aAAa,MAAM,SAAS;AAAA,MACnE,MAAM,MAAM,aAAa,QAAQ,aAAa,MAAM,aAAa,WAAM,MAAM,UAAU;AAAA,MACvF,aAAa,MAAM,aAAa,eAAe,GAAG,MAAM,UAAU,IAAI,MAAM,SAAS;AAAA,MACrF,aAAa,MAAM,aAAa,eAAe;AAAA,MAC/C,MAAM;AAAA,QACJ,eAAe,MAAM;AAAA,QACrB,YAAY,MAAM;AAAA,QAClB,WAAW,MAAM;AAAA,QACjB,GAAI,MAAM,aAAa,QAAQ,CAAC;AAAA,MAClC;AAAA,IACF;AAAA,IACA;AAAA,MACE,UAAU,MAAM;AAAA,MAChB,gBAAgB,MAAM;AAAA,MACtB,QAAQ,MAAM;AAAA,IAChB;AAAA,EACF,IACE;AAEJ,QAAM,MAAM,MAAM,eAAe;AAAA,IAC/B;AAAA,MACE,eAAe,MAAM;AAAA,MACrB,YAAY,MAAM;AAAA,MAClB,WAAW,MAAM;AAAA,MACjB,QAAQ,MAAM,UAAU;AAAA,MACxB,aAAa,MAAM,eAAe,MAAM,UAAU;AAAA,MAClD,eAAe,aAAa,MAAM;AAAA,IACpC;AAAA,IACA;AAAA,MACE,gBAAgB,MAAM;AAAA,MACtB,UAAU,MAAM;AAAA,IAClB;AAAA,EACF;AAEA,QAAM,YAAY,MAAM,cAAc,WAAW,qBAAqB;AACtE,QAAM,QAAQ,aAAa,SAAS;AACpC,QAAM,MAAM,QAAQ;AAAA,IAClB,OAAO,IAAI;AAAA,IACX,WAAW,MAAM,aAAa;AAAA,IAC9B,OAAO;AAAA,MACL,gBAAgB,MAAM;AAAA,MACtB,UAAU,MAAM;AAAA,MAChB,QAAQ,MAAM,UAAU;AAAA,IAC1B;AAAA,EACF,CAAC;AAED,SAAO;AAAA,IACL;AAAA,IACA;AAAA,EACF;AACF;",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { getIntegration } from "@open-mercato/shared/modules/integrations/types";
|
|
2
|
+
import { refreshCoverageSnapshot } from "../../query_index/lib/coverage.js";
|
|
2
3
|
import { emitDataSyncEvent } from "../events.js";
|
|
3
4
|
import { getDataSyncAdapter } from "./adapter-registry.js";
|
|
4
5
|
function resolveProviderKey(integrationId) {
|
|
@@ -12,6 +13,7 @@ function applyImportCounters(batch) {
|
|
|
12
13
|
for (const item of batch.items) {
|
|
13
14
|
if (item.action === "create") createdCount += 1;
|
|
14
15
|
else if (item.action === "update") updatedCount += 1;
|
|
16
|
+
else if (item.action === "failed") failedCount += 1;
|
|
15
17
|
else skippedCount += 1;
|
|
16
18
|
}
|
|
17
19
|
return { createdCount, updatedCount, skippedCount, failedCount };
|
|
@@ -55,6 +57,40 @@ function createSyncEngine(deps) {
|
|
|
55
57
|
}
|
|
56
58
|
);
|
|
57
59
|
}
|
|
60
|
+
async function refreshCoverageSnapshots(entityTypes, scope) {
|
|
61
|
+
if (!entityTypes || entityTypes.length === 0) return;
|
|
62
|
+
await Promise.allSettled(
|
|
63
|
+
Array.from(new Set(entityTypes.filter((value) => typeof value === "string" && value.trim().length > 0))).map((entityType) => refreshCoverageSnapshot(deps.em, {
|
|
64
|
+
entityType,
|
|
65
|
+
tenantId: scope.tenantId,
|
|
66
|
+
organizationId: scope.organizationId
|
|
67
|
+
}))
|
|
68
|
+
);
|
|
69
|
+
}
|
|
70
|
+
async function logImportItemFailures(runId, integrationId, items, scope) {
|
|
71
|
+
const failedItems = items.filter((item) => item.action === "failed");
|
|
72
|
+
for (const item of failedItems) {
|
|
73
|
+
const errorMessage = typeof item.data.errorMessage === "string" && item.data.errorMessage.trim().length > 0 ? item.data.errorMessage.trim() : "Import item failed";
|
|
74
|
+
const sourceProductUuid = typeof item.data.sourceProductUuid === "string" && item.data.sourceProductUuid.trim().length > 0 ? item.data.sourceProductUuid.trim() : null;
|
|
75
|
+
const sourceIdentifier = typeof item.data.sourceIdentifier === "string" && item.data.sourceIdentifier.trim().length > 0 ? item.data.sourceIdentifier.trim() : null;
|
|
76
|
+
const message = [
|
|
77
|
+
`Failed to import Akeneo product ${item.externalId}`,
|
|
78
|
+
sourceProductUuid ? `(uuid: ${sourceProductUuid})` : null,
|
|
79
|
+
sourceIdentifier ? `(identifier: ${sourceIdentifier})` : null,
|
|
80
|
+
`: ${errorMessage}`
|
|
81
|
+
].filter((part) => part !== null).join(" ");
|
|
82
|
+
await integrationLogService.write(
|
|
83
|
+
{
|
|
84
|
+
integrationId,
|
|
85
|
+
runId,
|
|
86
|
+
level: "error",
|
|
87
|
+
message,
|
|
88
|
+
payload: item.data
|
|
89
|
+
},
|
|
90
|
+
scope
|
|
91
|
+
);
|
|
92
|
+
}
|
|
93
|
+
}
|
|
58
94
|
async function finalizeRun(runId, status, scope, error) {
|
|
59
95
|
const run = await syncRunService.markStatus(runId, status, scope, error);
|
|
60
96
|
if (!run) return;
|
|
@@ -89,6 +125,15 @@ function createSyncEngine(deps) {
|
|
|
89
125
|
userId: scope.userId
|
|
90
126
|
}
|
|
91
127
|
);
|
|
128
|
+
} else if (status === "cancelled") {
|
|
129
|
+
await progressService.markCancelled(
|
|
130
|
+
run.progressJobId,
|
|
131
|
+
{
|
|
132
|
+
tenantId: scope.tenantId,
|
|
133
|
+
organizationId: scope.organizationId,
|
|
134
|
+
userId: scope.userId
|
|
135
|
+
}
|
|
136
|
+
);
|
|
92
137
|
}
|
|
93
138
|
}
|
|
94
139
|
if (status === "completed") {
|
|
@@ -130,6 +175,16 @@ function createSyncEngine(deps) {
|
|
|
130
175
|
console.warn(`[data-sync] Skipping stale import job for missing run ${runId}`);
|
|
131
176
|
return;
|
|
132
177
|
}
|
|
178
|
+
if (run.status === "cancelled") {
|
|
179
|
+
if (run.progressJobId) {
|
|
180
|
+
await progressService.markCancelled(run.progressJobId, {
|
|
181
|
+
tenantId: scope.tenantId,
|
|
182
|
+
organizationId: scope.organizationId,
|
|
183
|
+
userId: scope.userId
|
|
184
|
+
});
|
|
185
|
+
}
|
|
186
|
+
return;
|
|
187
|
+
}
|
|
133
188
|
const providerKey = resolveProviderKey(run.integrationId);
|
|
134
189
|
const adapter = getDataSyncAdapter(providerKey);
|
|
135
190
|
if (!adapter?.streamImport) {
|
|
@@ -139,7 +194,17 @@ function createSyncEngine(deps) {
|
|
|
139
194
|
if (!credentials) {
|
|
140
195
|
throw new Error(`Integration ${run.integrationId} is missing credentials`);
|
|
141
196
|
}
|
|
142
|
-
await syncRunService.markStatus(run.id, "running", scope);
|
|
197
|
+
const activeRun = await syncRunService.markStatus(run.id, "running", scope);
|
|
198
|
+
if (!activeRun || activeRun.status !== "running") {
|
|
199
|
+
if (run.progressJobId) {
|
|
200
|
+
await progressService.markCancelled(run.progressJobId, {
|
|
201
|
+
tenantId: scope.tenantId,
|
|
202
|
+
organizationId: scope.organizationId,
|
|
203
|
+
userId: scope.userId
|
|
204
|
+
});
|
|
205
|
+
}
|
|
206
|
+
return;
|
|
207
|
+
}
|
|
143
208
|
await emitDataSyncEvent("data_sync.run.started", {
|
|
144
209
|
runId: run.id,
|
|
145
210
|
integrationId: run.integrationId,
|
|
@@ -172,7 +237,8 @@ function createSyncEngine(deps) {
|
|
|
172
237
|
return;
|
|
173
238
|
}
|
|
174
239
|
const delta = applyImportCounters(batch);
|
|
175
|
-
processedCount
|
|
240
|
+
const processedBatchCount = batch.processedCount ?? batch.items.length;
|
|
241
|
+
processedCount += processedBatchCount;
|
|
176
242
|
totalCount = batch.totalEstimate ?? totalCount;
|
|
177
243
|
await syncRunService.updateCounts(
|
|
178
244
|
run.id,
|
|
@@ -184,15 +250,18 @@ function createSyncEngine(deps) {
|
|
|
184
250
|
);
|
|
185
251
|
await syncRunService.updateCursor(run.id, batch.cursor, scope);
|
|
186
252
|
await updateProgress(run.progressJobId, processedCount, totalCount, scope);
|
|
253
|
+
await refreshCoverageSnapshots(batch.refreshCoverageEntityTypes, scope);
|
|
254
|
+
await logImportItemFailures(run.id, run.integrationId, batch.items, scope);
|
|
187
255
|
await integrationLogService.write(
|
|
188
256
|
{
|
|
189
257
|
integrationId: run.integrationId,
|
|
190
258
|
runId: run.id,
|
|
191
259
|
level: "info",
|
|
192
|
-
message: `Processed import batch ${batch.batchIndex}`,
|
|
260
|
+
message: batch.message?.trim().length ? batch.message.trim() : `Processed import batch ${batch.batchIndex}`,
|
|
193
261
|
payload: {
|
|
194
262
|
processedCount,
|
|
195
263
|
batchSize: batch.items.length,
|
|
264
|
+
processedBatchCount,
|
|
196
265
|
cursor: batch.cursor
|
|
197
266
|
}
|
|
198
267
|
},
|
|
@@ -221,6 +290,16 @@ function createSyncEngine(deps) {
|
|
|
221
290
|
console.warn(`[data-sync] Skipping stale export job for missing run ${runId}`);
|
|
222
291
|
return;
|
|
223
292
|
}
|
|
293
|
+
if (run.status === "cancelled") {
|
|
294
|
+
if (run.progressJobId) {
|
|
295
|
+
await progressService.markCancelled(run.progressJobId, {
|
|
296
|
+
tenantId: scope.tenantId,
|
|
297
|
+
organizationId: scope.organizationId,
|
|
298
|
+
userId: scope.userId
|
|
299
|
+
});
|
|
300
|
+
}
|
|
301
|
+
return;
|
|
302
|
+
}
|
|
224
303
|
const providerKey = resolveProviderKey(run.integrationId);
|
|
225
304
|
const adapter = getDataSyncAdapter(providerKey);
|
|
226
305
|
if (!adapter?.streamExport) {
|
|
@@ -230,7 +309,17 @@ function createSyncEngine(deps) {
|
|
|
230
309
|
if (!credentials) {
|
|
231
310
|
throw new Error(`Integration ${run.integrationId} is missing credentials`);
|
|
232
311
|
}
|
|
233
|
-
await syncRunService.markStatus(run.id, "running", scope);
|
|
312
|
+
const activeRun = await syncRunService.markStatus(run.id, "running", scope);
|
|
313
|
+
if (!activeRun || activeRun.status !== "running") {
|
|
314
|
+
if (run.progressJobId) {
|
|
315
|
+
await progressService.markCancelled(run.progressJobId, {
|
|
316
|
+
tenantId: scope.tenantId,
|
|
317
|
+
organizationId: scope.organizationId,
|
|
318
|
+
userId: scope.userId
|
|
319
|
+
});
|
|
320
|
+
}
|
|
321
|
+
return;
|
|
322
|
+
}
|
|
234
323
|
await emitDataSyncEvent("data_sync.run.started", {
|
|
235
324
|
runId: run.id,
|
|
236
325
|
integrationId: run.integrationId,
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../../src/modules/data_sync/lib/sync-engine.ts"],
|
|
4
|
-
"sourcesContent": ["import type { EntityManager } from '@mikro-orm/postgresql'\nimport { getIntegration } from '@open-mercato/shared/modules/integrations/types'\nimport type { CredentialsService } from '../../integrations/lib/credentials-service'\nimport type { IntegrationLogService } from '../../integrations/lib/log-service'\nimport type { ProgressService } from '../../progress/lib/progressService'\nimport { emitDataSyncEvent } from '../events'\nimport type { DataSyncAdapter, DataMapping, ExportBatch, ImportBatch } from './adapter'\nimport { getDataSyncAdapter } from './adapter-registry'\nimport type { SyncRunService } from './sync-run-service'\n\ntype SyncScope = {\n organizationId: string\n tenantId: string\n userId?: string | null\n}\n\ntype EngineDeps = {\n em: EntityManager\n syncRunService: SyncRunService\n integrationCredentialsService: CredentialsService\n integrationLogService: IntegrationLogService\n progressService: ProgressService\n}\n\nfunction resolveProviderKey(integrationId: string): string {\n return getIntegration(integrationId)?.providerKey ?? integrationId\n}\n\nfunction applyImportCounters(batch: ImportBatch): Pick<Required<SyncCounterDelta>, 'createdCount' | 'updatedCount' | 'skippedCount' | 'failedCount'> {\n let createdCount = 0\n let updatedCount = 0\n let skippedCount = 0\n let failedCount = 0\n\n for (const item of batch.items) {\n if (item.action === 'create') createdCount += 1\n else if (item.action === 'update') updatedCount += 1\n else skippedCount += 1\n }\n\n return { createdCount, updatedCount, skippedCount, failedCount }\n}\n\ntype SyncCounterDelta = {\n createdCount?: number\n updatedCount?: number\n skippedCount?: number\n failedCount?: number\n processedCount: number\n}\n\nfunction applyExportCounters(batch: ExportBatch): SyncCounterDelta {\n let failedCount = 0\n let skippedCount = 0\n let updatedCount = 0\n\n for (const result of batch.results) {\n if (result.status === 'error') failedCount += 1\n else if (result.status === 'skipped') skippedCount += 1\n else updatedCount += 1\n }\n\n return {\n failedCount,\n skippedCount,\n updatedCount,\n processedCount: batch.results.length,\n }\n}\n\nexport function createSyncEngine(deps: EngineDeps) {\n const { syncRunService, integrationCredentialsService, integrationLogService, progressService } = deps\n\n async function resolveMapping(adapter: DataSyncAdapter, entityType: string, scope: SyncScope): Promise<DataMapping> {\n return adapter.getMapping({\n entityType,\n scope: { organizationId: scope.organizationId, tenantId: scope.tenantId },\n })\n }\n\n async function updateProgress(progressJobId: string | null | undefined, processedCount: number, totalCount: number | null, scope: SyncScope): Promise<void> {\n if (!progressJobId) return\n\n await progressService.updateProgress(\n progressJobId,\n {\n processedCount,\n totalCount: totalCount ?? undefined,\n },\n {\n tenantId: scope.tenantId,\n organizationId: scope.organizationId,\n userId: scope.userId,\n },\n )\n }\n\n async function finalizeRun(runId: string, status: 'completed' | 'failed' | 'cancelled', scope: SyncScope, error?: string): Promise<void> {\n const run = await syncRunService.markStatus(runId, status, scope, error)\n if (!run) return\n\n if (run.progressJobId) {\n if (status === 'completed') {\n await progressService.completeJob(\n run.progressJobId,\n {\n resultSummary: {\n createdCount: run.createdCount,\n updatedCount: run.updatedCount,\n skippedCount: run.skippedCount,\n failedCount: run.failedCount,\n batchesCompleted: run.batchesCompleted,\n },\n },\n {\n tenantId: scope.tenantId,\n organizationId: scope.organizationId,\n userId: scope.userId,\n },\n )\n } else if (status === 'failed') {\n await progressService.failJob(\n run.progressJobId,\n {\n errorMessage: error ?? 'Sync run failed',\n },\n {\n tenantId: scope.tenantId,\n organizationId: scope.organizationId,\n userId: scope.userId,\n },\n )\n }\n }\n\n if (status === 'completed') {\n await emitDataSyncEvent('data_sync.run.completed', {\n runId,\n integrationId: run.integrationId,\n entityType: run.entityType,\n direction: run.direction,\n tenantId: scope.tenantId,\n organizationId: scope.organizationId,\n })\n return\n }\n\n if (status === 'cancelled') {\n await emitDataSyncEvent('data_sync.run.cancelled', {\n runId,\n integrationId: run.integrationId,\n entityType: run.entityType,\n direction: run.direction,\n tenantId: scope.tenantId,\n organizationId: scope.organizationId,\n })\n return\n }\n\n await emitDataSyncEvent('data_sync.run.failed', {\n runId,\n integrationId: run.integrationId,\n entityType: run.entityType,\n direction: run.direction,\n error: error ?? null,\n tenantId: scope.tenantId,\n organizationId: scope.organizationId,\n })\n }\n\n return {\n async runImport(runId: string, batchSize: number, scope: SyncScope): Promise<void> {\n const run = await syncRunService.getRun(runId, scope)\n if (!run) {\n console.warn(`[data-sync] Skipping stale import job for missing run ${runId}`)\n return\n }\n\n const providerKey = resolveProviderKey(run.integrationId)\n const adapter = getDataSyncAdapter(providerKey)\n if (!adapter?.streamImport) {\n throw new Error(`No import adapter registered for provider ${providerKey}`)\n }\n\n const credentials = await integrationCredentialsService.resolve(run.integrationId, scope)\n if (!credentials) {\n throw new Error(`Integration ${run.integrationId} is missing credentials`)\n }\n\n await syncRunService.markStatus(run.id, 'running', scope)\n await emitDataSyncEvent('data_sync.run.started', {\n runId: run.id,\n integrationId: run.integrationId,\n entityType: run.entityType,\n direction: run.direction,\n tenantId: scope.tenantId,\n organizationId: scope.organizationId,\n })\n\n if (run.progressJobId) {\n await progressService.startJob(run.progressJobId, {\n tenantId: scope.tenantId,\n organizationId: scope.organizationId,\n userId: scope.userId,\n })\n }\n\n const mapping = await resolveMapping(adapter, run.entityType, scope)\n let processedCount = 0\n let totalCount: number | null = null\n\n try {\n for await (const batch of adapter.streamImport({\n entityType: run.entityType,\n cursor: run.cursor ?? undefined,\n batchSize,\n credentials,\n mapping,\n scope: { organizationId: scope.organizationId, tenantId: scope.tenantId },\n })) {\n if (run.progressJobId && await progressService.isCancellationRequested(run.progressJobId)) {\n await finalizeRun(run.id, 'cancelled', scope)\n return\n }\n\n const delta = applyImportCounters(batch)\n processedCount += batch.items.length\n totalCount = batch.totalEstimate ?? totalCount\n\n await syncRunService.updateCounts(\n run.id,\n {\n ...delta,\n batchesCompleted: 1,\n },\n scope,\n )\n await syncRunService.updateCursor(run.id, batch.cursor, scope)\n\n await updateProgress(run.progressJobId, processedCount, totalCount, scope)\n\n await integrationLogService.write(\n {\n integrationId: run.integrationId,\n runId: run.id,\n level: 'info',\n message: `Processed import batch ${batch.batchIndex}`,\n payload: {\n processedCount,\n batchSize: batch.items.length,\n cursor: batch.cursor,\n },\n },\n scope,\n )\n }\n } catch (error) {\n const message = error instanceof Error ? error.message : 'Sync import failed'\n await integrationLogService.write(\n {\n integrationId: run.integrationId,\n runId: run.id,\n level: 'error',\n message,\n },\n scope,\n )\n await finalizeRun(run.id, 'failed', scope, message)\n return\n }\n\n await finalizeRun(run.id, 'completed', scope)\n },\n\n async runExport(runId: string, batchSize: number, scope: SyncScope): Promise<void> {\n const run = await syncRunService.getRun(runId, scope)\n if (!run) {\n console.warn(`[data-sync] Skipping stale export job for missing run ${runId}`)\n return\n }\n\n const providerKey = resolveProviderKey(run.integrationId)\n const adapter = getDataSyncAdapter(providerKey)\n if (!adapter?.streamExport) {\n throw new Error(`No export adapter registered for provider ${providerKey}`)\n }\n\n const credentials = await integrationCredentialsService.resolve(run.integrationId, scope)\n if (!credentials) {\n throw new Error(`Integration ${run.integrationId} is missing credentials`)\n }\n\n await syncRunService.markStatus(run.id, 'running', scope)\n await emitDataSyncEvent('data_sync.run.started', {\n runId: run.id,\n integrationId: run.integrationId,\n entityType: run.entityType,\n direction: run.direction,\n tenantId: scope.tenantId,\n organizationId: scope.organizationId,\n })\n\n if (run.progressJobId) {\n await progressService.startJob(run.progressJobId, {\n tenantId: scope.tenantId,\n organizationId: scope.organizationId,\n userId: scope.userId,\n })\n }\n\n const mapping = await resolveMapping(adapter, run.entityType, scope)\n let processedCount = 0\n\n try {\n for await (const batch of adapter.streamExport({\n entityType: run.entityType,\n cursor: run.cursor ?? undefined,\n batchSize,\n credentials,\n mapping,\n scope: { organizationId: scope.organizationId, tenantId: scope.tenantId },\n })) {\n if (run.progressJobId && await progressService.isCancellationRequested(run.progressJobId)) {\n await finalizeRun(run.id, 'cancelled', scope)\n return\n }\n\n const delta = applyExportCounters(batch)\n processedCount += delta.processedCount\n\n await syncRunService.updateCounts(\n run.id,\n {\n createdCount: 0,\n updatedCount: delta.updatedCount,\n skippedCount: delta.skippedCount,\n failedCount: delta.failedCount,\n batchesCompleted: 1,\n },\n scope,\n )\n\n await syncRunService.updateCursor(run.id, batch.cursor, scope)\n await updateProgress(run.progressJobId, processedCount, null, scope)\n\n await integrationLogService.write(\n {\n integrationId: run.integrationId,\n runId: run.id,\n level: 'info',\n message: `Processed export batch ${batch.batchIndex}`,\n payload: {\n processedCount,\n batchSize: batch.results.length,\n cursor: batch.cursor,\n },\n },\n scope,\n )\n }\n } catch (error) {\n const message = error instanceof Error ? error.message : 'Sync export failed'\n await integrationLogService.write(\n {\n integrationId: run.integrationId,\n runId: run.id,\n level: 'error',\n message,\n },\n scope,\n )\n await finalizeRun(run.id, 'failed', scope, message)\n return\n }\n\n await finalizeRun(run.id, 'completed', scope)\n },\n }\n}\n\nexport type SyncEngine = ReturnType<typeof createSyncEngine>\n"],
|
|
5
|
-
"mappings": "AACA,SAAS,sBAAsB;AAI/B,SAAS,yBAAyB;AAElC,SAAS,0BAA0B;AAiBnC,SAAS,mBAAmB,eAA+B;AACzD,SAAO,eAAe,aAAa,GAAG,eAAe;AACvD;AAEA,SAAS,oBAAoB,OAAwH;AACnJ,MAAI,eAAe;AACnB,MAAI,eAAe;AACnB,MAAI,eAAe;AACnB,MAAI,cAAc;AAElB,aAAW,QAAQ,MAAM,OAAO;AAC9B,QAAI,KAAK,WAAW,SAAU,iBAAgB;AAAA,aACrC,KAAK,WAAW,SAAU,iBAAgB;AAAA,
|
|
4
|
+
"sourcesContent": ["import type { EntityManager } from '@mikro-orm/postgresql'\nimport { getIntegration } from '@open-mercato/shared/modules/integrations/types'\nimport type { CredentialsService } from '../../integrations/lib/credentials-service'\nimport type { IntegrationLogService } from '../../integrations/lib/log-service'\nimport type { ProgressService } from '../../progress/lib/progressService'\nimport { refreshCoverageSnapshot } from '../../query_index/lib/coverage'\nimport { emitDataSyncEvent } from '../events'\nimport type { DataSyncAdapter, DataMapping, ExportBatch, ImportBatch } from './adapter'\nimport { getDataSyncAdapter } from './adapter-registry'\nimport type { SyncRunService } from './sync-run-service'\n\ntype SyncScope = {\n organizationId: string\n tenantId: string\n userId?: string | null\n}\n\ntype EngineDeps = {\n em: EntityManager\n syncRunService: SyncRunService\n integrationCredentialsService: CredentialsService\n integrationLogService: IntegrationLogService\n progressService: ProgressService\n}\n\nfunction resolveProviderKey(integrationId: string): string {\n return getIntegration(integrationId)?.providerKey ?? integrationId\n}\n\nfunction applyImportCounters(batch: ImportBatch): Pick<Required<SyncCounterDelta>, 'createdCount' | 'updatedCount' | 'skippedCount' | 'failedCount'> {\n let createdCount = 0\n let updatedCount = 0\n let skippedCount = 0\n let failedCount = 0\n\n for (const item of batch.items) {\n if (item.action === 'create') createdCount += 1\n else if (item.action === 'update') updatedCount += 1\n else if (item.action === 'failed') failedCount += 1\n else skippedCount += 1\n }\n\n return { createdCount, updatedCount, skippedCount, failedCount }\n}\n\ntype SyncCounterDelta = {\n createdCount?: number\n updatedCount?: number\n skippedCount?: number\n failedCount?: number\n processedCount: number\n}\n\nfunction applyExportCounters(batch: ExportBatch): SyncCounterDelta {\n let failedCount = 0\n let skippedCount = 0\n let updatedCount = 0\n\n for (const result of batch.results) {\n if (result.status === 'error') failedCount += 1\n else if (result.status === 'skipped') skippedCount += 1\n else updatedCount += 1\n }\n\n return {\n failedCount,\n skippedCount,\n updatedCount,\n processedCount: batch.results.length,\n }\n}\n\nexport function createSyncEngine(deps: EngineDeps) {\n const { syncRunService, integrationCredentialsService, integrationLogService, progressService } = deps\n\n async function resolveMapping(adapter: DataSyncAdapter, entityType: string, scope: SyncScope): Promise<DataMapping> {\n return adapter.getMapping({\n entityType,\n scope: { organizationId: scope.organizationId, tenantId: scope.tenantId },\n })\n }\n\n async function updateProgress(progressJobId: string | null | undefined, processedCount: number, totalCount: number | null, scope: SyncScope): Promise<void> {\n if (!progressJobId) return\n\n await progressService.updateProgress(\n progressJobId,\n {\n processedCount,\n totalCount: totalCount ?? undefined,\n },\n {\n tenantId: scope.tenantId,\n organizationId: scope.organizationId,\n userId: scope.userId,\n },\n )\n }\n\n async function refreshCoverageSnapshots(entityTypes: string[] | undefined, scope: SyncScope): Promise<void> {\n if (!entityTypes || entityTypes.length === 0) return\n\n await Promise.allSettled(\n Array.from(new Set(entityTypes.filter((value) => typeof value === 'string' && value.trim().length > 0)))\n .map((entityType) => refreshCoverageSnapshot(deps.em, {\n entityType,\n tenantId: scope.tenantId,\n organizationId: scope.organizationId,\n })),\n )\n }\n\n async function logImportItemFailures(\n runId: string,\n integrationId: string,\n items: ImportBatch['items'],\n scope: SyncScope,\n ): Promise<void> {\n const failedItems = items.filter((item) => item.action === 'failed')\n for (const item of failedItems) {\n const errorMessage = typeof item.data.errorMessage === 'string' && item.data.errorMessage.trim().length > 0\n ? item.data.errorMessage.trim()\n : 'Import item failed'\n const sourceProductUuid = typeof item.data.sourceProductUuid === 'string' && item.data.sourceProductUuid.trim().length > 0\n ? item.data.sourceProductUuid.trim()\n : null\n const sourceIdentifier = typeof item.data.sourceIdentifier === 'string' && item.data.sourceIdentifier.trim().length > 0\n ? item.data.sourceIdentifier.trim()\n : null\n const message = [\n `Failed to import Akeneo product ${item.externalId}`,\n sourceProductUuid ? `(uuid: ${sourceProductUuid})` : null,\n sourceIdentifier ? `(identifier: ${sourceIdentifier})` : null,\n `: ${errorMessage}`,\n ].filter((part) => part !== null).join(' ')\n\n await integrationLogService.write(\n {\n integrationId,\n runId,\n level: 'error',\n message,\n payload: item.data,\n },\n scope,\n )\n }\n }\n\n async function finalizeRun(runId: string, status: 'completed' | 'failed' | 'cancelled', scope: SyncScope, error?: string): Promise<void> {\n const run = await syncRunService.markStatus(runId, status, scope, error)\n if (!run) return\n\n if (run.progressJobId) {\n if (status === 'completed') {\n await progressService.completeJob(\n run.progressJobId,\n {\n resultSummary: {\n createdCount: run.createdCount,\n updatedCount: run.updatedCount,\n skippedCount: run.skippedCount,\n failedCount: run.failedCount,\n batchesCompleted: run.batchesCompleted,\n },\n },\n {\n tenantId: scope.tenantId,\n organizationId: scope.organizationId,\n userId: scope.userId,\n },\n )\n } else if (status === 'failed') {\n await progressService.failJob(\n run.progressJobId,\n {\n errorMessage: error ?? 'Sync run failed',\n },\n {\n tenantId: scope.tenantId,\n organizationId: scope.organizationId,\n userId: scope.userId,\n },\n )\n } else if (status === 'cancelled') {\n await progressService.markCancelled(\n run.progressJobId,\n {\n tenantId: scope.tenantId,\n organizationId: scope.organizationId,\n userId: scope.userId,\n },\n )\n }\n }\n\n if (status === 'completed') {\n await emitDataSyncEvent('data_sync.run.completed', {\n runId,\n integrationId: run.integrationId,\n entityType: run.entityType,\n direction: run.direction,\n tenantId: scope.tenantId,\n organizationId: scope.organizationId,\n })\n return\n }\n\n if (status === 'cancelled') {\n await emitDataSyncEvent('data_sync.run.cancelled', {\n runId,\n integrationId: run.integrationId,\n entityType: run.entityType,\n direction: run.direction,\n tenantId: scope.tenantId,\n organizationId: scope.organizationId,\n })\n return\n }\n\n await emitDataSyncEvent('data_sync.run.failed', {\n runId,\n integrationId: run.integrationId,\n entityType: run.entityType,\n direction: run.direction,\n error: error ?? null,\n tenantId: scope.tenantId,\n organizationId: scope.organizationId,\n })\n }\n\n return {\n async runImport(runId: string, batchSize: number, scope: SyncScope): Promise<void> {\n const run = await syncRunService.getRun(runId, scope)\n if (!run) {\n console.warn(`[data-sync] Skipping stale import job for missing run ${runId}`)\n return\n }\n if (run.status === 'cancelled') {\n if (run.progressJobId) {\n await progressService.markCancelled(run.progressJobId, {\n tenantId: scope.tenantId,\n organizationId: scope.organizationId,\n userId: scope.userId,\n })\n }\n return\n }\n\n const providerKey = resolveProviderKey(run.integrationId)\n const adapter = getDataSyncAdapter(providerKey)\n if (!adapter?.streamImport) {\n throw new Error(`No import adapter registered for provider ${providerKey}`)\n }\n\n const credentials = await integrationCredentialsService.resolve(run.integrationId, scope)\n if (!credentials) {\n throw new Error(`Integration ${run.integrationId} is missing credentials`)\n }\n\n const activeRun = await syncRunService.markStatus(run.id, 'running', scope)\n if (!activeRun || activeRun.status !== 'running') {\n if (run.progressJobId) {\n await progressService.markCancelled(run.progressJobId, {\n tenantId: scope.tenantId,\n organizationId: scope.organizationId,\n userId: scope.userId,\n })\n }\n return\n }\n await emitDataSyncEvent('data_sync.run.started', {\n runId: run.id,\n integrationId: run.integrationId,\n entityType: run.entityType,\n direction: run.direction,\n tenantId: scope.tenantId,\n organizationId: scope.organizationId,\n })\n\n if (run.progressJobId) {\n await progressService.startJob(run.progressJobId, {\n tenantId: scope.tenantId,\n organizationId: scope.organizationId,\n userId: scope.userId,\n })\n }\n\n const mapping = await resolveMapping(adapter, run.entityType, scope)\n let processedCount = 0\n let totalCount: number | null = null\n\n try {\n for await (const batch of adapter.streamImport({\n entityType: run.entityType,\n cursor: run.cursor ?? undefined,\n batchSize,\n credentials,\n mapping,\n scope: { organizationId: scope.organizationId, tenantId: scope.tenantId },\n })) {\n if (run.progressJobId && await progressService.isCancellationRequested(run.progressJobId)) {\n await finalizeRun(run.id, 'cancelled', scope)\n return\n }\n\n const delta = applyImportCounters(batch)\n const processedBatchCount = batch.processedCount ?? batch.items.length\n processedCount += processedBatchCount\n totalCount = batch.totalEstimate ?? totalCount\n\n await syncRunService.updateCounts(\n run.id,\n {\n ...delta,\n batchesCompleted: 1,\n },\n scope,\n )\n await syncRunService.updateCursor(run.id, batch.cursor, scope)\n\n await updateProgress(run.progressJobId, processedCount, totalCount, scope)\n await refreshCoverageSnapshots(batch.refreshCoverageEntityTypes, scope)\n await logImportItemFailures(run.id, run.integrationId, batch.items, scope)\n\n await integrationLogService.write(\n {\n integrationId: run.integrationId,\n runId: run.id,\n level: 'info',\n message: batch.message?.trim().length\n ? batch.message.trim()\n : `Processed import batch ${batch.batchIndex}`,\n payload: {\n processedCount,\n batchSize: batch.items.length,\n processedBatchCount,\n cursor: batch.cursor,\n },\n },\n scope,\n )\n }\n } catch (error) {\n const message = error instanceof Error ? error.message : 'Sync import failed'\n await integrationLogService.write(\n {\n integrationId: run.integrationId,\n runId: run.id,\n level: 'error',\n message,\n },\n scope,\n )\n await finalizeRun(run.id, 'failed', scope, message)\n return\n }\n\n await finalizeRun(run.id, 'completed', scope)\n },\n\n async runExport(runId: string, batchSize: number, scope: SyncScope): Promise<void> {\n const run = await syncRunService.getRun(runId, scope)\n if (!run) {\n console.warn(`[data-sync] Skipping stale export job for missing run ${runId}`)\n return\n }\n if (run.status === 'cancelled') {\n if (run.progressJobId) {\n await progressService.markCancelled(run.progressJobId, {\n tenantId: scope.tenantId,\n organizationId: scope.organizationId,\n userId: scope.userId,\n })\n }\n return\n }\n\n const providerKey = resolveProviderKey(run.integrationId)\n const adapter = getDataSyncAdapter(providerKey)\n if (!adapter?.streamExport) {\n throw new Error(`No export adapter registered for provider ${providerKey}`)\n }\n\n const credentials = await integrationCredentialsService.resolve(run.integrationId, scope)\n if (!credentials) {\n throw new Error(`Integration ${run.integrationId} is missing credentials`)\n }\n\n const activeRun = await syncRunService.markStatus(run.id, 'running', scope)\n if (!activeRun || activeRun.status !== 'running') {\n if (run.progressJobId) {\n await progressService.markCancelled(run.progressJobId, {\n tenantId: scope.tenantId,\n organizationId: scope.organizationId,\n userId: scope.userId,\n })\n }\n return\n }\n await emitDataSyncEvent('data_sync.run.started', {\n runId: run.id,\n integrationId: run.integrationId,\n entityType: run.entityType,\n direction: run.direction,\n tenantId: scope.tenantId,\n organizationId: scope.organizationId,\n })\n\n if (run.progressJobId) {\n await progressService.startJob(run.progressJobId, {\n tenantId: scope.tenantId,\n organizationId: scope.organizationId,\n userId: scope.userId,\n })\n }\n\n const mapping = await resolveMapping(adapter, run.entityType, scope)\n let processedCount = 0\n\n try {\n for await (const batch of adapter.streamExport({\n entityType: run.entityType,\n cursor: run.cursor ?? undefined,\n batchSize,\n credentials,\n mapping,\n scope: { organizationId: scope.organizationId, tenantId: scope.tenantId },\n })) {\n if (run.progressJobId && await progressService.isCancellationRequested(run.progressJobId)) {\n await finalizeRun(run.id, 'cancelled', scope)\n return\n }\n\n const delta = applyExportCounters(batch)\n processedCount += delta.processedCount\n\n await syncRunService.updateCounts(\n run.id,\n {\n createdCount: 0,\n updatedCount: delta.updatedCount,\n skippedCount: delta.skippedCount,\n failedCount: delta.failedCount,\n batchesCompleted: 1,\n },\n scope,\n )\n\n await syncRunService.updateCursor(run.id, batch.cursor, scope)\n await updateProgress(run.progressJobId, processedCount, null, scope)\n\n await integrationLogService.write(\n {\n integrationId: run.integrationId,\n runId: run.id,\n level: 'info',\n message: `Processed export batch ${batch.batchIndex}`,\n payload: {\n processedCount,\n batchSize: batch.results.length,\n cursor: batch.cursor,\n },\n },\n scope,\n )\n }\n } catch (error) {\n const message = error instanceof Error ? error.message : 'Sync export failed'\n await integrationLogService.write(\n {\n integrationId: run.integrationId,\n runId: run.id,\n level: 'error',\n message,\n },\n scope,\n )\n await finalizeRun(run.id, 'failed', scope, message)\n return\n }\n\n await finalizeRun(run.id, 'completed', scope)\n },\n }\n}\n\nexport type SyncEngine = ReturnType<typeof createSyncEngine>\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -59,6 +59,10 @@ function createSyncRunService(em) {
|
|
|
59
59
|
async markStatus(runId, status, scope, error) {
|
|
60
60
|
const row = await this.getRun(runId, scope);
|
|
61
61
|
if (!row) return null;
|
|
62
|
+
const isTerminal = row.status === "completed" || row.status === "failed" || row.status === "cancelled";
|
|
63
|
+
if (isTerminal && row.status !== status) {
|
|
64
|
+
return row;
|
|
65
|
+
}
|
|
62
66
|
row.status = status;
|
|
63
67
|
if (error !== void 0) row.lastError = error;
|
|
64
68
|
await em.flush();
|
|
@@ -130,7 +134,7 @@ function createSyncRunService(em) {
|
|
|
130
134
|
integrationId,
|
|
131
135
|
entityType,
|
|
132
136
|
direction,
|
|
133
|
-
status: "running",
|
|
137
|
+
status: { $in: ["pending", "running"] },
|
|
134
138
|
organizationId: scope.organizationId,
|
|
135
139
|
tenantId: scope.tenantId,
|
|
136
140
|
deletedAt: null
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../../src/modules/data_sync/lib/sync-run-service.ts"],
|
|
4
|
-
"sourcesContent": ["import type { EntityManager, FilterQuery } from '@mikro-orm/postgresql'\nimport { findAndCountWithDecryption, findOneWithDecryption, findWithDecryption } from '@open-mercato/shared/lib/encryption/find'\nimport { SyncCursor, SyncRun } from '../data/entities'\n\ntype SyncScope = {\n organizationId: string\n tenantId: string\n}\n\nexport function createSyncRunService(em: EntityManager) {\n return {\n async createRun(input: {\n integrationId: string\n entityType: string\n direction: 'import' | 'export'\n cursor?: string | null\n triggeredBy?: string | null\n progressJobId?: string | null\n jobId?: string | null\n }, scope: SyncScope): Promise<SyncRun> {\n const row = em.create(SyncRun, {\n integrationId: input.integrationId,\n entityType: input.entityType,\n direction: input.direction,\n status: 'pending',\n cursor: input.cursor,\n initialCursor: input.cursor,\n triggeredBy: input.triggeredBy,\n progressJobId: input.progressJobId,\n jobId: input.jobId,\n organizationId: scope.organizationId,\n tenantId: scope.tenantId,\n })\n\n await em.persistAndFlush(row)\n return row\n },\n\n async getRun(runId: string, scope: SyncScope): Promise<SyncRun | null> {\n return findOneWithDecryption(\n em,\n SyncRun,\n {\n id: runId,\n organizationId: scope.organizationId,\n tenantId: scope.tenantId,\n deletedAt: null,\n },\n undefined,\n scope,\n )\n },\n\n async listRuns(query: {\n integrationId?: string\n entityType?: string\n direction?: 'import' | 'export'\n status?: string\n page: number\n pageSize: number\n }, scope: SyncScope): Promise<{ items: SyncRun[]; total: number }> {\n const where: FilterQuery<SyncRun> = {\n organizationId: scope.organizationId,\n tenantId: scope.tenantId,\n deletedAt: null,\n }\n\n if (query.integrationId) where.integrationId = query.integrationId\n if (query.entityType) where.entityType = query.entityType\n if (query.direction) where.direction = query.direction\n if (query.status) where.status = query.status as SyncRun['status']\n\n const [items, total] = await findAndCountWithDecryption(\n em,\n SyncRun,\n where,\n {\n orderBy: { createdAt: 'DESC' },\n limit: query.pageSize,\n offset: (query.page - 1) * query.pageSize,\n },\n scope,\n )\n\n return { items, total }\n },\n\n async markStatus(runId: string, status: SyncRun['status'], scope: SyncScope, error?: string): Promise<SyncRun | null> {\n const row = await this.getRun(runId, scope)\n if (!row) return null\n row.status = status\n if (error !== undefined) row.lastError = error\n await em.flush()\n return row\n },\n\n async updateCounts(\n runId: string,\n delta: Partial<Pick<SyncRun, 'createdCount' | 'updatedCount' | 'skippedCount' | 'failedCount' | 'batchesCompleted'>>,\n scope: SyncScope,\n ): Promise<SyncRun | null> {\n const row = await this.getRun(runId, scope)\n if (!row) return null\n\n row.createdCount += delta.createdCount ?? 0\n row.updatedCount += delta.updatedCount ?? 0\n row.skippedCount += delta.skippedCount ?? 0\n row.failedCount += delta.failedCount ?? 0\n row.batchesCompleted += delta.batchesCompleted ?? 0\n await em.flush()\n return row\n },\n\n async updateCursor(runId: string, cursor: string, scope: SyncScope): Promise<void> {\n const run = await this.getRun(runId, scope)\n if (!run) return\n run.cursor = cursor\n\n const cursorRow = await findOneWithDecryption(\n em,\n SyncCursor,\n {\n integrationId: run.integrationId,\n entityType: run.entityType,\n direction: run.direction,\n organizationId: scope.organizationId,\n tenantId: scope.tenantId,\n },\n undefined,\n scope,\n )\n\n if (cursorRow) {\n cursorRow.cursor = cursor\n } else {\n em.create(SyncCursor, {\n integrationId: run.integrationId,\n entityType: run.entityType,\n direction: run.direction,\n cursor,\n organizationId: scope.organizationId,\n tenantId: scope.tenantId,\n })\n }\n\n await em.flush()\n },\n\n async resolveCursor(integrationId: string, entityType: string, direction: 'import' | 'export', scope: SyncScope): Promise<string | null> {\n const row = await findOneWithDecryption(\n em,\n SyncCursor,\n {\n integrationId,\n entityType,\n direction,\n organizationId: scope.organizationId,\n tenantId: scope.tenantId,\n },\n undefined,\n scope,\n )\n return row?.cursor ?? null\n },\n\n async findRunningOverlap(integrationId: string, entityType: string, direction: 'import' | 'export', scope: SyncScope): Promise<SyncRun | null> {\n const [run] = await findWithDecryption(\n em,\n SyncRun,\n {\n integrationId,\n entityType,\n direction,\n status: 'running',\n organizationId: scope.organizationId,\n tenantId: scope.tenantId,\n deletedAt: null,\n },\n { limit: 1 },\n scope,\n )\n return run ?? null\n },\n }\n}\n\nexport type SyncRunService = ReturnType<typeof createSyncRunService>\n"],
|
|
5
|
-
"mappings": "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
|
|
4
|
+
"sourcesContent": ["import type { EntityManager, FilterQuery } from '@mikro-orm/postgresql'\nimport { findAndCountWithDecryption, findOneWithDecryption, findWithDecryption } from '@open-mercato/shared/lib/encryption/find'\nimport { SyncCursor, SyncRun } from '../data/entities'\n\ntype SyncScope = {\n organizationId: string\n tenantId: string\n}\n\nexport function createSyncRunService(em: EntityManager) {\n return {\n async createRun(input: {\n integrationId: string\n entityType: string\n direction: 'import' | 'export'\n cursor?: string | null\n triggeredBy?: string | null\n progressJobId?: string | null\n jobId?: string | null\n }, scope: SyncScope): Promise<SyncRun> {\n const row = em.create(SyncRun, {\n integrationId: input.integrationId,\n entityType: input.entityType,\n direction: input.direction,\n status: 'pending',\n cursor: input.cursor,\n initialCursor: input.cursor,\n triggeredBy: input.triggeredBy,\n progressJobId: input.progressJobId,\n jobId: input.jobId,\n organizationId: scope.organizationId,\n tenantId: scope.tenantId,\n })\n\n await em.persistAndFlush(row)\n return row\n },\n\n async getRun(runId: string, scope: SyncScope): Promise<SyncRun | null> {\n return findOneWithDecryption(\n em,\n SyncRun,\n {\n id: runId,\n organizationId: scope.organizationId,\n tenantId: scope.tenantId,\n deletedAt: null,\n },\n undefined,\n scope,\n )\n },\n\n async listRuns(query: {\n integrationId?: string\n entityType?: string\n direction?: 'import' | 'export'\n status?: string\n page: number\n pageSize: number\n }, scope: SyncScope): Promise<{ items: SyncRun[]; total: number }> {\n const where: FilterQuery<SyncRun> = {\n organizationId: scope.organizationId,\n tenantId: scope.tenantId,\n deletedAt: null,\n }\n\n if (query.integrationId) where.integrationId = query.integrationId\n if (query.entityType) where.entityType = query.entityType\n if (query.direction) where.direction = query.direction\n if (query.status) where.status = query.status as SyncRun['status']\n\n const [items, total] = await findAndCountWithDecryption(\n em,\n SyncRun,\n where,\n {\n orderBy: { createdAt: 'DESC' },\n limit: query.pageSize,\n offset: (query.page - 1) * query.pageSize,\n },\n scope,\n )\n\n return { items, total }\n },\n\n async markStatus(runId: string, status: SyncRun['status'], scope: SyncScope, error?: string): Promise<SyncRun | null> {\n const row = await this.getRun(runId, scope)\n if (!row) return null\n const isTerminal = row.status === 'completed' || row.status === 'failed' || row.status === 'cancelled'\n if (isTerminal && row.status !== status) {\n return row\n }\n row.status = status\n if (error !== undefined) row.lastError = error\n await em.flush()\n return row\n },\n\n async updateCounts(\n runId: string,\n delta: Partial<Pick<SyncRun, 'createdCount' | 'updatedCount' | 'skippedCount' | 'failedCount' | 'batchesCompleted'>>,\n scope: SyncScope,\n ): Promise<SyncRun | null> {\n const row = await this.getRun(runId, scope)\n if (!row) return null\n\n row.createdCount += delta.createdCount ?? 0\n row.updatedCount += delta.updatedCount ?? 0\n row.skippedCount += delta.skippedCount ?? 0\n row.failedCount += delta.failedCount ?? 0\n row.batchesCompleted += delta.batchesCompleted ?? 0\n await em.flush()\n return row\n },\n\n async updateCursor(runId: string, cursor: string, scope: SyncScope): Promise<void> {\n const run = await this.getRun(runId, scope)\n if (!run) return\n run.cursor = cursor\n\n const cursorRow = await findOneWithDecryption(\n em,\n SyncCursor,\n {\n integrationId: run.integrationId,\n entityType: run.entityType,\n direction: run.direction,\n organizationId: scope.organizationId,\n tenantId: scope.tenantId,\n },\n undefined,\n scope,\n )\n\n if (cursorRow) {\n cursorRow.cursor = cursor\n } else {\n em.create(SyncCursor, {\n integrationId: run.integrationId,\n entityType: run.entityType,\n direction: run.direction,\n cursor,\n organizationId: scope.organizationId,\n tenantId: scope.tenantId,\n })\n }\n\n await em.flush()\n },\n\n async resolveCursor(integrationId: string, entityType: string, direction: 'import' | 'export', scope: SyncScope): Promise<string | null> {\n const row = await findOneWithDecryption(\n em,\n SyncCursor,\n {\n integrationId,\n entityType,\n direction,\n organizationId: scope.organizationId,\n tenantId: scope.tenantId,\n },\n undefined,\n scope,\n )\n return row?.cursor ?? null\n },\n\n async findRunningOverlap(integrationId: string, entityType: string, direction: 'import' | 'export', scope: SyncScope): Promise<SyncRun | null> {\n const [run] = await findWithDecryption(\n em,\n SyncRun,\n {\n integrationId,\n entityType,\n direction,\n status: { $in: ['pending', 'running'] },\n organizationId: scope.organizationId,\n tenantId: scope.tenantId,\n deletedAt: null,\n },\n { limit: 1 },\n scope,\n )\n return run ?? null\n },\n }\n}\n\nexport type SyncRunService = ReturnType<typeof createSyncRunService>\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
import { randomUUID } from "node:crypto";
|
|
2
|
+
import { findAndCountWithDecryption, findOneWithDecryption } from "@open-mercato/shared/lib/encryption/find";
|
|
3
|
+
import { SyncSchedule } from "../data/entities.js";
|
|
4
|
+
function createSyncScheduleService(em, schedulerService) {
|
|
5
|
+
function requireScheduler() {
|
|
6
|
+
if (!schedulerService) {
|
|
7
|
+
throw new Error("Scheduler module is not available");
|
|
8
|
+
}
|
|
9
|
+
return schedulerService;
|
|
10
|
+
}
|
|
11
|
+
function buildScheduleName(row) {
|
|
12
|
+
return `Data sync: ${row.integrationId} ${row.entityType} ${row.direction}`;
|
|
13
|
+
}
|
|
14
|
+
function buildScheduleDescription(row) {
|
|
15
|
+
return `Scheduled ${row.direction} for ${row.integrationId} (${row.entityType})`;
|
|
16
|
+
}
|
|
17
|
+
async function getById(id, scope) {
|
|
18
|
+
return findOneWithDecryption(
|
|
19
|
+
em,
|
|
20
|
+
SyncSchedule,
|
|
21
|
+
{
|
|
22
|
+
id,
|
|
23
|
+
organizationId: scope.organizationId,
|
|
24
|
+
tenantId: scope.tenantId,
|
|
25
|
+
deletedAt: null
|
|
26
|
+
},
|
|
27
|
+
void 0,
|
|
28
|
+
scope
|
|
29
|
+
);
|
|
30
|
+
}
|
|
31
|
+
async function getByKey(integrationId, entityType, direction, scope) {
|
|
32
|
+
return findOneWithDecryption(
|
|
33
|
+
em,
|
|
34
|
+
SyncSchedule,
|
|
35
|
+
{
|
|
36
|
+
integrationId,
|
|
37
|
+
entityType,
|
|
38
|
+
direction,
|
|
39
|
+
organizationId: scope.organizationId,
|
|
40
|
+
tenantId: scope.tenantId,
|
|
41
|
+
deletedAt: null
|
|
42
|
+
},
|
|
43
|
+
void 0,
|
|
44
|
+
scope
|
|
45
|
+
);
|
|
46
|
+
}
|
|
47
|
+
return {
|
|
48
|
+
getById,
|
|
49
|
+
getByKey,
|
|
50
|
+
async listSchedules(query, scope) {
|
|
51
|
+
const where = {
|
|
52
|
+
organizationId: scope.organizationId,
|
|
53
|
+
tenantId: scope.tenantId,
|
|
54
|
+
deletedAt: null
|
|
55
|
+
};
|
|
56
|
+
if (query.integrationId) where.integrationId = query.integrationId;
|
|
57
|
+
if (query.entityType) where.entityType = query.entityType;
|
|
58
|
+
if (query.direction) where.direction = query.direction;
|
|
59
|
+
const [items, total] = await findAndCountWithDecryption(
|
|
60
|
+
em,
|
|
61
|
+
SyncSchedule,
|
|
62
|
+
where,
|
|
63
|
+
{
|
|
64
|
+
orderBy: { createdAt: "DESC" },
|
|
65
|
+
limit: query.pageSize,
|
|
66
|
+
offset: (query.page - 1) * query.pageSize
|
|
67
|
+
},
|
|
68
|
+
scope
|
|
69
|
+
);
|
|
70
|
+
return { items, total };
|
|
71
|
+
},
|
|
72
|
+
async saveSchedule(input, scope) {
|
|
73
|
+
const existing = input.id ? await getById(input.id, scope) : await getByKey(input.integrationId, input.entityType, input.direction, scope);
|
|
74
|
+
const row = existing ?? em.create(SyncSchedule, {
|
|
75
|
+
id: randomUUID(),
|
|
76
|
+
integrationId: input.integrationId,
|
|
77
|
+
entityType: input.entityType,
|
|
78
|
+
direction: input.direction,
|
|
79
|
+
scheduleType: input.scheduleType,
|
|
80
|
+
scheduleValue: input.scheduleValue,
|
|
81
|
+
timezone: input.timezone,
|
|
82
|
+
fullSync: input.fullSync,
|
|
83
|
+
isEnabled: input.isEnabled,
|
|
84
|
+
organizationId: scope.organizationId,
|
|
85
|
+
tenantId: scope.tenantId
|
|
86
|
+
});
|
|
87
|
+
row.integrationId = input.integrationId;
|
|
88
|
+
row.entityType = input.entityType;
|
|
89
|
+
row.direction = input.direction;
|
|
90
|
+
row.scheduleType = input.scheduleType;
|
|
91
|
+
row.scheduleValue = input.scheduleValue;
|
|
92
|
+
row.timezone = input.timezone;
|
|
93
|
+
row.fullSync = input.fullSync;
|
|
94
|
+
row.isEnabled = input.isEnabled;
|
|
95
|
+
row.scheduledJobId = row.scheduledJobId ?? row.id;
|
|
96
|
+
if (!existing) {
|
|
97
|
+
em.persist(row);
|
|
98
|
+
}
|
|
99
|
+
await em.flush();
|
|
100
|
+
await requireScheduler().register({
|
|
101
|
+
id: row.scheduledJobId,
|
|
102
|
+
name: buildScheduleName(row),
|
|
103
|
+
description: buildScheduleDescription(row),
|
|
104
|
+
scopeType: "organization",
|
|
105
|
+
organizationId: scope.organizationId,
|
|
106
|
+
tenantId: scope.tenantId,
|
|
107
|
+
scheduleType: row.scheduleType,
|
|
108
|
+
scheduleValue: row.scheduleValue,
|
|
109
|
+
timezone: row.timezone,
|
|
110
|
+
targetType: "queue",
|
|
111
|
+
targetQueue: "data-sync-scheduled",
|
|
112
|
+
targetPayload: {
|
|
113
|
+
scheduleId: row.id,
|
|
114
|
+
scope
|
|
115
|
+
},
|
|
116
|
+
requireFeature: "data_sync.run",
|
|
117
|
+
sourceType: "module",
|
|
118
|
+
sourceModule: "data_sync",
|
|
119
|
+
isEnabled: row.isEnabled
|
|
120
|
+
});
|
|
121
|
+
return row;
|
|
122
|
+
},
|
|
123
|
+
async deleteSchedule(id, scope) {
|
|
124
|
+
const row = await getById(id, scope);
|
|
125
|
+
if (!row) return false;
|
|
126
|
+
const scheduledJobId = row.scheduledJobId ?? row.id;
|
|
127
|
+
await requireScheduler().unregister(scheduledJobId);
|
|
128
|
+
row.deletedAt = /* @__PURE__ */ new Date();
|
|
129
|
+
row.isEnabled = false;
|
|
130
|
+
await em.flush();
|
|
131
|
+
return true;
|
|
132
|
+
}
|
|
133
|
+
};
|
|
134
|
+
}
|
|
135
|
+
export {
|
|
136
|
+
createSyncScheduleService
|
|
137
|
+
};
|
|
138
|
+
//# sourceMappingURL=sync-schedule-service.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../../../src/modules/data_sync/lib/sync-schedule-service.ts"],
|
|
4
|
+
"sourcesContent": ["import { randomUUID } from 'node:crypto'\nimport type { EntityManager, FilterQuery } from '@mikro-orm/postgresql'\nimport { findAndCountWithDecryption, findOneWithDecryption } from '@open-mercato/shared/lib/encryption/find'\nimport { SyncSchedule } from '../data/entities'\n\ntype SyncScope = {\n organizationId: string\n tenantId: string\n}\n\ntype SchedulerServiceLike = {\n register: (registration: {\n id: string\n name: string\n description?: string\n scopeType: 'organization'\n organizationId: string\n tenantId: string\n scheduleType: 'cron' | 'interval'\n scheduleValue: string\n timezone?: string\n targetType: 'queue'\n targetQueue: string\n targetPayload: Record<string, unknown>\n requireFeature?: string\n sourceType: 'module'\n sourceModule: string\n isEnabled?: boolean\n }) => Promise<void>\n unregister: (scheduleId: string) => Promise<void>\n}\n\nexport function createSyncScheduleService(em: EntityManager, schedulerService?: SchedulerServiceLike) {\n function requireScheduler(): SchedulerServiceLike {\n if (!schedulerService) {\n throw new Error('Scheduler module is not available')\n }\n return schedulerService\n }\n\n function buildScheduleName(row: SyncSchedule): string {\n return `Data sync: ${row.integrationId} ${row.entityType} ${row.direction}`\n }\n\n function buildScheduleDescription(row: SyncSchedule): string {\n return `Scheduled ${row.direction} for ${row.integrationId} (${row.entityType})`\n }\n\n async function getById(id: string, scope: SyncScope): Promise<SyncSchedule | null> {\n return findOneWithDecryption(\n em,\n SyncSchedule,\n {\n id,\n organizationId: scope.organizationId,\n tenantId: scope.tenantId,\n deletedAt: null,\n },\n undefined,\n scope,\n )\n }\n\n async function getByKey(\n integrationId: string,\n entityType: string,\n direction: 'import' | 'export',\n scope: SyncScope,\n ): Promise<SyncSchedule | null> {\n return findOneWithDecryption(\n em,\n SyncSchedule,\n {\n integrationId,\n entityType,\n direction,\n organizationId: scope.organizationId,\n tenantId: scope.tenantId,\n deletedAt: null,\n },\n undefined,\n scope,\n )\n }\n\n return {\n getById,\n getByKey,\n\n async listSchedules(query: {\n integrationId?: string\n entityType?: string\n direction?: 'import' | 'export'\n page: number\n pageSize: number\n }, scope: SyncScope): Promise<{ items: SyncSchedule[]; total: number }> {\n const where: FilterQuery<SyncSchedule> = {\n organizationId: scope.organizationId,\n tenantId: scope.tenantId,\n deletedAt: null,\n }\n\n if (query.integrationId) where.integrationId = query.integrationId\n if (query.entityType) where.entityType = query.entityType\n if (query.direction) where.direction = query.direction\n\n const [items, total] = await findAndCountWithDecryption(\n em,\n SyncSchedule,\n where,\n {\n orderBy: { createdAt: 'DESC' },\n limit: query.pageSize,\n offset: (query.page - 1) * query.pageSize,\n },\n scope,\n )\n\n return { items, total }\n },\n\n async saveSchedule(input: {\n id?: string\n integrationId: string\n entityType: string\n direction: 'import' | 'export'\n scheduleType: 'cron' | 'interval'\n scheduleValue: string\n timezone: string\n fullSync: boolean\n isEnabled: boolean\n }, scope: SyncScope): Promise<SyncSchedule> {\n const existing = input.id\n ? await getById(input.id, scope)\n : await getByKey(input.integrationId, input.entityType, input.direction, scope)\n\n const row = existing ?? em.create(SyncSchedule, {\n id: randomUUID(),\n integrationId: input.integrationId,\n entityType: input.entityType,\n direction: input.direction,\n scheduleType: input.scheduleType,\n scheduleValue: input.scheduleValue,\n timezone: input.timezone,\n fullSync: input.fullSync,\n isEnabled: input.isEnabled,\n organizationId: scope.organizationId,\n tenantId: scope.tenantId,\n })\n\n row.integrationId = input.integrationId\n row.entityType = input.entityType\n row.direction = input.direction\n row.scheduleType = input.scheduleType\n row.scheduleValue = input.scheduleValue\n row.timezone = input.timezone\n row.fullSync = input.fullSync\n row.isEnabled = input.isEnabled\n row.scheduledJobId = row.scheduledJobId ?? row.id\n\n if (!existing) {\n em.persist(row)\n }\n\n await em.flush()\n\n await requireScheduler().register({\n id: row.scheduledJobId,\n name: buildScheduleName(row),\n description: buildScheduleDescription(row),\n scopeType: 'organization',\n organizationId: scope.organizationId,\n tenantId: scope.tenantId,\n scheduleType: row.scheduleType,\n scheduleValue: row.scheduleValue,\n timezone: row.timezone,\n targetType: 'queue',\n targetQueue: 'data-sync-scheduled',\n targetPayload: {\n scheduleId: row.id,\n scope,\n },\n requireFeature: 'data_sync.run',\n sourceType: 'module',\n sourceModule: 'data_sync',\n isEnabled: row.isEnabled,\n })\n\n return row\n },\n\n async deleteSchedule(id: string, scope: SyncScope): Promise<boolean> {\n const row = await getById(id, scope)\n if (!row) return false\n\n const scheduledJobId = row.scheduledJobId ?? row.id\n await requireScheduler().unregister(scheduledJobId)\n\n row.deletedAt = new Date()\n row.isEnabled = false\n await em.flush()\n return true\n },\n }\n}\n\nexport type SyncScheduleService = ReturnType<typeof createSyncScheduleService>\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|