appwrite-utils-cli 0.10.86 → 1.0.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/.appwrite/.yaml_schemas/appwrite-config.schema.json +380 -0
- package/.appwrite/.yaml_schemas/collection.schema.json +255 -0
- package/.appwrite/collections/Categories.yaml +182 -0
- package/.appwrite/collections/ExampleCollection.yaml +36 -0
- package/.appwrite/collections/Posts.yaml +227 -0
- package/.appwrite/collections/Users.yaml +149 -0
- package/.appwrite/config.yaml +109 -0
- package/.appwrite/import/README.md +148 -0
- package/.appwrite/import/categories-import.yaml +129 -0
- package/.appwrite/import/posts-import.yaml +208 -0
- package/.appwrite/import/users-import.yaml +130 -0
- package/.appwrite/importData/categories.json +194 -0
- package/.appwrite/importData/posts.json +270 -0
- package/.appwrite/importData/users.json +220 -0
- package/.appwrite/schemas/categories.json +128 -0
- package/.appwrite/schemas/exampleCollection.json +52 -0
- package/.appwrite/schemas/posts.json +173 -0
- package/.appwrite/schemas/users.json +125 -0
- package/README.md +264 -33
- package/dist/collections/attributes.js +3 -2
- package/dist/collections/methods.js +56 -38
- package/dist/config/yamlConfig.d.ts +501 -0
- package/dist/config/yamlConfig.js +452 -0
- package/dist/databases/setup.d.ts +6 -0
- package/dist/databases/setup.js +119 -0
- package/dist/functions/methods.d.ts +1 -1
- package/dist/functions/methods.js +5 -2
- package/dist/functions/openapi.d.ts +4 -0
- package/dist/functions/openapi.js +60 -0
- package/dist/interactiveCLI.d.ts +5 -0
- package/dist/interactiveCLI.js +194 -49
- package/dist/main.js +91 -30
- package/dist/migrations/afterImportActions.js +2 -2
- package/dist/migrations/appwriteToX.d.ts +10 -0
- package/dist/migrations/appwriteToX.js +15 -4
- package/dist/migrations/backup.d.ts +16 -16
- package/dist/migrations/dataLoader.d.ts +83 -1
- package/dist/migrations/dataLoader.js +4 -4
- package/dist/migrations/importController.js +25 -18
- package/dist/migrations/importDataActions.js +2 -2
- package/dist/migrations/logging.d.ts +9 -1
- package/dist/migrations/logging.js +41 -22
- package/dist/migrations/migrationHelper.d.ts +4 -4
- package/dist/migrations/relationships.js +1 -1
- 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 +488 -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 +0 -6
- package/dist/migrations/transfer.js +16 -132
- package/dist/migrations/yaml/YamlImportConfigLoader.d.ts +384 -0
- package/dist/migrations/yaml/YamlImportConfigLoader.js +375 -0
- package/dist/migrations/yaml/YamlImportIntegration.d.ts +87 -0
- package/dist/migrations/yaml/YamlImportIntegration.js +330 -0
- package/dist/migrations/yaml/generateImportSchemas.d.ts +17 -0
- package/dist/migrations/yaml/generateImportSchemas.js +575 -0
- package/dist/schemas/authUser.d.ts +9 -9
- package/dist/shared/attributeManager.d.ts +17 -0
- package/dist/shared/attributeManager.js +273 -0
- package/dist/shared/confirmationDialogs.d.ts +75 -0
- package/dist/shared/confirmationDialogs.js +236 -0
- package/dist/shared/functionManager.d.ts +48 -0
- package/dist/shared/functionManager.js +322 -0
- package/dist/shared/indexManager.d.ts +24 -0
- package/dist/shared/indexManager.js +150 -0
- package/dist/shared/jsonSchemaGenerator.d.ts +51 -0
- package/dist/shared/jsonSchemaGenerator.js +313 -0
- package/dist/shared/logging.d.ts +10 -0
- package/dist/shared/logging.js +46 -0
- package/dist/shared/messageFormatter.d.ts +37 -0
- package/dist/shared/messageFormatter.js +152 -0
- package/dist/shared/migrationHelpers.d.ts +173 -0
- package/dist/shared/migrationHelpers.js +142 -0
- package/dist/shared/operationLogger.d.ts +3 -0
- package/dist/shared/operationLogger.js +25 -0
- package/dist/shared/operationQueue.d.ts +13 -0
- package/dist/shared/operationQueue.js +79 -0
- package/dist/shared/progressManager.d.ts +62 -0
- package/dist/shared/progressManager.js +215 -0
- package/dist/shared/schemaGenerator.d.ts +18 -0
- package/dist/shared/schemaGenerator.js +523 -0
- package/dist/storage/methods.d.ts +3 -1
- package/dist/storage/methods.js +144 -55
- package/dist/storage/schemas.d.ts +56 -16
- package/dist/types.d.ts +2 -2
- package/dist/types.js +1 -1
- package/dist/users/methods.d.ts +16 -0
- package/dist/users/methods.js +276 -0
- package/dist/utils/configMigration.d.ts +1 -0
- package/dist/utils/configMigration.js +262 -0
- package/dist/utils/dataConverters.d.ts +46 -0
- package/dist/utils/dataConverters.js +139 -0
- package/dist/utils/loadConfigs.d.ts +15 -4
- package/dist/utils/loadConfigs.js +379 -51
- package/dist/utils/schemaStrings.js +2 -1
- package/dist/utils/setupFiles.d.ts +2 -1
- package/dist/utils/setupFiles.js +723 -28
- package/dist/utils/validationRules.d.ts +43 -0
- package/dist/utils/validationRules.js +42 -0
- package/dist/utils/yamlConverter.d.ts +48 -0
- package/dist/utils/yamlConverter.js +98 -0
- package/dist/utilsController.js +65 -43
- package/package.json +19 -15
- package/src/collections/attributes.ts +3 -2
- package/src/collections/methods.ts +85 -51
- package/src/config/yamlConfig.ts +488 -0
- package/src/{migrations/setupDatabase.ts → databases/setup.ts} +11 -5
- package/src/functions/methods.ts +8 -4
- package/src/functions/templates/count-docs-in-collection/package.json +25 -0
- package/src/functions/templates/count-docs-in-collection/tsconfig.json +28 -0
- package/src/functions/templates/typescript-node/package.json +24 -0
- package/src/functions/templates/typescript-node/tsconfig.json +28 -0
- package/src/functions/templates/uv/README.md +31 -0
- package/src/functions/templates/uv/pyproject.toml +29 -0
- package/src/interactiveCLI.ts +226 -61
- package/src/main.ts +111 -37
- package/src/migrations/afterImportActions.ts +2 -2
- package/src/migrations/appwriteToX.ts +17 -4
- package/src/migrations/dataLoader.ts +4 -4
- package/src/migrations/importController.ts +30 -22
- package/src/migrations/importDataActions.ts +2 -2
- package/src/migrations/relationships.ts +1 -1
- package/src/migrations/services/DataTransformationService.ts +196 -0
- package/src/migrations/services/FileHandlerService.ts +311 -0
- package/src/migrations/services/ImportOrchestrator.ts +669 -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 +22 -228
- package/src/migrations/yaml/YamlImportConfigLoader.ts +427 -0
- package/src/migrations/yaml/YamlImportIntegration.ts +419 -0
- package/src/migrations/yaml/generateImportSchemas.ts +589 -0
- package/src/shared/attributeManager.ts +429 -0
- package/src/shared/confirmationDialogs.ts +327 -0
- package/src/shared/functionManager.ts +515 -0
- package/src/shared/indexManager.ts +253 -0
- package/src/shared/jsonSchemaGenerator.ts +403 -0
- package/src/shared/logging.ts +74 -0
- package/src/shared/messageFormatter.ts +195 -0
- package/src/{migrations/migrationHelper.ts → shared/migrationHelpers.ts} +22 -4
- package/src/{migrations/helper.ts → shared/operationLogger.ts} +7 -2
- package/src/{migrations/queue.ts → shared/operationQueue.ts} +1 -1
- package/src/shared/progressManager.ts +278 -0
- package/src/{migrations/schemaStrings.ts → shared/schemaGenerator.ts} +71 -17
- package/src/storage/methods.ts +199 -78
- package/src/types.ts +2 -2
- package/src/{migrations/users.ts → users/methods.ts} +2 -2
- package/src/utils/configMigration.ts +349 -0
- package/src/utils/loadConfigs.ts +416 -52
- package/src/utils/schemaStrings.ts +2 -1
- package/src/utils/setupFiles.ts +742 -40
- package/src/{migrations → utils}/validationRules.ts +1 -1
- package/src/utils/yamlConverter.ts +131 -0
- package/src/utilsController.ts +75 -54
- package/src/functions/templates/poetry/README.md +0 -30
- package/src/functions/templates/poetry/pyproject.toml +0 -16
- package/src/migrations/attributes.ts +0 -561
- package/src/migrations/backup.ts +0 -205
- package/src/migrations/databases.ts +0 -39
- package/src/migrations/dbHelpers.ts +0 -92
- package/src/migrations/indexes.ts +0 -40
- package/src/migrations/logging.ts +0 -29
- package/src/migrations/storage.ts +0 -538
- /package/src/{migrations → functions}/openapi.ts +0 -0
- /package/src/functions/templates/{poetry → uv}/src/__init__.py +0 -0
- /package/src/functions/templates/{poetry → uv}/src/index.py +0 -0
- /package/src/{migrations/converters.ts → utils/dataConverters.ts} +0 -0
@@ -0,0 +1,427 @@
|
|
1
|
+
import { z } from "zod";
|
2
|
+
import path from "path";
|
3
|
+
import fs from "fs";
|
4
|
+
import yaml from "js-yaml";
|
5
|
+
import { logger } from "../../shared/logging.js";
|
6
|
+
import type { ImportDef, AttributeMappings } from "appwrite-utils";
|
7
|
+
|
8
|
+
// YAML Import Configuration Schema
|
9
|
+
export const YamlImportConfigSchema = z.object({
|
10
|
+
source: z.object({
|
11
|
+
file: z.string().describe("Path to the data file relative to .appwrite directory"),
|
12
|
+
basePath: z.string().optional().describe("JSON path to the data array (e.g., 'RECORDS')"),
|
13
|
+
type: z.enum(["json", "csv", "yaml"]).default("json").describe("Source file type"),
|
14
|
+
}),
|
15
|
+
|
16
|
+
target: z.object({
|
17
|
+
collection: z.string().describe("Name of the target collection"),
|
18
|
+
type: z.enum(["create", "update"]).default("create").describe("Import operation type"),
|
19
|
+
primaryKey: z.string().default("id").describe("Primary key field name in source data"),
|
20
|
+
createUsers: z.boolean().default(false).describe("Whether to create user accounts"),
|
21
|
+
}),
|
22
|
+
|
23
|
+
mapping: z.object({
|
24
|
+
attributes: z.array(z.object({
|
25
|
+
// Source mapping
|
26
|
+
oldKey: z.string().optional().describe("Source field name"),
|
27
|
+
oldKeys: z.array(z.string()).optional().describe("Multiple source field names"),
|
28
|
+
|
29
|
+
// Target mapping
|
30
|
+
targetKey: z.string().describe("Target field name in collection"),
|
31
|
+
valueToSet: z.any().optional().describe("Static value to set"),
|
32
|
+
|
33
|
+
// File handling
|
34
|
+
fileData: z.object({
|
35
|
+
path: z.string().describe("File path template (supports {field} placeholders)"),
|
36
|
+
name: z.string().describe("File name template (supports {field} placeholders)"),
|
37
|
+
}).optional(),
|
38
|
+
|
39
|
+
// Data transformation
|
40
|
+
converters: z.array(z.string()).default([]).describe("Converter function names"),
|
41
|
+
|
42
|
+
// Validation
|
43
|
+
validation: z.array(z.object({
|
44
|
+
rule: z.string().describe("Validation rule name"),
|
45
|
+
params: z.array(z.string()).describe("Validation parameters with {field} placeholders"),
|
46
|
+
})).default([]).describe("Validation rules"),
|
47
|
+
|
48
|
+
// Post-import actions
|
49
|
+
afterImport: z.array(z.object({
|
50
|
+
action: z.string().describe("Action name"),
|
51
|
+
params: z.array(z.union([z.string(), z.record(z.string(), z.any())])).describe("Action parameters"),
|
52
|
+
})).default([]).describe("Actions to execute after import"),
|
53
|
+
})).describe("Field mapping configuration"),
|
54
|
+
|
55
|
+
relationships: z.array(z.object({
|
56
|
+
sourceField: z.string().describe("Source field containing old ID"),
|
57
|
+
targetField: z.string().describe("Target field to set new ID"),
|
58
|
+
targetCollection: z.string().describe("Collection to find new ID in"),
|
59
|
+
fieldToSet: z.string().optional().describe("Field to set (defaults to sourceField)"),
|
60
|
+
targetFieldToMatch: z.string().optional().describe("Field to match in target collection"),
|
61
|
+
})).default([]).describe("Relationship mappings"),
|
62
|
+
}),
|
63
|
+
|
64
|
+
options: z.object({
|
65
|
+
batchSize: z.number().min(1).max(1000).default(50).describe("Batch size for processing"),
|
66
|
+
skipValidation: z.boolean().default(false).describe("Skip data validation"),
|
67
|
+
dryRun: z.boolean().default(false).describe("Perform dry run without actual import"),
|
68
|
+
continueOnError: z.boolean().default(true).describe("Continue processing if individual items fail"),
|
69
|
+
updateMapping: z.object({
|
70
|
+
originalIdField: z.string().describe("Field in source data for matching"),
|
71
|
+
targetField: z.string().describe("Field in collection to match against"),
|
72
|
+
}).optional().describe("Configuration for update operations"),
|
73
|
+
}).default({}),
|
74
|
+
});
|
75
|
+
|
76
|
+
export type YamlImportConfig = z.infer<typeof YamlImportConfigSchema>;
|
77
|
+
|
78
|
+
/**
|
79
|
+
* Service for loading and converting YAML import configurations.
|
80
|
+
* Integrates with existing .appwrite YAML structure while providing
|
81
|
+
* enhanced import configuration capabilities.
|
82
|
+
*/
|
83
|
+
export class YamlImportConfigLoader {
|
84
|
+
private appwriteFolderPath: string;
|
85
|
+
|
86
|
+
constructor(appwriteFolderPath: string) {
|
87
|
+
this.appwriteFolderPath = appwriteFolderPath;
|
88
|
+
}
|
89
|
+
|
90
|
+
/**
|
91
|
+
* Loads a YAML import configuration file.
|
92
|
+
*
|
93
|
+
* @param configPath - Path to the YAML config file relative to .appwrite/import/
|
94
|
+
* @returns Parsed and validated YAML import configuration
|
95
|
+
*/
|
96
|
+
async loadImportConfig(configPath: string): Promise<YamlImportConfig> {
|
97
|
+
const fullPath = path.resolve(this.appwriteFolderPath, "import", configPath);
|
98
|
+
|
99
|
+
if (!fs.existsSync(fullPath)) {
|
100
|
+
throw new Error(`Import configuration file not found: ${fullPath}`);
|
101
|
+
}
|
102
|
+
|
103
|
+
try {
|
104
|
+
const yamlContent = fs.readFileSync(fullPath, "utf8");
|
105
|
+
const rawConfig = yaml.load(yamlContent) as any;
|
106
|
+
|
107
|
+
// Validate against schema
|
108
|
+
const validatedConfig = YamlImportConfigSchema.parse(rawConfig);
|
109
|
+
|
110
|
+
logger.info(`Loaded import configuration: ${configPath}`);
|
111
|
+
return validatedConfig;
|
112
|
+
|
113
|
+
} catch (error) {
|
114
|
+
if (error instanceof z.ZodError) {
|
115
|
+
const errorMessages = error.errors.map(err => `${err.path.join('.')}: ${err.message}`);
|
116
|
+
throw new Error(`Invalid import configuration in ${configPath}:\n${errorMessages.join('\n')}`);
|
117
|
+
}
|
118
|
+
throw new Error(`Failed to load import configuration ${configPath}: ${error}`);
|
119
|
+
}
|
120
|
+
}
|
121
|
+
|
122
|
+
/**
|
123
|
+
* Loads all import configurations from the .appwrite/import directory.
|
124
|
+
*
|
125
|
+
* @returns Map of collection names to their import configurations
|
126
|
+
*/
|
127
|
+
async loadAllImportConfigs(): Promise<Map<string, YamlImportConfig[]>> {
|
128
|
+
const importDir = path.join(this.appwriteFolderPath, "import");
|
129
|
+
const configs = new Map<string, YamlImportConfig[]>();
|
130
|
+
|
131
|
+
if (!fs.existsSync(importDir)) {
|
132
|
+
logger.info("No import directory found, skipping YAML import configurations");
|
133
|
+
return configs;
|
134
|
+
}
|
135
|
+
|
136
|
+
try {
|
137
|
+
const files = fs.readdirSync(importDir, { withFileTypes: true });
|
138
|
+
|
139
|
+
for (const file of files) {
|
140
|
+
if (file.isFile() && file.name.endsWith('.yaml')) {
|
141
|
+
try {
|
142
|
+
const config = await this.loadImportConfig(file.name);
|
143
|
+
const collectionName = config.target.collection;
|
144
|
+
|
145
|
+
if (!configs.has(collectionName)) {
|
146
|
+
configs.set(collectionName, []);
|
147
|
+
}
|
148
|
+
configs.get(collectionName)!.push(config);
|
149
|
+
|
150
|
+
} catch (error) {
|
151
|
+
logger.error(`Failed to load import config ${file.name}:`, error);
|
152
|
+
}
|
153
|
+
}
|
154
|
+
}
|
155
|
+
|
156
|
+
logger.info(`Loaded import configurations for ${configs.size} collections`);
|
157
|
+
return configs;
|
158
|
+
|
159
|
+
} catch (error) {
|
160
|
+
logger.error("Failed to scan import directory:", error);
|
161
|
+
return configs;
|
162
|
+
}
|
163
|
+
}
|
164
|
+
|
165
|
+
/**
|
166
|
+
* Converts YAML import configuration to legacy ImportDef format.
|
167
|
+
* Maintains compatibility with existing import system.
|
168
|
+
*
|
169
|
+
* @param yamlConfig - YAML import configuration
|
170
|
+
* @returns Legacy ImportDef object
|
171
|
+
*/
|
172
|
+
convertToImportDef(yamlConfig: YamlImportConfig): ImportDef {
|
173
|
+
const attributeMappings: AttributeMappings = yamlConfig.mapping.attributes.map(attr => ({
|
174
|
+
oldKey: attr.oldKey,
|
175
|
+
oldKeys: attr.oldKeys,
|
176
|
+
targetKey: attr.targetKey,
|
177
|
+
valueToSet: attr.valueToSet,
|
178
|
+
fileData: attr.fileData,
|
179
|
+
converters: attr.converters,
|
180
|
+
validationActions: attr.validation.map(v => ({
|
181
|
+
action: v.rule,
|
182
|
+
params: v.params,
|
183
|
+
})),
|
184
|
+
postImportActions: attr.afterImport.map(a => ({
|
185
|
+
action: a.action,
|
186
|
+
params: a.params,
|
187
|
+
})),
|
188
|
+
}));
|
189
|
+
|
190
|
+
const idMappings = yamlConfig.mapping.relationships.map(rel => ({
|
191
|
+
sourceField: rel.sourceField,
|
192
|
+
targetField: rel.targetField,
|
193
|
+
targetCollection: rel.targetCollection,
|
194
|
+
fieldToSet: rel.fieldToSet,
|
195
|
+
targetFieldToMatch: rel.targetFieldToMatch,
|
196
|
+
}));
|
197
|
+
|
198
|
+
return {
|
199
|
+
type: yamlConfig.target.type,
|
200
|
+
filePath: yamlConfig.source.file,
|
201
|
+
basePath: yamlConfig.source.basePath,
|
202
|
+
primaryKeyField: yamlConfig.target.primaryKey,
|
203
|
+
createUsers: yamlConfig.target.createUsers,
|
204
|
+
attributeMappings,
|
205
|
+
idMappings: idMappings.length > 0 ? idMappings : undefined,
|
206
|
+
updateMapping: yamlConfig.options.updateMapping,
|
207
|
+
};
|
208
|
+
}
|
209
|
+
|
210
|
+
/**
|
211
|
+
* Generates a template YAML import configuration.
|
212
|
+
* Useful for getting started with YAML-based imports.
|
213
|
+
*
|
214
|
+
* @param collectionName - Name of the collection
|
215
|
+
* @param sourceFile - Source data file name
|
216
|
+
* @returns YAML configuration template
|
217
|
+
*/
|
218
|
+
generateTemplate(collectionName: string, sourceFile: string): string {
|
219
|
+
const template = {
|
220
|
+
source: {
|
221
|
+
file: `importData/${sourceFile}`,
|
222
|
+
basePath: "RECORDS",
|
223
|
+
type: "json"
|
224
|
+
},
|
225
|
+
target: {
|
226
|
+
collection: collectionName,
|
227
|
+
type: "create",
|
228
|
+
primaryKey: "id",
|
229
|
+
createUsers: false
|
230
|
+
},
|
231
|
+
mapping: {
|
232
|
+
attributes: [
|
233
|
+
{
|
234
|
+
oldKey: "id",
|
235
|
+
targetKey: "id",
|
236
|
+
converters: ["anyToString"]
|
237
|
+
},
|
238
|
+
{
|
239
|
+
oldKey: "name",
|
240
|
+
targetKey: "name",
|
241
|
+
converters: ["anyToString"],
|
242
|
+
validation: [
|
243
|
+
{
|
244
|
+
rule: "required",
|
245
|
+
params: ["{name}"]
|
246
|
+
}
|
247
|
+
]
|
248
|
+
},
|
249
|
+
{
|
250
|
+
oldKey: "avatar_url",
|
251
|
+
targetKey: "avatar",
|
252
|
+
fileData: {
|
253
|
+
path: "{avatar_url}",
|
254
|
+
name: "{name}_avatar"
|
255
|
+
},
|
256
|
+
afterImport: [
|
257
|
+
{
|
258
|
+
action: "createFileAndUpdateField",
|
259
|
+
params: ["{dbId}", "{collId}", "{docId}", "avatar", "{bucketId}", "{filePath}", "{fileName}"]
|
260
|
+
}
|
261
|
+
]
|
262
|
+
}
|
263
|
+
],
|
264
|
+
relationships: [
|
265
|
+
{
|
266
|
+
sourceField: "user_id",
|
267
|
+
targetField: "userId",
|
268
|
+
targetCollection: "Users"
|
269
|
+
}
|
270
|
+
]
|
271
|
+
},
|
272
|
+
options: {
|
273
|
+
batchSize: 50,
|
274
|
+
skipValidation: false,
|
275
|
+
dryRun: false,
|
276
|
+
continueOnError: true
|
277
|
+
}
|
278
|
+
};
|
279
|
+
|
280
|
+
return yaml.dump(template, {
|
281
|
+
indent: 2,
|
282
|
+
lineWidth: 120,
|
283
|
+
sortKeys: false,
|
284
|
+
});
|
285
|
+
}
|
286
|
+
|
287
|
+
/**
|
288
|
+
* Creates the import directory structure if it doesn't exist.
|
289
|
+
* Sets up the recommended directory layout for YAML import configurations.
|
290
|
+
*/
|
291
|
+
async createImportStructure(): Promise<void> {
|
292
|
+
const importDir = path.join(this.appwriteFolderPath, "import");
|
293
|
+
const collectionsDir = path.join(importDir, "collections");
|
294
|
+
const templatesDir = path.join(importDir, "templates");
|
295
|
+
|
296
|
+
// Create directories
|
297
|
+
for (const dir of [importDir, collectionsDir, templatesDir]) {
|
298
|
+
if (!fs.existsSync(dir)) {
|
299
|
+
fs.mkdirSync(dir, { recursive: true });
|
300
|
+
logger.info(`Created directory: ${dir}`);
|
301
|
+
}
|
302
|
+
}
|
303
|
+
|
304
|
+
// Create README file
|
305
|
+
const readmePath = path.join(importDir, "README.md");
|
306
|
+
if (!fs.existsSync(readmePath)) {
|
307
|
+
const readmeContent = `# Import Configurations
|
308
|
+
|
309
|
+
This directory contains YAML-based import configurations for the Appwrite Utils CLI.
|
310
|
+
|
311
|
+
## Structure
|
312
|
+
|
313
|
+
- \`collections/\` - Collection-specific import configurations
|
314
|
+
- \`templates/\` - Template configurations for reference
|
315
|
+
- \`*.yaml\` - Individual import configuration files
|
316
|
+
|
317
|
+
## Configuration Format
|
318
|
+
|
319
|
+
Each YAML file defines:
|
320
|
+
- **source**: Data source configuration (file, type, basePath)
|
321
|
+
- **target**: Target collection and operation type
|
322
|
+
- **mapping**: Field mappings, transformations, and relationships
|
323
|
+
- **options**: Import options (batch size, validation, etc.)
|
324
|
+
|
325
|
+
## Example
|
326
|
+
|
327
|
+
\`\`\`yaml
|
328
|
+
source:
|
329
|
+
file: "importData/users.json"
|
330
|
+
basePath: "RECORDS"
|
331
|
+
type: "json"
|
332
|
+
|
333
|
+
target:
|
334
|
+
collection: "Users"
|
335
|
+
type: "create"
|
336
|
+
primaryKey: "id"
|
337
|
+
createUsers: true
|
338
|
+
|
339
|
+
mapping:
|
340
|
+
attributes:
|
341
|
+
- oldKey: "user_id"
|
342
|
+
targetKey: "userId"
|
343
|
+
converters: ["anyToString"]
|
344
|
+
- oldKey: "profile_image"
|
345
|
+
targetKey: "avatar"
|
346
|
+
fileData:
|
347
|
+
path: "assets/profiles/{user_id}.jpg"
|
348
|
+
name: "{firstName}_{lastName}_avatar"
|
349
|
+
|
350
|
+
options:
|
351
|
+
batchSize: 50
|
352
|
+
continueOnError: true
|
353
|
+
\`\`\`
|
354
|
+
|
355
|
+
## Usage
|
356
|
+
|
357
|
+
The CLI will automatically detect and load YAML import configurations during the import process.
|
358
|
+
`;
|
359
|
+
|
360
|
+
fs.writeFileSync(readmePath, readmeContent);
|
361
|
+
logger.info(`Created README: ${readmePath}`);
|
362
|
+
}
|
363
|
+
}
|
364
|
+
|
365
|
+
/**
|
366
|
+
* Validates import configuration against collection schema.
|
367
|
+
* Ensures that all target keys exist as attributes in the collection.
|
368
|
+
*
|
369
|
+
* @param yamlConfig - YAML import configuration
|
370
|
+
* @param collectionAttributes - Collection attribute definitions
|
371
|
+
* @returns Validation errors (empty if valid)
|
372
|
+
*/
|
373
|
+
validateAgainstCollection(
|
374
|
+
yamlConfig: YamlImportConfig,
|
375
|
+
collectionAttributes: any[]
|
376
|
+
): string[] {
|
377
|
+
const errors: string[] = [];
|
378
|
+
const attributeKeys = new Set(collectionAttributes.map(attr => attr.key));
|
379
|
+
|
380
|
+
for (const mapping of yamlConfig.mapping.attributes) {
|
381
|
+
if (!attributeKeys.has(mapping.targetKey)) {
|
382
|
+
errors.push(`Target key '${mapping.targetKey}' not found in collection attributes`);
|
383
|
+
}
|
384
|
+
}
|
385
|
+
|
386
|
+
return errors;
|
387
|
+
}
|
388
|
+
|
389
|
+
/**
|
390
|
+
* Gets statistics about import configurations.
|
391
|
+
*
|
392
|
+
* @param configs - Map of collection configurations
|
393
|
+
* @returns Statistics object
|
394
|
+
*/
|
395
|
+
getStatistics(configs: Map<string, YamlImportConfig[]>): {
|
396
|
+
totalConfigurations: number;
|
397
|
+
collectionsWithConfigs: number;
|
398
|
+
totalAttributeMappings: number;
|
399
|
+
totalRelationshipMappings: number;
|
400
|
+
configsByType: { [type: string]: number };
|
401
|
+
} {
|
402
|
+
let totalConfigurations = 0;
|
403
|
+
let totalAttributeMappings = 0;
|
404
|
+
let totalRelationshipMappings = 0;
|
405
|
+
const configsByType: { [type: string]: number } = {};
|
406
|
+
|
407
|
+
for (const [collectionName, collectionConfigs] of configs.entries()) {
|
408
|
+
totalConfigurations += collectionConfigs.length;
|
409
|
+
|
410
|
+
for (const config of collectionConfigs) {
|
411
|
+
totalAttributeMappings += config.mapping.attributes.length;
|
412
|
+
totalRelationshipMappings += config.mapping.relationships.length;
|
413
|
+
|
414
|
+
const type = config.target.type;
|
415
|
+
configsByType[type] = (configsByType[type] || 0) + 1;
|
416
|
+
}
|
417
|
+
}
|
418
|
+
|
419
|
+
return {
|
420
|
+
totalConfigurations,
|
421
|
+
collectionsWithConfigs: configs.size,
|
422
|
+
totalAttributeMappings,
|
423
|
+
totalRelationshipMappings,
|
424
|
+
configsByType,
|
425
|
+
};
|
426
|
+
}
|
427
|
+
}
|