@open-mercato/core 0.4.6-develop-6d72ec5960 → 0.4.6-develop-cd1e2a9a0e
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 +10 -0
- package/dist/generated/entities/integration_credentials/index.js +19 -0
- package/dist/generated/entities/integration_credentials/index.js.map +7 -0
- package/dist/generated/entities/integration_log/index.js +27 -0
- package/dist/generated/entities/integration_log/index.js.map +7 -0
- package/dist/generated/entities/integration_state/index.js +27 -0
- package/dist/generated/entities/integration_state/index.js.map +7 -0
- package/dist/generated/entities/sync_cursor/index.js +19 -0
- package/dist/generated/entities/sync_cursor/index.js.map +7 -0
- package/dist/generated/entities/sync_external_id_mapping/index.js +27 -0
- package/dist/generated/entities/sync_external_id_mapping/index.js.map +7 -0
- package/dist/generated/entities/sync_mapping/index.js +19 -0
- package/dist/generated/entities/sync_mapping/index.js.map +7 -0
- package/dist/generated/entities/sync_run/index.js +45 -0
- package/dist/generated/entities/sync_run/index.js.map +7 -0
- package/dist/generated/entities/sync_schedule/index.js +35 -0
- package/dist/generated/entities/sync_schedule/index.js.map +7 -0
- package/dist/generated/entities.ids.generated.js +14 -0
- package/dist/generated/entities.ids.generated.js.map +2 -2
- package/dist/generated/entity-fields-registry.js +16 -0
- package/dist/generated/entity-fields-registry.js.map +2 -2
- package/dist/modules/data_sync/acl.js +11 -0
- package/dist/modules/data_sync/acl.js.map +7 -0
- package/dist/modules/data_sync/api/mappings/[id]/route.js +137 -0
- package/dist/modules/data_sync/api/mappings/[id]/route.js.map +7 -0
- package/dist/modules/data_sync/api/mappings/route.js +132 -0
- package/dist/modules/data_sync/api/mappings/route.js.map +7 -0
- package/dist/modules/data_sync/api/run.js +87 -0
- package/dist/modules/data_sync/api/run.js.map +7 -0
- package/dist/modules/data_sync/api/runs/[id]/cancel.js +49 -0
- package/dist/modules/data_sync/api/runs/[id]/cancel.js.map +7 -0
- package/dist/modules/data_sync/api/runs/[id]/retry.js +93 -0
- package/dist/modules/data_sync/api/runs/[id]/retry.js.map +7 -0
- package/dist/modules/data_sync/api/runs/[id]/route.js +69 -0
- package/dist/modules/data_sync/api/runs/[id]/route.js.map +7 -0
- package/dist/modules/data_sync/api/runs.js +66 -0
- package/dist/modules/data_sync/api/runs.js.map +7 -0
- package/dist/modules/data_sync/api/validate.js +66 -0
- package/dist/modules/data_sync/api/validate.js.map +7 -0
- package/dist/modules/data_sync/backend/data-sync/page.js +216 -0
- package/dist/modules/data_sync/backend/data-sync/page.js.map +7 -0
- package/dist/modules/data_sync/backend/data-sync/page.meta.js +25 -0
- package/dist/modules/data_sync/backend/data-sync/page.meta.js.map +7 -0
- package/dist/modules/data_sync/backend/data-sync/runs/[id]/page.js +178 -0
- package/dist/modules/data_sync/backend/data-sync/runs/[id]/page.js.map +7 -0
- package/dist/modules/data_sync/backend/data-sync/runs/[id]/page.meta.js +14 -0
- package/dist/modules/data_sync/backend/data-sync/runs/[id]/page.meta.js.map +7 -0
- package/dist/modules/data_sync/data/entities.js +228 -0
- package/dist/modules/data_sync/data/entities.js.map +7 -0
- package/dist/modules/data_sync/data/validators.js +32 -0
- package/dist/modules/data_sync/data/validators.js.map +7 -0
- package/dist/modules/data_sync/di.js +26 -0
- package/dist/modules/data_sync/di.js.map +7 -0
- package/dist/modules/data_sync/events.js +16 -0
- package/dist/modules/data_sync/events.js.map +7 -0
- package/dist/modules/data_sync/index.js +9 -0
- package/dist/modules/data_sync/index.js.map +7 -0
- package/dist/modules/data_sync/lib/adapter-registry.js +16 -0
- package/dist/modules/data_sync/lib/adapter-registry.js.map +7 -0
- package/dist/modules/data_sync/lib/adapter.js +1 -0
- package/dist/modules/data_sync/lib/adapter.js.map +7 -0
- package/dist/modules/data_sync/lib/id-mapping.js +79 -0
- package/dist/modules/data_sync/lib/id-mapping.js.map +7 -0
- package/dist/modules/data_sync/lib/queue.js +17 -0
- package/dist/modules/data_sync/lib/queue.js.map +7 -0
- package/dist/modules/data_sync/lib/sync-engine.js +309 -0
- package/dist/modules/data_sync/lib/sync-engine.js.map +7 -0
- package/dist/modules/data_sync/lib/sync-run-service.js +148 -0
- package/dist/modules/data_sync/lib/sync-run-service.js.map +7 -0
- package/dist/modules/data_sync/migrations/Migration20260304113737.js +17 -0
- package/dist/modules/data_sync/migrations/Migration20260304113737.js.map +7 -0
- package/dist/modules/data_sync/setup.js +13 -0
- package/dist/modules/data_sync/setup.js.map +7 -0
- package/dist/modules/data_sync/workers/sync-export.js +14 -0
- package/dist/modules/data_sync/workers/sync-export.js.map +7 -0
- package/dist/modules/data_sync/workers/sync-import.js +14 -0
- package/dist/modules/data_sync/workers/sync-import.js.map +7 -0
- package/dist/modules/data_sync/workers/sync-scheduled.js +63 -0
- package/dist/modules/data_sync/workers/sync-scheduled.js.map +7 -0
- package/dist/modules/entities/lib/encryptionDefaults.js +4 -0
- package/dist/modules/entities/lib/encryptionDefaults.js.map +2 -2
- package/dist/modules/integrations/acl.js +4 -1
- package/dist/modules/integrations/acl.js.map +2 -2
- package/dist/modules/integrations/api/[id]/credentials/route.js +127 -0
- package/dist/modules/integrations/api/[id]/credentials/route.js.map +7 -0
- package/dist/modules/integrations/api/[id]/health/route.js +46 -0
- package/dist/modules/integrations/api/[id]/health/route.js.map +7 -0
- package/dist/modules/integrations/api/[id]/route.js +65 -0
- package/dist/modules/integrations/api/[id]/route.js.map +7 -0
- package/dist/modules/integrations/api/[id]/state/route.js +109 -0
- package/dist/modules/integrations/api/[id]/state/route.js.map +7 -0
- package/dist/modules/integrations/api/[id]/version/route.js +117 -0
- package/dist/modules/integrations/api/[id]/version/route.js.map +7 -0
- package/dist/modules/integrations/api/guards.js +31 -0
- package/dist/modules/integrations/api/guards.js.map +7 -0
- package/dist/modules/integrations/api/logs/route.js +60 -0
- package/dist/modules/integrations/api/logs/route.js.map +7 -0
- package/dist/modules/integrations/api/openapi.js +25 -0
- package/dist/modules/integrations/api/openapi.js.map +7 -0
- package/dist/modules/integrations/api/route.js +68 -0
- package/dist/modules/integrations/api/route.js.map +7 -0
- package/dist/modules/integrations/backend/integrations/[id]/page.js +313 -0
- package/dist/modules/integrations/backend/integrations/[id]/page.js.map +7 -0
- package/dist/modules/integrations/backend/integrations/[id]/page.meta.js +15 -0
- package/dist/modules/integrations/backend/integrations/[id]/page.meta.js.map +7 -0
- package/dist/modules/integrations/backend/integrations/bundle/[id]/page.js +189 -0
- package/dist/modules/integrations/backend/integrations/bundle/[id]/page.js.map +7 -0
- package/dist/modules/integrations/backend/integrations/bundle/[id]/page.meta.js +15 -0
- package/dist/modules/integrations/backend/integrations/bundle/[id]/page.meta.js.map +7 -0
- package/dist/modules/integrations/backend/integrations/page.js +212 -0
- package/dist/modules/integrations/backend/integrations/page.js.map +7 -0
- package/dist/modules/integrations/backend/integrations/page.meta.js +22 -0
- package/dist/modules/integrations/backend/integrations/page.meta.js.map +7 -0
- package/dist/modules/integrations/data/enrichers.js +27 -12
- package/dist/modules/integrations/data/enrichers.js.map +2 -2
- package/dist/modules/integrations/data/entities.js +136 -1
- package/dist/modules/integrations/data/entities.js.map +2 -2
- package/dist/modules/integrations/data/validators.js +36 -0
- package/dist/modules/integrations/data/validators.js.map +7 -0
- package/dist/modules/integrations/di.js +24 -0
- package/dist/modules/integrations/di.js.map +7 -0
- package/dist/modules/integrations/events.js +19 -0
- package/dist/modules/integrations/events.js.map +7 -0
- package/dist/modules/integrations/lib/credentials-service.js +159 -0
- package/dist/modules/integrations/lib/credentials-service.js.map +7 -0
- package/dist/modules/integrations/lib/health-service.js +37 -0
- package/dist/modules/integrations/lib/health-service.js.map +7 -0
- package/dist/modules/integrations/lib/log-service.js +66 -0
- package/dist/modules/integrations/lib/log-service.js.map +7 -0
- package/dist/modules/integrations/lib/registry-service.js +33 -0
- package/dist/modules/integrations/lib/registry-service.js.map +7 -0
- package/dist/modules/integrations/lib/state-service.js +55 -0
- package/dist/modules/integrations/lib/state-service.js.map +7 -0
- package/dist/modules/integrations/lib/types.js +1 -0
- package/dist/modules/integrations/lib/types.js.map +7 -0
- package/dist/modules/integrations/migrations/Migration20260304113737.js +19 -0
- package/dist/modules/integrations/migrations/Migration20260304113737.js.map +7 -0
- package/dist/modules/integrations/setup.js +2 -2
- package/dist/modules/integrations/setup.js.map +2 -2
- package/dist/modules/integrations/widgets/injection-table.js.map +1 -1
- package/dist/modules/integrations/workers/log-pruner.js +18 -0
- package/dist/modules/integrations/workers/log-pruner.js.map +7 -0
- package/generated/entities/integration_credentials/index.ts +8 -0
- package/generated/entities/integration_log/index.ts +12 -0
- package/generated/entities/integration_state/index.ts +12 -0
- package/generated/entities/sync_cursor/index.ts +8 -0
- package/generated/entities/sync_external_id_mapping/index.ts +12 -0
- package/generated/entities/sync_mapping/index.ts +8 -0
- package/generated/entities/sync_run/index.ts +21 -0
- package/generated/entities/sync_schedule/index.ts +16 -0
- package/generated/entities.ids.generated.ts +14 -0
- package/generated/entity-fields-registry.ts +16 -0
- package/package.json +2 -2
- package/src/modules/data_sync/AGENTS.md +157 -0
- package/src/modules/data_sync/acl.ts +7 -0
- package/src/modules/data_sync/api/mappings/[id]/route.ts +158 -0
- package/src/modules/data_sync/api/mappings/route.ts +144 -0
- package/src/modules/data_sync/api/run.ts +97 -0
- package/src/modules/data_sync/api/runs/[id]/cancel.ts +57 -0
- package/src/modules/data_sync/api/runs/[id]/retry.ts +108 -0
- package/src/modules/data_sync/api/runs/[id]/route.ts +81 -0
- package/src/modules/data_sync/api/runs.ts +69 -0
- package/src/modules/data_sync/api/validate.ts +73 -0
- package/src/modules/data_sync/backend/data-sync/page.meta.ts +21 -0
- package/src/modules/data_sync/backend/data-sync/page.tsx +244 -0
- package/src/modules/data_sync/backend/data-sync/runs/[id]/page.meta.ts +10 -0
- package/src/modules/data_sync/backend/data-sync/runs/[id]/page.tsx +278 -0
- package/src/modules/data_sync/data/entities.ts +180 -0
- package/src/modules/data_sync/data/validators.ts +35 -0
- package/src/modules/data_sync/di.ts +38 -0
- package/src/modules/data_sync/events.ts +12 -0
- package/src/modules/data_sync/i18n/de.json +48 -0
- package/src/modules/data_sync/i18n/en.json +48 -0
- package/src/modules/data_sync/i18n/es.json +48 -0
- package/src/modules/data_sync/i18n/pl.json +48 -0
- package/src/modules/data_sync/index.ts +5 -0
- package/src/modules/data_sync/lib/adapter-registry.ts +15 -0
- package/src/modules/data_sync/lib/adapter.ts +90 -0
- package/src/modules/data_sync/lib/id-mapping.ts +95 -0
- package/src/modules/data_sync/lib/queue.ts +19 -0
- package/src/modules/data_sync/lib/sync-engine.ts +375 -0
- package/src/modules/data_sync/lib/sync-run-service.ts +187 -0
- package/src/modules/data_sync/migrations/.snapshot-open-mercato.json +653 -0
- package/src/modules/data_sync/migrations/Migration20260304113737.ts +19 -0
- package/src/modules/data_sync/setup.ts +11 -0
- package/src/modules/data_sync/workers/sync-export.ts +27 -0
- package/src/modules/data_sync/workers/sync-import.ts +27 -0
- package/src/modules/data_sync/workers/sync-scheduled.ts +84 -0
- package/src/modules/entities/lib/encryptionDefaults.ts +4 -0
- package/src/modules/integrations/AGENTS.md +160 -0
- package/src/modules/integrations/acl.ts +3 -0
- package/src/modules/integrations/api/[id]/credentials/route.ts +142 -0
- package/src/modules/integrations/api/[id]/health/route.ts +53 -0
- package/src/modules/integrations/api/[id]/route.ts +76 -0
- package/src/modules/integrations/api/[id]/state/route.ts +121 -0
- package/src/modules/integrations/api/[id]/version/route.ts +132 -0
- package/src/modules/integrations/api/guards.ts +59 -0
- package/src/modules/integrations/api/logs/route.ts +63 -0
- package/src/modules/integrations/api/openapi.ts +22 -0
- package/src/modules/integrations/api/route.ts +73 -0
- package/src/modules/integrations/backend/integrations/[id]/page.meta.ts +11 -0
- package/src/modules/integrations/backend/integrations/[id]/page.tsx +424 -0
- package/src/modules/integrations/backend/integrations/bundle/[id]/page.meta.ts +11 -0
- package/src/modules/integrations/backend/integrations/bundle/[id]/page.tsx +249 -0
- package/src/modules/integrations/backend/integrations/page.meta.ts +18 -0
- package/src/modules/integrations/backend/integrations/page.tsx +296 -0
- package/src/modules/integrations/data/enrichers.ts +35 -18
- package/src/modules/integrations/data/entities.ts +114 -5
- package/src/modules/integrations/data/validators.ts +41 -0
- package/src/modules/integrations/di.ts +31 -0
- package/src/modules/integrations/events.ts +17 -0
- package/src/modules/integrations/i18n/de.json +70 -0
- package/src/modules/integrations/i18n/en.json +70 -0
- package/src/modules/integrations/i18n/es.json +70 -0
- package/src/modules/integrations/i18n/pl.json +70 -0
- package/src/modules/integrations/lib/credentials-service.ts +204 -0
- package/src/modules/integrations/lib/health-service.ts +59 -0
- package/src/modules/integrations/lib/log-service.ts +84 -0
- package/src/modules/integrations/lib/registry-service.ts +42 -0
- package/src/modules/integrations/lib/state-service.ts +64 -0
- package/src/modules/integrations/lib/types.ts +4 -0
- package/src/modules/integrations/migrations/.snapshot-open-mercato.json +582 -0
- package/src/modules/integrations/migrations/Migration20260304113737.ts +21 -0
- package/src/modules/integrations/setup.ts +2 -2
- package/src/modules/integrations/widgets/injection-table.ts +1 -1
- package/src/modules/integrations/workers/log-pruner.ts +30 -0
|
@@ -0,0 +1,180 @@
|
|
|
1
|
+
import { Entity, Index, OptionalProps, PrimaryKey, Property } from '@mikro-orm/core'
|
|
2
|
+
|
|
3
|
+
@Entity({ tableName: 'sync_runs' })
|
|
4
|
+
@Index({ properties: ['integrationId', 'entityType', 'status', 'organizationId', 'tenantId'] })
|
|
5
|
+
export class SyncRun {
|
|
6
|
+
[OptionalProps]?: 'status' | 'cursor' | 'initialCursor' | 'createdCount' | 'updatedCount' | 'skippedCount' | 'failedCount' | 'batchesCompleted' | 'lastError' | 'progressJobId' | 'jobId' | 'triggeredBy' | 'createdAt' | 'updatedAt' | 'deletedAt'
|
|
7
|
+
@PrimaryKey({ type: 'uuid', defaultRaw: 'gen_random_uuid()' })
|
|
8
|
+
id!: string
|
|
9
|
+
|
|
10
|
+
@Property({ name: 'integration_id', type: 'text' })
|
|
11
|
+
integrationId!: string
|
|
12
|
+
|
|
13
|
+
@Property({ name: 'entity_type', type: 'text' })
|
|
14
|
+
entityType!: string
|
|
15
|
+
|
|
16
|
+
@Property({ name: 'direction', type: 'text' })
|
|
17
|
+
direction!: 'import' | 'export'
|
|
18
|
+
|
|
19
|
+
@Property({ name: 'status', type: 'text' })
|
|
20
|
+
status!: 'pending' | 'running' | 'completed' | 'failed' | 'cancelled' | 'paused'
|
|
21
|
+
|
|
22
|
+
@Property({ name: 'cursor', type: 'text', nullable: true })
|
|
23
|
+
cursor?: string | null
|
|
24
|
+
|
|
25
|
+
@Property({ name: 'initial_cursor', type: 'text', nullable: true })
|
|
26
|
+
initialCursor?: string | null
|
|
27
|
+
|
|
28
|
+
@Property({ name: 'created_count', type: 'int', default: 0 })
|
|
29
|
+
createdCount: number = 0
|
|
30
|
+
|
|
31
|
+
@Property({ name: 'updated_count', type: 'int', default: 0 })
|
|
32
|
+
updatedCount: number = 0
|
|
33
|
+
|
|
34
|
+
@Property({ name: 'skipped_count', type: 'int', default: 0 })
|
|
35
|
+
skippedCount: number = 0
|
|
36
|
+
|
|
37
|
+
@Property({ name: 'failed_count', type: 'int', default: 0 })
|
|
38
|
+
failedCount: number = 0
|
|
39
|
+
|
|
40
|
+
@Property({ name: 'batches_completed', type: 'int', default: 0 })
|
|
41
|
+
batchesCompleted: number = 0
|
|
42
|
+
|
|
43
|
+
@Property({ name: 'last_error', type: 'text', nullable: true })
|
|
44
|
+
lastError?: string | null
|
|
45
|
+
|
|
46
|
+
@Property({ name: 'progress_job_id', type: 'uuid', nullable: true })
|
|
47
|
+
progressJobId?: string | null
|
|
48
|
+
|
|
49
|
+
@Property({ name: 'job_id', type: 'text', nullable: true })
|
|
50
|
+
jobId?: string | null
|
|
51
|
+
|
|
52
|
+
@Property({ name: 'triggered_by', type: 'text', nullable: true })
|
|
53
|
+
triggeredBy?: string | null
|
|
54
|
+
|
|
55
|
+
@Property({ name: 'organization_id', type: 'uuid' })
|
|
56
|
+
organizationId!: string
|
|
57
|
+
|
|
58
|
+
@Property({ name: 'tenant_id', type: 'uuid' })
|
|
59
|
+
tenantId!: string
|
|
60
|
+
|
|
61
|
+
@Property({ name: 'created_at', type: Date, onCreate: () => new Date() })
|
|
62
|
+
createdAt: Date = new Date()
|
|
63
|
+
|
|
64
|
+
@Property({ name: 'updated_at', type: Date, onUpdate: () => new Date() })
|
|
65
|
+
updatedAt: Date = new Date()
|
|
66
|
+
|
|
67
|
+
@Property({ name: 'deleted_at', type: Date, nullable: true })
|
|
68
|
+
deletedAt?: Date | null
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
@Entity({ tableName: 'sync_cursors' })
|
|
72
|
+
@Index({ properties: ['integrationId', 'entityType', 'direction', 'organizationId', 'tenantId'], options: { unique: true } })
|
|
73
|
+
export class SyncCursor {
|
|
74
|
+
[OptionalProps]?: 'cursor' | 'updatedAt'
|
|
75
|
+
@PrimaryKey({ type: 'uuid', defaultRaw: 'gen_random_uuid()' })
|
|
76
|
+
id!: string
|
|
77
|
+
|
|
78
|
+
@Property({ name: 'integration_id', type: 'text' })
|
|
79
|
+
integrationId!: string
|
|
80
|
+
|
|
81
|
+
@Property({ name: 'entity_type', type: 'text' })
|
|
82
|
+
entityType!: string
|
|
83
|
+
|
|
84
|
+
@Property({ name: 'direction', type: 'text' })
|
|
85
|
+
direction!: 'import' | 'export'
|
|
86
|
+
|
|
87
|
+
@Property({ name: 'cursor', type: 'text', nullable: true })
|
|
88
|
+
cursor?: string | null
|
|
89
|
+
|
|
90
|
+
@Property({ name: 'organization_id', type: 'uuid' })
|
|
91
|
+
organizationId!: string
|
|
92
|
+
|
|
93
|
+
@Property({ name: 'tenant_id', type: 'uuid' })
|
|
94
|
+
tenantId!: string
|
|
95
|
+
|
|
96
|
+
@Property({ name: 'updated_at', type: Date, onCreate: () => new Date(), onUpdate: () => new Date() })
|
|
97
|
+
updatedAt: Date = new Date()
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
@Entity({ tableName: 'sync_mappings' })
|
|
101
|
+
@Index({ properties: ['integrationId', 'entityType', 'organizationId', 'tenantId'], options: { unique: true } })
|
|
102
|
+
export class SyncMapping {
|
|
103
|
+
[OptionalProps]?: 'createdAt' | 'updatedAt'
|
|
104
|
+
@PrimaryKey({ type: 'uuid', defaultRaw: 'gen_random_uuid()' })
|
|
105
|
+
id!: string
|
|
106
|
+
|
|
107
|
+
@Property({ name: 'integration_id', type: 'text' })
|
|
108
|
+
integrationId!: string
|
|
109
|
+
|
|
110
|
+
@Property({ name: 'entity_type', type: 'text' })
|
|
111
|
+
entityType!: string
|
|
112
|
+
|
|
113
|
+
@Property({ name: 'mapping', type: 'json' })
|
|
114
|
+
mapping!: Record<string, unknown>
|
|
115
|
+
|
|
116
|
+
@Property({ name: 'organization_id', type: 'uuid' })
|
|
117
|
+
organizationId!: string
|
|
118
|
+
|
|
119
|
+
@Property({ name: 'tenant_id', type: 'uuid' })
|
|
120
|
+
tenantId!: string
|
|
121
|
+
|
|
122
|
+
@Property({ name: 'created_at', type: Date, onCreate: () => new Date() })
|
|
123
|
+
createdAt: Date = new Date()
|
|
124
|
+
|
|
125
|
+
@Property({ name: 'updated_at', type: Date, onUpdate: () => new Date() })
|
|
126
|
+
updatedAt: Date = new Date()
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
@Entity({ tableName: 'sync_schedules' })
|
|
130
|
+
@Index({ properties: ['integrationId', 'entityType', 'direction', 'organizationId', 'tenantId'] })
|
|
131
|
+
export class SyncSchedule {
|
|
132
|
+
[OptionalProps]?: 'timezone' | 'fullSync' | 'isEnabled' | 'scheduledJobId' | 'lastRunAt' | 'createdAt' | 'updatedAt' | 'deletedAt'
|
|
133
|
+
@PrimaryKey({ type: 'uuid', defaultRaw: 'gen_random_uuid()' })
|
|
134
|
+
id!: string
|
|
135
|
+
|
|
136
|
+
@Property({ name: 'integration_id', type: 'text' })
|
|
137
|
+
integrationId!: string
|
|
138
|
+
|
|
139
|
+
@Property({ name: 'entity_type', type: 'text' })
|
|
140
|
+
entityType!: string
|
|
141
|
+
|
|
142
|
+
@Property({ name: 'direction', type: 'text' })
|
|
143
|
+
direction!: 'import' | 'export'
|
|
144
|
+
|
|
145
|
+
@Property({ name: 'schedule_type', type: 'text' })
|
|
146
|
+
scheduleType!: 'cron' | 'interval'
|
|
147
|
+
|
|
148
|
+
@Property({ name: 'schedule_value', type: 'text' })
|
|
149
|
+
scheduleValue!: string
|
|
150
|
+
|
|
151
|
+
@Property({ name: 'timezone', type: 'text', default: 'UTC' })
|
|
152
|
+
timezone: string = 'UTC'
|
|
153
|
+
|
|
154
|
+
@Property({ name: 'full_sync', type: 'boolean', default: false })
|
|
155
|
+
fullSync: boolean = false
|
|
156
|
+
|
|
157
|
+
@Property({ name: 'is_enabled', type: 'boolean', default: true })
|
|
158
|
+
isEnabled: boolean = true
|
|
159
|
+
|
|
160
|
+
@Property({ name: 'scheduled_job_id', type: 'uuid', nullable: true })
|
|
161
|
+
scheduledJobId?: string | null
|
|
162
|
+
|
|
163
|
+
@Property({ name: 'last_run_at', type: Date, nullable: true })
|
|
164
|
+
lastRunAt?: Date | null
|
|
165
|
+
|
|
166
|
+
@Property({ name: 'organization_id', type: 'uuid' })
|
|
167
|
+
organizationId!: string
|
|
168
|
+
|
|
169
|
+
@Property({ name: 'tenant_id', type: 'uuid' })
|
|
170
|
+
tenantId!: string
|
|
171
|
+
|
|
172
|
+
@Property({ name: 'created_at', type: Date, onCreate: () => new Date() })
|
|
173
|
+
createdAt: Date = new Date()
|
|
174
|
+
|
|
175
|
+
@Property({ name: 'updated_at', type: Date, onUpdate: () => new Date() })
|
|
176
|
+
updatedAt: Date = new Date()
|
|
177
|
+
|
|
178
|
+
@Property({ name: 'deleted_at', type: Date, nullable: true })
|
|
179
|
+
deletedAt?: Date | null
|
|
180
|
+
}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { z } from 'zod'
|
|
2
|
+
|
|
3
|
+
export const runSyncSchema = z.object({
|
|
4
|
+
integrationId: z.string().min(1),
|
|
5
|
+
entityType: z.string().min(1),
|
|
6
|
+
direction: z.enum(['import', 'export']),
|
|
7
|
+
fullSync: z.boolean().default(false),
|
|
8
|
+
batchSize: z.number().int().min(1).max(1000).default(100),
|
|
9
|
+
triggeredBy: z.string().optional(),
|
|
10
|
+
})
|
|
11
|
+
|
|
12
|
+
export type RunSyncInput = z.infer<typeof runSyncSchema>
|
|
13
|
+
|
|
14
|
+
export const retrySyncSchema = z.object({
|
|
15
|
+
fromBeginning: z.boolean().default(false),
|
|
16
|
+
})
|
|
17
|
+
|
|
18
|
+
export type RetrySyncInput = z.infer<typeof retrySyncSchema>
|
|
19
|
+
|
|
20
|
+
export const validateConnectionSchema = z.object({
|
|
21
|
+
integrationId: z.string().min(1),
|
|
22
|
+
entityType: z.string().min(1),
|
|
23
|
+
direction: z.enum(['import', 'export']),
|
|
24
|
+
})
|
|
25
|
+
|
|
26
|
+
export const listSyncRunsQuerySchema = z.object({
|
|
27
|
+
integrationId: z.string().optional(),
|
|
28
|
+
entityType: z.string().optional(),
|
|
29
|
+
direction: z.enum(['import', 'export']).optional(),
|
|
30
|
+
status: z.enum(['pending', 'running', 'completed', 'failed', 'cancelled', 'paused']).optional(),
|
|
31
|
+
page: z.coerce.number().int().min(1).default(1),
|
|
32
|
+
pageSize: z.coerce.number().int().min(1).max(100).default(20),
|
|
33
|
+
})
|
|
34
|
+
|
|
35
|
+
export type ListSyncRunsQuery = z.infer<typeof listSyncRunsQuerySchema>
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { asFunction, asValue } from 'awilix'
|
|
2
|
+
import type { EntityManager } from '@mikro-orm/postgresql'
|
|
3
|
+
import type { AppContainer } from '@open-mercato/shared/lib/di/container'
|
|
4
|
+
import type { CredentialsService } from '../integrations/lib/credentials-service'
|
|
5
|
+
import type { IntegrationLogService } from '../integrations/lib/log-service'
|
|
6
|
+
import type { ProgressService } from '../progress/lib/progressService'
|
|
7
|
+
import { SyncCursor, SyncMapping, SyncRun, SyncSchedule } from './data/entities'
|
|
8
|
+
import { createExternalIdMappingService } from './lib/id-mapping'
|
|
9
|
+
import { createSyncRunService } from './lib/sync-run-service'
|
|
10
|
+
import { createSyncEngine } from './lib/sync-engine'
|
|
11
|
+
|
|
12
|
+
type Cradle = {
|
|
13
|
+
em: EntityManager
|
|
14
|
+
integrationCredentialsService: CredentialsService
|
|
15
|
+
integrationLogService: IntegrationLogService
|
|
16
|
+
progressService: ProgressService
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export function register(container: AppContainer) {
|
|
20
|
+
container.register({
|
|
21
|
+
externalIdMappingService: asFunction(({ em }: Cradle) => createExternalIdMappingService(em)).scoped().proxy(),
|
|
22
|
+
dataSyncRunService: asFunction(({ em }: Cradle) => createSyncRunService(em)).scoped().proxy(),
|
|
23
|
+
dataSyncEngine: asFunction(({ em, dataSyncRunService, integrationCredentialsService, integrationLogService, progressService }: Cradle & {
|
|
24
|
+
dataSyncRunService: ReturnType<typeof createSyncRunService>
|
|
25
|
+
}) => createSyncEngine({
|
|
26
|
+
em,
|
|
27
|
+
syncRunService: dataSyncRunService,
|
|
28
|
+
integrationCredentialsService,
|
|
29
|
+
integrationLogService,
|
|
30
|
+
progressService,
|
|
31
|
+
})).scoped().proxy(),
|
|
32
|
+
|
|
33
|
+
SyncRun: asValue(SyncRun),
|
|
34
|
+
SyncCursor: asValue(SyncCursor),
|
|
35
|
+
SyncMapping: asValue(SyncMapping),
|
|
36
|
+
SyncSchedule: asValue(SyncSchedule),
|
|
37
|
+
})
|
|
38
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { createModuleEvents } from '@open-mercato/shared/modules/events'
|
|
2
|
+
|
|
3
|
+
const events = [
|
|
4
|
+
{ id: 'data_sync.run.started', label: 'Sync Run Started', category: 'lifecycle', entity: 'run' },
|
|
5
|
+
{ id: 'data_sync.run.completed', label: 'Sync Run Completed', category: 'lifecycle', entity: 'run' },
|
|
6
|
+
{ id: 'data_sync.run.failed', label: 'Sync Run Failed', category: 'lifecycle', entity: 'run' },
|
|
7
|
+
{ id: 'data_sync.run.cancelled', label: 'Sync Run Cancelled', category: 'lifecycle', entity: 'run' },
|
|
8
|
+
] as const
|
|
9
|
+
|
|
10
|
+
export const eventsConfig = createModuleEvents({ moduleId: 'data_sync', events })
|
|
11
|
+
export const emitDataSyncEvent = eventsConfig.emit
|
|
12
|
+
export default eventsConfig
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
{
|
|
2
|
+
"data_sync.dashboard.actions.view": "Anzeigen",
|
|
3
|
+
"data_sync.dashboard.columns.created": "Erstellt",
|
|
4
|
+
"data_sync.dashboard.columns.createdAt": "Gestartet",
|
|
5
|
+
"data_sync.dashboard.columns.direction": "Richtung",
|
|
6
|
+
"data_sync.dashboard.columns.entityType": "Entitätstyp",
|
|
7
|
+
"data_sync.dashboard.columns.failed": "Fehlgeschlagen",
|
|
8
|
+
"data_sync.dashboard.columns.integration": "Integration",
|
|
9
|
+
"data_sync.dashboard.columns.status": "Status",
|
|
10
|
+
"data_sync.dashboard.columns.updated": "Aktualisiert",
|
|
11
|
+
"data_sync.dashboard.direction.export": "Export",
|
|
12
|
+
"data_sync.dashboard.direction.import": "Import",
|
|
13
|
+
"data_sync.dashboard.filters.allDirections": "Alle Richtungen",
|
|
14
|
+
"data_sync.dashboard.filters.allStatuses": "Alle Status",
|
|
15
|
+
"data_sync.dashboard.filters.status": "Status",
|
|
16
|
+
"data_sync.dashboard.loadError": "Synchronisierungsläufe konnten nicht geladen werden",
|
|
17
|
+
"data_sync.dashboard.noRuns": "Noch keine Synchronisierungsläufe",
|
|
18
|
+
"data_sync.dashboard.status.cancelled": "Abgebrochen",
|
|
19
|
+
"data_sync.dashboard.status.completed": "Abgeschlossen",
|
|
20
|
+
"data_sync.dashboard.status.failed": "Fehlgeschlagen",
|
|
21
|
+
"data_sync.dashboard.status.paused": "Pausiert",
|
|
22
|
+
"data_sync.dashboard.status.pending": "Ausstehend",
|
|
23
|
+
"data_sync.dashboard.status.running": "Läuft",
|
|
24
|
+
"data_sync.dashboard.title": "Synchronisierungsläufe",
|
|
25
|
+
"data_sync.nav.title": "Datensynchronisation",
|
|
26
|
+
"data_sync.runs.detail.back": "Zurück zu Läufen",
|
|
27
|
+
"data_sync.runs.detail.cancel": "Abbrechen",
|
|
28
|
+
"data_sync.runs.detail.cancelError": "Lauf konnte nicht abgebrochen werden",
|
|
29
|
+
"data_sync.runs.detail.cancelSuccess": "Lauf abgebrochen",
|
|
30
|
+
"data_sync.runs.detail.counters.created": "Erstellt",
|
|
31
|
+
"data_sync.runs.detail.counters.failed": "Fehlgeschlagen",
|
|
32
|
+
"data_sync.runs.detail.counters.skipped": "Übersprungen",
|
|
33
|
+
"data_sync.runs.detail.counters.updated": "Aktualisiert",
|
|
34
|
+
"data_sync.runs.detail.error": "Fehler",
|
|
35
|
+
"data_sync.runs.detail.loadError": "Lauf konnte nicht geladen werden",
|
|
36
|
+
"data_sync.runs.detail.logs": "Betriebsprotokolle",
|
|
37
|
+
"data_sync.runs.detail.logs.level": "Stufe",
|
|
38
|
+
"data_sync.runs.detail.logs.message": "Nachricht",
|
|
39
|
+
"data_sync.runs.detail.logs.time": "Zeit",
|
|
40
|
+
"data_sync.runs.detail.noLogs": "Keine Protokolleinträge",
|
|
41
|
+
"data_sync.runs.detail.progress": "Fortschritt",
|
|
42
|
+
"data_sync.runs.detail.progress.batches": "{count} Stapel",
|
|
43
|
+
"data_sync.runs.detail.progress.itemsProcessed": "{count} Elemente verarbeitet",
|
|
44
|
+
"data_sync.runs.detail.retry": "Wiederholen",
|
|
45
|
+
"data_sync.runs.detail.retryError": "Lauf konnte nicht wiederholt werden",
|
|
46
|
+
"data_sync.runs.detail.retrySuccess": "Lauf wiederholt",
|
|
47
|
+
"data_sync.runs.detail.title": "Synchronisierungslauf"
|
|
48
|
+
}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
{
|
|
2
|
+
"data_sync.dashboard.actions.view": "View",
|
|
3
|
+
"data_sync.dashboard.columns.created": "Created",
|
|
4
|
+
"data_sync.dashboard.columns.createdAt": "Started",
|
|
5
|
+
"data_sync.dashboard.columns.direction": "Direction",
|
|
6
|
+
"data_sync.dashboard.columns.entityType": "Entity Type",
|
|
7
|
+
"data_sync.dashboard.columns.failed": "Failed",
|
|
8
|
+
"data_sync.dashboard.columns.integration": "Integration",
|
|
9
|
+
"data_sync.dashboard.columns.status": "Status",
|
|
10
|
+
"data_sync.dashboard.columns.updated": "Updated",
|
|
11
|
+
"data_sync.dashboard.direction.export": "Export",
|
|
12
|
+
"data_sync.dashboard.direction.import": "Import",
|
|
13
|
+
"data_sync.dashboard.filters.allDirections": "All Directions",
|
|
14
|
+
"data_sync.dashboard.filters.allStatuses": "All Statuses",
|
|
15
|
+
"data_sync.dashboard.filters.status": "Status",
|
|
16
|
+
"data_sync.dashboard.loadError": "Failed to load sync runs",
|
|
17
|
+
"data_sync.dashboard.noRuns": "No sync runs yet",
|
|
18
|
+
"data_sync.dashboard.status.cancelled": "Cancelled",
|
|
19
|
+
"data_sync.dashboard.status.completed": "Completed",
|
|
20
|
+
"data_sync.dashboard.status.failed": "Failed",
|
|
21
|
+
"data_sync.dashboard.status.paused": "Paused",
|
|
22
|
+
"data_sync.dashboard.status.pending": "Pending",
|
|
23
|
+
"data_sync.dashboard.status.running": "Running",
|
|
24
|
+
"data_sync.dashboard.title": "Sync Runs",
|
|
25
|
+
"data_sync.nav.title": "Data Sync",
|
|
26
|
+
"data_sync.runs.detail.back": "Back to Sync Runs",
|
|
27
|
+
"data_sync.runs.detail.cancel": "Cancel",
|
|
28
|
+
"data_sync.runs.detail.cancelError": "Failed to cancel sync run",
|
|
29
|
+
"data_sync.runs.detail.cancelSuccess": "Sync run cancelled",
|
|
30
|
+
"data_sync.runs.detail.counters.created": "Created",
|
|
31
|
+
"data_sync.runs.detail.counters.failed": "Failed",
|
|
32
|
+
"data_sync.runs.detail.counters.skipped": "Skipped",
|
|
33
|
+
"data_sync.runs.detail.counters.updated": "Updated",
|
|
34
|
+
"data_sync.runs.detail.error": "Error",
|
|
35
|
+
"data_sync.runs.detail.loadError": "Failed to load sync run",
|
|
36
|
+
"data_sync.runs.detail.logs": "Operation Logs",
|
|
37
|
+
"data_sync.runs.detail.logs.level": "Level",
|
|
38
|
+
"data_sync.runs.detail.logs.message": "Message",
|
|
39
|
+
"data_sync.runs.detail.logs.time": "Time",
|
|
40
|
+
"data_sync.runs.detail.noLogs": "No log entries",
|
|
41
|
+
"data_sync.runs.detail.progress": "Progress",
|
|
42
|
+
"data_sync.runs.detail.progress.batches": "{count} batches",
|
|
43
|
+
"data_sync.runs.detail.progress.itemsProcessed": "{count} items processed",
|
|
44
|
+
"data_sync.runs.detail.retry": "Retry",
|
|
45
|
+
"data_sync.runs.detail.retryError": "Failed to retry sync run",
|
|
46
|
+
"data_sync.runs.detail.retrySuccess": "Sync run retried",
|
|
47
|
+
"data_sync.runs.detail.title": "Sync Run"
|
|
48
|
+
}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
{
|
|
2
|
+
"data_sync.dashboard.actions.view": "Ver",
|
|
3
|
+
"data_sync.dashboard.columns.created": "Creados",
|
|
4
|
+
"data_sync.dashboard.columns.createdAt": "Iniciado",
|
|
5
|
+
"data_sync.dashboard.columns.direction": "Dirección",
|
|
6
|
+
"data_sync.dashboard.columns.entityType": "Tipo de entidad",
|
|
7
|
+
"data_sync.dashboard.columns.failed": "Fallidos",
|
|
8
|
+
"data_sync.dashboard.columns.integration": "Integración",
|
|
9
|
+
"data_sync.dashboard.columns.status": "Estado",
|
|
10
|
+
"data_sync.dashboard.columns.updated": "Actualizados",
|
|
11
|
+
"data_sync.dashboard.direction.export": "Exportación",
|
|
12
|
+
"data_sync.dashboard.direction.import": "Importación",
|
|
13
|
+
"data_sync.dashboard.filters.allDirections": "Todas las direcciones",
|
|
14
|
+
"data_sync.dashboard.filters.allStatuses": "Todos los estados",
|
|
15
|
+
"data_sync.dashboard.filters.status": "Estado",
|
|
16
|
+
"data_sync.dashboard.loadError": "No se pudieron cargar las ejecuciones de sincronización",
|
|
17
|
+
"data_sync.dashboard.noRuns": "Sin ejecuciones de sincronización",
|
|
18
|
+
"data_sync.dashboard.status.cancelled": "Cancelado",
|
|
19
|
+
"data_sync.dashboard.status.completed": "Completado",
|
|
20
|
+
"data_sync.dashboard.status.failed": "Fallido",
|
|
21
|
+
"data_sync.dashboard.status.paused": "Pausado",
|
|
22
|
+
"data_sync.dashboard.status.pending": "Pendiente",
|
|
23
|
+
"data_sync.dashboard.status.running": "En ejecución",
|
|
24
|
+
"data_sync.dashboard.title": "Ejecuciones de sincronización",
|
|
25
|
+
"data_sync.nav.title": "Sincronización de datos",
|
|
26
|
+
"data_sync.runs.detail.back": "Volver a ejecuciones",
|
|
27
|
+
"data_sync.runs.detail.cancel": "Cancelar",
|
|
28
|
+
"data_sync.runs.detail.cancelError": "No se pudo cancelar la ejecución",
|
|
29
|
+
"data_sync.runs.detail.cancelSuccess": "Ejecución cancelada",
|
|
30
|
+
"data_sync.runs.detail.counters.created": "Creados",
|
|
31
|
+
"data_sync.runs.detail.counters.failed": "Fallidos",
|
|
32
|
+
"data_sync.runs.detail.counters.skipped": "Omitidos",
|
|
33
|
+
"data_sync.runs.detail.counters.updated": "Actualizados",
|
|
34
|
+
"data_sync.runs.detail.error": "Error",
|
|
35
|
+
"data_sync.runs.detail.loadError": "No se pudo cargar la ejecución",
|
|
36
|
+
"data_sync.runs.detail.logs": "Registros de operación",
|
|
37
|
+
"data_sync.runs.detail.logs.level": "Nivel",
|
|
38
|
+
"data_sync.runs.detail.logs.message": "Mensaje",
|
|
39
|
+
"data_sync.runs.detail.logs.time": "Hora",
|
|
40
|
+
"data_sync.runs.detail.noLogs": "Sin entradas de registro",
|
|
41
|
+
"data_sync.runs.detail.progress": "Progreso",
|
|
42
|
+
"data_sync.runs.detail.progress.batches": "{count} lotes",
|
|
43
|
+
"data_sync.runs.detail.progress.itemsProcessed": "{count} elementos procesados",
|
|
44
|
+
"data_sync.runs.detail.retry": "Reintentar",
|
|
45
|
+
"data_sync.runs.detail.retryError": "No se pudo reintentar la ejecución",
|
|
46
|
+
"data_sync.runs.detail.retrySuccess": "Ejecución reintentada",
|
|
47
|
+
"data_sync.runs.detail.title": "Ejecución de sincronización"
|
|
48
|
+
}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
{
|
|
2
|
+
"data_sync.dashboard.actions.view": "Zobacz",
|
|
3
|
+
"data_sync.dashboard.columns.created": "Utworzone",
|
|
4
|
+
"data_sync.dashboard.columns.createdAt": "Rozpoczęto",
|
|
5
|
+
"data_sync.dashboard.columns.direction": "Kierunek",
|
|
6
|
+
"data_sync.dashboard.columns.entityType": "Typ encji",
|
|
7
|
+
"data_sync.dashboard.columns.failed": "Nieudane",
|
|
8
|
+
"data_sync.dashboard.columns.integration": "Integracja",
|
|
9
|
+
"data_sync.dashboard.columns.status": "Status",
|
|
10
|
+
"data_sync.dashboard.columns.updated": "Zaktualizowane",
|
|
11
|
+
"data_sync.dashboard.direction.export": "Eksport",
|
|
12
|
+
"data_sync.dashboard.direction.import": "Import",
|
|
13
|
+
"data_sync.dashboard.filters.allDirections": "Wszystkie kierunki",
|
|
14
|
+
"data_sync.dashboard.filters.allStatuses": "Wszystkie statusy",
|
|
15
|
+
"data_sync.dashboard.filters.status": "Status",
|
|
16
|
+
"data_sync.dashboard.loadError": "Nie udało się załadować przebiegów",
|
|
17
|
+
"data_sync.dashboard.noRuns": "Brak przebiegów synchronizacji",
|
|
18
|
+
"data_sync.dashboard.status.cancelled": "Anulowany",
|
|
19
|
+
"data_sync.dashboard.status.completed": "Zakończony",
|
|
20
|
+
"data_sync.dashboard.status.failed": "Nieudany",
|
|
21
|
+
"data_sync.dashboard.status.paused": "Wstrzymany",
|
|
22
|
+
"data_sync.dashboard.status.pending": "Oczekujący",
|
|
23
|
+
"data_sync.dashboard.status.running": "W toku",
|
|
24
|
+
"data_sync.dashboard.title": "Przebiegi synchronizacji",
|
|
25
|
+
"data_sync.nav.title": "Synchronizacja danych",
|
|
26
|
+
"data_sync.runs.detail.back": "Powrót do przebiegów",
|
|
27
|
+
"data_sync.runs.detail.cancel": "Anuluj",
|
|
28
|
+
"data_sync.runs.detail.cancelError": "Nie udało się anulować przebiegu",
|
|
29
|
+
"data_sync.runs.detail.cancelSuccess": "Przebieg anulowany",
|
|
30
|
+
"data_sync.runs.detail.counters.created": "Utworzone",
|
|
31
|
+
"data_sync.runs.detail.counters.failed": "Nieudane",
|
|
32
|
+
"data_sync.runs.detail.counters.skipped": "Pominięte",
|
|
33
|
+
"data_sync.runs.detail.counters.updated": "Zaktualizowane",
|
|
34
|
+
"data_sync.runs.detail.error": "Błąd",
|
|
35
|
+
"data_sync.runs.detail.loadError": "Nie udało się załadować przebiegu",
|
|
36
|
+
"data_sync.runs.detail.logs": "Logi operacji",
|
|
37
|
+
"data_sync.runs.detail.logs.level": "Poziom",
|
|
38
|
+
"data_sync.runs.detail.logs.message": "Wiadomość",
|
|
39
|
+
"data_sync.runs.detail.logs.time": "Czas",
|
|
40
|
+
"data_sync.runs.detail.noLogs": "Brak wpisów w logach",
|
|
41
|
+
"data_sync.runs.detail.progress": "Postęp",
|
|
42
|
+
"data_sync.runs.detail.progress.batches": "{count} partii",
|
|
43
|
+
"data_sync.runs.detail.progress.itemsProcessed": "{count} elementów przetworzonych",
|
|
44
|
+
"data_sync.runs.detail.retry": "Ponów",
|
|
45
|
+
"data_sync.runs.detail.retryError": "Nie udało się ponowić przebiegu",
|
|
46
|
+
"data_sync.runs.detail.retrySuccess": "Przebieg ponowiony",
|
|
47
|
+
"data_sync.runs.detail.title": "Przebieg synchronizacji"
|
|
48
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { DataSyncAdapter } from './adapter'
|
|
2
|
+
|
|
3
|
+
const adapters = new Map<string, DataSyncAdapter>()
|
|
4
|
+
|
|
5
|
+
export function registerDataSyncAdapter(adapter: DataSyncAdapter): void {
|
|
6
|
+
adapters.set(adapter.providerKey, adapter)
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export function getDataSyncAdapter(providerKey: string): DataSyncAdapter | undefined {
|
|
10
|
+
return adapters.get(providerKey)
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export function getAllDataSyncAdapters(): DataSyncAdapter[] {
|
|
14
|
+
return Array.from(adapters.values())
|
|
15
|
+
}
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
export interface TenantScope {
|
|
2
|
+
organizationId: string
|
|
3
|
+
tenantId: string
|
|
4
|
+
}
|
|
5
|
+
|
|
6
|
+
export interface FieldMapping {
|
|
7
|
+
externalField: string
|
|
8
|
+
localField: string
|
|
9
|
+
transform?: string
|
|
10
|
+
required?: boolean
|
|
11
|
+
defaultValue?: unknown
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export interface DataMapping {
|
|
15
|
+
entityType: string
|
|
16
|
+
fields: FieldMapping[]
|
|
17
|
+
matchStrategy: 'externalId' | 'sku' | 'email' | 'custom'
|
|
18
|
+
matchField?: string
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export interface StreamImportInput {
|
|
22
|
+
entityType: string
|
|
23
|
+
cursor?: string
|
|
24
|
+
batchSize: number
|
|
25
|
+
credentials: Record<string, unknown>
|
|
26
|
+
mapping: DataMapping
|
|
27
|
+
scope: TenantScope
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export interface ImportItem {
|
|
31
|
+
externalId: string
|
|
32
|
+
data: Record<string, unknown>
|
|
33
|
+
action: 'create' | 'update' | 'skip'
|
|
34
|
+
hash?: string
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export interface ImportBatch {
|
|
38
|
+
items: ImportItem[]
|
|
39
|
+
cursor: string
|
|
40
|
+
hasMore: boolean
|
|
41
|
+
totalEstimate?: number
|
|
42
|
+
batchIndex: number
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
export interface StreamExportInput {
|
|
46
|
+
entityType: string
|
|
47
|
+
cursor?: string
|
|
48
|
+
batchSize: number
|
|
49
|
+
credentials: Record<string, unknown>
|
|
50
|
+
mapping: DataMapping
|
|
51
|
+
scope: TenantScope
|
|
52
|
+
filter?: Record<string, unknown>
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
export interface ExportItemResult {
|
|
56
|
+
localId: string
|
|
57
|
+
externalId?: string
|
|
58
|
+
status: 'success' | 'error' | 'skipped'
|
|
59
|
+
error?: string
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
export interface ExportBatch {
|
|
63
|
+
results: ExportItemResult[]
|
|
64
|
+
cursor: string
|
|
65
|
+
hasMore: boolean
|
|
66
|
+
batchIndex: number
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
export interface ValidationResult {
|
|
70
|
+
ok: boolean
|
|
71
|
+
message?: string
|
|
72
|
+
details?: Record<string, unknown>
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
export interface DataSyncAdapter {
|
|
76
|
+
readonly providerKey: string
|
|
77
|
+
readonly direction: 'import' | 'export' | 'bidirectional'
|
|
78
|
+
readonly supportedEntities: string[]
|
|
79
|
+
|
|
80
|
+
streamImport?(input: StreamImportInput): AsyncIterable<ImportBatch>
|
|
81
|
+
streamExport?(input: StreamExportInput): AsyncIterable<ExportBatch>
|
|
82
|
+
getInitialCursor?(input: { entityType: string; scope: TenantScope }): Promise<string | null>
|
|
83
|
+
getMapping(input: { entityType: string; scope: TenantScope }): Promise<DataMapping>
|
|
84
|
+
validateConnection?(input: {
|
|
85
|
+
entityType: string
|
|
86
|
+
credentials: Record<string, unknown>
|
|
87
|
+
mapping: DataMapping
|
|
88
|
+
scope: TenantScope
|
|
89
|
+
}): Promise<ValidationResult>
|
|
90
|
+
}
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
import type { EntityManager } from '@mikro-orm/postgresql'
|
|
2
|
+
import { findOneWithDecryption } from '@open-mercato/shared/lib/encryption/find'
|
|
3
|
+
import { SyncExternalIdMapping } from '../../integrations/data/entities'
|
|
4
|
+
|
|
5
|
+
type MappingScope = {
|
|
6
|
+
organizationId: string
|
|
7
|
+
tenantId: string
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export function createExternalIdMappingService(em: EntityManager) {
|
|
11
|
+
return {
|
|
12
|
+
async lookupLocalId(integrationId: string, entityType: string, externalId: string, scope: MappingScope): Promise<string | null> {
|
|
13
|
+
const row = await findOneWithDecryption(
|
|
14
|
+
em,
|
|
15
|
+
SyncExternalIdMapping,
|
|
16
|
+
{
|
|
17
|
+
integrationId,
|
|
18
|
+
internalEntityType: entityType,
|
|
19
|
+
externalId,
|
|
20
|
+
organizationId: scope.organizationId,
|
|
21
|
+
tenantId: scope.tenantId,
|
|
22
|
+
deletedAt: null,
|
|
23
|
+
},
|
|
24
|
+
undefined,
|
|
25
|
+
scope,
|
|
26
|
+
)
|
|
27
|
+
return row?.internalEntityId ?? null
|
|
28
|
+
},
|
|
29
|
+
|
|
30
|
+
async lookupExternalId(integrationId: string, entityType: string, localId: string, scope: MappingScope): Promise<string | null> {
|
|
31
|
+
const row = await findOneWithDecryption(
|
|
32
|
+
em,
|
|
33
|
+
SyncExternalIdMapping,
|
|
34
|
+
{
|
|
35
|
+
integrationId,
|
|
36
|
+
internalEntityType: entityType,
|
|
37
|
+
internalEntityId: localId,
|
|
38
|
+
organizationId: scope.organizationId,
|
|
39
|
+
tenantId: scope.tenantId,
|
|
40
|
+
deletedAt: null,
|
|
41
|
+
},
|
|
42
|
+
undefined,
|
|
43
|
+
scope,
|
|
44
|
+
)
|
|
45
|
+
return row?.externalId ?? null
|
|
46
|
+
},
|
|
47
|
+
|
|
48
|
+
async storeExternalIdMapping(
|
|
49
|
+
integrationId: string,
|
|
50
|
+
entityType: string,
|
|
51
|
+
localId: string,
|
|
52
|
+
externalId: string,
|
|
53
|
+
scope: MappingScope,
|
|
54
|
+
): Promise<SyncExternalIdMapping> {
|
|
55
|
+
const existing = await findOneWithDecryption(
|
|
56
|
+
em,
|
|
57
|
+
SyncExternalIdMapping,
|
|
58
|
+
{
|
|
59
|
+
integrationId,
|
|
60
|
+
internalEntityType: entityType,
|
|
61
|
+
internalEntityId: localId,
|
|
62
|
+
organizationId: scope.organizationId,
|
|
63
|
+
tenantId: scope.tenantId,
|
|
64
|
+
deletedAt: null,
|
|
65
|
+
},
|
|
66
|
+
undefined,
|
|
67
|
+
scope,
|
|
68
|
+
)
|
|
69
|
+
|
|
70
|
+
if (existing) {
|
|
71
|
+
existing.externalId = externalId
|
|
72
|
+
existing.syncStatus = 'synced'
|
|
73
|
+
existing.lastSyncedAt = new Date()
|
|
74
|
+
await em.flush()
|
|
75
|
+
return existing
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
const created = em.create(SyncExternalIdMapping, {
|
|
79
|
+
integrationId,
|
|
80
|
+
internalEntityType: entityType,
|
|
81
|
+
internalEntityId: localId,
|
|
82
|
+
externalId,
|
|
83
|
+
syncStatus: 'synced',
|
|
84
|
+
lastSyncedAt: new Date(),
|
|
85
|
+
organizationId: scope.organizationId,
|
|
86
|
+
tenantId: scope.tenantId,
|
|
87
|
+
})
|
|
88
|
+
|
|
89
|
+
await em.persistAndFlush(created)
|
|
90
|
+
return created
|
|
91
|
+
},
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
export type ExternalIdMappingService = ReturnType<typeof createExternalIdMappingService>
|