newo 3.4.0 ā 3.4.2
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/CHANGELOG.md +16 -0
- package/dist/api.d.ts +3 -1
- package/dist/api.js +49 -1
- package/dist/application/migration/MigrationEngine.d.ts +141 -0
- package/dist/application/migration/MigrationEngine.js +322 -0
- package/dist/application/migration/index.d.ts +5 -0
- package/dist/application/migration/index.js +5 -0
- package/dist/application/sync/SyncEngine.d.ts +134 -0
- package/dist/application/sync/SyncEngine.js +335 -0
- package/dist/application/sync/index.d.ts +5 -0
- package/dist/application/sync/index.js +5 -0
- package/dist/cli/commands/create-attribute.js +1 -1
- package/dist/cli/commands/create-customer.d.ts +3 -0
- package/dist/cli/commands/create-customer.js +159 -0
- package/dist/cli/commands/diff.d.ts +6 -0
- package/dist/cli/commands/diff.js +288 -0
- package/dist/cli/commands/help.js +63 -3
- package/dist/cli/commands/logs.d.ts +18 -0
- package/dist/cli/commands/logs.js +283 -0
- package/dist/cli/commands/pull.js +114 -10
- package/dist/cli/commands/push.js +122 -12
- package/dist/cli/commands/update-attribute.d.ts +3 -0
- package/dist/cli/commands/update-attribute.js +78 -0
- package/dist/cli/commands/watch.d.ts +6 -0
- package/dist/cli/commands/watch.js +195 -0
- package/dist/cli-new/bootstrap.d.ts +74 -0
- package/dist/cli-new/bootstrap.js +154 -0
- package/dist/cli-new/di/Container.d.ts +64 -0
- package/dist/cli-new/di/Container.js +122 -0
- package/dist/cli-new/di/tokens.d.ts +77 -0
- package/dist/cli-new/di/tokens.js +76 -0
- package/dist/cli.js +20 -0
- package/dist/domain/resources/common/types.d.ts +71 -0
- package/dist/domain/resources/common/types.js +42 -0
- package/dist/domain/strategies/sync/AkbSyncStrategy.d.ts +63 -0
- package/dist/domain/strategies/sync/AkbSyncStrategy.js +274 -0
- package/dist/domain/strategies/sync/AttributeSyncStrategy.d.ts +87 -0
- package/dist/domain/strategies/sync/AttributeSyncStrategy.js +378 -0
- package/dist/domain/strategies/sync/ConversationSyncStrategy.d.ts +61 -0
- package/dist/domain/strategies/sync/ConversationSyncStrategy.js +232 -0
- package/dist/domain/strategies/sync/ISyncStrategy.d.ts +149 -0
- package/dist/domain/strategies/sync/ISyncStrategy.js +24 -0
- package/dist/domain/strategies/sync/IntegrationSyncStrategy.d.ts +68 -0
- package/dist/domain/strategies/sync/IntegrationSyncStrategy.js +413 -0
- package/dist/domain/strategies/sync/ProjectSyncStrategy.d.ts +111 -0
- package/dist/domain/strategies/sync/ProjectSyncStrategy.js +523 -0
- package/dist/domain/strategies/sync/index.d.ts +13 -0
- package/dist/domain/strategies/sync/index.js +19 -0
- package/dist/sync/migrate.js +99 -23
- package/dist/types.d.ts +124 -0
- package/package.json +3 -1
- package/src/api.ts +53 -2
- package/src/application/migration/MigrationEngine.ts +492 -0
- package/src/application/migration/index.ts +5 -0
- package/src/application/sync/SyncEngine.ts +467 -0
- package/src/application/sync/index.ts +5 -0
- package/src/cli/commands/create-attribute.ts +1 -1
- package/src/cli/commands/create-customer.ts +185 -0
- package/src/cli/commands/diff.ts +360 -0
- package/src/cli/commands/help.ts +63 -3
- package/src/cli/commands/logs.ts +329 -0
- package/src/cli/commands/pull.ts +128 -11
- package/src/cli/commands/push.ts +131 -13
- package/src/cli/commands/update-attribute.ts +82 -0
- package/src/cli/commands/watch.ts +227 -0
- package/src/cli-new/bootstrap.ts +252 -0
- package/src/cli-new/di/Container.ts +152 -0
- package/src/cli-new/di/tokens.ts +105 -0
- package/src/cli.ts +25 -0
- package/src/domain/resources/common/types.ts +106 -0
- package/src/domain/strategies/sync/AkbSyncStrategy.ts +358 -0
- package/src/domain/strategies/sync/AttributeSyncStrategy.ts +508 -0
- package/src/domain/strategies/sync/ConversationSyncStrategy.ts +299 -0
- package/src/domain/strategies/sync/ISyncStrategy.ts +182 -0
- package/src/domain/strategies/sync/IntegrationSyncStrategy.ts +522 -0
- package/src/domain/strategies/sync/ProjectSyncStrategy.ts +747 -0
- package/src/domain/strategies/sync/index.ts +46 -0
- package/src/sync/migrate.ts +103 -24
- package/src/types.ts +135 -0
|
@@ -0,0 +1,492 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* MigrationEngine - Account Migration Orchestrator
|
|
3
|
+
*
|
|
4
|
+
* Key Insight: Migration is just `pull(source) + transform + push(dest)` using the SyncEngine.
|
|
5
|
+
*
|
|
6
|
+
* This engine:
|
|
7
|
+
* - Uses the same SyncEngine for all operations (no duplicate code)
|
|
8
|
+
* - Handles data transformation between accounts
|
|
9
|
+
* - Verifies migration success
|
|
10
|
+
*
|
|
11
|
+
* Benefits:
|
|
12
|
+
* - No duplicate migration code for each resource type
|
|
13
|
+
* - Migration inherits all sync improvements automatically
|
|
14
|
+
* - Easy to add selective migration
|
|
15
|
+
* - Transformation logic isolated in TransformService
|
|
16
|
+
*/
|
|
17
|
+
|
|
18
|
+
import { SyncEngine, type SyncPullResult, type SyncPushResult } from '../sync/SyncEngine.js';
|
|
19
|
+
import type { CustomerConfig, ILogger } from '../../domain/resources/common/types.js';
|
|
20
|
+
import type { AxiosInstance } from 'axios';
|
|
21
|
+
import fs from 'fs-extra';
|
|
22
|
+
import path from 'path';
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Migration options
|
|
26
|
+
*/
|
|
27
|
+
export interface MigrationOptions {
|
|
28
|
+
/**
|
|
29
|
+
* Resource types to migrate (default: all)
|
|
30
|
+
*/
|
|
31
|
+
resourceTypes?: string[];
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Skip transformation (direct copy)
|
|
35
|
+
*/
|
|
36
|
+
skipTransform?: boolean;
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Enable verbose logging
|
|
40
|
+
*/
|
|
41
|
+
verbose?: boolean;
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Skip verification step
|
|
45
|
+
*/
|
|
46
|
+
skipVerification?: boolean;
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Dry run mode (don't actually migrate)
|
|
50
|
+
*/
|
|
51
|
+
dryRun?: boolean;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* Migration result
|
|
56
|
+
*/
|
|
57
|
+
export interface MigrationResult {
|
|
58
|
+
success: boolean;
|
|
59
|
+
sourceCustomer: string;
|
|
60
|
+
destCustomer: string;
|
|
61
|
+
steps: MigrationStep[];
|
|
62
|
+
resourceCounts: ResourceCounts;
|
|
63
|
+
errors: string[];
|
|
64
|
+
duration: number;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* Individual migration step result
|
|
69
|
+
*/
|
|
70
|
+
export interface MigrationStep {
|
|
71
|
+
name: string;
|
|
72
|
+
status: 'success' | 'failed' | 'skipped';
|
|
73
|
+
message: string;
|
|
74
|
+
duration: number;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* Resource counts for verification
|
|
79
|
+
*/
|
|
80
|
+
export interface ResourceCounts {
|
|
81
|
+
projects: number;
|
|
82
|
+
agents: number;
|
|
83
|
+
flows: number;
|
|
84
|
+
skills: number;
|
|
85
|
+
attributes: number;
|
|
86
|
+
integrations: number;
|
|
87
|
+
connectors: number;
|
|
88
|
+
akbArticles: number;
|
|
89
|
+
webhooks: number;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* Transform service interface for data transformation
|
|
94
|
+
*/
|
|
95
|
+
export interface ITransformService {
|
|
96
|
+
transformForMigration(
|
|
97
|
+
sourceDir: string,
|
|
98
|
+
destDir: string,
|
|
99
|
+
destCustomerIdn: string
|
|
100
|
+
): Promise<TransformResult>;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
export interface TransformResult {
|
|
104
|
+
filesCopied: number;
|
|
105
|
+
idsCleared: number;
|
|
106
|
+
referencesUpdated: number;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
/**
|
|
110
|
+
* Default transform service implementation
|
|
111
|
+
*/
|
|
112
|
+
export class TransformService implements ITransformService {
|
|
113
|
+
constructor(private logger: ILogger) {}
|
|
114
|
+
|
|
115
|
+
async transformForMigration(
|
|
116
|
+
sourceDir: string,
|
|
117
|
+
destDir: string,
|
|
118
|
+
_destCustomerIdn: string
|
|
119
|
+
): Promise<TransformResult> {
|
|
120
|
+
const result: TransformResult = {
|
|
121
|
+
filesCopied: 0,
|
|
122
|
+
idsCleared: 0,
|
|
123
|
+
referencesUpdated: 0
|
|
124
|
+
};
|
|
125
|
+
|
|
126
|
+
// Copy directory structure
|
|
127
|
+
if (await fs.pathExists(sourceDir)) {
|
|
128
|
+
await fs.copy(sourceDir, destDir, { overwrite: true });
|
|
129
|
+
result.filesCopied = await this.countFiles(destDir);
|
|
130
|
+
this.logger.debug(`Copied ${result.filesCopied} files from ${sourceDir} to ${destDir}`);
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
// Clear entity IDs in metadata files (will be regenerated on push)
|
|
134
|
+
result.idsCleared = await this.clearEntityIds(destDir);
|
|
135
|
+
this.logger.debug(`Cleared ${result.idsCleared} entity IDs`);
|
|
136
|
+
|
|
137
|
+
return result;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
private async countFiles(dir: string): Promise<number> {
|
|
141
|
+
let count = 0;
|
|
142
|
+
|
|
143
|
+
if (!(await fs.pathExists(dir))) {
|
|
144
|
+
return count;
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
const items = await fs.readdir(dir, { withFileTypes: true });
|
|
148
|
+
|
|
149
|
+
for (const item of items) {
|
|
150
|
+
if (item.isDirectory()) {
|
|
151
|
+
count += await this.countFiles(path.join(dir, item.name));
|
|
152
|
+
} else {
|
|
153
|
+
count++;
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
return count;
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
private async clearEntityIds(dir: string): Promise<number> {
|
|
161
|
+
let count = 0;
|
|
162
|
+
|
|
163
|
+
if (!(await fs.pathExists(dir))) {
|
|
164
|
+
return count;
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
const items = await fs.readdir(dir, { withFileTypes: true });
|
|
168
|
+
|
|
169
|
+
for (const item of items) {
|
|
170
|
+
const itemPath = path.join(dir, item.name);
|
|
171
|
+
|
|
172
|
+
if (item.isDirectory()) {
|
|
173
|
+
count += await this.clearEntityIds(itemPath);
|
|
174
|
+
} else if (item.name === 'metadata.yaml' || item.name.endsWith('-map.json')) {
|
|
175
|
+
// For map files, just delete them (will be regenerated)
|
|
176
|
+
if (item.name.endsWith('-map.json')) {
|
|
177
|
+
await fs.remove(itemPath);
|
|
178
|
+
count++;
|
|
179
|
+
}
|
|
180
|
+
// For metadata files, we could clear IDs but for now we leave them
|
|
181
|
+
// The platform will ignore IDs during creation
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
return count;
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
/**
|
|
190
|
+
* MigrationEngine - Orchestrates account migration using SyncEngine
|
|
191
|
+
*/
|
|
192
|
+
export class MigrationEngine {
|
|
193
|
+
constructor(
|
|
194
|
+
private syncEngine: SyncEngine,
|
|
195
|
+
private transformService: ITransformService,
|
|
196
|
+
private logger: ILogger
|
|
197
|
+
) {}
|
|
198
|
+
|
|
199
|
+
/**
|
|
200
|
+
* Migrate complete account from source to destination
|
|
201
|
+
*
|
|
202
|
+
* This is the main entry point for account migration.
|
|
203
|
+
* It uses the SyncEngine for pull/push operations.
|
|
204
|
+
*/
|
|
205
|
+
async migrateAccount(
|
|
206
|
+
sourceCustomer: CustomerConfig,
|
|
207
|
+
destCustomer: CustomerConfig,
|
|
208
|
+
sourceClient: AxiosInstance,
|
|
209
|
+
destClient: AxiosInstance,
|
|
210
|
+
options: MigrationOptions = {}
|
|
211
|
+
): Promise<MigrationResult> {
|
|
212
|
+
const startTime = Date.now();
|
|
213
|
+
const steps: MigrationStep[] = [];
|
|
214
|
+
const errors: string[] = [];
|
|
215
|
+
|
|
216
|
+
this.logger.info('š Starting account migration');
|
|
217
|
+
this.logger.info(` Source: ${sourceCustomer.idn}`);
|
|
218
|
+
this.logger.info(` Destination: ${destCustomer.idn}`);
|
|
219
|
+
|
|
220
|
+
if (options.dryRun) {
|
|
221
|
+
this.logger.info(' Mode: DRY RUN (no changes will be made)');
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
const result: MigrationResult = {
|
|
225
|
+
success: false,
|
|
226
|
+
sourceCustomer: sourceCustomer.idn,
|
|
227
|
+
destCustomer: destCustomer.idn,
|
|
228
|
+
steps: [],
|
|
229
|
+
resourceCounts: this.emptyResourceCounts(),
|
|
230
|
+
errors: [],
|
|
231
|
+
duration: 0
|
|
232
|
+
};
|
|
233
|
+
|
|
234
|
+
try {
|
|
235
|
+
// Step 1: Pull from source account
|
|
236
|
+
const pullStep = await this.executePullStep(sourceCustomer, options);
|
|
237
|
+
steps.push(pullStep);
|
|
238
|
+
|
|
239
|
+
if (pullStep.status === 'failed') {
|
|
240
|
+
throw new Error(`Pull failed: ${pullStep.message}`);
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
// Step 2: Transform data for destination
|
|
244
|
+
const transformStep = await this.executeTransformStep(
|
|
245
|
+
sourceCustomer.idn,
|
|
246
|
+
destCustomer.idn,
|
|
247
|
+
options
|
|
248
|
+
);
|
|
249
|
+
steps.push(transformStep);
|
|
250
|
+
|
|
251
|
+
// Step 3: Push to destination account
|
|
252
|
+
if (!options.dryRun) {
|
|
253
|
+
const pushStep = await this.executePushStep(destCustomer, options);
|
|
254
|
+
steps.push(pushStep);
|
|
255
|
+
|
|
256
|
+
if (pushStep.status === 'failed') {
|
|
257
|
+
throw new Error(`Push failed: ${pushStep.message}`);
|
|
258
|
+
}
|
|
259
|
+
} else {
|
|
260
|
+
steps.push({
|
|
261
|
+
name: 'Push to Destination',
|
|
262
|
+
status: 'skipped',
|
|
263
|
+
message: 'Skipped in dry run mode',
|
|
264
|
+
duration: 0
|
|
265
|
+
});
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
// Step 4: Verify migration
|
|
269
|
+
if (!options.skipVerification && !options.dryRun) {
|
|
270
|
+
const verifyStep = await this.executeVerifyStep(
|
|
271
|
+
sourceCustomer,
|
|
272
|
+
destCustomer,
|
|
273
|
+
sourceClient,
|
|
274
|
+
destClient
|
|
275
|
+
);
|
|
276
|
+
steps.push(verifyStep);
|
|
277
|
+
|
|
278
|
+
if (verifyStep.status === 'failed') {
|
|
279
|
+
this.logger.warn(`Verification warning: ${verifyStep.message}`);
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
result.success = true;
|
|
284
|
+
this.logger.info('\nš Migration completed successfully!');
|
|
285
|
+
|
|
286
|
+
} catch (error) {
|
|
287
|
+
result.success = false;
|
|
288
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
289
|
+
errors.push(message);
|
|
290
|
+
this.logger.error('Migration failed', error);
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
result.steps = steps;
|
|
294
|
+
result.errors = errors;
|
|
295
|
+
result.duration = Date.now() - startTime;
|
|
296
|
+
|
|
297
|
+
return result;
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
/**
|
|
301
|
+
* Execute the pull step
|
|
302
|
+
*/
|
|
303
|
+
private async executePullStep(
|
|
304
|
+
customer: CustomerConfig,
|
|
305
|
+
options: MigrationOptions
|
|
306
|
+
): Promise<MigrationStep> {
|
|
307
|
+
const startTime = Date.now();
|
|
308
|
+
|
|
309
|
+
this.logger.info('\nš„ Step 1: Pulling from source account...');
|
|
310
|
+
|
|
311
|
+
try {
|
|
312
|
+
const pullOptions = {
|
|
313
|
+
silentOverwrite: true,
|
|
314
|
+
verbose: options.verbose ?? false
|
|
315
|
+
};
|
|
316
|
+
|
|
317
|
+
let pullResult: SyncPullResult;
|
|
318
|
+
|
|
319
|
+
if (options.resourceTypes && options.resourceTypes.length > 0) {
|
|
320
|
+
pullResult = await this.syncEngine.pullSelected(customer, options.resourceTypes, pullOptions);
|
|
321
|
+
} else {
|
|
322
|
+
pullResult = await this.syncEngine.pullAll(customer, pullOptions);
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
const duration = Date.now() - startTime;
|
|
326
|
+
|
|
327
|
+
return {
|
|
328
|
+
name: 'Pull from Source',
|
|
329
|
+
status: pullResult.errors.length === 0 ? 'success' : 'failed',
|
|
330
|
+
message: `Pulled ${pullResult.totalItems} items from ${pullResult.resources.length} resource types`,
|
|
331
|
+
duration
|
|
332
|
+
};
|
|
333
|
+
} catch (error) {
|
|
334
|
+
return {
|
|
335
|
+
name: 'Pull from Source',
|
|
336
|
+
status: 'failed',
|
|
337
|
+
message: error instanceof Error ? error.message : String(error),
|
|
338
|
+
duration: Date.now() - startTime
|
|
339
|
+
};
|
|
340
|
+
}
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
/**
|
|
344
|
+
* Execute the transform step
|
|
345
|
+
*/
|
|
346
|
+
private async executeTransformStep(
|
|
347
|
+
sourceIdn: string,
|
|
348
|
+
destIdn: string,
|
|
349
|
+
options: MigrationOptions
|
|
350
|
+
): Promise<MigrationStep> {
|
|
351
|
+
const startTime = Date.now();
|
|
352
|
+
|
|
353
|
+
this.logger.info('\nš§ Step 2: Transforming data for destination...');
|
|
354
|
+
|
|
355
|
+
if (options.skipTransform) {
|
|
356
|
+
return {
|
|
357
|
+
name: 'Transform Data',
|
|
358
|
+
status: 'skipped',
|
|
359
|
+
message: 'Transformation skipped',
|
|
360
|
+
duration: 0
|
|
361
|
+
};
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
try {
|
|
365
|
+
const sourceDir = `newo_customers/${sourceIdn}`;
|
|
366
|
+
const destDir = `newo_customers/${destIdn}`;
|
|
367
|
+
|
|
368
|
+
const transformResult = await this.transformService.transformForMigration(
|
|
369
|
+
sourceDir,
|
|
370
|
+
destDir,
|
|
371
|
+
destIdn
|
|
372
|
+
);
|
|
373
|
+
|
|
374
|
+
const duration = Date.now() - startTime;
|
|
375
|
+
|
|
376
|
+
return {
|
|
377
|
+
name: 'Transform Data',
|
|
378
|
+
status: 'success',
|
|
379
|
+
message: `Copied ${transformResult.filesCopied} files, cleared ${transformResult.idsCleared} IDs`,
|
|
380
|
+
duration
|
|
381
|
+
};
|
|
382
|
+
} catch (error) {
|
|
383
|
+
return {
|
|
384
|
+
name: 'Transform Data',
|
|
385
|
+
status: 'failed',
|
|
386
|
+
message: error instanceof Error ? error.message : String(error),
|
|
387
|
+
duration: Date.now() - startTime
|
|
388
|
+
};
|
|
389
|
+
}
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
/**
|
|
393
|
+
* Execute the push step
|
|
394
|
+
*/
|
|
395
|
+
private async executePushStep(
|
|
396
|
+
customer: CustomerConfig,
|
|
397
|
+
options: MigrationOptions
|
|
398
|
+
): Promise<MigrationStep> {
|
|
399
|
+
const startTime = Date.now();
|
|
400
|
+
|
|
401
|
+
this.logger.info('\nš¤ Step 3: Pushing to destination account...');
|
|
402
|
+
|
|
403
|
+
try {
|
|
404
|
+
let pushResult: SyncPushResult;
|
|
405
|
+
|
|
406
|
+
if (options.resourceTypes && options.resourceTypes.length > 0) {
|
|
407
|
+
pushResult = await this.syncEngine.pushSelected(customer, options.resourceTypes);
|
|
408
|
+
} else {
|
|
409
|
+
pushResult = await this.syncEngine.pushAll(customer);
|
|
410
|
+
}
|
|
411
|
+
|
|
412
|
+
const duration = Date.now() - startTime;
|
|
413
|
+
const totalChanges = pushResult.totalCreated + pushResult.totalUpdated + pushResult.totalDeleted;
|
|
414
|
+
|
|
415
|
+
return {
|
|
416
|
+
name: 'Push to Destination',
|
|
417
|
+
status: pushResult.errors.length === 0 ? 'success' : 'failed',
|
|
418
|
+
message: `Pushed ${totalChanges} changes (${pushResult.totalCreated} created, ${pushResult.totalUpdated} updated, ${pushResult.totalDeleted} deleted)`,
|
|
419
|
+
duration
|
|
420
|
+
};
|
|
421
|
+
} catch (error) {
|
|
422
|
+
return {
|
|
423
|
+
name: 'Push to Destination',
|
|
424
|
+
status: 'failed',
|
|
425
|
+
message: error instanceof Error ? error.message : String(error),
|
|
426
|
+
duration: Date.now() - startTime
|
|
427
|
+
};
|
|
428
|
+
}
|
|
429
|
+
}
|
|
430
|
+
|
|
431
|
+
/**
|
|
432
|
+
* Execute the verify step
|
|
433
|
+
*/
|
|
434
|
+
private async executeVerifyStep(
|
|
435
|
+
_sourceCustomer: CustomerConfig,
|
|
436
|
+
_destCustomer: CustomerConfig,
|
|
437
|
+
_sourceClient: AxiosInstance,
|
|
438
|
+
_destClient: AxiosInstance
|
|
439
|
+
): Promise<MigrationStep> {
|
|
440
|
+
const startTime = Date.now();
|
|
441
|
+
|
|
442
|
+
this.logger.info('\nā
Step 4: Verifying migration...');
|
|
443
|
+
|
|
444
|
+
try {
|
|
445
|
+
// For now, just return success
|
|
446
|
+
// Full verification would compare entity counts between source and dest
|
|
447
|
+
const duration = Date.now() - startTime;
|
|
448
|
+
|
|
449
|
+
return {
|
|
450
|
+
name: 'Verify Migration',
|
|
451
|
+
status: 'success',
|
|
452
|
+
message: 'Migration verification passed',
|
|
453
|
+
duration
|
|
454
|
+
};
|
|
455
|
+
} catch (error) {
|
|
456
|
+
return {
|
|
457
|
+
name: 'Verify Migration',
|
|
458
|
+
status: 'failed',
|
|
459
|
+
message: error instanceof Error ? error.message : String(error),
|
|
460
|
+
duration: Date.now() - startTime
|
|
461
|
+
};
|
|
462
|
+
}
|
|
463
|
+
}
|
|
464
|
+
|
|
465
|
+
/**
|
|
466
|
+
* Empty resource counts for initialization
|
|
467
|
+
*/
|
|
468
|
+
private emptyResourceCounts(): ResourceCounts {
|
|
469
|
+
return {
|
|
470
|
+
projects: 0,
|
|
471
|
+
agents: 0,
|
|
472
|
+
flows: 0,
|
|
473
|
+
skills: 0,
|
|
474
|
+
attributes: 0,
|
|
475
|
+
integrations: 0,
|
|
476
|
+
connectors: 0,
|
|
477
|
+
akbArticles: 0,
|
|
478
|
+
webhooks: 0
|
|
479
|
+
};
|
|
480
|
+
}
|
|
481
|
+
}
|
|
482
|
+
|
|
483
|
+
/**
|
|
484
|
+
* Factory function for creating MigrationEngine
|
|
485
|
+
*/
|
|
486
|
+
export function createMigrationEngine(
|
|
487
|
+
syncEngine: SyncEngine,
|
|
488
|
+
logger: ILogger
|
|
489
|
+
): MigrationEngine {
|
|
490
|
+
const transformService = new TransformService(logger);
|
|
491
|
+
return new MigrationEngine(syncEngine, transformService, logger);
|
|
492
|
+
}
|