appwrite-utils-cli 0.0.268 → 0.0.270
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 +21 -1
- package/dist/main.js +18 -1
- package/dist/migrations/appwriteToX.d.ts +11 -0
- package/dist/migrations/appwriteToX.js +8 -1
- package/dist/migrations/backup.d.ts +48 -2
- package/dist/migrations/collections.js +29 -18
- package/dist/migrations/dataLoader.d.ts +106 -12
- package/dist/migrations/openapi.d.ts +4 -0
- package/dist/migrations/openapi.js +44 -0
- package/dist/migrations/relationships.d.ts +1 -0
- package/dist/migrations/schema.d.ts +358 -49
- package/dist/migrations/schema.js +67 -10
- package/dist/migrations/schemaStrings.js +47 -41
- package/dist/schemas/authUser.d.ts +3 -3
- package/dist/utilsController.d.ts +4 -1
- package/dist/utilsController.js +18 -6
- package/package.json +2 -1
- package/src/main.ts +18 -1
- package/src/migrations/appwriteToX.ts +12 -1
- package/src/migrations/collections.ts +40 -38
- package/src/migrations/openapi.ts +69 -0
- package/src/migrations/schema.ts +89 -10
- package/src/migrations/schemaStrings.ts +49 -42
- package/src/utilsController.ts +22 -6
@@ -0,0 +1,69 @@
|
|
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 "./schema.js";
|
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
|
+
for (const collection of config.collections) {
|
21
|
+
// Transform and register each attribute schema
|
22
|
+
const attributeSchemas = collection.attributes.map((attribute) => {
|
23
|
+
return transformTypeToOpenApi(attributeSchema);
|
24
|
+
});
|
25
|
+
|
26
|
+
// Create and register the collection schema with descriptions
|
27
|
+
const updatedCollectionSchema = collectionSchema
|
28
|
+
.extend({
|
29
|
+
// @ts-ignore
|
30
|
+
attributes: z.array(z.union(attributeSchemas)),
|
31
|
+
})
|
32
|
+
.openapi(collection.description ?? "No description");
|
33
|
+
|
34
|
+
// Register the updated collection schema under the collection name
|
35
|
+
registry.register(collection.name, updatedCollectionSchema);
|
36
|
+
}
|
37
|
+
|
38
|
+
// Convert the registry to OpenAPI JSON
|
39
|
+
// @ts-ignore
|
40
|
+
const openApiSpec = registry.toOpenAPI();
|
41
|
+
|
42
|
+
// Output the OpenAPI spec to a file
|
43
|
+
writeFileSync(
|
44
|
+
"./appwrite/openapi/openapi.json",
|
45
|
+
JSON.stringify(openApiSpec, null, 2)
|
46
|
+
);
|
47
|
+
};
|
48
|
+
|
49
|
+
export function transformTypeToOpenApi<T extends z.ZodTypeAny>(
|
50
|
+
schema: T
|
51
|
+
): z.infer<T> {
|
52
|
+
return schema.transform((data) => {
|
53
|
+
let finalData = data;
|
54
|
+
if (data._def.attributes) {
|
55
|
+
finalData._def.attributes = data._def.attributes.map(
|
56
|
+
(attribute: typeof attributeSchema) => {
|
57
|
+
if (attribute.description) {
|
58
|
+
return attribute.openapi(attribute.description);
|
59
|
+
}
|
60
|
+
return attribute;
|
61
|
+
}
|
62
|
+
);
|
63
|
+
}
|
64
|
+
if (schema.description) {
|
65
|
+
finalData.openapi(schema.description);
|
66
|
+
}
|
67
|
+
return finalData;
|
68
|
+
});
|
69
|
+
}
|
package/src/migrations/schema.ts
CHANGED
@@ -30,6 +30,14 @@ const stringAttributeSchema = z.object({
|
|
30
30
|
.boolean()
|
31
31
|
.optional()
|
32
32
|
.describe("Whether the attribute is encrypted or not"),
|
33
|
+
format: z.string().nullish().describe("The format of the attribute"),
|
34
|
+
description: z
|
35
|
+
.string()
|
36
|
+
.or(z.record(z.string()))
|
37
|
+
.nullish()
|
38
|
+
.describe(
|
39
|
+
"The description of the attribute, also used for OpenApi Generation"
|
40
|
+
),
|
33
41
|
});
|
34
42
|
|
35
43
|
type StringAttribute = z.infer<typeof stringAttributeSchema>;
|
@@ -68,6 +76,13 @@ const integerAttributeSchema = z.object({
|
|
68
76
|
.int()
|
69
77
|
.nullish()
|
70
78
|
.describe("The default value of the attribute"),
|
79
|
+
description: z
|
80
|
+
.string()
|
81
|
+
.or(z.record(z.string()))
|
82
|
+
.nullish()
|
83
|
+
.describe(
|
84
|
+
"The description of the attribute, also used for OpenApi Generation"
|
85
|
+
),
|
71
86
|
});
|
72
87
|
|
73
88
|
type IntegerAttribute = z.infer<typeof integerAttributeSchema>;
|
@@ -94,6 +109,13 @@ const floatAttributeSchema = z.object({
|
|
94
109
|
min: z.number().optional().describe("The minimum value of the attribute"),
|
95
110
|
max: z.number().optional().describe("The maximum value of the attribute"),
|
96
111
|
xdefault: z.number().nullish().describe("The default value of the attribute"),
|
112
|
+
description: z
|
113
|
+
.string()
|
114
|
+
.or(z.record(z.string()))
|
115
|
+
.nullish()
|
116
|
+
.describe(
|
117
|
+
"The description of the attribute, also used for OpenApi Generation"
|
118
|
+
),
|
97
119
|
});
|
98
120
|
|
99
121
|
type FloatAttribute = z.infer<typeof floatAttributeSchema>;
|
@@ -121,6 +143,13 @@ const booleanAttributeSchema = z.object({
|
|
121
143
|
.boolean()
|
122
144
|
.nullish()
|
123
145
|
.describe("The default value of the attribute"),
|
146
|
+
description: z
|
147
|
+
.string()
|
148
|
+
.or(z.record(z.string()))
|
149
|
+
.nullish()
|
150
|
+
.describe(
|
151
|
+
"The description of the attribute, also used for OpenApi Generation"
|
152
|
+
),
|
124
153
|
});
|
125
154
|
|
126
155
|
type BooleanAttribute = z.infer<typeof booleanAttributeSchema>;
|
@@ -145,6 +174,13 @@ const datetimeAttributeSchema = z.object({
|
|
145
174
|
.default(false)
|
146
175
|
.describe("Whether the attribute is an array or not"),
|
147
176
|
xdefault: z.string().nullish().describe("The default value of the attribute"),
|
177
|
+
description: z
|
178
|
+
.string()
|
179
|
+
.or(z.record(z.string()))
|
180
|
+
.nullish()
|
181
|
+
.describe(
|
182
|
+
"The description of the attribute, also used for OpenApi Generation"
|
183
|
+
),
|
148
184
|
});
|
149
185
|
|
150
186
|
type DatetimeAttribute = z.infer<typeof datetimeAttributeSchema>;
|
@@ -169,6 +205,13 @@ const emailAttributeSchema = z.object({
|
|
169
205
|
.default(false)
|
170
206
|
.describe("Whether the attribute is an array or not"),
|
171
207
|
xdefault: z.string().nullish().describe("The default value of the attribute"),
|
208
|
+
description: z
|
209
|
+
.string()
|
210
|
+
.or(z.record(z.string()))
|
211
|
+
.nullish()
|
212
|
+
.describe(
|
213
|
+
"The description of the attribute, also used for OpenApi Generation"
|
214
|
+
),
|
172
215
|
});
|
173
216
|
|
174
217
|
type EmailAttribute = z.infer<typeof emailAttributeSchema>;
|
@@ -190,6 +233,13 @@ const ipAttributeSchema = z.object({
|
|
190
233
|
.default(false)
|
191
234
|
.describe("Whether the attribute is an array or not"),
|
192
235
|
xdefault: z.string().nullish().describe("The default value of the attribute"),
|
236
|
+
description: z
|
237
|
+
.string()
|
238
|
+
.or(z.record(z.string()))
|
239
|
+
.nullish()
|
240
|
+
.describe(
|
241
|
+
"The description of the attribute, also used for OpenApi Generation"
|
242
|
+
),
|
193
243
|
});
|
194
244
|
|
195
245
|
type IpAttribute = z.infer<typeof ipAttributeSchema>;
|
@@ -211,6 +261,13 @@ const urlAttributeSchema = z.object({
|
|
211
261
|
.default(false)
|
212
262
|
.describe("Whether the attribute is an array or not"),
|
213
263
|
xdefault: z.string().nullish().describe("The default value of the attribute"),
|
264
|
+
description: z
|
265
|
+
.string()
|
266
|
+
.or(z.record(z.string()))
|
267
|
+
.nullish()
|
268
|
+
.describe(
|
269
|
+
"The description of the attribute, also used for OpenApi Generation"
|
270
|
+
),
|
214
271
|
});
|
215
272
|
|
216
273
|
type UrlAttribute = z.infer<typeof urlAttributeSchema>;
|
@@ -236,6 +293,13 @@ const enumAttributeSchema = z.object({
|
|
236
293
|
.describe("The elements of the enum attribute")
|
237
294
|
.default([]),
|
238
295
|
xdefault: z.string().nullish().describe("The default value of the attribute"),
|
296
|
+
description: z
|
297
|
+
.string()
|
298
|
+
.or(z.record(z.string()))
|
299
|
+
.nullish()
|
300
|
+
.describe(
|
301
|
+
"The description of the attribute, also used for OpenApi Generation"
|
302
|
+
),
|
239
303
|
});
|
240
304
|
|
241
305
|
type EnumAttribute = z.infer<typeof enumAttributeSchema>;
|
@@ -291,6 +355,13 @@ const relationshipAttributeSchema = z.object({
|
|
291
355
|
.describe(
|
292
356
|
"Configuration for mapping and resolving relationships during data import"
|
293
357
|
),
|
358
|
+
description: z
|
359
|
+
.string()
|
360
|
+
.or(z.record(z.string()))
|
361
|
+
.nullish()
|
362
|
+
.describe(
|
363
|
+
"The description of the attribute, also used for OpenApi Generation"
|
364
|
+
),
|
294
365
|
});
|
295
366
|
|
296
367
|
export type RelationshipAttribute = z.infer<typeof relationshipAttributeSchema>;
|
@@ -313,16 +384,18 @@ export const createRelationshipAttributes = (
|
|
313
384
|
});
|
314
385
|
};
|
315
386
|
|
316
|
-
export const attributeSchema =
|
317
|
-
|
318
|
-
|
319
|
-
|
320
|
-
|
321
|
-
|
322
|
-
|
323
|
-
|
324
|
-
|
325
|
-
|
387
|
+
export const attributeSchema = z.discriminatedUnion("type", [
|
388
|
+
stringAttributeSchema,
|
389
|
+
integerAttributeSchema,
|
390
|
+
floatAttributeSchema,
|
391
|
+
booleanAttributeSchema,
|
392
|
+
datetimeAttributeSchema,
|
393
|
+
emailAttributeSchema,
|
394
|
+
ipAttributeSchema,
|
395
|
+
urlAttributeSchema,
|
396
|
+
enumAttributeSchema,
|
397
|
+
relationshipAttributeSchema,
|
398
|
+
]);
|
326
399
|
|
327
400
|
export const parseAttribute = (
|
328
401
|
attribute:
|
@@ -551,6 +624,12 @@ export const collectionSchema = z.object({
|
|
551
624
|
.boolean()
|
552
625
|
.default(false)
|
553
626
|
.describe("Whether document security is enabled or not"),
|
627
|
+
description: z
|
628
|
+
.string()
|
629
|
+
.optional()
|
630
|
+
.describe(
|
631
|
+
"The description of the collection, if any, used to generate OpenAPI documentation"
|
632
|
+
),
|
554
633
|
$createdAt: z.string(),
|
555
634
|
$updatedAt: z.string(),
|
556
635
|
$permissions: z
|
@@ -218,15 +218,22 @@ export class SchemaGenerator {
|
|
218
218
|
|
219
219
|
typeToZod = (attribute: Attribute) => {
|
220
220
|
let baseSchemaCode = "";
|
221
|
-
|
222
|
-
|
221
|
+
const finalAttribute: Attribute = (
|
222
|
+
attribute.type === "string" &&
|
223
|
+
attribute.format &&
|
224
|
+
attribute.format === "enum" &&
|
225
|
+
attribute.type === "string"
|
226
|
+
? { ...attribute, type: attribute.format }
|
227
|
+
: attribute
|
228
|
+
) as Attribute;
|
229
|
+
switch (finalAttribute.type) {
|
223
230
|
case "string":
|
224
231
|
baseSchemaCode = "z.string()";
|
225
|
-
if (
|
226
|
-
baseSchemaCode += `.max(${
|
232
|
+
if (finalAttribute.size) {
|
233
|
+
baseSchemaCode += `.max(${finalAttribute.size}, "Maximum length of ${finalAttribute.size} characters exceeded")`;
|
227
234
|
}
|
228
|
-
if (
|
229
|
-
baseSchemaCode += `.default("${
|
235
|
+
if (finalAttribute.xdefault !== undefined) {
|
236
|
+
baseSchemaCode += `.default("${finalAttribute.xdefault}")`;
|
230
237
|
}
|
231
238
|
if (!attribute.required && !attribute.array) {
|
232
239
|
baseSchemaCode += ".nullish()";
|
@@ -234,93 +241,93 @@ export class SchemaGenerator {
|
|
234
241
|
break;
|
235
242
|
case "integer":
|
236
243
|
baseSchemaCode = "z.number().int()";
|
237
|
-
if (
|
238
|
-
if (BigInt(
|
239
|
-
delete
|
244
|
+
if (finalAttribute.min !== undefined) {
|
245
|
+
if (BigInt(finalAttribute.min) === BigInt(-9223372036854776000)) {
|
246
|
+
delete finalAttribute.min;
|
240
247
|
} else {
|
241
|
-
baseSchemaCode += `.min(${
|
248
|
+
baseSchemaCode += `.min(${finalAttribute.min}, "Minimum value of ${finalAttribute.min} not met")`;
|
242
249
|
}
|
243
250
|
}
|
244
|
-
if (
|
245
|
-
if (BigInt(
|
246
|
-
delete
|
251
|
+
if (finalAttribute.max !== undefined) {
|
252
|
+
if (BigInt(finalAttribute.max) === BigInt(9223372036854776000)) {
|
253
|
+
delete finalAttribute.max;
|
247
254
|
} else {
|
248
|
-
baseSchemaCode += `.max(${
|
255
|
+
baseSchemaCode += `.max(${finalAttribute.max}, "Maximum value of ${finalAttribute.max} exceeded")`;
|
249
256
|
}
|
250
257
|
}
|
251
|
-
if (
|
252
|
-
baseSchemaCode += `.default(${
|
258
|
+
if (finalAttribute.xdefault !== undefined) {
|
259
|
+
baseSchemaCode += `.default(${finalAttribute.xdefault})`;
|
253
260
|
}
|
254
|
-
if (!
|
261
|
+
if (!finalAttribute.required && !finalAttribute.array) {
|
255
262
|
baseSchemaCode += ".nullish()";
|
256
263
|
}
|
257
264
|
break;
|
258
265
|
case "float":
|
259
266
|
baseSchemaCode = "z.number()";
|
260
|
-
if (
|
261
|
-
baseSchemaCode += `.min(${
|
267
|
+
if (finalAttribute.min !== undefined) {
|
268
|
+
baseSchemaCode += `.min(${finalAttribute.min}, "Minimum value of ${finalAttribute.min} not met")`;
|
262
269
|
}
|
263
|
-
if (
|
264
|
-
baseSchemaCode += `.max(${
|
270
|
+
if (finalAttribute.max !== undefined) {
|
271
|
+
baseSchemaCode += `.max(${finalAttribute.max}, "Maximum value of ${finalAttribute.max} exceeded")`;
|
265
272
|
}
|
266
|
-
if (
|
267
|
-
baseSchemaCode += `.default(${
|
273
|
+
if (finalAttribute.xdefault !== undefined) {
|
274
|
+
baseSchemaCode += `.default(${finalAttribute.xdefault})`;
|
268
275
|
}
|
269
|
-
if (!
|
276
|
+
if (!finalAttribute.required && !finalAttribute.array) {
|
270
277
|
baseSchemaCode += ".nullish()";
|
271
278
|
}
|
272
279
|
break;
|
273
280
|
case "boolean":
|
274
281
|
baseSchemaCode = "z.boolean()";
|
275
|
-
if (
|
276
|
-
baseSchemaCode += `.default(${
|
282
|
+
if (finalAttribute.xdefault !== undefined) {
|
283
|
+
baseSchemaCode += `.default(${finalAttribute.xdefault})`;
|
277
284
|
}
|
278
|
-
if (!
|
285
|
+
if (!finalAttribute.required && !finalAttribute.array) {
|
279
286
|
baseSchemaCode += ".nullish()";
|
280
287
|
}
|
281
288
|
break;
|
282
289
|
case "datetime":
|
283
290
|
baseSchemaCode = "z.date()";
|
284
|
-
if (
|
285
|
-
baseSchemaCode += `.default(new Date("${
|
291
|
+
if (finalAttribute.xdefault !== undefined) {
|
292
|
+
baseSchemaCode += `.default(new Date("${finalAttribute.xdefault}"))`;
|
286
293
|
}
|
287
|
-
if (!
|
294
|
+
if (!finalAttribute.required && !finalAttribute.array) {
|
288
295
|
baseSchemaCode += ".nullish()";
|
289
296
|
}
|
290
297
|
break;
|
291
298
|
case "email":
|
292
299
|
baseSchemaCode = "z.string().email()";
|
293
|
-
if (
|
294
|
-
baseSchemaCode += `.default("${
|
300
|
+
if (finalAttribute.xdefault !== undefined) {
|
301
|
+
baseSchemaCode += `.default("${finalAttribute.xdefault}")`;
|
295
302
|
}
|
296
|
-
if (!
|
303
|
+
if (!finalAttribute.required && !finalAttribute.array) {
|
297
304
|
baseSchemaCode += ".nullish()";
|
298
305
|
}
|
299
306
|
break;
|
300
307
|
case "ip":
|
301
308
|
baseSchemaCode = "z.string()"; // Add custom validation as needed
|
302
|
-
if (
|
303
|
-
baseSchemaCode += `.default("${
|
309
|
+
if (finalAttribute.xdefault !== undefined) {
|
310
|
+
baseSchemaCode += `.default("${finalAttribute.xdefault}")`;
|
304
311
|
}
|
305
|
-
if (!
|
312
|
+
if (!finalAttribute.required && !finalAttribute.array) {
|
306
313
|
baseSchemaCode += ".nullish()";
|
307
314
|
}
|
308
315
|
break;
|
309
316
|
case "url":
|
310
317
|
baseSchemaCode = "z.string().url()";
|
311
|
-
if (
|
312
|
-
baseSchemaCode += `.default("${
|
318
|
+
if (finalAttribute.xdefault !== undefined) {
|
319
|
+
baseSchemaCode += `.default("${finalAttribute.xdefault}")`;
|
313
320
|
}
|
314
|
-
if (!
|
321
|
+
if (!finalAttribute.required && !finalAttribute.array) {
|
315
322
|
baseSchemaCode += ".nullish()";
|
316
323
|
}
|
317
324
|
break;
|
318
325
|
case "enum":
|
319
|
-
baseSchemaCode = `z.enum([${
|
326
|
+
baseSchemaCode = `z.enum([${finalAttribute.elements
|
320
327
|
.map((element) => `"${element}"`)
|
321
328
|
.join(", ")}])`;
|
322
|
-
if (
|
323
|
-
baseSchemaCode += `.default("${
|
329
|
+
if (finalAttribute.xdefault !== undefined) {
|
330
|
+
baseSchemaCode += `.default("${finalAttribute.xdefault}")`;
|
324
331
|
}
|
325
332
|
if (!attribute.required && !attribute.array) {
|
326
333
|
baseSchemaCode += ".nullish()";
|
package/src/utilsController.ts
CHANGED
@@ -49,6 +49,9 @@ export interface SetupOptions {
|
|
49
49
|
importData: boolean;
|
50
50
|
checkDuplicates: boolean;
|
51
51
|
shouldWriteFile: boolean;
|
52
|
+
endpoint?: string;
|
53
|
+
project?: string;
|
54
|
+
key?: string;
|
52
55
|
}
|
53
56
|
|
54
57
|
type CollectionConfig = AppwriteConfig["collections"];
|
@@ -108,14 +111,27 @@ export class UtilsController {
|
|
108
111
|
// }
|
109
112
|
// }
|
110
113
|
|
111
|
-
async init() {
|
114
|
+
async init(setupOptions: SetupOptions) {
|
112
115
|
if (!this.config) {
|
113
116
|
console.log("Initializing appwrite client & loading config...");
|
114
117
|
this.config = await loadConfig(this.appwriteConfigPath);
|
115
|
-
this.appwriteServer = new Client()
|
116
|
-
|
117
|
-
|
118
|
-
|
118
|
+
this.appwriteServer = new Client();
|
119
|
+
if (setupOptions.endpoint) {
|
120
|
+
if (!setupOptions.project || !setupOptions.key) {
|
121
|
+
throw new Error(
|
122
|
+
"Project ID and API key required when setting endpoint"
|
123
|
+
);
|
124
|
+
}
|
125
|
+
this.appwriteServer
|
126
|
+
.setEndpoint(setupOptions.endpoint)
|
127
|
+
.setProject(setupOptions.project)
|
128
|
+
.setKey(setupOptions.key);
|
129
|
+
} else {
|
130
|
+
this.appwriteServer
|
131
|
+
.setEndpoint(this.config.appwriteEndpoint)
|
132
|
+
.setProject(this.config.appwriteProject)
|
133
|
+
.setKey(this.config.appwriteKey);
|
134
|
+
}
|
119
135
|
this.database = new Databases(this.appwriteServer);
|
120
136
|
this.storage = new Storage(this.appwriteServer);
|
121
137
|
this.config.appwriteClient = this.appwriteServer;
|
@@ -124,7 +140,7 @@ export class UtilsController {
|
|
124
140
|
}
|
125
141
|
|
126
142
|
async run(options: SetupOptions) {
|
127
|
-
await this.init(); // Ensure initialization is done
|
143
|
+
await this.init(options); // Ensure initialization is done
|
128
144
|
if (!this.database || !this.storage || !this.config) {
|
129
145
|
throw new Error("Database or storage not initialized");
|
130
146
|
}
|