appwrite-utils-cli 0.0.269 → 0.0.271

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.
@@ -29,6 +29,12 @@ const stringAttributeSchema = z.object({
29
29
  .boolean()
30
30
  .optional()
31
31
  .describe("Whether the attribute is encrypted or not"),
32
+ format: z.string().nullish().describe("The format of the attribute"),
33
+ description: z
34
+ .string()
35
+ .or(z.record(z.string()))
36
+ .nullish()
37
+ .describe("The description of the attribute, also used for OpenApi Generation"),
32
38
  });
33
39
  const integerAttributeSchema = z.object({
34
40
  key: z.string().describe("The key of the attribute"),
@@ -64,6 +70,11 @@ const integerAttributeSchema = z.object({
64
70
  .int()
65
71
  .nullish()
66
72
  .describe("The default value of the attribute"),
73
+ description: z
74
+ .string()
75
+ .or(z.record(z.string()))
76
+ .nullish()
77
+ .describe("The description of the attribute, also used for OpenApi Generation"),
67
78
  });
68
79
  const floatAttributeSchema = z.object({
69
80
  key: z.string().describe("The key of the attribute"),
@@ -87,6 +98,11 @@ const floatAttributeSchema = z.object({
87
98
  min: z.number().optional().describe("The minimum value of the attribute"),
88
99
  max: z.number().optional().describe("The maximum value of the attribute"),
89
100
  xdefault: z.number().nullish().describe("The default value of the attribute"),
101
+ description: z
102
+ .string()
103
+ .or(z.record(z.string()))
104
+ .nullish()
105
+ .describe("The description of the attribute, also used for OpenApi Generation"),
90
106
  });
91
107
  const booleanAttributeSchema = z.object({
92
108
  key: z.string().describe("The key of the attribute"),
@@ -111,6 +127,11 @@ const booleanAttributeSchema = z.object({
111
127
  .boolean()
112
128
  .nullish()
113
129
  .describe("The default value of the attribute"),
130
+ description: z
131
+ .string()
132
+ .or(z.record(z.string()))
133
+ .nullish()
134
+ .describe("The description of the attribute, also used for OpenApi Generation"),
114
135
  });
115
136
  const datetimeAttributeSchema = z.object({
116
137
  key: z.string().describe("The key of the attribute"),
@@ -132,6 +153,11 @@ const datetimeAttributeSchema = z.object({
132
153
  .default(false)
133
154
  .describe("Whether the attribute is an array or not"),
134
155
  xdefault: z.string().nullish().describe("The default value of the attribute"),
156
+ description: z
157
+ .string()
158
+ .or(z.record(z.string()))
159
+ .nullish()
160
+ .describe("The description of the attribute, also used for OpenApi Generation"),
135
161
  });
136
162
  const emailAttributeSchema = z.object({
137
163
  key: z.string().describe("The key of the attribute"),
@@ -153,6 +179,11 @@ const emailAttributeSchema = z.object({
153
179
  .default(false)
154
180
  .describe("Whether the attribute is an array or not"),
155
181
  xdefault: z.string().nullish().describe("The default value of the attribute"),
182
+ description: z
183
+ .string()
184
+ .or(z.record(z.string()))
185
+ .nullish()
186
+ .describe("The description of the attribute, also used for OpenApi Generation"),
156
187
  });
157
188
  const ipAttributeSchema = z.object({
158
189
  key: z.string().describe("The key of the attribute"),
@@ -171,6 +202,11 @@ const ipAttributeSchema = z.object({
171
202
  .default(false)
172
203
  .describe("Whether the attribute is an array or not"),
173
204
  xdefault: z.string().nullish().describe("The default value of the attribute"),
205
+ description: z
206
+ .string()
207
+ .or(z.record(z.string()))
208
+ .nullish()
209
+ .describe("The description of the attribute, also used for OpenApi Generation"),
174
210
  });
175
211
  const urlAttributeSchema = z.object({
176
212
  key: z.string().describe("The key of the attribute"),
@@ -189,6 +225,11 @@ const urlAttributeSchema = z.object({
189
225
  .default(false)
190
226
  .describe("Whether the attribute is an array or not"),
191
227
  xdefault: z.string().nullish().describe("The default value of the attribute"),
228
+ description: z
229
+ .string()
230
+ .or(z.record(z.string()))
231
+ .nullish()
232
+ .describe("The description of the attribute, also used for OpenApi Generation"),
192
233
  });
193
234
  const enumAttributeSchema = z.object({
194
235
  key: z.string().describe("The key of the attribute"),
@@ -211,6 +252,11 @@ const enumAttributeSchema = z.object({
211
252
  .describe("The elements of the enum attribute")
212
253
  .default([]),
213
254
  xdefault: z.string().nullish().describe("The default value of the attribute"),
255
+ description: z
256
+ .string()
257
+ .or(z.record(z.string()))
258
+ .nullish()
259
+ .describe("The description of the attribute, also used for OpenApi Generation"),
214
260
  });
215
261
  const relationshipAttributeSchema = z.object({
216
262
  key: z.string().describe("The key of the attribute"),
@@ -257,6 +303,11 @@ const relationshipAttributeSchema = z.object({
257
303
  })
258
304
  .optional()
259
305
  .describe("Configuration for mapping and resolving relationships during data import"),
306
+ description: z
307
+ .string()
308
+ .or(z.record(z.string()))
309
+ .nullish()
310
+ .describe("The description of the attribute, also used for OpenApi Generation"),
260
311
  });
261
312
  export const createRelationshipAttributes = (relatedCollection, relationType, twoWay, twoWayKey, onDelete, side) => {
262
313
  return relationshipAttributeSchema.parse({
@@ -268,16 +319,18 @@ export const createRelationshipAttributes = (relatedCollection, relationType, tw
268
319
  side,
269
320
  });
270
321
  };
271
- export const attributeSchema = stringAttributeSchema
272
- .or(integerAttributeSchema)
273
- .or(floatAttributeSchema)
274
- .or(booleanAttributeSchema)
275
- .or(datetimeAttributeSchema)
276
- .or(emailAttributeSchema)
277
- .or(ipAttributeSchema)
278
- .or(urlAttributeSchema)
279
- .or(enumAttributeSchema)
280
- .or(relationshipAttributeSchema);
322
+ export const attributeSchema = z.discriminatedUnion("type", [
323
+ stringAttributeSchema,
324
+ integerAttributeSchema,
325
+ floatAttributeSchema,
326
+ booleanAttributeSchema,
327
+ datetimeAttributeSchema,
328
+ emailAttributeSchema,
329
+ ipAttributeSchema,
330
+ urlAttributeSchema,
331
+ enumAttributeSchema,
332
+ relationshipAttributeSchema,
333
+ ]);
281
334
  export const parseAttribute = (attribute) => {
282
335
  if (attribute.type === "string") {
283
336
  return stringAttributeSchema.parse(attribute);
@@ -451,6 +504,10 @@ export const collectionSchema = z.object({
451
504
  .boolean()
452
505
  .default(false)
453
506
  .describe("Whether document security is enabled or not"),
507
+ description: z
508
+ .string()
509
+ .optional()
510
+ .describe("The description of the collection, if any, used to generate OpenAPI documentation"),
454
511
  $createdAt: z.string(),
455
512
  $updatedAt: z.string(),
456
513
  $permissions: z
@@ -166,14 +166,20 @@ export class SchemaGenerator {
166
166
  };
167
167
  typeToZod = (attribute) => {
168
168
  let baseSchemaCode = "";
169
- switch (attribute.type) {
169
+ const finalAttribute = (attribute.type === "string" &&
170
+ attribute.format &&
171
+ attribute.format === "enum" &&
172
+ attribute.type === "string"
173
+ ? { ...attribute, type: attribute.format }
174
+ : attribute);
175
+ switch (finalAttribute.type) {
170
176
  case "string":
171
177
  baseSchemaCode = "z.string()";
172
- if (attribute.size) {
173
- baseSchemaCode += `.max(${attribute.size}, "Maximum length of ${attribute.size} characters exceeded")`;
178
+ if (finalAttribute.size) {
179
+ baseSchemaCode += `.max(${finalAttribute.size}, "Maximum length of ${finalAttribute.size} characters exceeded")`;
174
180
  }
175
- if (attribute.xdefault !== undefined) {
176
- baseSchemaCode += `.default("${attribute.xdefault}")`;
181
+ if (finalAttribute.xdefault !== undefined) {
182
+ baseSchemaCode += `.default("${finalAttribute.xdefault}")`;
177
183
  }
178
184
  if (!attribute.required && !attribute.array) {
179
185
  baseSchemaCode += ".nullish()";
@@ -181,95 +187,95 @@ export class SchemaGenerator {
181
187
  break;
182
188
  case "integer":
183
189
  baseSchemaCode = "z.number().int()";
184
- if (attribute.min !== undefined) {
185
- if (BigInt(attribute.min) === BigInt(-9223372036854776000)) {
186
- delete attribute.min;
190
+ if (finalAttribute.min !== undefined) {
191
+ if (BigInt(finalAttribute.min) === BigInt(-9223372036854776000)) {
192
+ delete finalAttribute.min;
187
193
  }
188
194
  else {
189
- baseSchemaCode += `.min(${attribute.min}, "Minimum value of ${attribute.min} not met")`;
195
+ baseSchemaCode += `.min(${finalAttribute.min}, "Minimum value of ${finalAttribute.min} not met")`;
190
196
  }
191
197
  }
192
- if (attribute.max !== undefined) {
193
- if (BigInt(attribute.max) === BigInt(9223372036854776000)) {
194
- delete attribute.max;
198
+ if (finalAttribute.max !== undefined) {
199
+ if (BigInt(finalAttribute.max) === BigInt(9223372036854776000)) {
200
+ delete finalAttribute.max;
195
201
  }
196
202
  else {
197
- baseSchemaCode += `.max(${attribute.max}, "Maximum value of ${attribute.max} exceeded")`;
203
+ baseSchemaCode += `.max(${finalAttribute.max}, "Maximum value of ${finalAttribute.max} exceeded")`;
198
204
  }
199
205
  }
200
- if (attribute.xdefault !== undefined) {
201
- baseSchemaCode += `.default(${attribute.xdefault})`;
206
+ if (finalAttribute.xdefault !== undefined) {
207
+ baseSchemaCode += `.default(${finalAttribute.xdefault})`;
202
208
  }
203
- if (!attribute.required && !attribute.array) {
209
+ if (!finalAttribute.required && !finalAttribute.array) {
204
210
  baseSchemaCode += ".nullish()";
205
211
  }
206
212
  break;
207
213
  case "float":
208
214
  baseSchemaCode = "z.number()";
209
- if (attribute.min !== undefined) {
210
- baseSchemaCode += `.min(${attribute.min}, "Minimum value of ${attribute.min} not met")`;
215
+ if (finalAttribute.min !== undefined) {
216
+ baseSchemaCode += `.min(${finalAttribute.min}, "Minimum value of ${finalAttribute.min} not met")`;
211
217
  }
212
- if (attribute.max !== undefined) {
213
- baseSchemaCode += `.max(${attribute.max}, "Maximum value of ${attribute.max} exceeded")`;
218
+ if (finalAttribute.max !== undefined) {
219
+ baseSchemaCode += `.max(${finalAttribute.max}, "Maximum value of ${finalAttribute.max} exceeded")`;
214
220
  }
215
- if (attribute.xdefault !== undefined) {
216
- baseSchemaCode += `.default(${attribute.xdefault})`;
221
+ if (finalAttribute.xdefault !== undefined) {
222
+ baseSchemaCode += `.default(${finalAttribute.xdefault})`;
217
223
  }
218
- if (!attribute.required && !attribute.array) {
224
+ if (!finalAttribute.required && !finalAttribute.array) {
219
225
  baseSchemaCode += ".nullish()";
220
226
  }
221
227
  break;
222
228
  case "boolean":
223
229
  baseSchemaCode = "z.boolean()";
224
- if (attribute.xdefault !== undefined) {
225
- baseSchemaCode += `.default(${attribute.xdefault})`;
230
+ if (finalAttribute.xdefault !== undefined) {
231
+ baseSchemaCode += `.default(${finalAttribute.xdefault})`;
226
232
  }
227
- if (!attribute.required && !attribute.array) {
233
+ if (!finalAttribute.required && !finalAttribute.array) {
228
234
  baseSchemaCode += ".nullish()";
229
235
  }
230
236
  break;
231
237
  case "datetime":
232
238
  baseSchemaCode = "z.date()";
233
- if (attribute.xdefault !== undefined) {
234
- baseSchemaCode += `.default(new Date("${attribute.xdefault}"))`;
239
+ if (finalAttribute.xdefault !== undefined) {
240
+ baseSchemaCode += `.default(new Date("${finalAttribute.xdefault}"))`;
235
241
  }
236
- if (!attribute.required && !attribute.array) {
242
+ if (!finalAttribute.required && !finalAttribute.array) {
237
243
  baseSchemaCode += ".nullish()";
238
244
  }
239
245
  break;
240
246
  case "email":
241
247
  baseSchemaCode = "z.string().email()";
242
- if (attribute.xdefault !== undefined) {
243
- baseSchemaCode += `.default("${attribute.xdefault}")`;
248
+ if (finalAttribute.xdefault !== undefined) {
249
+ baseSchemaCode += `.default("${finalAttribute.xdefault}")`;
244
250
  }
245
- if (!attribute.required && !attribute.array) {
251
+ if (!finalAttribute.required && !finalAttribute.array) {
246
252
  baseSchemaCode += ".nullish()";
247
253
  }
248
254
  break;
249
255
  case "ip":
250
256
  baseSchemaCode = "z.string()"; // Add custom validation as needed
251
- if (attribute.xdefault !== undefined) {
252
- baseSchemaCode += `.default("${attribute.xdefault}")`;
257
+ if (finalAttribute.xdefault !== undefined) {
258
+ baseSchemaCode += `.default("${finalAttribute.xdefault}")`;
253
259
  }
254
- if (!attribute.required && !attribute.array) {
260
+ if (!finalAttribute.required && !finalAttribute.array) {
255
261
  baseSchemaCode += ".nullish()";
256
262
  }
257
263
  break;
258
264
  case "url":
259
265
  baseSchemaCode = "z.string().url()";
260
- if (attribute.xdefault !== undefined) {
261
- baseSchemaCode += `.default("${attribute.xdefault}")`;
266
+ if (finalAttribute.xdefault !== undefined) {
267
+ baseSchemaCode += `.default("${finalAttribute.xdefault}")`;
262
268
  }
263
- if (!attribute.required && !attribute.array) {
269
+ if (!finalAttribute.required && !finalAttribute.array) {
264
270
  baseSchemaCode += ".nullish()";
265
271
  }
266
272
  break;
267
273
  case "enum":
268
- baseSchemaCode = `z.enum([${attribute.elements
274
+ baseSchemaCode = `z.enum([${finalAttribute.elements
269
275
  .map((element) => `"${element}"`)
270
276
  .join(", ")}])`;
271
- if (attribute.xdefault !== undefined) {
272
- baseSchemaCode += `.default("${attribute.xdefault}")`;
277
+ if (finalAttribute.xdefault !== undefined) {
278
+ baseSchemaCode += `.default("${finalAttribute.xdefault}")`;
273
279
  }
274
280
  if (!attribute.required && !attribute.array) {
275
281
  baseSchemaCode += ".nullish()";
@@ -29,8 +29,8 @@ export declare const AuthUserSchema: z.ZodObject<{
29
29
  }>;
30
30
  export type AuthUser = z.infer<typeof AuthUserSchema>;
31
31
  export declare const AuthUserCreateSchema: z.ZodObject<{
32
- email: z.ZodOptional<z.ZodNullable<z.ZodString>>;
33
32
  name: z.ZodOptional<z.ZodNullable<z.ZodString>>;
33
+ email: z.ZodOptional<z.ZodNullable<z.ZodString>>;
34
34
  $createdAt: z.ZodOptional<z.ZodString>;
35
35
  $updatedAt: z.ZodOptional<z.ZodString>;
36
36
  phone: z.ZodOptional<z.ZodNullable<z.ZodString>>;
@@ -41,16 +41,16 @@ export declare const AuthUserCreateSchema: z.ZodObject<{
41
41
  }, "strip", z.ZodTypeAny, {
42
42
  prefs: Record<string, string>;
43
43
  labels: string[];
44
- email?: string | null | undefined;
45
44
  name?: string | null | undefined;
45
+ email?: string | null | undefined;
46
46
  $createdAt?: string | undefined;
47
47
  $updatedAt?: string | undefined;
48
48
  phone?: string | null | undefined;
49
49
  userId?: string | undefined;
50
50
  password?: string | undefined;
51
51
  }, {
52
- email?: string | null | undefined;
53
52
  name?: string | null | undefined;
53
+ email?: string | null | undefined;
54
54
  $createdAt?: string | undefined;
55
55
  $updatedAt?: string | undefined;
56
56
  phone?: string | null | undefined;
@@ -15,6 +15,9 @@ export interface SetupOptions {
15
15
  importData: boolean;
16
16
  checkDuplicates: boolean;
17
17
  shouldWriteFile: boolean;
18
+ endpoint?: string;
19
+ project?: string;
20
+ key?: string;
18
21
  }
19
22
  export declare class UtilsController {
20
23
  private appwriteFolderPath;
@@ -27,6 +30,6 @@ export declare class UtilsController {
27
30
  validityRuleDefinitions: ValidationRules;
28
31
  afterImportActionsDefinitions: AfterImportActions;
29
32
  constructor();
30
- init(): Promise<void>;
33
+ init(setupOptions: SetupOptions): Promise<void>;
31
34
  run(options: SetupOptions): Promise<void>;
32
35
  }
@@ -66,14 +66,26 @@ export class UtilsController {
66
66
  // console.error("Failed to load custom definitions:", error);
67
67
  // }
68
68
  // }
69
- async init() {
69
+ async init(setupOptions) {
70
70
  if (!this.config) {
71
71
  console.log("Initializing appwrite client & loading config...");
72
72
  this.config = await loadConfig(this.appwriteConfigPath);
73
- this.appwriteServer = new Client()
74
- .setEndpoint(this.config.appwriteEndpoint)
75
- .setProject(this.config.appwriteProject)
76
- .setKey(this.config.appwriteKey);
73
+ this.appwriteServer = new Client();
74
+ if (setupOptions.endpoint) {
75
+ if (!setupOptions.project || !setupOptions.key) {
76
+ throw new Error("Project ID and API key required when setting endpoint");
77
+ }
78
+ this.appwriteServer
79
+ .setEndpoint(setupOptions.endpoint)
80
+ .setProject(setupOptions.project)
81
+ .setKey(setupOptions.key);
82
+ }
83
+ else {
84
+ this.appwriteServer
85
+ .setEndpoint(this.config.appwriteEndpoint)
86
+ .setProject(this.config.appwriteProject)
87
+ .setKey(this.config.appwriteKey);
88
+ }
77
89
  this.database = new Databases(this.appwriteServer);
78
90
  this.storage = new Storage(this.appwriteServer);
79
91
  this.config.appwriteClient = this.appwriteServer;
@@ -81,7 +93,7 @@ export class UtilsController {
81
93
  }
82
94
  }
83
95
  async run(options) {
84
- await this.init(); // Ensure initialization is done
96
+ await this.init(options); // Ensure initialization is done
85
97
  if (!this.database || !this.storage || !this.config) {
86
98
  throw new Error("Database or storage not initialized");
87
99
  }
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "appwrite-utils-cli",
3
3
  "description": "Appwrite Utility Functions to help with database management, data conversion, data import, migrations, and much more. Meant to be used as a CLI tool, I do not recommend installing this in frontend environments.",
4
- "version": "0.0.269",
4
+ "version": "0.0.271",
5
5
  "main": "src/main.ts",
6
6
  "type": "module",
7
7
  "repository": {
@@ -30,6 +30,7 @@
30
30
  "postinstall": "echo 'This package is intended for CLI use only and should not be added as a dependency in other projects.'"
31
31
  },
32
32
  "dependencies": {
33
+ "@asteasolutions/zod-to-openapi": "^7.0.0",
33
34
  "js-yaml": "^4.1.0",
34
35
  "lodash": "^4.17.21",
35
36
  "luxon": "^3.4.4",
package/src/main.ts CHANGED
@@ -5,7 +5,6 @@ const args = process.argv.slice(2);
5
5
 
6
6
  async function main() {
7
7
  const controller = new UtilsController();
8
- await controller.init();
9
8
 
10
9
  let sync = false;
11
10
  let runProd = false;
@@ -18,6 +17,9 @@ async function main() {
18
17
  let importData = false;
19
18
  let wipeDocuments = false;
20
19
  let shouldWriteFile = false;
20
+ let endpoint: string | undefined;
21
+ let project: string | undefined;
22
+ let key: string | undefined;
21
23
  if (args.includes("--sync")) {
22
24
  sync = true;
23
25
  }
@@ -51,6 +53,15 @@ async function main() {
51
53
  if (args.includes("--write-data") || args.includes("--writeData")) {
52
54
  shouldWriteFile = true;
53
55
  }
56
+ if (args.includes("--endpoint")) {
57
+ endpoint = args[args.indexOf("--endpoint") + 1];
58
+ }
59
+ if (args.includes("--project")) {
60
+ project = args[args.indexOf("--project") + 1];
61
+ }
62
+ if (args.includes("--key")) {
63
+ key = args[args.indexOf("--key") + 1];
64
+ }
54
65
  if (args.includes("--init")) {
55
66
  await controller.run({
56
67
  sync: sync,
@@ -66,6 +77,9 @@ async function main() {
66
77
  importData: false,
67
78
  checkDuplicates: false,
68
79
  shouldWriteFile: shouldWriteFile,
80
+ endpoint: endpoint,
81
+ project: project,
82
+ key: key,
69
83
  });
70
84
  } else {
71
85
  await controller.run({
@@ -82,6 +96,9 @@ async function main() {
82
96
  importData: importData,
83
97
  checkDuplicates: false,
84
98
  shouldWriteFile: shouldWriteFile,
99
+ endpoint: endpoint,
100
+ project: project,
101
+ key: key,
85
102
  });
86
103
  }
87
104
  }
@@ -81,7 +81,18 @@ export class AppwriteToX {
81
81
  collection.$permissions
82
82
  );
83
83
  const collAttributes = attributesSchema
84
- .parse(collection.attributes)
84
+ .parse(
85
+ collection.attributes.map((attr: any) => {
86
+ if (
87
+ attr.type === "string" &&
88
+ attr.format &&
89
+ attr.format.length > 0
90
+ ) {
91
+ return { ...attr, type: attr.format };
92
+ }
93
+ return attr;
94
+ })
95
+ )
85
96
  .filter((attribute) =>
86
97
  attribute.type === "relationship"
87
98
  ? attribute.side !== "child"
@@ -1,6 +1,7 @@
1
1
  import { Query, type Databases, type Models } from "node-appwrite";
2
2
  import type { Attribute } from "./schema.js";
3
3
  import { nameToIdMapping, enqueueOperation } from "./queue.js";
4
+ import _ from "lodash";
4
5
 
5
6
  export const createOrUpdateAttribute = async (
6
7
  db: Databases,
@@ -16,11 +17,14 @@ export const createOrUpdateAttribute = async (
16
17
  foundAttribute = undefined;
17
18
  }
18
19
  let numSameAttributes = 0;
19
- if (foundAttribute && foundAttribute.key === attribute.key) {
20
+ if (foundAttribute && _.isEqual(foundAttribute, attribute)) {
20
21
  numSameAttributes++;
21
22
  return;
22
- } else if (foundAttribute) {
23
- action = "update";
23
+ } else if (foundAttribute && !_.isEqual(foundAttribute, attribute)) {
24
+ console.log(
25
+ "Deleting attribute with same key but different values, assuming update..."
26
+ );
27
+ await db.deleteAttribute(dbId, collection.$id, attribute.key);
24
28
  }
25
29
 
26
30
  // Relationship attribute logic with adjustments
@@ -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
+ }