appwrite-utils-cli 1.3.5 → 1.4.0
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/dist/adapters/AdapterFactory.d.ts +87 -0
- package/dist/adapters/AdapterFactory.js +217 -0
- package/dist/adapters/DatabaseAdapter.d.ts +217 -0
- package/dist/adapters/DatabaseAdapter.js +50 -0
- package/dist/adapters/LegacyAdapter.d.ts +49 -0
- package/dist/adapters/LegacyAdapter.js +382 -0
- package/dist/adapters/TablesDBAdapter.d.ts +55 -0
- package/dist/adapters/TablesDBAdapter.js +302 -0
- package/dist/adapters/index.d.ts +11 -0
- package/dist/adapters/index.js +12 -0
- package/dist/collections/attributes.js +41 -22
- package/dist/collections/methods.d.ts +4 -3
- package/dist/collections/methods.js +34 -14
- package/dist/config/yamlConfig.d.ts +40 -437
- package/dist/config/yamlConfig.js +8 -2
- package/dist/databases/setup.js +2 -2
- package/dist/main.js +0 -0
- package/dist/migrations/appwriteToX.d.ts +26 -37
- package/dist/migrations/comprehensiveTransfer.js +4 -4
- package/dist/migrations/dataLoader.d.ts +124 -1484
- package/dist/migrations/dataLoader.js +2 -1
- package/dist/migrations/relationships.d.ts +2 -3
- package/dist/migrations/relationships.js +1 -1
- package/dist/migrations/services/UserMappingService.js +1 -1
- package/dist/migrations/yaml/YamlImportConfigLoader.d.ts +24 -279
- package/dist/migrations/yaml/YamlImportConfigLoader.js +7 -2
- package/dist/schemas/authUser.d.ts +7 -47
- package/dist/schemas/authUser.js +1 -1
- package/dist/shared/jsonSchemaGenerator.d.ts +0 -2
- package/dist/shared/jsonSchemaGenerator.js +4 -17
- package/dist/shared/migrationHelpers.d.ts +17 -119
- package/dist/shared/operationQueue.js +16 -7
- package/dist/shared/schemaGenerator.js +2 -17
- package/dist/storage/schemas.d.ts +149 -296
- package/dist/users/methods.d.ts +2 -2
- package/dist/utils/configMigration.js +0 -1
- package/dist/utils/getClientFromConfig.d.ts +26 -0
- package/dist/utils/getClientFromConfig.js +37 -0
- package/dist/utils/loadConfigs.js +0 -2
- package/dist/utils/schemaStrings.js +2 -17
- package/dist/utils/setupFiles.js +2 -0
- package/dist/utils/versionDetection.d.ts +56 -0
- package/dist/utils/versionDetection.js +217 -0
- package/dist/utils/yamlConverter.d.ts +0 -1
- package/dist/utils/yamlConverter.js +0 -2
- package/dist/utilsController.js +2 -0
- package/package.json +3 -2
- package/src/adapters/AdapterFactory.ts +296 -0
- package/src/adapters/DatabaseAdapter.ts +290 -0
- package/src/adapters/LegacyAdapter.ts +667 -0
- package/src/adapters/TablesDBAdapter.ts +429 -0
- package/src/adapters/index.ts +37 -0
- package/src/collections/attributes.ts +347 -153
- package/src/collections/methods.ts +43 -28
- package/src/config/yamlConfig.ts +8 -2
- package/src/databases/setup.ts +2 -2
- package/src/migrations/afterImportActions.ts +2 -2
- package/src/migrations/comprehensiveTransfer.ts +4 -0
- package/src/migrations/dataLoader.ts +2 -1
- package/src/migrations/relationships.ts +1 -1
- package/src/migrations/services/UserMappingService.ts +1 -1
- package/src/migrations/yaml/YamlImportConfigLoader.ts +7 -2
- package/src/schemas/authUser.ts +1 -1
- package/src/shared/jsonSchemaGenerator.ts +4 -19
- package/src/shared/operationQueue.ts +20 -13
- package/src/shared/schemaGenerator.ts +2 -16
- package/src/types/node-appwrite-tablesdb.d.ts +44 -0
- package/src/users/methods.ts +2 -2
- package/src/utils/configMigration.ts +0 -1
- package/src/utils/getClientFromConfig.ts +56 -0
- package/src/utils/loadConfigs.ts +0 -2
- package/src/utils/schemaStrings.ts +2 -16
- package/src/utils/setupFiles.ts +2 -0
- package/src/utils/versionDetection.ts +265 -0
- package/src/utils/yamlConverter.ts +0 -2
- package/src/utilsController.ts +2 -0
- package/dist/functions/openapi.d.ts +0 -4
- package/dist/functions/openapi.js +0 -60
- package/dist/migrations/attributes.d.ts +0 -4
- package/dist/migrations/attributes.js +0 -301
- package/dist/migrations/backup.d.ts +0 -687
- package/dist/migrations/backup.js +0 -175
- package/dist/migrations/collections.d.ts +0 -22
- package/dist/migrations/collections.js +0 -347
- package/dist/migrations/converters.d.ts +0 -46
- package/dist/migrations/converters.js +0 -139
- package/dist/migrations/databases.d.ts +0 -2
- package/dist/migrations/databases.js +0 -28
- package/dist/migrations/dbHelpers.d.ts +0 -5
- package/dist/migrations/dbHelpers.js +0 -57
- package/dist/migrations/helper.d.ts +0 -3
- package/dist/migrations/helper.js +0 -21
- package/dist/migrations/indexes.d.ts +0 -4
- package/dist/migrations/indexes.js +0 -19
- package/dist/migrations/logging.d.ts +0 -10
- package/dist/migrations/logging.js +0 -46
- package/dist/migrations/migrationHelper.d.ts +0 -173
- package/dist/migrations/migrationHelper.js +0 -130
- package/dist/migrations/openapi.d.ts +0 -4
- package/dist/migrations/openapi.js +0 -60
- package/dist/migrations/queue.d.ts +0 -13
- package/dist/migrations/queue.js +0 -79
- package/dist/migrations/schemaStrings.d.ts +0 -14
- package/dist/migrations/schemaStrings.js +0 -478
- package/dist/migrations/setupDatabase.d.ts +0 -6
- package/dist/migrations/setupDatabase.js +0 -115
- package/dist/migrations/storage.d.ts +0 -10
- package/dist/migrations/storage.js +0 -340
- package/dist/migrations/users.d.ts +0 -16
- package/dist/migrations/users.js +0 -276
- package/dist/migrations/validationRules.d.ts +0 -43
- package/dist/migrations/validationRules.js +0 -42
- package/dist/shared/attributeManager.d.ts +0 -17
- package/dist/shared/attributeManager.js +0 -272
- package/src/functions/openapi.ts +0 -83
- package/src/shared/attributeManager.ts +0 -428
package/src/functions/openapi.ts
DELETED
@@ -1,83 +0,0 @@
|
|
1
|
-
import {
|
2
|
-
OpenAPIRegistry,
|
3
|
-
OpenApiGeneratorV3,
|
4
|
-
OpenApiGeneratorV31,
|
5
|
-
} from "@asteasolutions/zod-to-openapi";
|
6
|
-
import {
|
7
|
-
attributeSchema,
|
8
|
-
CollectionSchema,
|
9
|
-
type AppwriteConfig,
|
10
|
-
type Attribute,
|
11
|
-
type Collection,
|
12
|
-
type CollectionCreate,
|
13
|
-
} from "appwrite-utils";
|
14
|
-
import { z } from "zod";
|
15
|
-
import { writeFileSync } from "fs";
|
16
|
-
|
17
|
-
const registry = new OpenAPIRegistry();
|
18
|
-
|
19
|
-
export const generateOpenApi = async (config: AppwriteConfig) => {
|
20
|
-
if (!config.collections) {
|
21
|
-
return;
|
22
|
-
}
|
23
|
-
for (const collection of config.collections) {
|
24
|
-
// Transform and register each attribute schema
|
25
|
-
const attributeSchemas = collection.attributes.map((attribute) => {
|
26
|
-
return transformTypeToOpenApi(attributeSchema, attribute.description);
|
27
|
-
});
|
28
|
-
|
29
|
-
// Create and register the collection schema with descriptions
|
30
|
-
const updatedCollectionSchema = CollectionSchema.extend({
|
31
|
-
// @ts-ignore
|
32
|
-
attributes: attributeSchemas,
|
33
|
-
}).openapi(collection.description ?? "No description");
|
34
|
-
|
35
|
-
// Register the updated collection schema under the collection name
|
36
|
-
registry.register(collection.name, updatedCollectionSchema);
|
37
|
-
}
|
38
|
-
|
39
|
-
// Convert the registry to OpenAPI JSON
|
40
|
-
const generator = new OpenApiGeneratorV31(registry.definitions);
|
41
|
-
const openApiSpec = generator.generateComponents();
|
42
|
-
|
43
|
-
// Output the OpenAPI spec to a file
|
44
|
-
writeFileSync(
|
45
|
-
"./appwrite/openapi/openapi.json",
|
46
|
-
JSON.stringify(openApiSpec, null, 2)
|
47
|
-
);
|
48
|
-
};
|
49
|
-
|
50
|
-
export function transformTypeToOpenApi<T extends z.ZodTypeAny>(
|
51
|
-
schema: T,
|
52
|
-
description?: string | Record<string, any> | null | undefined
|
53
|
-
): T {
|
54
|
-
// Check if description is an object (OpenAPI properties) or a string
|
55
|
-
let updatedSchema: z.infer<T>;
|
56
|
-
if (!description) {
|
57
|
-
return schema;
|
58
|
-
}
|
59
|
-
if (typeof description === "string") {
|
60
|
-
updatedSchema = schema.openapi(description);
|
61
|
-
} else if (typeof description === "object") {
|
62
|
-
updatedSchema = schema.openapi(description);
|
63
|
-
} else {
|
64
|
-
updatedSchema = schema;
|
65
|
-
}
|
66
|
-
|
67
|
-
// Check and transform attributes if they exist
|
68
|
-
if ((schema as any)._def && (schema as any)._def.shape) {
|
69
|
-
const shape = (schema as any)._def.shape();
|
70
|
-
for (const key in shape) {
|
71
|
-
const attributeDesc = shape[key].description;
|
72
|
-
if (attributeDesc) {
|
73
|
-
if (typeof attributeDesc === "string") {
|
74
|
-
shape[key] = shape[key].openapi(attributeDesc);
|
75
|
-
} else if (typeof attributeDesc === "object") {
|
76
|
-
shape[key] = shape[key].openapi(attributeDesc);
|
77
|
-
}
|
78
|
-
}
|
79
|
-
}
|
80
|
-
}
|
81
|
-
|
82
|
-
return updatedSchema;
|
83
|
-
}
|
@@ -1,428 +0,0 @@
|
|
1
|
-
import { type Databases, type Models } from "node-appwrite";
|
2
|
-
import {
|
3
|
-
parseAttribute,
|
4
|
-
type Attribute,
|
5
|
-
type CollectionCreate,
|
6
|
-
} from "appwrite-utils";
|
7
|
-
import { nameToIdMapping, type QueuedOperation, enqueueOperation } from "./operationQueue.js";
|
8
|
-
import { delay, tryAwaitWithRetry } from "../utils/helperFunctions.js";
|
9
|
-
import chalk from "chalk";
|
10
|
-
import pLimit from "p-limit";
|
11
|
-
|
12
|
-
// Concurrency limits for different operations
|
13
|
-
const attributeLimit = pLimit(3); // Low limit for attribute operations
|
14
|
-
const queryLimit = pLimit(25); // Higher limit for read operations
|
15
|
-
|
16
|
-
export const attributesSame = (
|
17
|
-
databaseAttribute: Attribute,
|
18
|
-
configAttribute: Attribute
|
19
|
-
): boolean => {
|
20
|
-
const attributesToCheck = [
|
21
|
-
"key",
|
22
|
-
"type",
|
23
|
-
"array",
|
24
|
-
"encrypted",
|
25
|
-
"required",
|
26
|
-
"size",
|
27
|
-
"min",
|
28
|
-
"max",
|
29
|
-
"xdefault",
|
30
|
-
"elements",
|
31
|
-
"relationType",
|
32
|
-
"twoWay",
|
33
|
-
"twoWayKey",
|
34
|
-
"onDelete",
|
35
|
-
"relatedCollection",
|
36
|
-
];
|
37
|
-
|
38
|
-
return attributesToCheck.every((attr) => {
|
39
|
-
// Special handling for min/max values
|
40
|
-
if (attr === "min" || attr === "max") {
|
41
|
-
const dbValue = databaseAttribute[attr as keyof typeof databaseAttribute];
|
42
|
-
const configValue = configAttribute[attr as keyof typeof configAttribute];
|
43
|
-
|
44
|
-
// Use type-specific default values when comparing
|
45
|
-
if (databaseAttribute.type === "integer") {
|
46
|
-
if (attr === "min") {
|
47
|
-
return (dbValue ?? -2147483647) === (configValue ?? -2147483647);
|
48
|
-
} else { // attr === "max"
|
49
|
-
return (dbValue ?? 2147483647) === (configValue ?? 2147483647);
|
50
|
-
}
|
51
|
-
}
|
52
|
-
if (databaseAttribute.type === "double" || databaseAttribute.type === "float") {
|
53
|
-
if (attr === "min") {
|
54
|
-
return (dbValue ?? -2147483647) === (configValue ?? -2147483647);
|
55
|
-
} else { // attr === "max"
|
56
|
-
return (dbValue ?? 2147483647) === (configValue ?? 2147483647);
|
57
|
-
}
|
58
|
-
}
|
59
|
-
}
|
60
|
-
|
61
|
-
// Check if both objects have the attribute
|
62
|
-
const dbHasAttr = attr in databaseAttribute;
|
63
|
-
const configHasAttr = attr in configAttribute;
|
64
|
-
|
65
|
-
// If both have the attribute, compare values
|
66
|
-
if (dbHasAttr && configHasAttr) {
|
67
|
-
const dbValue = databaseAttribute[attr as keyof typeof databaseAttribute];
|
68
|
-
const configValue = configAttribute[attr as keyof typeof configAttribute];
|
69
|
-
|
70
|
-
// Consider undefined and null as equivalent
|
71
|
-
if (
|
72
|
-
(dbValue === undefined || dbValue === null) &&
|
73
|
-
(configValue === undefined || configValue === null)
|
74
|
-
) {
|
75
|
-
return true;
|
76
|
-
}
|
77
|
-
|
78
|
-
return dbValue === configValue;
|
79
|
-
}
|
80
|
-
|
81
|
-
// If neither has the attribute, consider it the same
|
82
|
-
if (!dbHasAttr && !configHasAttr) {
|
83
|
-
return true;
|
84
|
-
}
|
85
|
-
|
86
|
-
// If one has the attribute and the other doesn't, check if it's undefined or null
|
87
|
-
if (dbHasAttr && !configHasAttr) {
|
88
|
-
const dbValue = databaseAttribute[attr as keyof typeof databaseAttribute];
|
89
|
-
return dbValue === undefined || dbValue === null;
|
90
|
-
}
|
91
|
-
|
92
|
-
if (!dbHasAttr && configHasAttr) {
|
93
|
-
const configValue = configAttribute[attr as keyof typeof configAttribute];
|
94
|
-
return configValue === undefined || configValue === null;
|
95
|
-
}
|
96
|
-
|
97
|
-
// If we reach here, the attributes are different
|
98
|
-
return false;
|
99
|
-
});
|
100
|
-
};
|
101
|
-
|
102
|
-
export const createOrUpdateAttribute = async (
|
103
|
-
db: Databases,
|
104
|
-
dbId: string,
|
105
|
-
collection: Models.Collection,
|
106
|
-
attribute: Attribute,
|
107
|
-
options: {
|
108
|
-
updateEnabled?: boolean;
|
109
|
-
useQueue?: boolean;
|
110
|
-
verbose?: boolean;
|
111
|
-
} = {}
|
112
|
-
): Promise<void> => {
|
113
|
-
const { updateEnabled = true, useQueue = true, verbose = false } = options;
|
114
|
-
|
115
|
-
let action = "create";
|
116
|
-
let foundAttribute: Attribute | undefined;
|
117
|
-
let finalAttribute: any = attribute;
|
118
|
-
|
119
|
-
try {
|
120
|
-
const collectionAttr = collection.attributes.find(
|
121
|
-
(attr: any) => attr.key === attribute.key
|
122
|
-
) as unknown as any;
|
123
|
-
foundAttribute = parseAttribute(collectionAttr);
|
124
|
-
|
125
|
-
if (verbose) {
|
126
|
-
console.log(`Found attribute: ${JSON.stringify(foundAttribute)}`);
|
127
|
-
}
|
128
|
-
} catch (error) {
|
129
|
-
foundAttribute = undefined;
|
130
|
-
}
|
131
|
-
|
132
|
-
if (
|
133
|
-
foundAttribute &&
|
134
|
-
attributesSame(foundAttribute, attribute) &&
|
135
|
-
updateEnabled
|
136
|
-
) {
|
137
|
-
if (verbose) {
|
138
|
-
console.log(chalk.green(`✓ Attribute ${attribute.key} is up to date`));
|
139
|
-
}
|
140
|
-
return;
|
141
|
-
}
|
142
|
-
|
143
|
-
if (foundAttribute) {
|
144
|
-
action = "update";
|
145
|
-
if (verbose) {
|
146
|
-
console.log(chalk.yellow(`⚠ Updating attribute ${attribute.key}`));
|
147
|
-
}
|
148
|
-
} else {
|
149
|
-
if (verbose) {
|
150
|
-
console.log(chalk.blue(`+ Creating attribute ${attribute.key}`));
|
151
|
-
}
|
152
|
-
}
|
153
|
-
|
154
|
-
// Handle relationship attributes with nameToIdMapping
|
155
|
-
if (attribute.type === "relationship" && attribute.relatedCollection) {
|
156
|
-
const relatedCollectionId = nameToIdMapping.get(attribute.relatedCollection);
|
157
|
-
if (relatedCollectionId) {
|
158
|
-
finalAttribute = {
|
159
|
-
...attribute,
|
160
|
-
relatedCollection: relatedCollectionId,
|
161
|
-
};
|
162
|
-
}
|
163
|
-
}
|
164
|
-
|
165
|
-
// Handle BigInt values for integer, double and float types
|
166
|
-
if (attribute.type === "integer" || attribute.type === "double" || attribute.type === "float") {
|
167
|
-
if (typeof finalAttribute.min === "bigint") {
|
168
|
-
finalAttribute.min = Number(finalAttribute.min);
|
169
|
-
}
|
170
|
-
if (typeof finalAttribute.max === "bigint") {
|
171
|
-
finalAttribute.max = Number(finalAttribute.max);
|
172
|
-
}
|
173
|
-
}
|
174
|
-
|
175
|
-
const queuedOperation: QueuedOperation = {
|
176
|
-
type: "attribute",
|
177
|
-
collectionId: collection.$id,
|
178
|
-
attribute: finalAttribute,
|
179
|
-
collection,
|
180
|
-
};
|
181
|
-
|
182
|
-
const executeOperation = async () => {
|
183
|
-
await attributeLimit(async () => {
|
184
|
-
if (action === "update" && foundAttribute) {
|
185
|
-
// Delete existing attribute first
|
186
|
-
await tryAwaitWithRetry(async () => {
|
187
|
-
await db.deleteAttribute(dbId, collection.$id, attribute.key);
|
188
|
-
});
|
189
|
-
await delay(250);
|
190
|
-
}
|
191
|
-
|
192
|
-
// Create attribute based on type
|
193
|
-
switch (finalAttribute.type) {
|
194
|
-
case "string":
|
195
|
-
await tryAwaitWithRetry(async () => {
|
196
|
-
await db.createStringAttribute(
|
197
|
-
dbId,
|
198
|
-
collection.$id,
|
199
|
-
finalAttribute.key,
|
200
|
-
finalAttribute.size,
|
201
|
-
finalAttribute.required,
|
202
|
-
finalAttribute.xdefault,
|
203
|
-
finalAttribute.array,
|
204
|
-
finalAttribute.encrypted
|
205
|
-
);
|
206
|
-
});
|
207
|
-
break;
|
208
|
-
case "integer":
|
209
|
-
await tryAwaitWithRetry(async () => {
|
210
|
-
await db.createIntegerAttribute(
|
211
|
-
dbId,
|
212
|
-
collection.$id,
|
213
|
-
finalAttribute.key,
|
214
|
-
finalAttribute.required,
|
215
|
-
finalAttribute.min,
|
216
|
-
finalAttribute.max,
|
217
|
-
finalAttribute.xdefault,
|
218
|
-
finalAttribute.array
|
219
|
-
);
|
220
|
-
});
|
221
|
-
break;
|
222
|
-
case "double":
|
223
|
-
case "float": // Backward compatibility
|
224
|
-
await tryAwaitWithRetry(async () => {
|
225
|
-
await db.createFloatAttribute(
|
226
|
-
dbId,
|
227
|
-
collection.$id,
|
228
|
-
finalAttribute.key,
|
229
|
-
finalAttribute.required,
|
230
|
-
finalAttribute.min,
|
231
|
-
finalAttribute.max,
|
232
|
-
finalAttribute.xdefault,
|
233
|
-
finalAttribute.array
|
234
|
-
);
|
235
|
-
});
|
236
|
-
break;
|
237
|
-
case "boolean":
|
238
|
-
await tryAwaitWithRetry(async () => {
|
239
|
-
await db.createBooleanAttribute(
|
240
|
-
dbId,
|
241
|
-
collection.$id,
|
242
|
-
finalAttribute.key,
|
243
|
-
finalAttribute.required,
|
244
|
-
finalAttribute.xdefault,
|
245
|
-
finalAttribute.array
|
246
|
-
);
|
247
|
-
});
|
248
|
-
break;
|
249
|
-
case "datetime":
|
250
|
-
await tryAwaitWithRetry(async () => {
|
251
|
-
await db.createDatetimeAttribute(
|
252
|
-
dbId,
|
253
|
-
collection.$id,
|
254
|
-
finalAttribute.key,
|
255
|
-
finalAttribute.required,
|
256
|
-
finalAttribute.xdefault,
|
257
|
-
finalAttribute.array
|
258
|
-
);
|
259
|
-
});
|
260
|
-
break;
|
261
|
-
case "email":
|
262
|
-
await tryAwaitWithRetry(async () => {
|
263
|
-
await db.createEmailAttribute(
|
264
|
-
dbId,
|
265
|
-
collection.$id,
|
266
|
-
finalAttribute.key,
|
267
|
-
finalAttribute.required,
|
268
|
-
finalAttribute.xdefault,
|
269
|
-
finalAttribute.array
|
270
|
-
);
|
271
|
-
});
|
272
|
-
break;
|
273
|
-
case "ip":
|
274
|
-
await tryAwaitWithRetry(async () => {
|
275
|
-
await db.createIpAttribute(
|
276
|
-
dbId,
|
277
|
-
collection.$id,
|
278
|
-
finalAttribute.key,
|
279
|
-
finalAttribute.required,
|
280
|
-
finalAttribute.xdefault,
|
281
|
-
finalAttribute.array
|
282
|
-
);
|
283
|
-
});
|
284
|
-
break;
|
285
|
-
case "url":
|
286
|
-
await tryAwaitWithRetry(async () => {
|
287
|
-
await db.createUrlAttribute(
|
288
|
-
dbId,
|
289
|
-
collection.$id,
|
290
|
-
finalAttribute.key,
|
291
|
-
finalAttribute.required,
|
292
|
-
finalAttribute.xdefault,
|
293
|
-
finalAttribute.array
|
294
|
-
);
|
295
|
-
});
|
296
|
-
break;
|
297
|
-
case "enum":
|
298
|
-
await tryAwaitWithRetry(async () => {
|
299
|
-
await db.createEnumAttribute(
|
300
|
-
dbId,
|
301
|
-
collection.$id,
|
302
|
-
finalAttribute.key,
|
303
|
-
finalAttribute.elements,
|
304
|
-
finalAttribute.required,
|
305
|
-
finalAttribute.xdefault,
|
306
|
-
finalAttribute.array
|
307
|
-
);
|
308
|
-
});
|
309
|
-
break;
|
310
|
-
case "relationship":
|
311
|
-
await tryAwaitWithRetry(async () => {
|
312
|
-
await db.createRelationshipAttribute(
|
313
|
-
dbId,
|
314
|
-
collection.$id,
|
315
|
-
finalAttribute.relatedCollection,
|
316
|
-
finalAttribute.relationType,
|
317
|
-
finalAttribute.twoWay,
|
318
|
-
finalAttribute.key,
|
319
|
-
finalAttribute.twoWayKey,
|
320
|
-
finalAttribute.onDelete
|
321
|
-
);
|
322
|
-
});
|
323
|
-
break;
|
324
|
-
default:
|
325
|
-
throw new Error(`Unknown attribute type: ${finalAttribute.type}`);
|
326
|
-
}
|
327
|
-
});
|
328
|
-
};
|
329
|
-
|
330
|
-
if (useQueue) {
|
331
|
-
enqueueOperation(queuedOperation);
|
332
|
-
} else {
|
333
|
-
await executeOperation();
|
334
|
-
}
|
335
|
-
};
|
336
|
-
|
337
|
-
export const createUpdateCollectionAttributes = async (
|
338
|
-
db: Databases,
|
339
|
-
dbId: string,
|
340
|
-
collection: Models.Collection,
|
341
|
-
collectionConfig: CollectionCreate,
|
342
|
-
options: {
|
343
|
-
updateEnabled?: boolean;
|
344
|
-
useQueue?: boolean;
|
345
|
-
verbose?: boolean;
|
346
|
-
} = {}
|
347
|
-
): Promise<void> => {
|
348
|
-
if (!collectionConfig.attributes) return;
|
349
|
-
|
350
|
-
const { verbose = false } = options;
|
351
|
-
|
352
|
-
if (verbose) {
|
353
|
-
console.log(chalk.blue(`Processing ${collectionConfig.attributes.length} attributes for collection ${collection.name}`));
|
354
|
-
}
|
355
|
-
|
356
|
-
for (const attribute of collectionConfig.attributes) {
|
357
|
-
try {
|
358
|
-
await createOrUpdateAttribute(db, dbId, collection, attribute, options);
|
359
|
-
|
360
|
-
if (verbose) {
|
361
|
-
console.log(chalk.green(`✓ Processed attribute ${attribute.key}`));
|
362
|
-
}
|
363
|
-
|
364
|
-
// Add delay between attribute operations to prevent rate limiting
|
365
|
-
await delay(250);
|
366
|
-
} catch (error) {
|
367
|
-
console.error(chalk.red(`❌ Failed to process attribute ${attribute.key}:`), error);
|
368
|
-
throw error;
|
369
|
-
}
|
370
|
-
}
|
371
|
-
};
|
372
|
-
|
373
|
-
export const deleteObsoleteAttributes = async (
|
374
|
-
db: Databases,
|
375
|
-
dbId: string,
|
376
|
-
collection: Models.Collection,
|
377
|
-
collectionConfig: CollectionCreate,
|
378
|
-
options: {
|
379
|
-
useQueue?: boolean;
|
380
|
-
verbose?: boolean;
|
381
|
-
} = {}
|
382
|
-
): Promise<void> => {
|
383
|
-
const { useQueue = true, verbose = false } = options;
|
384
|
-
|
385
|
-
const configAttributes = collectionConfig.attributes || [];
|
386
|
-
const configAttributeKeys = new Set(configAttributes.map(attr => attr.key));
|
387
|
-
|
388
|
-
// Find attributes that exist in the database but not in the config
|
389
|
-
const obsoleteAttributes = collection.attributes.filter(
|
390
|
-
(attr: any) => !configAttributeKeys.has(attr.key)
|
391
|
-
);
|
392
|
-
|
393
|
-
if (obsoleteAttributes.length === 0) {
|
394
|
-
return;
|
395
|
-
}
|
396
|
-
|
397
|
-
if (verbose) {
|
398
|
-
console.log(chalk.yellow(`🗑️ Removing ${obsoleteAttributes.length} obsolete attributes from collection ${collection.name}`));
|
399
|
-
}
|
400
|
-
|
401
|
-
for (const attr of obsoleteAttributes) {
|
402
|
-
const queuedOperation: QueuedOperation = {
|
403
|
-
type: "attribute",
|
404
|
-
collectionId: collection.$id,
|
405
|
-
attribute: { key: (attr as any).key, type: "delete" } as unknown as Attribute,
|
406
|
-
collection,
|
407
|
-
};
|
408
|
-
|
409
|
-
const executeOperation = async () => {
|
410
|
-
await attributeLimit(() =>
|
411
|
-
tryAwaitWithRetry(async () => {
|
412
|
-
await db.deleteAttribute(dbId, collection.$id, (attr as any).key);
|
413
|
-
})
|
414
|
-
);
|
415
|
-
};
|
416
|
-
|
417
|
-
if (useQueue) {
|
418
|
-
enqueueOperation(queuedOperation);
|
419
|
-
} else {
|
420
|
-
await executeOperation();
|
421
|
-
await delay(250);
|
422
|
-
}
|
423
|
-
|
424
|
-
if (verbose) {
|
425
|
-
console.log(chalk.gray(`🗑️ Deleted obsolete attribute ${(attr as any).key}`));
|
426
|
-
}
|
427
|
-
}
|
428
|
-
};
|