@njdamstra/appwrite-utils-cli 1.8.9
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 +19 -0
- package/README.md +1133 -0
- package/dist/adapters/AdapterFactory.d.ts +94 -0
- package/dist/adapters/AdapterFactory.js +405 -0
- package/dist/adapters/DatabaseAdapter.d.ts +233 -0
- package/dist/adapters/DatabaseAdapter.js +50 -0
- package/dist/adapters/LegacyAdapter.d.ts +50 -0
- package/dist/adapters/LegacyAdapter.js +612 -0
- package/dist/adapters/TablesDBAdapter.d.ts +45 -0
- package/dist/adapters/TablesDBAdapter.js +571 -0
- package/dist/adapters/index.d.ts +11 -0
- package/dist/adapters/index.js +12 -0
- package/dist/backups/operations/bucketBackup.d.ts +19 -0
- package/dist/backups/operations/bucketBackup.js +197 -0
- package/dist/backups/operations/collectionBackup.d.ts +30 -0
- package/dist/backups/operations/collectionBackup.js +201 -0
- package/dist/backups/operations/comprehensiveBackup.d.ts +25 -0
- package/dist/backups/operations/comprehensiveBackup.js +238 -0
- package/dist/backups/schemas/bucketManifest.d.ts +93 -0
- package/dist/backups/schemas/bucketManifest.js +33 -0
- package/dist/backups/schemas/comprehensiveManifest.d.ts +108 -0
- package/dist/backups/schemas/comprehensiveManifest.js +32 -0
- package/dist/backups/tracking/centralizedTracking.d.ts +34 -0
- package/dist/backups/tracking/centralizedTracking.js +274 -0
- package/dist/cli/commands/configCommands.d.ts +8 -0
- package/dist/cli/commands/configCommands.js +166 -0
- package/dist/cli/commands/databaseCommands.d.ts +13 -0
- package/dist/cli/commands/databaseCommands.js +554 -0
- package/dist/cli/commands/functionCommands.d.ts +7 -0
- package/dist/cli/commands/functionCommands.js +330 -0
- package/dist/cli/commands/schemaCommands.d.ts +7 -0
- package/dist/cli/commands/schemaCommands.js +169 -0
- package/dist/cli/commands/storageCommands.d.ts +5 -0
- package/dist/cli/commands/storageCommands.js +143 -0
- package/dist/cli/commands/transferCommands.d.ts +5 -0
- package/dist/cli/commands/transferCommands.js +384 -0
- package/dist/collections/attributes.d.ts +13 -0
- package/dist/collections/attributes.js +1364 -0
- package/dist/collections/indexes.d.ts +12 -0
- package/dist/collections/indexes.js +217 -0
- package/dist/collections/methods.d.ts +19 -0
- package/dist/collections/methods.js +682 -0
- package/dist/collections/tableOperations.d.ts +86 -0
- package/dist/collections/tableOperations.js +434 -0
- package/dist/collections/transferOperations.d.ts +8 -0
- package/dist/collections/transferOperations.js +412 -0
- package/dist/collections/wipeOperations.d.ts +16 -0
- package/dist/collections/wipeOperations.js +233 -0
- package/dist/config/ConfigManager.d.ts +445 -0
- package/dist/config/ConfigManager.js +625 -0
- package/dist/config/configMigration.d.ts +87 -0
- package/dist/config/configMigration.js +390 -0
- package/dist/config/configValidation.d.ts +66 -0
- package/dist/config/configValidation.js +358 -0
- package/dist/config/index.d.ts +8 -0
- package/dist/config/index.js +7 -0
- package/dist/config/services/ConfigDiscoveryService.d.ts +126 -0
- package/dist/config/services/ConfigDiscoveryService.js +374 -0
- package/dist/config/services/ConfigLoaderService.d.ts +129 -0
- package/dist/config/services/ConfigLoaderService.js +540 -0
- package/dist/config/services/ConfigMergeService.d.ts +208 -0
- package/dist/config/services/ConfigMergeService.js +308 -0
- package/dist/config/services/ConfigValidationService.d.ts +214 -0
- package/dist/config/services/ConfigValidationService.js +310 -0
- package/dist/config/services/SessionAuthService.d.ts +225 -0
- package/dist/config/services/SessionAuthService.js +456 -0
- package/dist/config/services/__tests__/ConfigMergeService.test.d.ts +1 -0
- package/dist/config/services/__tests__/ConfigMergeService.test.js +271 -0
- package/dist/config/services/index.d.ts +13 -0
- package/dist/config/services/index.js +10 -0
- package/dist/config/yamlConfig.d.ts +722 -0
- package/dist/config/yamlConfig.js +702 -0
- package/dist/databases/methods.d.ts +6 -0
- package/dist/databases/methods.js +35 -0
- package/dist/databases/setup.d.ts +5 -0
- package/dist/databases/setup.js +45 -0
- package/dist/examples/yamlTerminologyExample.d.ts +42 -0
- package/dist/examples/yamlTerminologyExample.js +272 -0
- package/dist/functions/deployments.d.ts +4 -0
- package/dist/functions/deployments.js +146 -0
- package/dist/functions/fnConfigDiscovery.d.ts +3 -0
- package/dist/functions/fnConfigDiscovery.js +108 -0
- package/dist/functions/methods.d.ts +16 -0
- package/dist/functions/methods.js +162 -0
- package/dist/functions/pathResolution.d.ts +37 -0
- package/dist/functions/pathResolution.js +185 -0
- package/dist/functions/templates/count-docs-in-collection/README.md +54 -0
- package/dist/functions/templates/count-docs-in-collection/src/main.ts +159 -0
- package/dist/functions/templates/count-docs-in-collection/src/request.ts +9 -0
- package/dist/functions/templates/hono-typescript/README.md +286 -0
- package/dist/functions/templates/hono-typescript/src/adapters/request.ts +74 -0
- package/dist/functions/templates/hono-typescript/src/adapters/response.ts +106 -0
- package/dist/functions/templates/hono-typescript/src/app.ts +180 -0
- package/dist/functions/templates/hono-typescript/src/context.ts +103 -0
- package/dist/functions/templates/hono-typescript/src/index.ts +54 -0
- package/dist/functions/templates/hono-typescript/src/middleware/appwrite.ts +119 -0
- package/dist/functions/templates/typescript-node/README.md +32 -0
- package/dist/functions/templates/typescript-node/src/context.ts +103 -0
- package/dist/functions/templates/typescript-node/src/index.ts +29 -0
- package/dist/functions/templates/uv/README.md +31 -0
- package/dist/functions/templates/uv/pyproject.toml +30 -0
- package/dist/functions/templates/uv/src/__init__.py +0 -0
- package/dist/functions/templates/uv/src/context.py +125 -0
- package/dist/functions/templates/uv/src/index.py +46 -0
- package/dist/init.d.ts +2 -0
- package/dist/init.js +57 -0
- package/dist/interactiveCLI.d.ts +31 -0
- package/dist/interactiveCLI.js +898 -0
- package/dist/main.d.ts +2 -0
- package/dist/main.js +1172 -0
- package/dist/migrations/afterImportActions.d.ts +17 -0
- package/dist/migrations/afterImportActions.js +306 -0
- package/dist/migrations/appwriteToX.d.ts +211 -0
- package/dist/migrations/appwriteToX.js +491 -0
- package/dist/migrations/comprehensiveTransfer.d.ts +147 -0
- package/dist/migrations/comprehensiveTransfer.js +1317 -0
- package/dist/migrations/dataLoader.d.ts +753 -0
- package/dist/migrations/dataLoader.js +1250 -0
- package/dist/migrations/importController.d.ts +23 -0
- package/dist/migrations/importController.js +268 -0
- package/dist/migrations/importDataActions.d.ts +50 -0
- package/dist/migrations/importDataActions.js +230 -0
- package/dist/migrations/relationships.d.ts +29 -0
- package/dist/migrations/relationships.js +204 -0
- package/dist/migrations/services/DataTransformationService.d.ts +55 -0
- package/dist/migrations/services/DataTransformationService.js +158 -0
- package/dist/migrations/services/FileHandlerService.d.ts +75 -0
- package/dist/migrations/services/FileHandlerService.js +236 -0
- package/dist/migrations/services/ImportOrchestrator.d.ts +97 -0
- package/dist/migrations/services/ImportOrchestrator.js +485 -0
- package/dist/migrations/services/RateLimitManager.d.ts +138 -0
- package/dist/migrations/services/RateLimitManager.js +279 -0
- package/dist/migrations/services/RelationshipResolver.d.ts +120 -0
- package/dist/migrations/services/RelationshipResolver.js +332 -0
- package/dist/migrations/services/UserMappingService.d.ts +109 -0
- package/dist/migrations/services/UserMappingService.js +277 -0
- package/dist/migrations/services/ValidationService.d.ts +74 -0
- package/dist/migrations/services/ValidationService.js +260 -0
- package/dist/migrations/transfer.d.ts +26 -0
- package/dist/migrations/transfer.js +608 -0
- package/dist/migrations/yaml/YamlImportConfigLoader.d.ts +131 -0
- package/dist/migrations/yaml/YamlImportConfigLoader.js +383 -0
- package/dist/migrations/yaml/YamlImportIntegration.d.ts +93 -0
- package/dist/migrations/yaml/YamlImportIntegration.js +341 -0
- package/dist/migrations/yaml/generateImportSchemas.d.ts +30 -0
- package/dist/migrations/yaml/generateImportSchemas.js +1327 -0
- package/dist/schemas/authUser.d.ts +24 -0
- package/dist/schemas/authUser.js +17 -0
- package/dist/setup.d.ts +2 -0
- package/dist/setup.js +5 -0
- package/dist/setupCommands.d.ts +58 -0
- package/dist/setupCommands.js +490 -0
- package/dist/setupController.d.ts +9 -0
- package/dist/setupController.js +34 -0
- package/dist/shared/attributeMapper.d.ts +20 -0
- package/dist/shared/attributeMapper.js +203 -0
- package/dist/shared/backupMetadataSchema.d.ts +94 -0
- package/dist/shared/backupMetadataSchema.js +38 -0
- package/dist/shared/backupTracking.d.ts +18 -0
- package/dist/shared/backupTracking.js +176 -0
- package/dist/shared/confirmationDialogs.d.ts +75 -0
- package/dist/shared/confirmationDialogs.js +236 -0
- package/dist/shared/errorUtils.d.ts +54 -0
- package/dist/shared/errorUtils.js +95 -0
- package/dist/shared/functionManager.d.ts +48 -0
- package/dist/shared/functionManager.js +336 -0
- package/dist/shared/indexManager.d.ts +24 -0
- package/dist/shared/indexManager.js +151 -0
- package/dist/shared/jsonSchemaGenerator.d.ts +50 -0
- package/dist/shared/jsonSchemaGenerator.js +290 -0
- package/dist/shared/logging.d.ts +61 -0
- package/dist/shared/logging.js +116 -0
- package/dist/shared/messageFormatter.d.ts +39 -0
- package/dist/shared/messageFormatter.js +162 -0
- package/dist/shared/migrationHelpers.d.ts +61 -0
- package/dist/shared/migrationHelpers.js +145 -0
- package/dist/shared/operationLogger.d.ts +10 -0
- package/dist/shared/operationLogger.js +12 -0
- package/dist/shared/operationQueue.d.ts +40 -0
- package/dist/shared/operationQueue.js +311 -0
- package/dist/shared/operationsTable.d.ts +26 -0
- package/dist/shared/operationsTable.js +286 -0
- package/dist/shared/operationsTableSchema.d.ts +48 -0
- package/dist/shared/operationsTableSchema.js +35 -0
- package/dist/shared/progressManager.d.ts +62 -0
- package/dist/shared/progressManager.js +215 -0
- package/dist/shared/pydanticModelGenerator.d.ts +17 -0
- package/dist/shared/pydanticModelGenerator.js +615 -0
- package/dist/shared/relationshipExtractor.d.ts +56 -0
- package/dist/shared/relationshipExtractor.js +138 -0
- package/dist/shared/schemaGenerator.d.ts +40 -0
- package/dist/shared/schemaGenerator.js +556 -0
- package/dist/shared/selectionDialogs.d.ts +214 -0
- package/dist/shared/selectionDialogs.js +544 -0
- package/dist/storage/backupCompression.d.ts +20 -0
- package/dist/storage/backupCompression.js +67 -0
- package/dist/storage/methods.d.ts +32 -0
- package/dist/storage/methods.js +472 -0
- package/dist/storage/schemas.d.ts +842 -0
- package/dist/storage/schemas.js +175 -0
- package/dist/types.d.ts +4 -0
- package/dist/types.js +3 -0
- package/dist/users/methods.d.ts +16 -0
- package/dist/users/methods.js +277 -0
- package/dist/utils/ClientFactory.d.ts +87 -0
- package/dist/utils/ClientFactory.js +212 -0
- package/dist/utils/configDiscovery.d.ts +78 -0
- package/dist/utils/configDiscovery.js +472 -0
- package/dist/utils/configMigration.d.ts +1 -0
- package/dist/utils/configMigration.js +261 -0
- package/dist/utils/constantsGenerator.d.ts +31 -0
- package/dist/utils/constantsGenerator.js +321 -0
- package/dist/utils/dataConverters.d.ts +46 -0
- package/dist/utils/dataConverters.js +139 -0
- package/dist/utils/directoryUtils.d.ts +22 -0
- package/dist/utils/directoryUtils.js +59 -0
- package/dist/utils/getClientFromConfig.d.ts +39 -0
- package/dist/utils/getClientFromConfig.js +199 -0
- package/dist/utils/helperFunctions.d.ts +63 -0
- package/dist/utils/helperFunctions.js +156 -0
- package/dist/utils/index.d.ts +2 -0
- package/dist/utils/index.js +2 -0
- package/dist/utils/loadConfigs.d.ts +50 -0
- package/dist/utils/loadConfigs.js +358 -0
- package/dist/utils/pathResolvers.d.ts +53 -0
- package/dist/utils/pathResolvers.js +72 -0
- package/dist/utils/projectConfig.d.ts +119 -0
- package/dist/utils/projectConfig.js +171 -0
- package/dist/utils/retryFailedPromises.d.ts +2 -0
- package/dist/utils/retryFailedPromises.js +23 -0
- package/dist/utils/sessionAuth.d.ts +48 -0
- package/dist/utils/sessionAuth.js +164 -0
- package/dist/utils/setupFiles.d.ts +4 -0
- package/dist/utils/setupFiles.js +1192 -0
- package/dist/utils/typeGuards.d.ts +35 -0
- package/dist/utils/typeGuards.js +57 -0
- package/dist/utils/validationRules.d.ts +43 -0
- package/dist/utils/validationRules.js +42 -0
- package/dist/utils/versionDetection.d.ts +58 -0
- package/dist/utils/versionDetection.js +251 -0
- package/dist/utils/yamlConverter.d.ts +100 -0
- package/dist/utils/yamlConverter.js +428 -0
- package/dist/utils/yamlLoader.d.ts +70 -0
- package/dist/utils/yamlLoader.js +267 -0
- package/dist/utilsController.d.ts +106 -0
- package/dist/utilsController.js +863 -0
- package/package.json +75 -0
- package/scripts/copy-templates.ts +23 -0
- package/src/adapters/AdapterFactory.ts +510 -0
- package/src/adapters/DatabaseAdapter.ts +306 -0
- package/src/adapters/LegacyAdapter.ts +841 -0
- package/src/adapters/TablesDBAdapter.ts +773 -0
- package/src/adapters/index.ts +37 -0
- package/src/backups/operations/bucketBackup.ts +277 -0
- package/src/backups/operations/collectionBackup.ts +310 -0
- package/src/backups/operations/comprehensiveBackup.ts +342 -0
- package/src/backups/schemas/bucketManifest.ts +78 -0
- package/src/backups/schemas/comprehensiveManifest.ts +76 -0
- package/src/backups/tracking/centralizedTracking.ts +352 -0
- package/src/cli/commands/configCommands.ts +201 -0
- package/src/cli/commands/databaseCommands.ts +749 -0
- package/src/cli/commands/functionCommands.ts +418 -0
- package/src/cli/commands/schemaCommands.ts +200 -0
- package/src/cli/commands/storageCommands.ts +152 -0
- package/src/cli/commands/transferCommands.ts +457 -0
- package/src/collections/attributes.ts +2054 -0
- package/src/collections/attributes.ts.backup +1555 -0
- package/src/collections/indexes.ts +352 -0
- package/src/collections/methods.ts +745 -0
- package/src/collections/tableOperations.ts +506 -0
- package/src/collections/transferOperations.ts +590 -0
- package/src/collections/wipeOperations.ts +346 -0
- package/src/config/ConfigManager.ts +808 -0
- package/src/config/README.md +274 -0
- package/src/config/configMigration.ts +575 -0
- package/src/config/configValidation.ts +445 -0
- package/src/config/index.ts +10 -0
- package/src/config/services/ConfigDiscoveryService.ts +463 -0
- package/src/config/services/ConfigLoaderService.ts +740 -0
- package/src/config/services/ConfigMergeService.ts +388 -0
- package/src/config/services/ConfigValidationService.ts +394 -0
- package/src/config/services/SessionAuthService.ts +565 -0
- package/src/config/services/__tests__/ConfigMergeService.test.ts +351 -0
- package/src/config/services/index.ts +29 -0
- package/src/config/yamlConfig.ts +761 -0
- package/src/databases/methods.ts +49 -0
- package/src/databases/setup.ts +77 -0
- package/src/examples/yamlTerminologyExample.ts +346 -0
- package/src/functions/deployments.ts +220 -0
- package/src/functions/fnConfigDiscovery.ts +103 -0
- package/src/functions/methods.ts +271 -0
- package/src/functions/pathResolution.ts +227 -0
- package/src/functions/templates/count-docs-in-collection/README.md +54 -0
- package/src/functions/templates/count-docs-in-collection/src/main.ts +159 -0
- package/src/functions/templates/count-docs-in-collection/src/request.ts +9 -0
- package/src/functions/templates/hono-typescript/README.md +286 -0
- package/src/functions/templates/hono-typescript/src/adapters/request.ts +74 -0
- package/src/functions/templates/hono-typescript/src/adapters/response.ts +106 -0
- package/src/functions/templates/hono-typescript/src/app.ts +180 -0
- package/src/functions/templates/hono-typescript/src/context.ts +103 -0
- package/src/functions/templates/hono-typescript/src/index.ts +54 -0
- package/src/functions/templates/hono-typescript/src/middleware/appwrite.ts +119 -0
- package/src/functions/templates/typescript-node/README.md +32 -0
- package/src/functions/templates/typescript-node/src/context.ts +103 -0
- package/src/functions/templates/typescript-node/src/index.ts +29 -0
- package/src/functions/templates/uv/README.md +31 -0
- package/src/functions/templates/uv/pyproject.toml +30 -0
- package/src/functions/templates/uv/src/__init__.py +0 -0
- package/src/functions/templates/uv/src/context.py +125 -0
- package/src/functions/templates/uv/src/index.py +46 -0
- package/src/init.ts +62 -0
- package/src/interactiveCLI.ts +1136 -0
- package/src/main.ts +1661 -0
- package/src/migrations/afterImportActions.ts +580 -0
- package/src/migrations/appwriteToX.ts +664 -0
- package/src/migrations/comprehensiveTransfer.ts +2285 -0
- package/src/migrations/dataLoader.ts +1702 -0
- package/src/migrations/importController.ts +428 -0
- package/src/migrations/importDataActions.ts +315 -0
- package/src/migrations/relationships.ts +334 -0
- package/src/migrations/services/DataTransformationService.ts +196 -0
- package/src/migrations/services/FileHandlerService.ts +311 -0
- package/src/migrations/services/ImportOrchestrator.ts +666 -0
- package/src/migrations/services/RateLimitManager.ts +363 -0
- package/src/migrations/services/RelationshipResolver.ts +461 -0
- package/src/migrations/services/UserMappingService.ts +345 -0
- package/src/migrations/services/ValidationService.ts +349 -0
- package/src/migrations/transfer.ts +1068 -0
- package/src/migrations/yaml/YamlImportConfigLoader.ts +439 -0
- package/src/migrations/yaml/YamlImportIntegration.ts +446 -0
- package/src/migrations/yaml/generateImportSchemas.ts +1354 -0
- package/src/schemas/authUser.ts +23 -0
- package/src/setup.ts +8 -0
- package/src/setupCommands.ts +603 -0
- package/src/setupController.ts +43 -0
- package/src/shared/attributeMapper.ts +229 -0
- package/src/shared/backupMetadataSchema.ts +93 -0
- package/src/shared/backupTracking.ts +211 -0
- package/src/shared/confirmationDialogs.ts +327 -0
- package/src/shared/errorUtils.ts +110 -0
- package/src/shared/functionManager.ts +525 -0
- package/src/shared/indexManager.ts +254 -0
- package/src/shared/jsonSchemaGenerator.ts +383 -0
- package/src/shared/logging.ts +149 -0
- package/src/shared/messageFormatter.ts +208 -0
- package/src/shared/migrationHelpers.ts +232 -0
- package/src/shared/operationLogger.ts +20 -0
- package/src/shared/operationQueue.ts +377 -0
- package/src/shared/operationsTable.ts +338 -0
- package/src/shared/operationsTableSchema.ts +60 -0
- package/src/shared/progressManager.ts +278 -0
- package/src/shared/pydanticModelGenerator.ts +618 -0
- package/src/shared/relationshipExtractor.ts +214 -0
- package/src/shared/schemaGenerator.ts +644 -0
- package/src/shared/selectionDialogs.ts +749 -0
- package/src/storage/backupCompression.ts +88 -0
- package/src/storage/methods.ts +698 -0
- package/src/storage/schemas.ts +205 -0
- package/src/types/node-appwrite-tablesdb.d.ts +44 -0
- package/src/types.ts +9 -0
- package/src/users/methods.ts +359 -0
- package/src/utils/ClientFactory.ts +240 -0
- package/src/utils/configDiscovery.ts +557 -0
- package/src/utils/configMigration.ts +348 -0
- package/src/utils/constantsGenerator.ts +369 -0
- package/src/utils/dataConverters.ts +159 -0
- package/src/utils/directoryUtils.ts +61 -0
- package/src/utils/getClientFromConfig.ts +257 -0
- package/src/utils/helperFunctions.ts +228 -0
- package/src/utils/index.ts +2 -0
- package/src/utils/loadConfigs.ts +449 -0
- package/src/utils/pathResolvers.ts +81 -0
- package/src/utils/projectConfig.ts +299 -0
- package/src/utils/retryFailedPromises.ts +29 -0
- package/src/utils/sessionAuth.ts +230 -0
- package/src/utils/setupFiles.ts +1238 -0
- package/src/utils/typeGuards.ts +65 -0
- package/src/utils/validationRules.ts +88 -0
- package/src/utils/versionDetection.ts +292 -0
- package/src/utils/yamlConverter.ts +542 -0
- package/src/utils/yamlLoader.ts +371 -0
- package/src/utilsController.ts +1203 -0
- package/tests/README.md +497 -0
- package/tests/adapters/AdapterFactory.test.ts +277 -0
- package/tests/integration/syncOperations.test.ts +463 -0
- package/tests/jest.config.js +25 -0
- package/tests/migration/configMigration.test.ts +546 -0
- package/tests/setup.ts +62 -0
- package/tests/testUtils.ts +340 -0
- package/tests/utils/loadConfigs.test.ts +350 -0
- package/tests/validation/configValidation.test.ts +412 -0
- package/tsconfig.json +44 -0
|
@@ -0,0 +1,315 @@
|
|
|
1
|
+
import { type Databases, type Storage } from "node-appwrite";
|
|
2
|
+
import type { AppwriteConfig } from "@njdamstra/appwrite-utils";
|
|
3
|
+
import {
|
|
4
|
+
validationRules,
|
|
5
|
+
type ValidationRules,
|
|
6
|
+
type AttributeMappings,
|
|
7
|
+
} from "@njdamstra/appwrite-utils";
|
|
8
|
+
import { converterFunctions, type ConverterFunctions } from "@njdamstra/appwrite-utils";
|
|
9
|
+
import { convertObjectBySchema } from "../utils/dataConverters.js";
|
|
10
|
+
import { type AfterImportActions } from "@njdamstra/appwrite-utils";
|
|
11
|
+
import { afterImportActions } from "./afterImportActions.js";
|
|
12
|
+
import { logger } from "../shared/logging.js";
|
|
13
|
+
import { tryAwaitWithRetry } from "../utils/helperFunctions.js";
|
|
14
|
+
|
|
15
|
+
export class ImportDataActions {
|
|
16
|
+
private db: Databases;
|
|
17
|
+
private storage: Storage;
|
|
18
|
+
private config: AppwriteConfig;
|
|
19
|
+
private converterDefinitions: ConverterFunctions;
|
|
20
|
+
private validityRuleDefinitions: ValidationRules;
|
|
21
|
+
private afterImportActionsDefinitions: AfterImportActions;
|
|
22
|
+
|
|
23
|
+
constructor(
|
|
24
|
+
db: Databases,
|
|
25
|
+
storage: Storage,
|
|
26
|
+
config: AppwriteConfig,
|
|
27
|
+
converterDefinitions: ConverterFunctions,
|
|
28
|
+
validityRuleDefinitions: ValidationRules,
|
|
29
|
+
afterImportActionsDefinitions: AfterImportActions
|
|
30
|
+
) {
|
|
31
|
+
this.db = db;
|
|
32
|
+
this.storage = storage;
|
|
33
|
+
this.config = config;
|
|
34
|
+
this.converterDefinitions = converterDefinitions;
|
|
35
|
+
this.validityRuleDefinitions = validityRuleDefinitions;
|
|
36
|
+
this.afterImportActionsDefinitions = afterImportActionsDefinitions;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Runs converter functions on the item based on the provided attribute mappings.
|
|
41
|
+
*
|
|
42
|
+
* @param item - The item to be transformed.
|
|
43
|
+
* @param attributeMappings - The mappings that define how each attribute should be transformed.
|
|
44
|
+
* @returns The transformed item.
|
|
45
|
+
*/
|
|
46
|
+
runConverterFunctions(item: any, attributeMappings: AttributeMappings) {
|
|
47
|
+
const conversionSchema = attributeMappings.reduce((schema, mapping) => {
|
|
48
|
+
schema[mapping.targetKey] = (originalValue: any) => {
|
|
49
|
+
if (!mapping.converters) {
|
|
50
|
+
return originalValue;
|
|
51
|
+
}
|
|
52
|
+
return mapping.converters?.reduce((value, converterName) => {
|
|
53
|
+
let shouldProcessAsArray = false;
|
|
54
|
+
if (
|
|
55
|
+
(converterName.includes("[Arr]") ||
|
|
56
|
+
converterName.includes("[arr]")) &&
|
|
57
|
+
Array.isArray(value)
|
|
58
|
+
) {
|
|
59
|
+
shouldProcessAsArray = true;
|
|
60
|
+
converterName = converterName
|
|
61
|
+
.replace("[Arr]", "")
|
|
62
|
+
.replace("[arr]", "");
|
|
63
|
+
} else if (
|
|
64
|
+
(!Array.isArray(value) && converterName.includes("[Arr]")) ||
|
|
65
|
+
converterName.includes("[arr]")
|
|
66
|
+
) {
|
|
67
|
+
converterName = converterName
|
|
68
|
+
.replace("[Arr]", "")
|
|
69
|
+
.replace("[arr]", "");
|
|
70
|
+
}
|
|
71
|
+
const converterFunction =
|
|
72
|
+
converterFunctions[
|
|
73
|
+
converterName as keyof typeof converterFunctions
|
|
74
|
+
];
|
|
75
|
+
if (converterFunction) {
|
|
76
|
+
if (Array.isArray(value) && !shouldProcessAsArray) {
|
|
77
|
+
return value.map((item) => converterFunction(item));
|
|
78
|
+
} else {
|
|
79
|
+
return converterFunction(value);
|
|
80
|
+
}
|
|
81
|
+
} else {
|
|
82
|
+
logger.warn(
|
|
83
|
+
`Converter function '${converterName}' is not defined.`
|
|
84
|
+
);
|
|
85
|
+
return value;
|
|
86
|
+
}
|
|
87
|
+
}, originalValue);
|
|
88
|
+
};
|
|
89
|
+
return schema;
|
|
90
|
+
}, {} as Record<string, (value: any) => any>);
|
|
91
|
+
|
|
92
|
+
// Convert the item using the constructed schema
|
|
93
|
+
const convertedItem = convertObjectBySchema(item, conversionSchema);
|
|
94
|
+
// Merge the converted item back into the original item object
|
|
95
|
+
Object.assign(item, convertedItem);
|
|
96
|
+
return item;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
/**
|
|
100
|
+
* Validates a single data item based on defined validation rules.
|
|
101
|
+
* @param item The data item to validate.
|
|
102
|
+
* @param attributeMap The attribute mappings for the data item.
|
|
103
|
+
* @param context The context for resolving templated parameters in validation rules.
|
|
104
|
+
* @returns A promise that resolves to true if the item is valid, false otherwise.
|
|
105
|
+
*/
|
|
106
|
+
validateItem(
|
|
107
|
+
item: any,
|
|
108
|
+
attributeMap: AttributeMappings,
|
|
109
|
+
context: { [key: string]: any }
|
|
110
|
+
): boolean {
|
|
111
|
+
for (const mapping of attributeMap) {
|
|
112
|
+
const { validationActions } = mapping;
|
|
113
|
+
if (
|
|
114
|
+
!validationActions ||
|
|
115
|
+
!Array.isArray(validationActions) ||
|
|
116
|
+
!validationActions.length
|
|
117
|
+
) {
|
|
118
|
+
return true; // Assume items without validation actions as valid.
|
|
119
|
+
}
|
|
120
|
+
for (const ruleDef of validationActions) {
|
|
121
|
+
const { action, params } = ruleDef;
|
|
122
|
+
const validationRule =
|
|
123
|
+
validationRules[action as keyof typeof validationRules];
|
|
124
|
+
|
|
125
|
+
if (!validationRule) {
|
|
126
|
+
logger.warn(`Validation rule '${action}' is not defined.`);
|
|
127
|
+
continue; // Optionally, consider undefined rules as a validation failure.
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
// Resolve templated parameters
|
|
131
|
+
const resolvedParams = params.map((param: any) =>
|
|
132
|
+
this.resolveTemplate(param, context, item)
|
|
133
|
+
);
|
|
134
|
+
|
|
135
|
+
// Apply the validation rule
|
|
136
|
+
let isValid = false;
|
|
137
|
+
if (Array.isArray(item)) {
|
|
138
|
+
isValid = item.every((item) =>
|
|
139
|
+
(validationRule as any)(item, ...resolvedParams)
|
|
140
|
+
);
|
|
141
|
+
} else {
|
|
142
|
+
isValid = (validationRule as any)(item, ...resolvedParams);
|
|
143
|
+
}
|
|
144
|
+
if (!isValid) {
|
|
145
|
+
logger.error(
|
|
146
|
+
`Validation failed for rule '${action}' with params ${params.join(
|
|
147
|
+
", "
|
|
148
|
+
)}`
|
|
149
|
+
);
|
|
150
|
+
return false; // Stop validation on first failure
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
return true; // The item passed all validations
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
async executeAfterImportActions(
|
|
159
|
+
item: any,
|
|
160
|
+
attributeMap: AttributeMappings,
|
|
161
|
+
context: { [key: string]: any }
|
|
162
|
+
): Promise<void> {
|
|
163
|
+
for (const mapping of attributeMap) {
|
|
164
|
+
const { postImportActions } = mapping;
|
|
165
|
+
if (!postImportActions || !Array.isArray(postImportActions)) {
|
|
166
|
+
continue; // Skip to the next attribute if no actions are defined
|
|
167
|
+
}
|
|
168
|
+
for (const actionDef of postImportActions) {
|
|
169
|
+
const { action, params } = actionDef;
|
|
170
|
+
console.log(
|
|
171
|
+
`Executing post-import action '${action}' for attribute '${
|
|
172
|
+
mapping.targetKey
|
|
173
|
+
}' with params ${params.join(", ")}...`
|
|
174
|
+
);
|
|
175
|
+
try {
|
|
176
|
+
await tryAwaitWithRetry(
|
|
177
|
+
async () => await this.executeAction(action, params, context, item)
|
|
178
|
+
);
|
|
179
|
+
} catch (error) {
|
|
180
|
+
logger.error(
|
|
181
|
+
`Failed to execute post-import action '${action}' for attribute '${mapping.targetKey}':`,
|
|
182
|
+
error
|
|
183
|
+
);
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
async executeAction(
|
|
190
|
+
actionName: string,
|
|
191
|
+
params: any[], // Accepts any type, including objects
|
|
192
|
+
context: { [key: string]: any },
|
|
193
|
+
item: any
|
|
194
|
+
): Promise<void> {
|
|
195
|
+
const actionMethod =
|
|
196
|
+
afterImportActions[actionName as keyof typeof afterImportActions];
|
|
197
|
+
if (typeof actionMethod === "function") {
|
|
198
|
+
try {
|
|
199
|
+
// Resolve parameters, handling both strings and objects
|
|
200
|
+
const resolvedParams = params.map((param) => {
|
|
201
|
+
// Directly resolve each param, whether it's an object or a string
|
|
202
|
+
return this.resolveTemplate(param, context, item);
|
|
203
|
+
});
|
|
204
|
+
|
|
205
|
+
// Execute the action with resolved parameters
|
|
206
|
+
// Parameters are passed as-is, with objects treated as single parameters
|
|
207
|
+
console.log(
|
|
208
|
+
`Executing action '${actionName}' from context with params:`,
|
|
209
|
+
resolvedParams
|
|
210
|
+
);
|
|
211
|
+
logger.info(
|
|
212
|
+
`Executing action '${actionName}' from context: ${JSON.stringify(
|
|
213
|
+
context,
|
|
214
|
+
null,
|
|
215
|
+
2
|
|
216
|
+
)} with params:`,
|
|
217
|
+
resolvedParams
|
|
218
|
+
);
|
|
219
|
+
await (actionMethod as any)(this.config, ...resolvedParams);
|
|
220
|
+
} catch (error: any) {
|
|
221
|
+
logger.error(
|
|
222
|
+
`Error executing action '${actionName}' with context:`,
|
|
223
|
+
context,
|
|
224
|
+
error
|
|
225
|
+
);
|
|
226
|
+
throw new Error(
|
|
227
|
+
`Execution failed for action '${actionName}': ${error.message}`
|
|
228
|
+
);
|
|
229
|
+
}
|
|
230
|
+
} else {
|
|
231
|
+
logger.warn(`Action '${actionName}' is not defined.`);
|
|
232
|
+
throw new Error(`Action '${actionName}' is not defined.`);
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
/**
|
|
237
|
+
* Resolves a templated string or object using the provided context and current data item.
|
|
238
|
+
* If the template is a string that starts and ends with "{}", it replaces it with the corresponding value from item or context.
|
|
239
|
+
* If the template is an object, it recursively resolves its properties.
|
|
240
|
+
* @param template The templated string or object.
|
|
241
|
+
* @param context The context for resolving the template.
|
|
242
|
+
* @param item The current data item being processed.
|
|
243
|
+
*/
|
|
244
|
+
resolveTemplate(
|
|
245
|
+
template: any,
|
|
246
|
+
context: { [key: string]: any },
|
|
247
|
+
item: any
|
|
248
|
+
): any {
|
|
249
|
+
// Function to recursively resolve paths, including handling [any] notation
|
|
250
|
+
const resolvePath = (path: string, currentContext: any): any => {
|
|
251
|
+
const anyKeyRegex = /\[any\]/g;
|
|
252
|
+
let pathParts = path.split(".").filter(Boolean);
|
|
253
|
+
|
|
254
|
+
return pathParts.reduce((acc, part, index) => {
|
|
255
|
+
// Handle [any] part by iterating over all elements if it's an object or an array
|
|
256
|
+
if (part === "[any]") {
|
|
257
|
+
if (Array.isArray(acc)) {
|
|
258
|
+
return acc
|
|
259
|
+
.map((item) => item[pathParts[index + 1]])
|
|
260
|
+
.filter((item) => item !== undefined);
|
|
261
|
+
} else if (typeof acc === "object") {
|
|
262
|
+
return Object.values(acc)
|
|
263
|
+
.map((item: any) => item[pathParts[index + 1]])
|
|
264
|
+
.filter((item) => item !== undefined);
|
|
265
|
+
}
|
|
266
|
+
} else {
|
|
267
|
+
return acc?.[part];
|
|
268
|
+
}
|
|
269
|
+
}, currentContext);
|
|
270
|
+
};
|
|
271
|
+
|
|
272
|
+
if (typeof template === "string") {
|
|
273
|
+
// Matches placeholders in the template
|
|
274
|
+
const regex = /\{([^}]+)\}/g;
|
|
275
|
+
let match;
|
|
276
|
+
let resolvedString = template;
|
|
277
|
+
while ((match = regex.exec(template)) !== null) {
|
|
278
|
+
const path = match[1];
|
|
279
|
+
// Resolve the path, handling [any] notation and arrays/objects
|
|
280
|
+
const resolvedValue = resolvePath(path, { ...context, ...item });
|
|
281
|
+
if (resolvedValue !== undefined) {
|
|
282
|
+
// If it's an array (from [any] notation), join the values; adjust as needed
|
|
283
|
+
const value = Array.isArray(resolvedValue)
|
|
284
|
+
? resolvedValue.join(", ")
|
|
285
|
+
: resolvedValue;
|
|
286
|
+
resolvedString = resolvedString.replace(match[0], value);
|
|
287
|
+
} else {
|
|
288
|
+
logger.warn(
|
|
289
|
+
`Failed to resolve ${template} in context: `,
|
|
290
|
+
JSON.stringify({ ...context, ...item }, null, 2)
|
|
291
|
+
);
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
// console.log(`Resolved string: ${resolvedString}`);
|
|
295
|
+
return resolvedString;
|
|
296
|
+
} else if (typeof template === "object" && template !== null) {
|
|
297
|
+
// Recursively resolve templates for each property in the object
|
|
298
|
+
const resolvedObject: any = Array.isArray(template) ? [] : {};
|
|
299
|
+
for (const key in template) {
|
|
300
|
+
const resolvedValue = this.resolveTemplate(
|
|
301
|
+
template[key],
|
|
302
|
+
context,
|
|
303
|
+
item
|
|
304
|
+
);
|
|
305
|
+
if (resolvedValue !== undefined) {
|
|
306
|
+
// Only assign if resolvedValue is not undefined
|
|
307
|
+
resolvedObject[key] = resolvedValue;
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
return resolvedObject;
|
|
311
|
+
}
|
|
312
|
+
// console.log(`Template is not a string or object: ${template}`);
|
|
313
|
+
return template;
|
|
314
|
+
}
|
|
315
|
+
}
|
|
@@ -0,0 +1,334 @@
|
|
|
1
|
+
import { Databases, Query, type Models } from "node-appwrite";
|
|
2
|
+
import { fetchAllCollections } from "../collections/methods.js";
|
|
3
|
+
import type {
|
|
4
|
+
AppwriteConfig,
|
|
5
|
+
Attribute,
|
|
6
|
+
RelationshipAttribute,
|
|
7
|
+
} from "@njdamstra/appwrite-utils";
|
|
8
|
+
import { logger } from "../shared/logging.js";
|
|
9
|
+
import { MessageFormatter } from "../shared/messageFormatter.js";
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Finds collections that have defined relationship attributes.
|
|
13
|
+
*/
|
|
14
|
+
export const findCollectionsWithRelationships = (config: AppwriteConfig) => {
|
|
15
|
+
const toReturn = new Map<string, RelationshipAttribute[]>();
|
|
16
|
+
if (!config.collections) {
|
|
17
|
+
return toReturn;
|
|
18
|
+
}
|
|
19
|
+
for (const collection of config.collections) {
|
|
20
|
+
if (collection.attributes) {
|
|
21
|
+
for (const attribute of collection.attributes) {
|
|
22
|
+
if (
|
|
23
|
+
attribute.type === "relationship" &&
|
|
24
|
+
attribute.twoWay &&
|
|
25
|
+
attribute.side === "parent"
|
|
26
|
+
) {
|
|
27
|
+
toReturn.set(collection.name, toReturn.get(collection.name) || []);
|
|
28
|
+
toReturn
|
|
29
|
+
.get(collection.name)
|
|
30
|
+
?.push(attribute as RelationshipAttribute);
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
return toReturn;
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
export async function resolveAndUpdateRelationships(
|
|
39
|
+
dbId: string,
|
|
40
|
+
database: Databases,
|
|
41
|
+
config: AppwriteConfig
|
|
42
|
+
) {
|
|
43
|
+
const collections = await fetchAllCollections(dbId, database);
|
|
44
|
+
const collectionsWithRelationships = findCollectionsWithRelationships(config);
|
|
45
|
+
|
|
46
|
+
// Process each collection sequentially
|
|
47
|
+
for (const collection of collections) {
|
|
48
|
+
MessageFormatter.processing(
|
|
49
|
+
`Processing collection: ${collection.name} (${collection.$id})`,
|
|
50
|
+
{ prefix: "Migration" }
|
|
51
|
+
);
|
|
52
|
+
const relAttributeMap = collectionsWithRelationships.get(
|
|
53
|
+
collection.name
|
|
54
|
+
) as RelationshipAttribute[]; // Get the relationship attributes for the collections
|
|
55
|
+
|
|
56
|
+
if (!relAttributeMap) {
|
|
57
|
+
MessageFormatter.info(
|
|
58
|
+
`No mapping found for collection: ${collection.name}, skipping...`,
|
|
59
|
+
{ prefix: "Migration" }
|
|
60
|
+
);
|
|
61
|
+
continue;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
await processCollection(dbId, database, collection, relAttributeMap);
|
|
65
|
+
}
|
|
66
|
+
MessageFormatter.success(
|
|
67
|
+
`Completed relationship resolution and update for database ID: ${dbId}`,
|
|
68
|
+
{ prefix: "Migration" }
|
|
69
|
+
);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
async function processCollection(
|
|
73
|
+
dbId: string,
|
|
74
|
+
database: Databases,
|
|
75
|
+
collection: Models.Collection,
|
|
76
|
+
relAttributeMap: RelationshipAttribute[]
|
|
77
|
+
) {
|
|
78
|
+
let after; // For pagination
|
|
79
|
+
let hasMore = true;
|
|
80
|
+
|
|
81
|
+
while (hasMore) {
|
|
82
|
+
const response: Models.DocumentList<Models.Document> =
|
|
83
|
+
await database.listDocuments(dbId, collection.$id, [
|
|
84
|
+
Query.limit(100), // Fetch documents in batches of 100
|
|
85
|
+
...(after ? [Query.cursorAfter(after)] : []),
|
|
86
|
+
]);
|
|
87
|
+
|
|
88
|
+
const documents = response.documents;
|
|
89
|
+
MessageFormatter.info(
|
|
90
|
+
`Fetched ${documents.length} documents from collection: ${collection.name}`,
|
|
91
|
+
{ prefix: "Migration" }
|
|
92
|
+
);
|
|
93
|
+
|
|
94
|
+
if (documents.length > 0) {
|
|
95
|
+
const updates = await prepareDocumentUpdates(
|
|
96
|
+
database,
|
|
97
|
+
dbId,
|
|
98
|
+
collection.name,
|
|
99
|
+
documents,
|
|
100
|
+
relAttributeMap
|
|
101
|
+
);
|
|
102
|
+
|
|
103
|
+
// Execute updates for the current batch
|
|
104
|
+
await executeUpdatesInBatches(dbId, database, updates);
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
if (documents.length === 100) {
|
|
108
|
+
after = documents[documents.length - 1].$id; // Prepare for the next page
|
|
109
|
+
} else {
|
|
110
|
+
hasMore = false; // No more documents to fetch
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
async function findDocumentsByOriginalId(
|
|
116
|
+
database: Databases,
|
|
117
|
+
dbId: string,
|
|
118
|
+
targetCollection: Models.Collection,
|
|
119
|
+
targetKey: string,
|
|
120
|
+
originalId: string | string[]
|
|
121
|
+
): Promise<Models.Document[] | undefined> {
|
|
122
|
+
const relatedCollectionId = targetCollection.$id;
|
|
123
|
+
const collection = await database.listCollections(dbId, [
|
|
124
|
+
Query.equal("$id", relatedCollectionId),
|
|
125
|
+
]);
|
|
126
|
+
if (collection.total === 0) {
|
|
127
|
+
MessageFormatter.warning(
|
|
128
|
+
`Collection ${relatedCollectionId} doesn't exist, skipping...`,
|
|
129
|
+
{ prefix: "Migration" }
|
|
130
|
+
);
|
|
131
|
+
return undefined;
|
|
132
|
+
}
|
|
133
|
+
const targetAttr = collection.collections[0].attributes.find(
|
|
134
|
+
// @ts-ignore
|
|
135
|
+
(attr) => attr.key === targetKey
|
|
136
|
+
) as any;
|
|
137
|
+
if (!targetAttr) {
|
|
138
|
+
MessageFormatter.warning(
|
|
139
|
+
`Attribute ${targetKey} not found in collection ${relatedCollectionId}, skipping...`,
|
|
140
|
+
{ prefix: "Migration" }
|
|
141
|
+
);
|
|
142
|
+
return undefined;
|
|
143
|
+
}
|
|
144
|
+
let queries: string[] = [];
|
|
145
|
+
if (targetAttr.array) {
|
|
146
|
+
// @ts-ignore
|
|
147
|
+
queries.push(Query.contains(targetKey, originalId));
|
|
148
|
+
} else {
|
|
149
|
+
queries.push(Query.equal(targetKey, originalId));
|
|
150
|
+
}
|
|
151
|
+
const response = await database.listDocuments(dbId, relatedCollectionId, [
|
|
152
|
+
...queries,
|
|
153
|
+
Query.limit(500), // Adjust the limit based on your needs or implement pagination
|
|
154
|
+
]);
|
|
155
|
+
|
|
156
|
+
if (response.documents.length < 0) {
|
|
157
|
+
return undefined;
|
|
158
|
+
} else if (response.documents.length > 0) {
|
|
159
|
+
return response.documents;
|
|
160
|
+
} else {
|
|
161
|
+
return undefined;
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
async function prepareDocumentUpdates(
|
|
166
|
+
database: Databases,
|
|
167
|
+
dbId: string,
|
|
168
|
+
collectionName: string,
|
|
169
|
+
documents: Models.Document[],
|
|
170
|
+
relationships: RelationshipAttribute[]
|
|
171
|
+
): Promise<{ collectionId: string; documentId: string; updatePayload: any }[]> {
|
|
172
|
+
MessageFormatter.processing(`Preparing updates for collection: ${collectionName}`, { prefix: "Migration" });
|
|
173
|
+
const updates: {
|
|
174
|
+
collectionId: string;
|
|
175
|
+
documentId: string;
|
|
176
|
+
updatePayload: any;
|
|
177
|
+
}[] = [];
|
|
178
|
+
|
|
179
|
+
const thisCollection = (
|
|
180
|
+
await database.listCollections(dbId, [Query.equal("name", collectionName)])
|
|
181
|
+
).collections[0];
|
|
182
|
+
const thisCollectionId = thisCollection?.$id;
|
|
183
|
+
|
|
184
|
+
if (!thisCollectionId) {
|
|
185
|
+
MessageFormatter.warning(`No collection found with name: ${collectionName}`, { prefix: "Migration" });
|
|
186
|
+
return [];
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
for (const doc of documents) {
|
|
190
|
+
let updatePayload: { [key: string]: any } = {};
|
|
191
|
+
|
|
192
|
+
for (const rel of relationships) {
|
|
193
|
+
// Skip if not dealing with the parent side of a two-way relationship
|
|
194
|
+
if (rel.twoWay && rel.side !== "parent") {
|
|
195
|
+
MessageFormatter.info("Skipping non-parent side of two-way relationship...", { prefix: "Migration" });
|
|
196
|
+
continue;
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
const isSingleReference =
|
|
200
|
+
rel.relationType === "oneToOne" || rel.relationType === "manyToOne";
|
|
201
|
+
const originalIdField = rel.importMapping?.originalIdField;
|
|
202
|
+
const targetField = rel.importMapping?.targetField || originalIdField; // Use originalIdField if targetField is not specified
|
|
203
|
+
if (!originalIdField) {
|
|
204
|
+
MessageFormatter.warning("Missing originalIdField in importMapping, skipping...", { prefix: "Migration" });
|
|
205
|
+
continue;
|
|
206
|
+
}
|
|
207
|
+
const originalId = doc[originalIdField as keyof typeof doc];
|
|
208
|
+
if (!originalId) {
|
|
209
|
+
continue;
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
const relatedCollection = (
|
|
213
|
+
await database.listCollections(dbId, [
|
|
214
|
+
Query.equal("name", rel.relatedCollection),
|
|
215
|
+
])
|
|
216
|
+
).collections[0];
|
|
217
|
+
|
|
218
|
+
if (!relatedCollection) {
|
|
219
|
+
MessageFormatter.warning(
|
|
220
|
+
`Related collection ${rel.relatedCollection} not found, skipping...`,
|
|
221
|
+
{ prefix: "Migration" }
|
|
222
|
+
);
|
|
223
|
+
continue;
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
const foundDocuments = await findDocumentsByOriginalId(
|
|
227
|
+
database,
|
|
228
|
+
dbId,
|
|
229
|
+
relatedCollection,
|
|
230
|
+
targetField!,
|
|
231
|
+
String(originalId)
|
|
232
|
+
);
|
|
233
|
+
|
|
234
|
+
if (foundDocuments && foundDocuments.length > 0) {
|
|
235
|
+
const relationshipKey = rel.key;
|
|
236
|
+
const existingRefs = doc[relationshipKey as keyof typeof doc] || [];
|
|
237
|
+
let existingRefIds: string[] = [];
|
|
238
|
+
if (Array.isArray(existingRefs)) {
|
|
239
|
+
// @ts-ignore
|
|
240
|
+
existingRefIds = existingRefs.map((ref) => ref.$id);
|
|
241
|
+
} else if (existingRefs) {
|
|
242
|
+
// @ts-ignore
|
|
243
|
+
existingRefIds = [existingRefs.$id];
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
const newRefs = foundDocuments.map((fd) => fd.$id);
|
|
247
|
+
const allRefs = [...new Set([...existingRefIds, ...newRefs])]; // Combine and remove duplicates
|
|
248
|
+
|
|
249
|
+
// Update logic based on the relationship cardinality
|
|
250
|
+
updatePayload[relationshipKey] = isSingleReference
|
|
251
|
+
? newRefs[0] || existingRefIds[0]
|
|
252
|
+
: allRefs;
|
|
253
|
+
MessageFormatter.info(`Updating ${relationshipKey} with ${allRefs.length} refs`, { prefix: "Migration" });
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
if (Object.keys(updatePayload).length > 0) {
|
|
258
|
+
updates.push({
|
|
259
|
+
collectionId: thisCollectionId,
|
|
260
|
+
documentId: doc.$id,
|
|
261
|
+
updatePayload: updatePayload,
|
|
262
|
+
});
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
return updates;
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
async function processInBatches<T>(
|
|
270
|
+
items: T[],
|
|
271
|
+
batchSize: number,
|
|
272
|
+
processFunction: (batch: T[]) => Promise<void>
|
|
273
|
+
) {
|
|
274
|
+
const maxParallelBatches = 25; // Adjust this value to control the number of parallel batches
|
|
275
|
+
let currentIndex = 0;
|
|
276
|
+
let activeBatchPromises: Promise<void>[] = [];
|
|
277
|
+
|
|
278
|
+
while (currentIndex < items.length) {
|
|
279
|
+
// While there's still data to process and we haven't reached our parallel limit
|
|
280
|
+
while (
|
|
281
|
+
currentIndex < items.length &&
|
|
282
|
+
activeBatchPromises.length < maxParallelBatches
|
|
283
|
+
) {
|
|
284
|
+
const batch = items.slice(currentIndex, currentIndex + batchSize);
|
|
285
|
+
currentIndex += batchSize;
|
|
286
|
+
// Add new batch processing promise to the array
|
|
287
|
+
activeBatchPromises.push(processFunction(batch));
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
// Wait for one of the batch processes to complete
|
|
291
|
+
await Promise.race(activeBatchPromises).then(() => {
|
|
292
|
+
// Remove the resolved promise from the activeBatchPromises array
|
|
293
|
+
activeBatchPromises = activeBatchPromises.filter(
|
|
294
|
+
(p) => p !== Promise.race(activeBatchPromises)
|
|
295
|
+
);
|
|
296
|
+
});
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
// After processing all batches, ensure all active promises are resolved
|
|
300
|
+
await Promise.all(activeBatchPromises);
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
async function executeUpdatesInBatches(
|
|
304
|
+
dbId: string,
|
|
305
|
+
database: Databases,
|
|
306
|
+
updates: { collectionId: string; documentId: string; updatePayload: any }[]
|
|
307
|
+
) {
|
|
308
|
+
const batchSize = 25; // Adjust based on your rate limit and performance testing
|
|
309
|
+
for (let i = 0; i < updates.length; i += batchSize) {
|
|
310
|
+
const batch = updates.slice(i, i + batchSize);
|
|
311
|
+
await Promise.all(
|
|
312
|
+
batch.map((update) =>
|
|
313
|
+
database
|
|
314
|
+
.updateDocument(
|
|
315
|
+
dbId,
|
|
316
|
+
update.collectionId,
|
|
317
|
+
update.documentId,
|
|
318
|
+
update.updatePayload
|
|
319
|
+
)
|
|
320
|
+
.catch((error) => {
|
|
321
|
+
logger.error(
|
|
322
|
+
`Error updating doc ${
|
|
323
|
+
update.documentId
|
|
324
|
+
} in ${dbId}, update payload: ${JSON.stringify(
|
|
325
|
+
update.updatePayload,
|
|
326
|
+
undefined,
|
|
327
|
+
4
|
|
328
|
+
)}, error: ${error}`
|
|
329
|
+
);
|
|
330
|
+
})
|
|
331
|
+
)
|
|
332
|
+
);
|
|
333
|
+
}
|
|
334
|
+
}
|