appwrite-utils-cli 0.9.0 → 0.9.3
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/README.md +41 -0
- package/dist/interactiveCLI.d.ts +3 -2
- package/dist/interactiveCLI.js +18 -13
- package/dist/main.js +121 -109
- package/dist/migrations/transfer.js +1 -1
- package/dist/setupCommands.d.ts +1 -0
- package/dist/setupCommands.js +1 -0
- package/dist/utils/schemaStrings.js +37 -37
- package/dist/utils/setupFiles.d.ts +1 -1
- package/dist/utils/setupFiles.js +2 -2
- package/package.json +53 -53
- package/src/collections/attributes.ts +483 -483
- package/src/collections/indexes.ts +53 -53
- package/src/collections/methods.ts +331 -331
- package/src/interactiveCLI.ts +771 -767
- package/src/main.ts +126 -112
- package/src/migrations/helper.ts +40 -40
- package/src/migrations/transfer.ts +608 -608
- package/src/setupCommands.ts +0 -0
- package/src/storage/methods.ts +371 -371
- package/src/storage/schemas.ts +205 -205
- package/src/utils/getClientFromConfig.ts +17 -17
- package/src/utils/retryFailedPromises.ts +27 -27
- package/src/utils/schemaStrings.ts +473 -473
- package/src/utils/setupFiles.ts +5 -2
@@ -1,483 +1,483 @@
|
|
1
|
-
import { Query, type Databases, type Models } from "node-appwrite";
|
2
|
-
import {
|
3
|
-
attributeSchema,
|
4
|
-
parseAttribute,
|
5
|
-
type Attribute,
|
6
|
-
} from "appwrite-utils";
|
7
|
-
import { nameToIdMapping, enqueueOperation } from "../migrations/queue.js";
|
8
|
-
import _ from "lodash";
|
9
|
-
import { tryAwaitWithRetry } from "../utils/helperFunctions.js";
|
10
|
-
|
11
|
-
const attributesSame = (
|
12
|
-
databaseAttribute: Attribute,
|
13
|
-
configAttribute: Attribute
|
14
|
-
): boolean => {
|
15
|
-
return (
|
16
|
-
databaseAttribute.key == configAttribute.key &&
|
17
|
-
databaseAttribute.type == configAttribute.type &&
|
18
|
-
databaseAttribute.array == configAttribute.array
|
19
|
-
);
|
20
|
-
};
|
21
|
-
|
22
|
-
export const createOrUpdateAttribute = async (
|
23
|
-
db: Databases,
|
24
|
-
dbId: string,
|
25
|
-
collection: Models.Collection,
|
26
|
-
attribute: Attribute
|
27
|
-
): Promise<void> => {
|
28
|
-
let action = "create";
|
29
|
-
let foundAttribute: Attribute | undefined;
|
30
|
-
const updateEnabled = false;
|
31
|
-
let finalAttribute: any = attribute;
|
32
|
-
try {
|
33
|
-
const collectionAttr = collection.attributes.find(
|
34
|
-
// @ts-expect-error
|
35
|
-
(attr) => attr.key === attribute.key
|
36
|
-
) as unknown as any;
|
37
|
-
foundAttribute = parseAttribute(collectionAttr);
|
38
|
-
} catch (error) {
|
39
|
-
foundAttribute = undefined;
|
40
|
-
}
|
41
|
-
|
42
|
-
if (
|
43
|
-
foundAttribute &&
|
44
|
-
attributesSame(foundAttribute, attribute) &&
|
45
|
-
updateEnabled
|
46
|
-
) {
|
47
|
-
// Check if mutable properties have changed and set action to "update" if necessary
|
48
|
-
const requiredChanged =
|
49
|
-
"required" in foundAttribute && "required" in attribute
|
50
|
-
? foundAttribute.required !== attribute.required
|
51
|
-
: false;
|
52
|
-
|
53
|
-
// const xdefaultChanged =
|
54
|
-
// "xdefault" in foundAttribute && "xdefault" in attribute
|
55
|
-
// ? foundAttribute.xdefault !== attribute.xdefault
|
56
|
-
// : false;
|
57
|
-
|
58
|
-
const onDeleteChanged =
|
59
|
-
foundAttribute.type === "relationship" &&
|
60
|
-
attribute.type === "relationship" &&
|
61
|
-
"onDelete" in foundAttribute &&
|
62
|
-
"onDelete" in attribute
|
63
|
-
? foundAttribute.onDelete !== attribute.onDelete
|
64
|
-
: false;
|
65
|
-
|
66
|
-
if (requiredChanged || onDeleteChanged) {
|
67
|
-
console.log(
|
68
|
-
`Required changed: ${requiredChanged}\nOnDelete changed: ${onDeleteChanged}`
|
69
|
-
);
|
70
|
-
console.log(
|
71
|
-
`Found attribute: ${JSON.stringify(foundAttribute, null, 2)}`
|
72
|
-
);
|
73
|
-
console.log(`Attribute: ${JSON.stringify(attribute, null, 2)}`);
|
74
|
-
finalAttribute = {
|
75
|
-
...attribute,
|
76
|
-
...foundAttribute,
|
77
|
-
};
|
78
|
-
action = "update";
|
79
|
-
} else {
|
80
|
-
// If no properties that can be updated have changed, return early
|
81
|
-
return;
|
82
|
-
}
|
83
|
-
} else if (
|
84
|
-
foundAttribute &&
|
85
|
-
!attributesSame(foundAttribute, attribute) &&
|
86
|
-
updateEnabled
|
87
|
-
) {
|
88
|
-
console.log(
|
89
|
-
`Deleting attribute with same key ${
|
90
|
-
attribute.key
|
91
|
-
} -- but different values -- ${JSON.stringify(
|
92
|
-
attribute,
|
93
|
-
null,
|
94
|
-
2
|
95
|
-
)} -- ${JSON.stringify(foundAttribute, null, 2)}`
|
96
|
-
);
|
97
|
-
await db.deleteAttribute(dbId, collection.$id, attribute.key);
|
98
|
-
// After deletion, you might want to create the attribute anew
|
99
|
-
finalAttribute = attribute;
|
100
|
-
action = "create";
|
101
|
-
} else if (!updateEnabled && foundAttribute) {
|
102
|
-
return;
|
103
|
-
}
|
104
|
-
|
105
|
-
// Relationship attribute logic with adjustments
|
106
|
-
let collectionFoundViaRelatedCollection: Models.Collection | undefined;
|
107
|
-
let relatedCollectionId: string | undefined;
|
108
|
-
if (finalAttribute.type === "relationship") {
|
109
|
-
if (nameToIdMapping.has(finalAttribute.relatedCollection)) {
|
110
|
-
relatedCollectionId = nameToIdMapping.get(
|
111
|
-
finalAttribute.relatedCollection
|
112
|
-
);
|
113
|
-
try {
|
114
|
-
collectionFoundViaRelatedCollection = await db.getCollection(
|
115
|
-
dbId,
|
116
|
-
relatedCollectionId!
|
117
|
-
);
|
118
|
-
} catch (e) {
|
119
|
-
console.log(
|
120
|
-
`Collection not found: ${finalAttribute.relatedCollection} when nameToIdMapping was set`
|
121
|
-
);
|
122
|
-
collectionFoundViaRelatedCollection = undefined;
|
123
|
-
}
|
124
|
-
} else {
|
125
|
-
const collectionsPulled = await db.listCollections(dbId, [
|
126
|
-
Query.equal("name", finalAttribute.relatedCollection),
|
127
|
-
]);
|
128
|
-
if (collectionsPulled.total > 0) {
|
129
|
-
collectionFoundViaRelatedCollection = collectionsPulled.collections[0];
|
130
|
-
relatedCollectionId = collectionFoundViaRelatedCollection.$id;
|
131
|
-
nameToIdMapping.set(
|
132
|
-
finalAttribute.relatedCollection,
|
133
|
-
relatedCollectionId
|
134
|
-
);
|
135
|
-
}
|
136
|
-
}
|
137
|
-
if (!(relatedCollectionId && collectionFoundViaRelatedCollection)) {
|
138
|
-
console.log(`Enqueueing operation for attribute: ${finalAttribute.key}`);
|
139
|
-
enqueueOperation({
|
140
|
-
type: "attribute",
|
141
|
-
collectionId: collection.$id,
|
142
|
-
collection: collection,
|
143
|
-
attribute,
|
144
|
-
dependencies: [finalAttribute.relatedCollection],
|
145
|
-
});
|
146
|
-
return;
|
147
|
-
}
|
148
|
-
}
|
149
|
-
finalAttribute = attributeSchema.parse(finalAttribute);
|
150
|
-
switch (finalAttribute.type) {
|
151
|
-
case "string":
|
152
|
-
if (action === "create") {
|
153
|
-
await tryAwaitWithRetry(
|
154
|
-
async () =>
|
155
|
-
await db.createStringAttribute(
|
156
|
-
dbId,
|
157
|
-
collection.$id,
|
158
|
-
finalAttribute.key,
|
159
|
-
finalAttribute.size,
|
160
|
-
finalAttribute.required || false,
|
161
|
-
(finalAttribute.xdefault as string) || undefined,
|
162
|
-
finalAttribute.array || false,
|
163
|
-
finalAttribute.encrypted
|
164
|
-
)
|
165
|
-
);
|
166
|
-
} else {
|
167
|
-
await tryAwaitWithRetry(
|
168
|
-
async () =>
|
169
|
-
await db.updateStringAttribute(
|
170
|
-
dbId,
|
171
|
-
collection.$id,
|
172
|
-
finalAttribute.key,
|
173
|
-
finalAttribute.required || false,
|
174
|
-
(finalAttribute.xdefault as string) || undefined
|
175
|
-
)
|
176
|
-
);
|
177
|
-
}
|
178
|
-
break;
|
179
|
-
case "integer":
|
180
|
-
if (action === "create") {
|
181
|
-
if (
|
182
|
-
finalAttribute.min &&
|
183
|
-
BigInt(finalAttribute.min) === BigInt(-9223372036854776000)
|
184
|
-
) {
|
185
|
-
delete finalAttribute.min;
|
186
|
-
}
|
187
|
-
if (
|
188
|
-
finalAttribute.max &&
|
189
|
-
BigInt(finalAttribute.max) === BigInt(9223372036854776000)
|
190
|
-
) {
|
191
|
-
delete finalAttribute.max;
|
192
|
-
}
|
193
|
-
await tryAwaitWithRetry(
|
194
|
-
async () =>
|
195
|
-
await db.createIntegerAttribute(
|
196
|
-
dbId,
|
197
|
-
collection.$id,
|
198
|
-
finalAttribute.key,
|
199
|
-
finalAttribute.required || false,
|
200
|
-
finalAttribute.min,
|
201
|
-
finalAttribute.max,
|
202
|
-
finalAttribute.xdefault || undefined,
|
203
|
-
finalAttribute.array
|
204
|
-
)
|
205
|
-
);
|
206
|
-
} else {
|
207
|
-
if (
|
208
|
-
finalAttribute.min &&
|
209
|
-
BigInt(finalAttribute.min) === BigInt(-9223372036854776000)
|
210
|
-
) {
|
211
|
-
delete finalAttribute.min;
|
212
|
-
}
|
213
|
-
if (
|
214
|
-
finalAttribute.max &&
|
215
|
-
BigInt(finalAttribute.max) === BigInt(9223372036854776000)
|
216
|
-
) {
|
217
|
-
delete finalAttribute.max;
|
218
|
-
}
|
219
|
-
await tryAwaitWithRetry(
|
220
|
-
async () =>
|
221
|
-
await db.updateIntegerAttribute(
|
222
|
-
dbId,
|
223
|
-
collection.$id,
|
224
|
-
finalAttribute.key,
|
225
|
-
finalAttribute.required || false,
|
226
|
-
finalAttribute.min || 0,
|
227
|
-
finalAttribute.max || 2147483647,
|
228
|
-
finalAttribute.xdefault || undefined
|
229
|
-
)
|
230
|
-
);
|
231
|
-
}
|
232
|
-
break;
|
233
|
-
case "float":
|
234
|
-
if (action === "create") {
|
235
|
-
await tryAwaitWithRetry(
|
236
|
-
async () =>
|
237
|
-
await db.createFloatAttribute(
|
238
|
-
dbId,
|
239
|
-
collection.$id,
|
240
|
-
finalAttribute.key,
|
241
|
-
finalAttribute.required || false,
|
242
|
-
finalAttribute.min,
|
243
|
-
finalAttribute.max,
|
244
|
-
finalAttribute.xdefault || undefined,
|
245
|
-
finalAttribute.array
|
246
|
-
)
|
247
|
-
);
|
248
|
-
} else {
|
249
|
-
await tryAwaitWithRetry(
|
250
|
-
async () =>
|
251
|
-
await db.updateFloatAttribute(
|
252
|
-
dbId,
|
253
|
-
collection.$id,
|
254
|
-
finalAttribute.key,
|
255
|
-
finalAttribute.required || false,
|
256
|
-
finalAttribute.min || 0,
|
257
|
-
finalAttribute.max || 2147483647,
|
258
|
-
finalAttribute.xdefault || undefined
|
259
|
-
)
|
260
|
-
);
|
261
|
-
}
|
262
|
-
break;
|
263
|
-
case "boolean":
|
264
|
-
if (action === "create") {
|
265
|
-
await tryAwaitWithRetry(
|
266
|
-
async () =>
|
267
|
-
await db.createBooleanAttribute(
|
268
|
-
dbId,
|
269
|
-
collection.$id,
|
270
|
-
finalAttribute.key,
|
271
|
-
finalAttribute.required || false,
|
272
|
-
finalAttribute.xdefault || undefined,
|
273
|
-
finalAttribute.array
|
274
|
-
)
|
275
|
-
);
|
276
|
-
} else {
|
277
|
-
await tryAwaitWithRetry(
|
278
|
-
async () =>
|
279
|
-
await db.updateBooleanAttribute(
|
280
|
-
dbId,
|
281
|
-
collection.$id,
|
282
|
-
finalAttribute.key,
|
283
|
-
finalAttribute.required || false,
|
284
|
-
finalAttribute.xdefault || null
|
285
|
-
)
|
286
|
-
);
|
287
|
-
}
|
288
|
-
break;
|
289
|
-
case "datetime":
|
290
|
-
if (action === "create") {
|
291
|
-
await tryAwaitWithRetry(
|
292
|
-
async () =>
|
293
|
-
await db.createDatetimeAttribute(
|
294
|
-
dbId,
|
295
|
-
collection.$id,
|
296
|
-
finalAttribute.key,
|
297
|
-
finalAttribute.required || false,
|
298
|
-
finalAttribute.xdefault || undefined,
|
299
|
-
finalAttribute.array
|
300
|
-
)
|
301
|
-
);
|
302
|
-
} else {
|
303
|
-
await tryAwaitWithRetry(
|
304
|
-
async () =>
|
305
|
-
await db.updateDatetimeAttribute(
|
306
|
-
dbId,
|
307
|
-
collection.$id,
|
308
|
-
finalAttribute.key,
|
309
|
-
finalAttribute.required || false,
|
310
|
-
finalAttribute.xdefault || undefined
|
311
|
-
)
|
312
|
-
);
|
313
|
-
}
|
314
|
-
break;
|
315
|
-
case "email":
|
316
|
-
if (action === "create") {
|
317
|
-
await tryAwaitWithRetry(
|
318
|
-
async () =>
|
319
|
-
await db.createEmailAttribute(
|
320
|
-
dbId,
|
321
|
-
collection.$id,
|
322
|
-
finalAttribute.key,
|
323
|
-
finalAttribute.required || false,
|
324
|
-
finalAttribute.xdefault || undefined,
|
325
|
-
finalAttribute.array
|
326
|
-
)
|
327
|
-
);
|
328
|
-
} else {
|
329
|
-
await tryAwaitWithRetry(
|
330
|
-
async () =>
|
331
|
-
await db.updateEmailAttribute(
|
332
|
-
dbId,
|
333
|
-
collection.$id,
|
334
|
-
finalAttribute.key,
|
335
|
-
finalAttribute.required || false,
|
336
|
-
finalAttribute.xdefault || undefined
|
337
|
-
)
|
338
|
-
);
|
339
|
-
}
|
340
|
-
break;
|
341
|
-
case "ip":
|
342
|
-
if (action === "create") {
|
343
|
-
await tryAwaitWithRetry(
|
344
|
-
async () =>
|
345
|
-
await db.createIpAttribute(
|
346
|
-
dbId,
|
347
|
-
collection.$id,
|
348
|
-
finalAttribute.key,
|
349
|
-
finalAttribute.required || false,
|
350
|
-
finalAttribute.xdefault || undefined,
|
351
|
-
finalAttribute.array
|
352
|
-
)
|
353
|
-
);
|
354
|
-
} else {
|
355
|
-
await tryAwaitWithRetry(
|
356
|
-
async () =>
|
357
|
-
await db.updateIpAttribute(
|
358
|
-
dbId,
|
359
|
-
collection.$id,
|
360
|
-
finalAttribute.key,
|
361
|
-
finalAttribute.required || false,
|
362
|
-
finalAttribute.xdefault || undefined
|
363
|
-
)
|
364
|
-
);
|
365
|
-
}
|
366
|
-
break;
|
367
|
-
case "url":
|
368
|
-
if (action === "create") {
|
369
|
-
await tryAwaitWithRetry(
|
370
|
-
async () =>
|
371
|
-
await db.createUrlAttribute(
|
372
|
-
dbId,
|
373
|
-
collection.$id,
|
374
|
-
finalAttribute.key,
|
375
|
-
finalAttribute.required || false,
|
376
|
-
finalAttribute.xdefault || undefined,
|
377
|
-
finalAttribute.array
|
378
|
-
)
|
379
|
-
);
|
380
|
-
} else {
|
381
|
-
await tryAwaitWithRetry(
|
382
|
-
async () =>
|
383
|
-
await db.updateUrlAttribute(
|
384
|
-
dbId,
|
385
|
-
collection.$id,
|
386
|
-
finalAttribute.key,
|
387
|
-
finalAttribute.required || false,
|
388
|
-
finalAttribute.xdefault || undefined
|
389
|
-
)
|
390
|
-
);
|
391
|
-
}
|
392
|
-
break;
|
393
|
-
case "enum":
|
394
|
-
if (action === "create") {
|
395
|
-
await tryAwaitWithRetry(
|
396
|
-
async () =>
|
397
|
-
await db.createEnumAttribute(
|
398
|
-
dbId,
|
399
|
-
collection.$id,
|
400
|
-
finalAttribute.key,
|
401
|
-
finalAttribute.elements,
|
402
|
-
finalAttribute.required || false,
|
403
|
-
finalAttribute.xdefault || undefined,
|
404
|
-
finalAttribute.array
|
405
|
-
)
|
406
|
-
);
|
407
|
-
} else {
|
408
|
-
await tryAwaitWithRetry(
|
409
|
-
async () =>
|
410
|
-
await db.updateEnumAttribute(
|
411
|
-
dbId,
|
412
|
-
collection.$id,
|
413
|
-
finalAttribute.key,
|
414
|
-
finalAttribute.elements,
|
415
|
-
finalAttribute.required || false,
|
416
|
-
finalAttribute.xdefault || undefined
|
417
|
-
)
|
418
|
-
);
|
419
|
-
}
|
420
|
-
break;
|
421
|
-
case "relationship":
|
422
|
-
if (action === "create") {
|
423
|
-
await tryAwaitWithRetry(
|
424
|
-
async () =>
|
425
|
-
await db.createRelationshipAttribute(
|
426
|
-
dbId,
|
427
|
-
collection.$id,
|
428
|
-
relatedCollectionId!,
|
429
|
-
finalAttribute.relationType,
|
430
|
-
finalAttribute.twoWay,
|
431
|
-
finalAttribute.key,
|
432
|
-
finalAttribute.twoWayKey,
|
433
|
-
finalAttribute.onDelete
|
434
|
-
)
|
435
|
-
);
|
436
|
-
} else {
|
437
|
-
await tryAwaitWithRetry(
|
438
|
-
async () =>
|
439
|
-
await db.updateRelationshipAttribute(
|
440
|
-
dbId,
|
441
|
-
collection.$id,
|
442
|
-
finalAttribute.key,
|
443
|
-
finalAttribute.onDelete
|
444
|
-
)
|
445
|
-
);
|
446
|
-
}
|
447
|
-
break;
|
448
|
-
default:
|
449
|
-
console.error("Invalid attribute type");
|
450
|
-
break;
|
451
|
-
}
|
452
|
-
};
|
453
|
-
|
454
|
-
export const createUpdateCollectionAttributes = async (
|
455
|
-
db: Databases,
|
456
|
-
dbId: string,
|
457
|
-
collection: Models.Collection,
|
458
|
-
attributes: Attribute[]
|
459
|
-
): Promise<void> => {
|
460
|
-
console.log(
|
461
|
-
`Creating/Updating attributes for collection: ${collection.name}`
|
462
|
-
);
|
463
|
-
|
464
|
-
const batchSize = 3; // Size of each batch
|
465
|
-
for (let i = 0; i < attributes.length; i += batchSize) {
|
466
|
-
// Slice the attributes array to get a batch of at most batchSize elements
|
467
|
-
const batch = attributes.slice(i, i + batchSize);
|
468
|
-
const attributePromises = batch.map((attribute) =>
|
469
|
-
createOrUpdateAttribute(db, dbId, collection, attribute)
|
470
|
-
);
|
471
|
-
|
472
|
-
// Await the completion of all promises in the current batch
|
473
|
-
const results = await Promise.allSettled(attributePromises);
|
474
|
-
results.forEach((result) => {
|
475
|
-
if (result.status === "rejected") {
|
476
|
-
console.error("An attribute promise was rejected:", result.reason);
|
477
|
-
}
|
478
|
-
});
|
479
|
-
}
|
480
|
-
console.log(
|
481
|
-
`Finished creating/updating attributes for collection: ${collection.name}`
|
482
|
-
);
|
483
|
-
};
|
1
|
+
import { Query, type Databases, type Models } from "node-appwrite";
|
2
|
+
import {
|
3
|
+
attributeSchema,
|
4
|
+
parseAttribute,
|
5
|
+
type Attribute,
|
6
|
+
} from "appwrite-utils";
|
7
|
+
import { nameToIdMapping, enqueueOperation } from "../migrations/queue.js";
|
8
|
+
import _ from "lodash";
|
9
|
+
import { tryAwaitWithRetry } from "../utils/helperFunctions.js";
|
10
|
+
|
11
|
+
const attributesSame = (
|
12
|
+
databaseAttribute: Attribute,
|
13
|
+
configAttribute: Attribute
|
14
|
+
): boolean => {
|
15
|
+
return (
|
16
|
+
databaseAttribute.key == configAttribute.key &&
|
17
|
+
databaseAttribute.type == configAttribute.type &&
|
18
|
+
databaseAttribute.array == configAttribute.array
|
19
|
+
);
|
20
|
+
};
|
21
|
+
|
22
|
+
export const createOrUpdateAttribute = async (
|
23
|
+
db: Databases,
|
24
|
+
dbId: string,
|
25
|
+
collection: Models.Collection,
|
26
|
+
attribute: Attribute
|
27
|
+
): Promise<void> => {
|
28
|
+
let action = "create";
|
29
|
+
let foundAttribute: Attribute | undefined;
|
30
|
+
const updateEnabled = false;
|
31
|
+
let finalAttribute: any = attribute;
|
32
|
+
try {
|
33
|
+
const collectionAttr = collection.attributes.find(
|
34
|
+
// @ts-expect-error
|
35
|
+
(attr) => attr.key === attribute.key
|
36
|
+
) as unknown as any;
|
37
|
+
foundAttribute = parseAttribute(collectionAttr);
|
38
|
+
} catch (error) {
|
39
|
+
foundAttribute = undefined;
|
40
|
+
}
|
41
|
+
|
42
|
+
if (
|
43
|
+
foundAttribute &&
|
44
|
+
attributesSame(foundAttribute, attribute) &&
|
45
|
+
updateEnabled
|
46
|
+
) {
|
47
|
+
// Check if mutable properties have changed and set action to "update" if necessary
|
48
|
+
const requiredChanged =
|
49
|
+
"required" in foundAttribute && "required" in attribute
|
50
|
+
? foundAttribute.required !== attribute.required
|
51
|
+
: false;
|
52
|
+
|
53
|
+
// const xdefaultChanged =
|
54
|
+
// "xdefault" in foundAttribute && "xdefault" in attribute
|
55
|
+
// ? foundAttribute.xdefault !== attribute.xdefault
|
56
|
+
// : false;
|
57
|
+
|
58
|
+
const onDeleteChanged =
|
59
|
+
foundAttribute.type === "relationship" &&
|
60
|
+
attribute.type === "relationship" &&
|
61
|
+
"onDelete" in foundAttribute &&
|
62
|
+
"onDelete" in attribute
|
63
|
+
? foundAttribute.onDelete !== attribute.onDelete
|
64
|
+
: false;
|
65
|
+
|
66
|
+
if (requiredChanged || onDeleteChanged) {
|
67
|
+
console.log(
|
68
|
+
`Required changed: ${requiredChanged}\nOnDelete changed: ${onDeleteChanged}`
|
69
|
+
);
|
70
|
+
console.log(
|
71
|
+
`Found attribute: ${JSON.stringify(foundAttribute, null, 2)}`
|
72
|
+
);
|
73
|
+
console.log(`Attribute: ${JSON.stringify(attribute, null, 2)}`);
|
74
|
+
finalAttribute = {
|
75
|
+
...attribute,
|
76
|
+
...foundAttribute,
|
77
|
+
};
|
78
|
+
action = "update";
|
79
|
+
} else {
|
80
|
+
// If no properties that can be updated have changed, return early
|
81
|
+
return;
|
82
|
+
}
|
83
|
+
} else if (
|
84
|
+
foundAttribute &&
|
85
|
+
!attributesSame(foundAttribute, attribute) &&
|
86
|
+
updateEnabled
|
87
|
+
) {
|
88
|
+
console.log(
|
89
|
+
`Deleting attribute with same key ${
|
90
|
+
attribute.key
|
91
|
+
} -- but different values -- ${JSON.stringify(
|
92
|
+
attribute,
|
93
|
+
null,
|
94
|
+
2
|
95
|
+
)} -- ${JSON.stringify(foundAttribute, null, 2)}`
|
96
|
+
);
|
97
|
+
await db.deleteAttribute(dbId, collection.$id, attribute.key);
|
98
|
+
// After deletion, you might want to create the attribute anew
|
99
|
+
finalAttribute = attribute;
|
100
|
+
action = "create";
|
101
|
+
} else if (!updateEnabled && foundAttribute) {
|
102
|
+
return;
|
103
|
+
}
|
104
|
+
|
105
|
+
// Relationship attribute logic with adjustments
|
106
|
+
let collectionFoundViaRelatedCollection: Models.Collection | undefined;
|
107
|
+
let relatedCollectionId: string | undefined;
|
108
|
+
if (finalAttribute.type === "relationship") {
|
109
|
+
if (nameToIdMapping.has(finalAttribute.relatedCollection)) {
|
110
|
+
relatedCollectionId = nameToIdMapping.get(
|
111
|
+
finalAttribute.relatedCollection
|
112
|
+
);
|
113
|
+
try {
|
114
|
+
collectionFoundViaRelatedCollection = await db.getCollection(
|
115
|
+
dbId,
|
116
|
+
relatedCollectionId!
|
117
|
+
);
|
118
|
+
} catch (e) {
|
119
|
+
console.log(
|
120
|
+
`Collection not found: ${finalAttribute.relatedCollection} when nameToIdMapping was set`
|
121
|
+
);
|
122
|
+
collectionFoundViaRelatedCollection = undefined;
|
123
|
+
}
|
124
|
+
} else {
|
125
|
+
const collectionsPulled = await db.listCollections(dbId, [
|
126
|
+
Query.equal("name", finalAttribute.relatedCollection),
|
127
|
+
]);
|
128
|
+
if (collectionsPulled.total > 0) {
|
129
|
+
collectionFoundViaRelatedCollection = collectionsPulled.collections[0];
|
130
|
+
relatedCollectionId = collectionFoundViaRelatedCollection.$id;
|
131
|
+
nameToIdMapping.set(
|
132
|
+
finalAttribute.relatedCollection,
|
133
|
+
relatedCollectionId
|
134
|
+
);
|
135
|
+
}
|
136
|
+
}
|
137
|
+
if (!(relatedCollectionId && collectionFoundViaRelatedCollection)) {
|
138
|
+
console.log(`Enqueueing operation for attribute: ${finalAttribute.key}`);
|
139
|
+
enqueueOperation({
|
140
|
+
type: "attribute",
|
141
|
+
collectionId: collection.$id,
|
142
|
+
collection: collection,
|
143
|
+
attribute,
|
144
|
+
dependencies: [finalAttribute.relatedCollection],
|
145
|
+
});
|
146
|
+
return;
|
147
|
+
}
|
148
|
+
}
|
149
|
+
finalAttribute = attributeSchema.parse(finalAttribute);
|
150
|
+
switch (finalAttribute.type) {
|
151
|
+
case "string":
|
152
|
+
if (action === "create") {
|
153
|
+
await tryAwaitWithRetry(
|
154
|
+
async () =>
|
155
|
+
await db.createStringAttribute(
|
156
|
+
dbId,
|
157
|
+
collection.$id,
|
158
|
+
finalAttribute.key,
|
159
|
+
finalAttribute.size,
|
160
|
+
finalAttribute.required || false,
|
161
|
+
(finalAttribute.xdefault as string) || undefined,
|
162
|
+
finalAttribute.array || false,
|
163
|
+
finalAttribute.encrypted
|
164
|
+
)
|
165
|
+
);
|
166
|
+
} else {
|
167
|
+
await tryAwaitWithRetry(
|
168
|
+
async () =>
|
169
|
+
await db.updateStringAttribute(
|
170
|
+
dbId,
|
171
|
+
collection.$id,
|
172
|
+
finalAttribute.key,
|
173
|
+
finalAttribute.required || false,
|
174
|
+
(finalAttribute.xdefault as string) || undefined
|
175
|
+
)
|
176
|
+
);
|
177
|
+
}
|
178
|
+
break;
|
179
|
+
case "integer":
|
180
|
+
if (action === "create") {
|
181
|
+
if (
|
182
|
+
finalAttribute.min &&
|
183
|
+
BigInt(finalAttribute.min) === BigInt(-9223372036854776000)
|
184
|
+
) {
|
185
|
+
delete finalAttribute.min;
|
186
|
+
}
|
187
|
+
if (
|
188
|
+
finalAttribute.max &&
|
189
|
+
BigInt(finalAttribute.max) === BigInt(9223372036854776000)
|
190
|
+
) {
|
191
|
+
delete finalAttribute.max;
|
192
|
+
}
|
193
|
+
await tryAwaitWithRetry(
|
194
|
+
async () =>
|
195
|
+
await db.createIntegerAttribute(
|
196
|
+
dbId,
|
197
|
+
collection.$id,
|
198
|
+
finalAttribute.key,
|
199
|
+
finalAttribute.required || false,
|
200
|
+
finalAttribute.min,
|
201
|
+
finalAttribute.max,
|
202
|
+
finalAttribute.xdefault || undefined,
|
203
|
+
finalAttribute.array
|
204
|
+
)
|
205
|
+
);
|
206
|
+
} else {
|
207
|
+
if (
|
208
|
+
finalAttribute.min &&
|
209
|
+
BigInt(finalAttribute.min) === BigInt(-9223372036854776000)
|
210
|
+
) {
|
211
|
+
delete finalAttribute.min;
|
212
|
+
}
|
213
|
+
if (
|
214
|
+
finalAttribute.max &&
|
215
|
+
BigInt(finalAttribute.max) === BigInt(9223372036854776000)
|
216
|
+
) {
|
217
|
+
delete finalAttribute.max;
|
218
|
+
}
|
219
|
+
await tryAwaitWithRetry(
|
220
|
+
async () =>
|
221
|
+
await db.updateIntegerAttribute(
|
222
|
+
dbId,
|
223
|
+
collection.$id,
|
224
|
+
finalAttribute.key,
|
225
|
+
finalAttribute.required || false,
|
226
|
+
finalAttribute.min || 0,
|
227
|
+
finalAttribute.max || 2147483647,
|
228
|
+
finalAttribute.xdefault || undefined
|
229
|
+
)
|
230
|
+
);
|
231
|
+
}
|
232
|
+
break;
|
233
|
+
case "float":
|
234
|
+
if (action === "create") {
|
235
|
+
await tryAwaitWithRetry(
|
236
|
+
async () =>
|
237
|
+
await db.createFloatAttribute(
|
238
|
+
dbId,
|
239
|
+
collection.$id,
|
240
|
+
finalAttribute.key,
|
241
|
+
finalAttribute.required || false,
|
242
|
+
finalAttribute.min,
|
243
|
+
finalAttribute.max,
|
244
|
+
finalAttribute.xdefault || undefined,
|
245
|
+
finalAttribute.array
|
246
|
+
)
|
247
|
+
);
|
248
|
+
} else {
|
249
|
+
await tryAwaitWithRetry(
|
250
|
+
async () =>
|
251
|
+
await db.updateFloatAttribute(
|
252
|
+
dbId,
|
253
|
+
collection.$id,
|
254
|
+
finalAttribute.key,
|
255
|
+
finalAttribute.required || false,
|
256
|
+
finalAttribute.min || 0,
|
257
|
+
finalAttribute.max || 2147483647,
|
258
|
+
finalAttribute.xdefault || undefined
|
259
|
+
)
|
260
|
+
);
|
261
|
+
}
|
262
|
+
break;
|
263
|
+
case "boolean":
|
264
|
+
if (action === "create") {
|
265
|
+
await tryAwaitWithRetry(
|
266
|
+
async () =>
|
267
|
+
await db.createBooleanAttribute(
|
268
|
+
dbId,
|
269
|
+
collection.$id,
|
270
|
+
finalAttribute.key,
|
271
|
+
finalAttribute.required || false,
|
272
|
+
finalAttribute.xdefault || undefined,
|
273
|
+
finalAttribute.array
|
274
|
+
)
|
275
|
+
);
|
276
|
+
} else {
|
277
|
+
await tryAwaitWithRetry(
|
278
|
+
async () =>
|
279
|
+
await db.updateBooleanAttribute(
|
280
|
+
dbId,
|
281
|
+
collection.$id,
|
282
|
+
finalAttribute.key,
|
283
|
+
finalAttribute.required || false,
|
284
|
+
finalAttribute.xdefault || null
|
285
|
+
)
|
286
|
+
);
|
287
|
+
}
|
288
|
+
break;
|
289
|
+
case "datetime":
|
290
|
+
if (action === "create") {
|
291
|
+
await tryAwaitWithRetry(
|
292
|
+
async () =>
|
293
|
+
await db.createDatetimeAttribute(
|
294
|
+
dbId,
|
295
|
+
collection.$id,
|
296
|
+
finalAttribute.key,
|
297
|
+
finalAttribute.required || false,
|
298
|
+
finalAttribute.xdefault || undefined,
|
299
|
+
finalAttribute.array
|
300
|
+
)
|
301
|
+
);
|
302
|
+
} else {
|
303
|
+
await tryAwaitWithRetry(
|
304
|
+
async () =>
|
305
|
+
await db.updateDatetimeAttribute(
|
306
|
+
dbId,
|
307
|
+
collection.$id,
|
308
|
+
finalAttribute.key,
|
309
|
+
finalAttribute.required || false,
|
310
|
+
finalAttribute.xdefault || undefined
|
311
|
+
)
|
312
|
+
);
|
313
|
+
}
|
314
|
+
break;
|
315
|
+
case "email":
|
316
|
+
if (action === "create") {
|
317
|
+
await tryAwaitWithRetry(
|
318
|
+
async () =>
|
319
|
+
await db.createEmailAttribute(
|
320
|
+
dbId,
|
321
|
+
collection.$id,
|
322
|
+
finalAttribute.key,
|
323
|
+
finalAttribute.required || false,
|
324
|
+
finalAttribute.xdefault || undefined,
|
325
|
+
finalAttribute.array
|
326
|
+
)
|
327
|
+
);
|
328
|
+
} else {
|
329
|
+
await tryAwaitWithRetry(
|
330
|
+
async () =>
|
331
|
+
await db.updateEmailAttribute(
|
332
|
+
dbId,
|
333
|
+
collection.$id,
|
334
|
+
finalAttribute.key,
|
335
|
+
finalAttribute.required || false,
|
336
|
+
finalAttribute.xdefault || undefined
|
337
|
+
)
|
338
|
+
);
|
339
|
+
}
|
340
|
+
break;
|
341
|
+
case "ip":
|
342
|
+
if (action === "create") {
|
343
|
+
await tryAwaitWithRetry(
|
344
|
+
async () =>
|
345
|
+
await db.createIpAttribute(
|
346
|
+
dbId,
|
347
|
+
collection.$id,
|
348
|
+
finalAttribute.key,
|
349
|
+
finalAttribute.required || false,
|
350
|
+
finalAttribute.xdefault || undefined,
|
351
|
+
finalAttribute.array
|
352
|
+
)
|
353
|
+
);
|
354
|
+
} else {
|
355
|
+
await tryAwaitWithRetry(
|
356
|
+
async () =>
|
357
|
+
await db.updateIpAttribute(
|
358
|
+
dbId,
|
359
|
+
collection.$id,
|
360
|
+
finalAttribute.key,
|
361
|
+
finalAttribute.required || false,
|
362
|
+
finalAttribute.xdefault || undefined
|
363
|
+
)
|
364
|
+
);
|
365
|
+
}
|
366
|
+
break;
|
367
|
+
case "url":
|
368
|
+
if (action === "create") {
|
369
|
+
await tryAwaitWithRetry(
|
370
|
+
async () =>
|
371
|
+
await db.createUrlAttribute(
|
372
|
+
dbId,
|
373
|
+
collection.$id,
|
374
|
+
finalAttribute.key,
|
375
|
+
finalAttribute.required || false,
|
376
|
+
finalAttribute.xdefault || undefined,
|
377
|
+
finalAttribute.array
|
378
|
+
)
|
379
|
+
);
|
380
|
+
} else {
|
381
|
+
await tryAwaitWithRetry(
|
382
|
+
async () =>
|
383
|
+
await db.updateUrlAttribute(
|
384
|
+
dbId,
|
385
|
+
collection.$id,
|
386
|
+
finalAttribute.key,
|
387
|
+
finalAttribute.required || false,
|
388
|
+
finalAttribute.xdefault || undefined
|
389
|
+
)
|
390
|
+
);
|
391
|
+
}
|
392
|
+
break;
|
393
|
+
case "enum":
|
394
|
+
if (action === "create") {
|
395
|
+
await tryAwaitWithRetry(
|
396
|
+
async () =>
|
397
|
+
await db.createEnumAttribute(
|
398
|
+
dbId,
|
399
|
+
collection.$id,
|
400
|
+
finalAttribute.key,
|
401
|
+
finalAttribute.elements,
|
402
|
+
finalAttribute.required || false,
|
403
|
+
finalAttribute.xdefault || undefined,
|
404
|
+
finalAttribute.array
|
405
|
+
)
|
406
|
+
);
|
407
|
+
} else {
|
408
|
+
await tryAwaitWithRetry(
|
409
|
+
async () =>
|
410
|
+
await db.updateEnumAttribute(
|
411
|
+
dbId,
|
412
|
+
collection.$id,
|
413
|
+
finalAttribute.key,
|
414
|
+
finalAttribute.elements,
|
415
|
+
finalAttribute.required || false,
|
416
|
+
finalAttribute.xdefault || undefined
|
417
|
+
)
|
418
|
+
);
|
419
|
+
}
|
420
|
+
break;
|
421
|
+
case "relationship":
|
422
|
+
if (action === "create") {
|
423
|
+
await tryAwaitWithRetry(
|
424
|
+
async () =>
|
425
|
+
await db.createRelationshipAttribute(
|
426
|
+
dbId,
|
427
|
+
collection.$id,
|
428
|
+
relatedCollectionId!,
|
429
|
+
finalAttribute.relationType,
|
430
|
+
finalAttribute.twoWay,
|
431
|
+
finalAttribute.key,
|
432
|
+
finalAttribute.twoWayKey,
|
433
|
+
finalAttribute.onDelete
|
434
|
+
)
|
435
|
+
);
|
436
|
+
} else {
|
437
|
+
await tryAwaitWithRetry(
|
438
|
+
async () =>
|
439
|
+
await db.updateRelationshipAttribute(
|
440
|
+
dbId,
|
441
|
+
collection.$id,
|
442
|
+
finalAttribute.key,
|
443
|
+
finalAttribute.onDelete
|
444
|
+
)
|
445
|
+
);
|
446
|
+
}
|
447
|
+
break;
|
448
|
+
default:
|
449
|
+
console.error("Invalid attribute type");
|
450
|
+
break;
|
451
|
+
}
|
452
|
+
};
|
453
|
+
|
454
|
+
export const createUpdateCollectionAttributes = async (
|
455
|
+
db: Databases,
|
456
|
+
dbId: string,
|
457
|
+
collection: Models.Collection,
|
458
|
+
attributes: Attribute[]
|
459
|
+
): Promise<void> => {
|
460
|
+
console.log(
|
461
|
+
`Creating/Updating attributes for collection: ${collection.name}`
|
462
|
+
);
|
463
|
+
|
464
|
+
const batchSize = 3; // Size of each batch
|
465
|
+
for (let i = 0; i < attributes.length; i += batchSize) {
|
466
|
+
// Slice the attributes array to get a batch of at most batchSize elements
|
467
|
+
const batch = attributes.slice(i, i + batchSize);
|
468
|
+
const attributePromises = batch.map((attribute) =>
|
469
|
+
createOrUpdateAttribute(db, dbId, collection, attribute)
|
470
|
+
);
|
471
|
+
|
472
|
+
// Await the completion of all promises in the current batch
|
473
|
+
const results = await Promise.allSettled(attributePromises);
|
474
|
+
results.forEach((result) => {
|
475
|
+
if (result.status === "rejected") {
|
476
|
+
console.error("An attribute promise was rejected:", result.reason);
|
477
|
+
}
|
478
|
+
});
|
479
|
+
}
|
480
|
+
console.log(
|
481
|
+
`Finished creating/updating attributes for collection: ${collection.name}`
|
482
|
+
);
|
483
|
+
};
|