From 5bbb5a1bad35d0f29098d9808d9aab0ce2ea25da Mon Sep 17 00:00:00 2001 From: loks0n <22452787+loks0n@users.noreply.github.com> Date: Wed, 6 Mar 2024 10:46:29 +0100 Subject: [PATCH 01/44] feat: initial draft types generator --- src/SDK/Language/CLI.php | 25 +++++ templates/cli/lib/commands/types.js.twig | 76 +++++++++++++++ .../lib/type-generation/attributes.js.twig | 23 +++++ .../languages/language.js.twig | 93 +++++++++++++++++++ .../lib/type-generation/languages/php.js.twig | 58 ++++++++++++ .../languages/typescript.js.twig | 56 +++++++++++ 6 files changed, 331 insertions(+) create mode 100644 templates/cli/lib/commands/types.js.twig create mode 100644 templates/cli/lib/type-generation/attributes.js.twig create mode 100644 templates/cli/lib/type-generation/languages/language.js.twig create mode 100644 templates/cli/lib/type-generation/languages/php.js.twig create mode 100644 templates/cli/lib/type-generation/languages/typescript.js.twig diff --git a/src/SDK/Language/CLI.php b/src/SDK/Language/CLI.php index 3bd0d77a4..d28e645ed 100644 --- a/src/SDK/Language/CLI.php +++ b/src/SDK/Language/CLI.php @@ -122,6 +122,26 @@ public function getFiles(): array 'destination' => 'lib/sdks.js', 'template' => 'cli/lib/sdks.js.twig', ], + [ + 'scope' => 'default', + 'destination' => 'lib/type-generation/attributes.js', + 'template' => 'cli/lib/type-generation/attributes.js.twig', + ], + [ + 'scope' => 'default', + 'destination' => 'lib/type-generation/languages/langauge.js', + 'template' => 'cli/lib/type-generation/languages/langauge.js.twig', + ], + [ + 'scope' => 'default', + 'destination' => 'lib/type-generation/languages/php.js', + 'template' => 'cli/lib/type-generation/languages/php.js.twig', + ], + [ + 'scope' => 'default', + 'destination' => 'lib/type-generation/languages/typescript.js', + 'template' => 'cli/lib/type-generation/languages/typescript.js.twig', + ], [ 'scope' => 'default', 'destination' => 'lib/questions.js', @@ -176,6 +196,11 @@ public function getFiles(): array 'scope' => 'default', 'destination' => 'lib/commands/generic.js', 'template' => 'cli/lib/commands/generic.js.twig', + ], + [ + 'scope' => 'default', + 'destination' => 'lib/commands/types.js', + 'template' => 'cli/lib/commands/types.js.twig', ] ]; } diff --git a/templates/cli/lib/commands/types.js.twig b/templates/cli/lib/commands/types.js.twig new file mode 100644 index 000000000..d7ba8bfa3 --- /dev/null +++ b/templates/cli/lib/commands/types.js.twig @@ -0,0 +1,76 @@ +const { PHP } = require("../type-generation/languages/php"); +const { TypeScript } = require("../type-generation/languages/typescript"); + +const fs = require("fs"); +const path = require("path"); +const { Command, Option } = require("commander"); +const { localConfig } = require("../config"); +const { success, log, actionRunner } = require("../parser"); +const ejs = require("ejs"); + +const typesLanguageOption = new Option( + "-l, --language ", + "The language of the types" +) + .choices(["ts", "php"]) + .default("auto"); + +const typesOutputOption = new Option( + "-o, --output ", + "The output path of the types" +).default("auto"); + +const languages = { + ts: new TypeScript(), + php: new PHP(), +}; + +function detectLanguage() { + if (fs.existsSync(path.join(process.cwd(), "package.json"))) { + return "ts"; + } + if (fs.existsSync(path.join(process.cwd(), "composer.json"))) { + return "php"; + } + throw new Error("Could not detect language, please specify with -l"); +} + +const typesCommand = actionRunner(async (options) => { + let { language, output } = options; + + if (language === "auto") { + language = detectLanguage(); + log(`Detected language: ${language}`); + } + + const generator = languages[language]; + + if (output === "auto") { + output = generator.getOutputPath(); + log(`Using default output path: ${output}`); + } + + const collections = localConfig.getCollections(); + + const templateContent = generator.getTemplate(); + const templater = ejs.compile(templateContent); + const typesContent = templater({ collections }); + + const outputDir = path.dirname(output); + if (!fs.existsSync(outputDir)) { + fs.mkdirSync(outputDir, { recursive: true }); + } + + fs.writeFileSync(output, typesContent); + log(`Types written to ${output}`); + + success(); +}); + +const types = new Command("types") + .description("Generate types for your Appwrite project") + .addOption(typesLanguageOption) + .addOption(typesOutputOption) + .action(actionRunner(typesCommand)); + +export { types }; diff --git a/templates/cli/lib/type-generation/attributes.js.twig b/templates/cli/lib/type-generation/attributes.js.twig new file mode 100644 index 000000000..24409725c --- /dev/null +++ b/templates/cli/lib/type-generation/attributes.js.twig @@ -0,0 +1,23 @@ +export const AttributeType = { + STRING: "string", + INTEGER: "integer", + FLOAT: "float", + BOOLEAN: "boolean", + DATETIME: "datetime", + EMAIL: "email", + IP: "ip", + URL: "url", + ENUM: "enum", + RELATIONSHIP: "relationship", +}; + +/** + * @typedef {Object} Attribute + * @property {string} key - The unique identifier of the attribute. + * @property {"string"|"integer"|"float"|"boolean"|"datetime"|"email"|"ip"|"url"|"enum"|"relationship"} type - The type of the attribute. + * @property {string} status - The status of the attribute. + * @property {boolean} required - The required status of the attribute. + * @property {boolean} array - The array status of the attribute. + * @property {number} size - The size of the attribute. + * @property {string} default - The default value of the attribute. + */ \ No newline at end of file diff --git a/templates/cli/lib/type-generation/languages/language.js.twig b/templates/cli/lib/type-generation/languages/language.js.twig new file mode 100644 index 000000000..1111710ed --- /dev/null +++ b/templates/cli/lib/type-generation/languages/language.js.twig @@ -0,0 +1,93 @@ +/** @typedef {import('../attributes').Attribute} Attribute */ + +export class Language { + constructor() { + if (new.target === Language) { + throw new TypeError("Abstract classes can't be instantiated."); + } + } + + /** + * @param {string} string + * @returns {string} + */ + static toKebabCase(string) { + return string + .split("") + .map((letter) => { + if (/[A-Z]/.test(letter)) { + return ` ${letter.toLowerCase()}`; + } + return letter; + }) + .join("") + .trim() + .replace(/[_\s]+/g, "-"); + } + + /** + * @param {string} string + * @returns {string} + */ + static toSnakeCase(string) { + return this.toKebabCase(string).replace(/-/g, "_"); + } + + /** + * @param {string} string + * @returns {string} + */ + static toUpperSnakeCase(string) { + return this.toSnakeCase(string).toUpperCase(); + } + + /** + * @param {string} string + * @returns {string} + */ + static toPascalCase(string) { + return this.toKebabCase(string) + .split("-") + .map((word) => word.charAt(0).toUpperCase() + word.slice(1)) + .join(""); + } + + /** + * @param {string} string + * @returns {string} + */ + static toCamelCase(string) { + return this.toPascalCase(string).charAt(0).toLowerCase() + string.slice(1); + } + + /** + * Get the type literal of the given attribute. + * + * @abstract + * @param {Attribute} attribute + * @return {string} + */ + static getType(attribute) { + throw new TypeError("Stub."); + } + + /** + * Get the EJS template used to generate the types for this language. + * + * @abstract + * @returns {string} + */ + static getTemplate() { + throw new TypeError("Stub."); + } + + /** + * Get the file extension used by files of this language. + * + * @abstract + * @returns {string} + */ + static getOutputPath() { + throw new TypeError("Stub."); + } +} diff --git a/templates/cli/lib/type-generation/languages/php.js.twig b/templates/cli/lib/type-generation/languages/php.js.twig new file mode 100644 index 000000000..b7954bfc9 --- /dev/null +++ b/templates/cli/lib/type-generation/languages/php.js.twig @@ -0,0 +1,58 @@ +/** @typedef {import('../attributes').Attribute} Attribute */ +const { AttributeType } = require('../attributes'); +const { Language } = require("./language"); + +export class PHP extends Language { + /** + * @param {Attribute} attribute + */ + static getType(attribute) { + let type = "" + switch (attribute.type) { + case AttributeType.STRING: + case AttributeType.EMAIL: + case AttributeType.DATETIME: + case AttributeType.ENUM: + type = "string"; + break; + case AttributeType.INTEGER: + type = "int"; + break; + case AttributeType.FLOAT: + type = "float"; + break; + case AttributeType.BOOLEAN: + type = "bool"; + break; + case AttributeType.RELATIONSHIP: + type = "mixed"; + default: + throw new Error(`Unknown attribute type: ${attribute.type}`); + } + if (attribute.array) { + type += "[]"; + } + if (attribute.required) { + type += "|null"; + } + return type; + } + + static getTemplate() { + return ` +class <%= toPascalCase(collection.key) %> extends Document { + <% for (const attribute of collection.attributes ){ %> + public <%= toCamelCase(attribute.key) %>: <%= getType(attribute) %>; + <% } %> +} + +<% } %>`; + } + + static getOutputPath() { + return "./generated/Appwrite.php"; + } +} diff --git a/templates/cli/lib/type-generation/languages/typescript.js.twig b/templates/cli/lib/type-generation/languages/typescript.js.twig new file mode 100644 index 000000000..df63fd688 --- /dev/null +++ b/templates/cli/lib/type-generation/languages/typescript.js.twig @@ -0,0 +1,56 @@ +/** @typedef {import('../attributes').Attribute} Attribute */ +const { AttributeType } = require('../attributes'); +const { Language } = require("./language"); + +export class TypeScript extends Language { + /** + * @param {Attribute} attribute + */ + static getType(attribute) { + let type = "" + switch (attribute.type) { + case AttributeType.STRING: + case AttributeType.EMAIL: + case AttributeType.DATETIME: + case AttributeType.ENUM: + type = "string"; + break; + case AttributeType.INTEGER: + type = "number"; + break; + case AttributeType.FLOAT: + type = "number"; + break; + case AttributeType.BOOLEAN: + type = "boolean"; + break; + case AttributeType.RELATIONSHIP: + type = "any"; + default: + throw new Error(`Unknown attribute type: ${attribute.type}`); + } + if (attribute.array) { + type += "[]"; + } + if (attribute.required) { + type += "|null"; + } + return type; + } + + static getTemplate() { + return ` +<% for (const collection of collections.attributes ){ %> +export type <%= toPascalCase(collection.key) %> = { + <% for (const attribute of collection.attributes) { %> + <%= toCamelCase(attribute.key) %>: <%= getType(attribute) %>; + <% } %> +} + +<% } %>`; + } + + static getOutputPath() { + return "./generated/appwrite.d.ts"; + } +} From 3a7250448948702cb9e53ab67718f463a81b69a8 Mon Sep 17 00:00:00 2001 From: loks0n <22452787+loks0n@users.noreply.github.com> Date: Wed, 6 Mar 2024 12:39:29 +0100 Subject: [PATCH 02/44] feat: typo in template name --- src/SDK/Language/CLI.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/SDK/Language/CLI.php b/src/SDK/Language/CLI.php index d28e645ed..9d3e063e8 100644 --- a/src/SDK/Language/CLI.php +++ b/src/SDK/Language/CLI.php @@ -129,8 +129,8 @@ public function getFiles(): array ], [ 'scope' => 'default', - 'destination' => 'lib/type-generation/languages/langauge.js', - 'template' => 'cli/lib/type-generation/languages/langauge.js.twig', + 'destination' => 'lib/type-generation/languages/language.js', + 'template' => 'cli/lib/type-generation/languages/language.js.twig', ], [ 'scope' => 'default', From 3d501a5aeb4bfecd7451c2da7e78fad876b471ed Mon Sep 17 00:00:00 2001 From: loks0n <22452787+loks0n@users.noreply.github.com> Date: Wed, 6 Mar 2024 14:35:49 +0100 Subject: [PATCH 03/44] fix: use cjs --- templates/cli/index.js.twig | 2 ++ templates/cli/lib/commands/types.js.twig | 2 +- templates/cli/lib/type-generation/attributes.js.twig | 8 ++++++-- .../cli/lib/type-generation/languages/language.js.twig | 4 +++- templates/cli/lib/type-generation/languages/php.js.twig | 4 +++- .../cli/lib/type-generation/languages/typescript.js.twig | 4 +++- 6 files changed, 18 insertions(+), 6 deletions(-) diff --git a/templates/cli/index.js.twig b/templates/cli/index.js.twig index d4415db72..e8a466772 100644 --- a/templates/cli/index.js.twig +++ b/templates/cli/index.js.twig @@ -13,6 +13,7 @@ const { client } = require("./lib/commands/generic"); {% if sdk.test != "true" %} const { login, logout } = require("./lib/commands/generic"); const { init } = require("./lib/commands/init"); +const { types } = require("./lib/commands/types"); const { deploy } = require("./lib/commands/deploy"); {% endif %} {% for service in spec.services %} @@ -38,6 +39,7 @@ program {% if sdk.test != "true" %} .addCommand(login) .addCommand(init) + .addCommand(types) .addCommand(deploy) .addCommand(logout) {% endif %} diff --git a/templates/cli/lib/commands/types.js.twig b/templates/cli/lib/commands/types.js.twig index d7ba8bfa3..177120b24 100644 --- a/templates/cli/lib/commands/types.js.twig +++ b/templates/cli/lib/commands/types.js.twig @@ -73,4 +73,4 @@ const types = new Command("types") .addOption(typesOutputOption) .action(actionRunner(typesCommand)); -export { types }; +module.exports = { types }; diff --git a/templates/cli/lib/type-generation/attributes.js.twig b/templates/cli/lib/type-generation/attributes.js.twig index 24409725c..e357b0d40 100644 --- a/templates/cli/lib/type-generation/attributes.js.twig +++ b/templates/cli/lib/type-generation/attributes.js.twig @@ -1,4 +1,4 @@ -export const AttributeType = { +const AttributeType = { STRING: "string", INTEGER: "integer", FLOAT: "float", @@ -20,4 +20,8 @@ export const AttributeType = { * @property {boolean} array - The array status of the attribute. * @property {number} size - The size of the attribute. * @property {string} default - The default value of the attribute. - */ \ No newline at end of file + */ + + module.exports = { + AttributeType, + }; \ No newline at end of file diff --git a/templates/cli/lib/type-generation/languages/language.js.twig b/templates/cli/lib/type-generation/languages/language.js.twig index 1111710ed..53e7829a8 100644 --- a/templates/cli/lib/type-generation/languages/language.js.twig +++ b/templates/cli/lib/type-generation/languages/language.js.twig @@ -1,6 +1,6 @@ /** @typedef {import('../attributes').Attribute} Attribute */ -export class Language { +class Language { constructor() { if (new.target === Language) { throw new TypeError("Abstract classes can't be instantiated."); @@ -91,3 +91,5 @@ export class Language { throw new TypeError("Stub."); } } + +module.exports = { Language }; \ No newline at end of file diff --git a/templates/cli/lib/type-generation/languages/php.js.twig b/templates/cli/lib/type-generation/languages/php.js.twig index b7954bfc9..31f9c22d7 100644 --- a/templates/cli/lib/type-generation/languages/php.js.twig +++ b/templates/cli/lib/type-generation/languages/php.js.twig @@ -2,7 +2,7 @@ const { AttributeType } = require('../attributes'); const { Language } = require("./language"); -export class PHP extends Language { +class PHP extends Language { /** * @param {Attribute} attribute */ @@ -56,3 +56,5 @@ class <%= toPascalCase(collection.key) %> extends Document { return "./generated/Appwrite.php"; } } + +module.exports = { PHP }; \ No newline at end of file diff --git a/templates/cli/lib/type-generation/languages/typescript.js.twig b/templates/cli/lib/type-generation/languages/typescript.js.twig index df63fd688..8e16c717c 100644 --- a/templates/cli/lib/type-generation/languages/typescript.js.twig +++ b/templates/cli/lib/type-generation/languages/typescript.js.twig @@ -2,7 +2,7 @@ const { AttributeType } = require('../attributes'); const { Language } = require("./language"); -export class TypeScript extends Language { +class TypeScript extends Language { /** * @param {Attribute} attribute */ @@ -54,3 +54,5 @@ export type <%= toPascalCase(collection.key) %> = { return "./generated/appwrite.d.ts"; } } + +module.exports = { TypeScript }; \ No newline at end of file From b058588f547e67d7d4eead6c92b84cdc56955ff6 Mon Sep 17 00:00:00 2001 From: loks0n <22452787+loks0n@users.noreply.github.com> Date: Wed, 6 Mar 2024 14:38:05 +0100 Subject: [PATCH 04/44] feat: add ejs --- templates/cli/package.json.twig | 1 + 1 file changed, 1 insertion(+) diff --git a/templates/cli/package.json.twig b/templates/cli/package.json.twig index 1392ff294..6016bdb85 100644 --- a/templates/cli/package.json.twig +++ b/templates/cli/package.json.twig @@ -23,6 +23,7 @@ }, "dependencies": { "undici": "^5.28.2", + "ejs": "^3.1.9", "chalk": "4.1.2", "cli-table3": "^0.6.2", "commander": "^9.2.0", From 1ef2bdc0c1fa1b863910c59bdbd4d234e5284ece Mon Sep 17 00:00:00 2001 From: loks0n <22452787+loks0n@users.noreply.github.com> Date: Thu, 7 Mar 2024 15:27:29 +0100 Subject: [PATCH 05/44] feat: more stuff --- src/SDK/Language/CLI.php | 9 +- templates/cli/lib/commands/types.js.twig | 123 +++++++++++------- .../{attributes.js.twig => attribute.js.twig} | 0 .../lib/type-generation/collection.js.twig | 13 ++ .../languages/language.js.twig | 99 +++++++------- .../lib/type-generation/languages/php.js.twig | 38 +++--- .../languages/typescript.js.twig | 71 +++++++--- templates/web/src/models.ts.twig | 2 +- 8 files changed, 222 insertions(+), 133 deletions(-) rename templates/cli/lib/type-generation/{attributes.js.twig => attribute.js.twig} (100%) create mode 100644 templates/cli/lib/type-generation/collection.js.twig diff --git a/src/SDK/Language/CLI.php b/src/SDK/Language/CLI.php index 9d3e063e8..2c9826a73 100644 --- a/src/SDK/Language/CLI.php +++ b/src/SDK/Language/CLI.php @@ -124,8 +124,13 @@ public function getFiles(): array ], [ 'scope' => 'default', - 'destination' => 'lib/type-generation/attributes.js', - 'template' => 'cli/lib/type-generation/attributes.js.twig', + 'destination' => 'lib/type-generation/attribute.js', + 'template' => 'cli/lib/type-generation/attribute.js.twig', + ], + [ + 'scope' => 'default', + 'destination' => 'lib/type-generation/collection.js', + 'template' => 'cli/lib/type-generation/collection.js.twig', ], [ 'scope' => 'default', diff --git a/templates/cli/lib/commands/types.js.twig b/templates/cli/lib/commands/types.js.twig index 177120b24..d14d60fac 100644 --- a/templates/cli/lib/commands/types.js.twig +++ b/templates/cli/lib/commands/types.js.twig @@ -1,12 +1,40 @@ -const { PHP } = require("../type-generation/languages/php"); -const { TypeScript } = require("../type-generation/languages/typescript"); - +const ejs = require("ejs"); const fs = require("fs"); const path = require("path"); -const { Command, Option } = require("commander"); +const { LanguageMeta, detectLanguage } = require("../type-generation/languages/language"); +const { Command, Option, Argument } = require("commander"); const { localConfig } = require("../config"); const { success, log, actionRunner } = require("../parser"); -const ejs = require("ejs"); +const { PHP } = require("../type-generation/languages/php"); +const { TypeScript } = require("../type-generation/languages/typescript"); + +/** + * @param {string} language + * @returns {import("../type-generation/languages/language").LanguageMeta} + */ +function createLanguageMeta(language) { + switch (language) { + case "ts": + return new TypeScript(); + case "php": + return new PHP(); + default: + throw new Error(`Unknown language: ${language}`); + } +} + +const templateHelpers = { + toPascalCase: LanguageMeta.toPascalCase, + toCamelCase: LanguageMeta.toCamelCase, + toSnakeCase: LanguageMeta.toSnakeCase, + toKebabCase: LanguageMeta.toKebabCase, + toUpperSnakeCase: LanguageMeta.toUpperSnakeCase +} + +const typesOutputArgument = new Argument( + "", + "The directory to write the types to" +); const typesLanguageOption = new Option( "-l, --language ", @@ -15,62 +43,69 @@ const typesLanguageOption = new Option( .choices(["ts", "php"]) .default("auto"); -const typesOutputOption = new Option( - "-o, --output ", - "The output path of the types" -).default("auto"); - -const languages = { - ts: new TypeScript(), - php: new PHP(), -}; - -function detectLanguage() { - if (fs.existsSync(path.join(process.cwd(), "package.json"))) { - return "ts"; - } - if (fs.existsSync(path.join(process.cwd(), "composer.json"))) { - return "php"; - } - throw new Error("Could not detect language, please specify with -l"); -} - -const typesCommand = actionRunner(async (options) => { - let { language, output } = options; - +const typesCommand = actionRunner(async (rawOutputDirectory, {language}) => { if (language === "auto") { language = detectLanguage(); log(`Detected language: ${language}`); } - const generator = languages[language]; + const meta = createLanguageMeta(language); - if (output === "auto") { - output = generator.getOutputPath(); - log(`Using default output path: ${output}`); + const outputDirectory = path.resolve(rawOutputDirectory); + if (!fs.existsSync(outputDirectory)) { + log(`Directory: ${outputDirectory} does not exist, creating...`); + fs.mkdirSync(outputDirectory, { recursive: true }); } - const collections = localConfig.getCollections(); - - const templateContent = generator.getTemplate(); - const templater = ejs.compile(templateContent); - const typesContent = templater({ collections }); - - const outputDir = path.dirname(output); - if (!fs.existsSync(outputDir)) { - fs.mkdirSync(outputDir, { recursive: true }); + if (!fs.statSync("appwrite.json").isFile()) { + throw new Error("appwrite.json not found in current directory"); } - fs.writeFileSync(output, typesContent); - log(`Types written to ${output}`); + const collections = localConfig.getCollections(); + if (collections.length === 0) { + throw new Error("No collections found in appwrite.json"); + } + log(`Found ${collections.length} collections: ${collections.map(c => c.name).join(", ")}`); + + const totalAttributes = collections.reduce((count, collection) => count + collection.attributes.length, 0); + log(`Found ${totalAttributes} attributes across all collections`); + + const templater = ejs.compile(meta.getTemplate()); + + if (meta.isSingleFile()) { + const content = templater({ + collections, + ...templateHelpers, + getType: meta.getType + }); + + const destination = path.join(outputDirectory, meta.getFileName()); + + fs.writeFileSync(destination, content); + log(`Added types to ${destination}`); + } else { + for (const collection of collections) { + const content = templater({ + collection, + ...templateHelpers, + getType: meta.getType + }); + + const destination = path.join(outputDirectory, meta.getFileName(collection)); + + fs.writeFileSync(destination, content); + log(`Added types for ${collection.name} to ${destination}`); + } + } + success(); }); const types = new Command("types") .description("Generate types for your Appwrite project") + .addArgument(typesOutputArgument) .addOption(typesLanguageOption) - .addOption(typesOutputOption) .action(actionRunner(typesCommand)); module.exports = { types }; diff --git a/templates/cli/lib/type-generation/attributes.js.twig b/templates/cli/lib/type-generation/attribute.js.twig similarity index 100% rename from templates/cli/lib/type-generation/attributes.js.twig rename to templates/cli/lib/type-generation/attribute.js.twig diff --git a/templates/cli/lib/type-generation/collection.js.twig b/templates/cli/lib/type-generation/collection.js.twig new file mode 100644 index 000000000..6fcb0dd71 --- /dev/null +++ b/templates/cli/lib/type-generation/collection.js.twig @@ -0,0 +1,13 @@ +/** + * Represents a collection within a database. + * + * @typedef {Object} Collection + * @property {string} $id - The unique identifier of the collection. + * @property {string[]} $permissions - The permissions for accessing the collection. + * @property {string} databaseId - The identifier of the database this collection belongs to. + * @property {string} name - The name of the collection. + * @property {boolean} enabled - Indicates if the collection is enabled. + * @property {boolean} documentSecurity - Indicates if document-level security is enabled for the collection. + * @property {import('./attribute.js).Attribute[]} attributes - The attributes (fields) defined in the collection. + * @property {unknown[]} indexes - The indexes defined on the collection for optimized query performance. + */ \ No newline at end of file diff --git a/templates/cli/lib/type-generation/languages/language.js.twig b/templates/cli/lib/type-generation/languages/language.js.twig index 53e7829a8..86b758349 100644 --- a/templates/cli/lib/type-generation/languages/language.js.twig +++ b/templates/cli/lib/type-generation/languages/language.js.twig @@ -1,63 +1,43 @@ -/** @typedef {import('../attributes').Attribute} Attribute */ +/** @typedef {import('../attribute').Attribute} Attribute */ +/** @typedef {import('../collection').Collection} Collection */ -class Language { +const fs = require("fs"); +const path = require("path"); + +class LanguageMeta { constructor() { - if (new.target === Language) { + if (new.target === LanguageMeta) { throw new TypeError("Abstract classes can't be instantiated."); } } - /** - * @param {string} string - * @returns {string} - */ static toKebabCase(string) { return string - .split("") - .map((letter) => { - if (/[A-Z]/.test(letter)) { - return ` ${letter.toLowerCase()}`; - } - return letter; - }) - .join("") - .trim() - .replace(/[_\s]+/g, "-"); + .replace(/[^a-zA-Z0-9\s-_]/g, "") // Remove invalid characters + .replace(/([a-z])([A-Z])/g, "$1-$2") // Add hyphen between camelCase + .replace(/([A-Z])([A-Z][a-z])/g, "$1-$2") // Add hyphen between PascalCase + .replace(/[_\s]+/g, "-") // Replace spaces and underscores with hyphens + .replace(/^-+|-+$/g, "") // Remove leading and trailing hyphens + .replace(/--+/g, "-") // Replace multiple hyphens with a single hyphen + .toLowerCase(); } - /** - * @param {string} string - * @returns {string} - */ static toSnakeCase(string) { return this.toKebabCase(string).replace(/-/g, "_"); } - /** - * @param {string} string - * @returns {string} - */ static toUpperSnakeCase(string) { return this.toSnakeCase(string).toUpperCase(); } - /** - * @param {string} string - * @returns {string} - */ - static toPascalCase(string) { - return this.toKebabCase(string) - .split("-") - .map((word) => word.charAt(0).toUpperCase() + word.slice(1)) - .join(""); + static toCamelCase(string) { + return this.toKebabCase(string).replace(/-([a-z])/g, (g) => + g[1].toUpperCase() + ); } - /** - * @param {string} string - * @returns {string} - */ - static toCamelCase(string) { - return this.toPascalCase(string).charAt(0).toLowerCase() + string.slice(1); + static toPascalCase(string) { + return this.toCamelCase(string).replace(/^./, (g) => g.toUpperCase()); } /** @@ -67,29 +47,56 @@ class Language { * @param {Attribute} attribute * @return {string} */ - static getType(attribute) { + getType(attribute) { throw new TypeError("Stub."); } + /** + * Should types be generated in a single file? + * + * @returns {boolean} + */ + isSingleFile() { + return false; + } + /** * Get the EJS template used to generate the types for this language. - * + * * @abstract * @returns {string} */ - static getTemplate() { + getTemplate() { throw new TypeError("Stub."); } /** * Get the file extension used by files of this language. - * + * * @abstract + * @param {Collection|undefined} collection * @returns {string} */ - static getOutputPath() { + getFileName(collection) { throw new TypeError("Stub."); } } -module.exports = { Language }; \ No newline at end of file +/** + * @returns {string} + */ +function detectLanguage() { + if ( + fs.existsSync(path.join(process.cwd(), "package.json")) || + fs.existsSync(path.join(process.cwd(), "tsconfig.json")) || + fs.existsSync(path.join(process.cwd(), "deno.json")) + ) { + return "ts"; + } + if (fs.existsSync(path.join(process.cwd(), "composer.json"))) { + return "php"; + } + throw new Error("Could not detect language, please specify with -l"); +} + +module.exports = { LanguageMeta, detectLanguage }; diff --git a/templates/cli/lib/type-generation/languages/php.js.twig b/templates/cli/lib/type-generation/languages/php.js.twig index 31f9c22d7..1459d7002 100644 --- a/templates/cli/lib/type-generation/languages/php.js.twig +++ b/templates/cli/lib/type-generation/languages/php.js.twig @@ -1,12 +1,9 @@ -/** @typedef {import('../attributes').Attribute} Attribute */ -const { AttributeType } = require('../attributes'); -const { Language } = require("./language"); - -class PHP extends Language { - /** - * @param {Attribute} attribute - */ - static getType(attribute) { +/** @typedef {import('../attribute').Attribute} Attribute */ +const { AttributeType } = require('../attribute'); +const { LanguageMeta } = require("./language"); + +class PHP extends LanguageMeta { + getType(attribute) { let type = "" switch (attribute.type) { case AttributeType.STRING: @@ -38,22 +35,23 @@ class PHP extends Language { return type; } - static getTemplate() { + + + getTemplate() { return ` -class <%= toPascalCase(collection.key) %> extends Document { - <% for (const attribute of collection.attributes ){ %> - public <%= toCamelCase(attribute.key) %>: <%= getType(attribute) %>; - <% } %> -} +use Appwrite\\Models\\Document; -<% } %>`; +class <%- toPascalCase(collection.name) %> extends Document { + <%_ for (const attribute of collection.attributes ){ -%> + public <%- toCamelCase(attribute.key) %>: <%- getType(attribute) %>; + <%_ } %> +}`; } - static getOutputPath() { - return "./generated/Appwrite.php"; + getFileName(collection) { + return LanguageMeta.toPascalCase(collection.name) + ".php"; } } diff --git a/templates/cli/lib/type-generation/languages/typescript.js.twig b/templates/cli/lib/type-generation/languages/typescript.js.twig index 8e16c717c..df8b43433 100644 --- a/templates/cli/lib/type-generation/languages/typescript.js.twig +++ b/templates/cli/lib/type-generation/languages/typescript.js.twig @@ -1,12 +1,12 @@ -/** @typedef {import('../attributes').Attribute} Attribute */ -const { AttributeType } = require('../attributes'); -const { Language } = require("./language"); - -class TypeScript extends Language { - /** - * @param {Attribute} attribute - */ - static getType(attribute) { +/** @typedef {import('../attribute').Attribute} Attribute */ +const fs = require("fs"); +const path = require("path"); + +const { AttributeType } = require('../attribute'); +const { LanguageMeta } = require("./language"); + +class TypeScript extends LanguageMeta { + getType(attribute) { let type = "" switch (attribute.type) { case AttributeType.STRING: @@ -33,25 +33,56 @@ class TypeScript extends Language { type += "[]"; } if (attribute.required) { - type += "|null"; + type += " | null"; } return type; } - static getTemplate() { - return ` -<% for (const collection of collections.attributes ){ %> -export type <%= toPascalCase(collection.key) %> = { - <% for (const attribute of collection.attributes) { %> - <%= toCamelCase(attribute.key) %>: <%= getType(attribute) %>; - <% } %> + isSingleFile() { + return true; + } + + _usingNodeAppwrite() { + if (fs.existsSync(path.resolve(process.cwd(), 'package.json'))) { + + return + } + if (fs.existsSync(path.resolve(process.cwd(), 'deno.json'))) { + const denoJsonRaw = fs.readFileSync(path.resolve(process.cwd(), 'deno.json')); + const denoJson = JSON.parse(denoJsonRaw.toString('utf-8')); + return denoJson.dependencies['appwrite'] ? true : false; + } + } + + _getAppwriteDependency() { + if (fs.existsSync(path.resolve(process.cwd(), 'package.json'))) { + const packageJsonRaw = fs.readFileSync(path.resolve(process.cwd(), 'package.json')); + const packageJson = JSON.parse(packageJsonRaw.toString('utf-8')); + return packageJson.dependencies['node-appwrite'] ? 'node-appwrite' : 'appwrite'; + } + + if (fs.existsSync(path.resolve(process.cwd(), 'deno.json'))) { + return "https://deno.land/x/appwrite/mod.ts" + } + + return "appwrite"; + } + + getTemplate() { + return `import { Models } from '${this._getAppwriteDependency()}'; + +<% for (const collection of collections) { -%> +export type <%- toPascalCase(collection.name) %> = Models.Document & { + <%_ for (const attribute of collection.attributes) { -%> + <%- toCamelCase(attribute.key) %>: <%- getType(attribute) %>; + <%_ } -%> } -<% } %>`; +<% } -%>`; } - static getOutputPath() { - return "./generated/appwrite.d.ts"; + getFileName(_) { + return "appwrite.d.ts"; } } diff --git a/templates/web/src/models.ts.twig b/templates/web/src/models.ts.twig index 4b0f63f8b..216478bba 100644 --- a/templates/web/src/models.ts.twig +++ b/templates/web/src/models.ts.twig @@ -12,6 +12,6 @@ export namespace Models { {% endfor %} {% if definition.additionalProperties %} [key: string]: any; {% endif %} - } + } {% endfor %} } From ec07a0991348da88c70729e271c6acd7a35e2bfb Mon Sep 17 00:00:00 2001 From: loks0n <22452787+loks0n@users.noreply.github.com> Date: Thu, 7 Mar 2024 15:47:48 +0100 Subject: [PATCH 06/44] feat: python --- src/SDK/Language/CLI.php | 5 ++ templates/cli/lib/commands/types.js.twig | 3 + .../lib/type-generation/languages/php.js.twig | 2 - .../type-generation/languages/python.js.twig | 58 +++++++++++++++++++ .../languages/typescript.js.twig | 12 ---- 5 files changed, 66 insertions(+), 14 deletions(-) create mode 100644 templates/cli/lib/type-generation/languages/python.js.twig diff --git a/src/SDK/Language/CLI.php b/src/SDK/Language/CLI.php index 2c9826a73..dfc6e0609 100644 --- a/src/SDK/Language/CLI.php +++ b/src/SDK/Language/CLI.php @@ -147,6 +147,11 @@ public function getFiles(): array 'destination' => 'lib/type-generation/languages/typescript.js', 'template' => 'cli/lib/type-generation/languages/typescript.js.twig', ], + [ + 'scope' => 'default', + 'destination' => 'lib/type-generation/languages/python.js', + 'template' => 'cli/lib/type-generation/languages/python.js.twig', + ], [ 'scope' => 'default', 'destination' => 'lib/questions.js', diff --git a/templates/cli/lib/commands/types.js.twig b/templates/cli/lib/commands/types.js.twig index d14d60fac..58a6f6aa4 100644 --- a/templates/cli/lib/commands/types.js.twig +++ b/templates/cli/lib/commands/types.js.twig @@ -7,6 +7,7 @@ const { localConfig } = require("../config"); const { success, log, actionRunner } = require("../parser"); const { PHP } = require("../type-generation/languages/php"); const { TypeScript } = require("../type-generation/languages/typescript"); +const { Python } = require("../type-generation/languages/python"); /** * @param {string} language @@ -18,6 +19,8 @@ function createLanguageMeta(language) { return new TypeScript(); case "php": return new PHP(); + case "python": + return new Python(); default: throw new Error(`Unknown language: ${language}`); } diff --git a/templates/cli/lib/type-generation/languages/php.js.twig b/templates/cli/lib/type-generation/languages/php.js.twig index 1459d7002..b1b486858 100644 --- a/templates/cli/lib/type-generation/languages/php.js.twig +++ b/templates/cli/lib/type-generation/languages/php.js.twig @@ -35,8 +35,6 @@ class PHP extends LanguageMeta { return type; } - - getTemplate() { return ` +class <%= toPascalCase(collection.name) %>(typing.TypedDict): + <% for (const attribute of collection.attributes) { -%> + <%= attribute.name %>: <%= getType(attribute) %> + <% } -%> +<% } -%>`; + } + + getFileName(_) { + return "collections.py"; + } +} + +module.exports = { Python }; \ No newline at end of file diff --git a/templates/cli/lib/type-generation/languages/typescript.js.twig b/templates/cli/lib/type-generation/languages/typescript.js.twig index df8b43433..98be0485a 100644 --- a/templates/cli/lib/type-generation/languages/typescript.js.twig +++ b/templates/cli/lib/type-generation/languages/typescript.js.twig @@ -42,18 +42,6 @@ class TypeScript extends LanguageMeta { return true; } - _usingNodeAppwrite() { - if (fs.existsSync(path.resolve(process.cwd(), 'package.json'))) { - - return - } - if (fs.existsSync(path.resolve(process.cwd(), 'deno.json'))) { - const denoJsonRaw = fs.readFileSync(path.resolve(process.cwd(), 'deno.json')); - const denoJson = JSON.parse(denoJsonRaw.toString('utf-8')); - return denoJson.dependencies['appwrite'] ? true : false; - } - } - _getAppwriteDependency() { if (fs.existsSync(path.resolve(process.cwd(), 'package.json'))) { const packageJsonRaw = fs.readFileSync(path.resolve(process.cwd(), 'package.json')); From 0163bfa2bac7682175569120c91692d6ed8afc59 Mon Sep 17 00:00:00 2001 From: loks0n <22452787+loks0n@users.noreply.github.com> Date: Thu, 7 Mar 2024 15:49:10 +0100 Subject: [PATCH 07/44] chore: fmt fixes --- templates/cli/lib/type-generation/languages/php.js.twig | 2 +- templates/cli/lib/type-generation/languages/python.js.twig | 2 +- templates/cli/lib/type-generation/languages/typescript.js.twig | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/templates/cli/lib/type-generation/languages/php.js.twig b/templates/cli/lib/type-generation/languages/php.js.twig index b1b486858..56d15b11a 100644 --- a/templates/cli/lib/type-generation/languages/php.js.twig +++ b/templates/cli/lib/type-generation/languages/php.js.twig @@ -43,7 +43,7 @@ use Appwrite\\Models\\Document; class <%- toPascalCase(collection.name) %> extends Document { <%_ for (const attribute of collection.attributes ){ -%> - public <%- toCamelCase(attribute.key) %>: <%- getType(attribute) %>; + public <%= toCamelCase(attribute.key) %>: <%= getType(attribute) %>; <%_ } %> }`; } diff --git a/templates/cli/lib/type-generation/languages/python.js.twig b/templates/cli/lib/type-generation/languages/python.js.twig index 91710117c..2f5e5727a 100644 --- a/templates/cli/lib/type-generation/languages/python.js.twig +++ b/templates/cli/lib/type-generation/languages/python.js.twig @@ -44,7 +44,7 @@ class Python extends LanguageMeta { <% for (const collection of collections) { -%> class <%= toPascalCase(collection.name) %>(typing.TypedDict): - <% for (const attribute of collection.attributes) { -%> + <%_ for (const attribute of collection.attributes) { -%> <%= attribute.name %>: <%= getType(attribute) %> <% } -%> <% } -%>`; diff --git a/templates/cli/lib/type-generation/languages/typescript.js.twig b/templates/cli/lib/type-generation/languages/typescript.js.twig index 98be0485a..82a2172f8 100644 --- a/templates/cli/lib/type-generation/languages/typescript.js.twig +++ b/templates/cli/lib/type-generation/languages/typescript.js.twig @@ -50,7 +50,7 @@ class TypeScript extends LanguageMeta { } if (fs.existsSync(path.resolve(process.cwd(), 'deno.json'))) { - return "https://deno.land/x/appwrite/mod.ts" + return "https://deno.land/x/appwrite/mod.ts"; } return "appwrite"; From 29cbd6bc25c6d3815e6c760b165da0636b0ad761 Mon Sep 17 00:00:00 2001 From: loks0n <22452787+loks0n@users.noreply.github.com> Date: Thu, 7 Mar 2024 15:53:29 +0100 Subject: [PATCH 08/44] fix: php, python --- templates/cli/lib/commands/types.js.twig | 2 +- templates/cli/lib/type-generation/languages/php.js.twig | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/templates/cli/lib/commands/types.js.twig b/templates/cli/lib/commands/types.js.twig index 58a6f6aa4..dee1b0080 100644 --- a/templates/cli/lib/commands/types.js.twig +++ b/templates/cli/lib/commands/types.js.twig @@ -43,7 +43,7 @@ const typesLanguageOption = new Option( "-l, --language ", "The language of the types" ) - .choices(["ts", "php"]) + .choices(["ts", "php", "python"]) .default("auto"); const typesCommand = actionRunner(async (rawOutputDirectory, {language}) => { diff --git a/templates/cli/lib/type-generation/languages/php.js.twig b/templates/cli/lib/type-generation/languages/php.js.twig index 56d15b11a..2e9797391 100644 --- a/templates/cli/lib/type-generation/languages/php.js.twig +++ b/templates/cli/lib/type-generation/languages/php.js.twig @@ -43,7 +43,7 @@ use Appwrite\\Models\\Document; class <%- toPascalCase(collection.name) %> extends Document { <%_ for (const attribute of collection.attributes ){ -%> - public <%= toCamelCase(attribute.key) %>: <%= getType(attribute) %>; + public <%= getType(attribute) %> $<%= toCamelCase(attribute.key) %>; <%_ } %> }`; } From 08689e1b1c585d2c69f3a6c06ede078d744aea41 Mon Sep 17 00:00:00 2001 From: loks0n <22452787+loks0n@users.noreply.github.com> Date: Thu, 7 Mar 2024 16:00:18 +0100 Subject: [PATCH 09/44] fix: python, php --- templates/cli/lib/type-generation/languages/php.js.twig | 3 ++- templates/cli/lib/type-generation/languages/python.js.twig | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/templates/cli/lib/type-generation/languages/php.js.twig b/templates/cli/lib/type-generation/languages/php.js.twig index 2e9797391..f6d9f15e8 100644 --- a/templates/cli/lib/type-generation/languages/php.js.twig +++ b/templates/cli/lib/type-generation/languages/php.js.twig @@ -44,7 +44,8 @@ use Appwrite\\Models\\Document; class <%- toPascalCase(collection.name) %> extends Document { <%_ for (const attribute of collection.attributes ){ -%> public <%= getType(attribute) %> $<%= toCamelCase(attribute.key) %>; - <%_ } %> + + <%_ } %-> }`; } diff --git a/templates/cli/lib/type-generation/languages/python.js.twig b/templates/cli/lib/type-generation/languages/python.js.twig index 2f5e5727a..a5c76021c 100644 --- a/templates/cli/lib/type-generation/languages/python.js.twig +++ b/templates/cli/lib/type-generation/languages/python.js.twig @@ -45,7 +45,7 @@ class Python extends LanguageMeta { <% for (const collection of collections) { -%> class <%= toPascalCase(collection.name) %>(typing.TypedDict): <%_ for (const attribute of collection.attributes) { -%> - <%= attribute.name %>: <%= getType(attribute) %> + <%= toSnakeCase(attribute.key) %>: <%= getType(attribute) %> <% } -%> <% } -%>`; } From f5ce7c471efc99a55181b07e0b2d85e61b655eca Mon Sep 17 00:00:00 2001 From: loks0n <22452787+loks0n@users.noreply.github.com> Date: Thu, 7 Mar 2024 16:04:18 +0100 Subject: [PATCH 10/44] fix: python formatting --- templates/cli/lib/type-generation/languages/python.js.twig | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/templates/cli/lib/type-generation/languages/python.js.twig b/templates/cli/lib/type-generation/languages/python.js.twig index a5c76021c..94f9e524f 100644 --- a/templates/cli/lib/type-generation/languages/python.js.twig +++ b/templates/cli/lib/type-generation/languages/python.js.twig @@ -42,11 +42,13 @@ class Python extends LanguageMeta { getTemplate() { return `import typing + <% for (const collection of collections) { -%> class <%= toPascalCase(collection.name) %>(typing.TypedDict): <%_ for (const attribute of collection.attributes) { -%> <%= toSnakeCase(attribute.key) %>: <%= getType(attribute) %> - <% } -%> + <%_ } -%> + <% } -%>`; } From fe9ed0e7ba289eaa36515ef8fa23bda418f301d5 Mon Sep 17 00:00:00 2001 From: loks0n <22452787+loks0n@users.noreply.github.com> Date: Thu, 7 Mar 2024 16:12:47 +0100 Subject: [PATCH 11/44] fix: formatting --- .../cli/lib/type-generation/languages/php.js.twig | 6 +++--- .../cli/lib/type-generation/languages/python.js.twig | 12 ++++++------ .../lib/type-generation/languages/typescript.js.twig | 6 +++--- 3 files changed, 12 insertions(+), 12 deletions(-) diff --git a/templates/cli/lib/type-generation/languages/php.js.twig b/templates/cli/lib/type-generation/languages/php.js.twig index f6d9f15e8..88ba60d80 100644 --- a/templates/cli/lib/type-generation/languages/php.js.twig +++ b/templates/cli/lib/type-generation/languages/php.js.twig @@ -42,10 +42,10 @@ namespace Appwrite\\Models; use Appwrite\\Models\\Document; class <%- toPascalCase(collection.name) %> extends Document { - <%_ for (const attribute of collection.attributes ){ -%> - public <%= getType(attribute) %> $<%= toCamelCase(attribute.key) %>; +<% for (const attribute of collection.attributes ){ %> + public <%- getType(attribute) %> $<%- toCamelCase(attribute.key) %>; - <%_ } %-> +<% } %> }`; } diff --git a/templates/cli/lib/type-generation/languages/python.js.twig b/templates/cli/lib/type-generation/languages/python.js.twig index 94f9e524f..e8b69762d 100644 --- a/templates/cli/lib/type-generation/languages/python.js.twig +++ b/templates/cli/lib/type-generation/languages/python.js.twig @@ -43,13 +43,13 @@ class Python extends LanguageMeta { return `import typing -<% for (const collection of collections) { -%> -class <%= toPascalCase(collection.name) %>(typing.TypedDict): - <%_ for (const attribute of collection.attributes) { -%> - <%= toSnakeCase(attribute.key) %>: <%= getType(attribute) %> - <%_ } -%> +<% for (const collection of collections) { %> +class <%- toPascalCase(collection.name) %>(typing.TypedDict): +<% for (const attribute of collection.attributes) { %> + <%- toSnakeCase(attribute.key) %>: <%- getType(attribute) %> -<% } -%>`; +<% } %> +<% } %>`; } getFileName(_) { diff --git a/templates/cli/lib/type-generation/languages/typescript.js.twig b/templates/cli/lib/type-generation/languages/typescript.js.twig index 82a2172f8..77a862b66 100644 --- a/templates/cli/lib/type-generation/languages/typescript.js.twig +++ b/templates/cli/lib/type-generation/languages/typescript.js.twig @@ -59,11 +59,11 @@ class TypeScript extends LanguageMeta { getTemplate() { return `import { Models } from '${this._getAppwriteDependency()}'; -<% for (const collection of collections) { -%> +<% for (const collection of collections) { %> export type <%- toPascalCase(collection.name) %> = Models.Document & { - <%_ for (const attribute of collection.attributes) { -%> +<% for (const attribute of collection.attributes) { %> <%- toCamelCase(attribute.key) %>: <%- getType(attribute) %>; - <%_ } -%> +<% } %> } <% } -%>`; From d3c471fc8602bc4431787a6af5051393755a021b Mon Sep 17 00:00:00 2001 From: loks0n <22452787+loks0n@users.noreply.github.com> Date: Thu, 7 Mar 2024 16:16:39 +0100 Subject: [PATCH 12/44] fix: formatting --- templates/cli/lib/type-generation/languages/php.js.twig | 1 - templates/cli/lib/type-generation/languages/python.js.twig | 1 - .../cli/lib/type-generation/languages/typescript.js.twig | 4 ++-- 3 files changed, 2 insertions(+), 4 deletions(-) diff --git a/templates/cli/lib/type-generation/languages/php.js.twig b/templates/cli/lib/type-generation/languages/php.js.twig index 88ba60d80..cd6aa3a08 100644 --- a/templates/cli/lib/type-generation/languages/php.js.twig +++ b/templates/cli/lib/type-generation/languages/php.js.twig @@ -44,7 +44,6 @@ use Appwrite\\Models\\Document; class <%- toPascalCase(collection.name) %> extends Document { <% for (const attribute of collection.attributes ){ %> public <%- getType(attribute) %> $<%- toCamelCase(attribute.key) %>; - <% } %> }`; } diff --git a/templates/cli/lib/type-generation/languages/python.js.twig b/templates/cli/lib/type-generation/languages/python.js.twig index e8b69762d..e9e39044f 100644 --- a/templates/cli/lib/type-generation/languages/python.js.twig +++ b/templates/cli/lib/type-generation/languages/python.js.twig @@ -47,7 +47,6 @@ class Python extends LanguageMeta { class <%- toPascalCase(collection.name) %>(typing.TypedDict): <% for (const attribute of collection.attributes) { %> <%- toSnakeCase(attribute.key) %>: <%- getType(attribute) %> - <% } %> <% } %>`; } diff --git a/templates/cli/lib/type-generation/languages/typescript.js.twig b/templates/cli/lib/type-generation/languages/typescript.js.twig index 77a862b66..6bf002333 100644 --- a/templates/cli/lib/type-generation/languages/typescript.js.twig +++ b/templates/cli/lib/type-generation/languages/typescript.js.twig @@ -61,9 +61,9 @@ class TypeScript extends LanguageMeta { <% for (const collection of collections) { %> export type <%- toPascalCase(collection.name) %> = Models.Document & { -<% for (const attribute of collection.attributes) { %> +<% for (const attribute of collection.attributes) { %> <%- toCamelCase(attribute.key) %>: <%- getType(attribute) %>; -<% } %> +<% } -%> } <% } -%>`; From 731dcf8fe9dbe037ae4fe4e889b1883d9c339bb1 Mon Sep 17 00:00:00 2001 From: loks0n <22452787+loks0n@users.noreply.github.com> Date: Thu, 7 Mar 2024 16:22:59 +0100 Subject: [PATCH 13/44] fix: formatting 2 --- templates/cli/lib/type-generation/languages/php.js.twig | 2 +- templates/cli/lib/type-generation/languages/python.js.twig | 5 +++-- .../cli/lib/type-generation/languages/typescript.js.twig | 4 ++-- 3 files changed, 6 insertions(+), 5 deletions(-) diff --git a/templates/cli/lib/type-generation/languages/php.js.twig b/templates/cli/lib/type-generation/languages/php.js.twig index cd6aa3a08..ec8f75a70 100644 --- a/templates/cli/lib/type-generation/languages/php.js.twig +++ b/templates/cli/lib/type-generation/languages/php.js.twig @@ -42,7 +42,7 @@ namespace Appwrite\\Models; use Appwrite\\Models\\Document; class <%- toPascalCase(collection.name) %> extends Document { -<% for (const attribute of collection.attributes ){ %> +<% for (const attribute of collection.attributes ){ -%> public <%- getType(attribute) %> $<%- toCamelCase(attribute.key) %>; <% } %> }`; diff --git a/templates/cli/lib/type-generation/languages/python.js.twig b/templates/cli/lib/type-generation/languages/python.js.twig index e9e39044f..d092dab09 100644 --- a/templates/cli/lib/type-generation/languages/python.js.twig +++ b/templates/cli/lib/type-generation/languages/python.js.twig @@ -45,9 +45,10 @@ class Python extends LanguageMeta { <% for (const collection of collections) { %> class <%- toPascalCase(collection.name) %>(typing.TypedDict): -<% for (const attribute of collection.attributes) { %> +<% for (const attribute of collection.attributes) { -%> <%- toSnakeCase(attribute.key) %>: <%- getType(attribute) %> -<% } %> +<% } -%> + <% } %>`; } diff --git a/templates/cli/lib/type-generation/languages/typescript.js.twig b/templates/cli/lib/type-generation/languages/typescript.js.twig index 6bf002333..b78d4570a 100644 --- a/templates/cli/lib/type-generation/languages/typescript.js.twig +++ b/templates/cli/lib/type-generation/languages/typescript.js.twig @@ -61,12 +61,12 @@ class TypeScript extends LanguageMeta { <% for (const collection of collections) { %> export type <%- toPascalCase(collection.name) %> = Models.Document & { -<% for (const attribute of collection.attributes) { %> +<% for (const attribute of collection.attributes) { -%> <%- toCamelCase(attribute.key) %>: <%- getType(attribute) %>; <% } -%> } -<% } -%>`; +<% } %>`; } getFileName(_) { From 6f067aa766688a02c306d10942945a1044d5b87f Mon Sep 17 00:00:00 2001 From: loks0n <22452787+loks0n@users.noreply.github.com> Date: Thu, 7 Mar 2024 19:34:15 +0100 Subject: [PATCH 14/44] feat: swift, kotlin --- src/SDK/Language/CLI.php | 10 ++++ templates/cli/lib/commands/types.js.twig | 12 +++- .../type-generation/languages/kotlin.js.twig | 53 +++++++++++++++++ .../languages/language.js.twig | 34 ++++++++--- .../lib/type-generation/languages/php.js.twig | 2 +- .../type-generation/languages/python.js.twig | 3 +- .../type-generation/languages/swift.js.twig | 59 +++++++++++++++++++ .../languages/typescript.js.twig | 2 - 8 files changed, 160 insertions(+), 15 deletions(-) create mode 100644 templates/cli/lib/type-generation/languages/kotlin.js.twig create mode 100644 templates/cli/lib/type-generation/languages/swift.js.twig diff --git a/src/SDK/Language/CLI.php b/src/SDK/Language/CLI.php index dfc6e0609..e8de4555d 100644 --- a/src/SDK/Language/CLI.php +++ b/src/SDK/Language/CLI.php @@ -152,6 +152,16 @@ public function getFiles(): array 'destination' => 'lib/type-generation/languages/python.js', 'template' => 'cli/lib/type-generation/languages/python.js.twig', ], + [ + 'scope' => 'default', + 'destination' => 'lib/type-generation/languages/kotlin.js', + 'template' => 'cli/lib/type-generation/languages/kotlin.js.twig', + ], + [ + 'scope' => 'default', + 'destination' => 'lib/type-generation/languages/swift.js', + 'template' => 'cli/lib/type-generation/languages/swift.js.twig', + ], [ 'scope' => 'default', 'destination' => 'lib/questions.js', diff --git a/templates/cli/lib/commands/types.js.twig b/templates/cli/lib/commands/types.js.twig index dee1b0080..5643927ff 100644 --- a/templates/cli/lib/commands/types.js.twig +++ b/templates/cli/lib/commands/types.js.twig @@ -8,6 +8,8 @@ const { success, log, actionRunner } = require("../parser"); const { PHP } = require("../type-generation/languages/php"); const { TypeScript } = require("../type-generation/languages/typescript"); const { Python } = require("../type-generation/languages/python"); +const { Kotlin } = require("../type-generation/languages/kotlin"); +const { Swift } = require("../type-generation/languages/swift"); /** * @param {string} language @@ -21,8 +23,12 @@ function createLanguageMeta(language) { return new PHP(); case "python": return new Python(); + case "kotlin": + return new Kotlin(); + case "swift": + return new Swift(); default: - throw new Error(`Unknown language: ${language}`); + throw new Error(`Language '${language}' is not supported`); } } @@ -43,7 +49,7 @@ const typesLanguageOption = new Option( "-l, --language ", "The language of the types" ) - .choices(["ts", "php", "python"]) + .choices(["ts", "php", "python", "kotlin", "swift"]) .default("auto"); const typesCommand = actionRunner(async (rawOutputDirectory, {language}) => { @@ -60,7 +66,7 @@ const typesCommand = actionRunner(async (rawOutputDirectory, {language}) => { fs.mkdirSync(outputDirectory, { recursive: true }); } - if (!fs.statSync("appwrite.json").isFile()) { + if (!fs.existsSync("appwrite.json")) { throw new Error("appwrite.json not found in current directory"); } diff --git a/templates/cli/lib/type-generation/languages/kotlin.js.twig b/templates/cli/lib/type-generation/languages/kotlin.js.twig new file mode 100644 index 000000000..dec3052c9 --- /dev/null +++ b/templates/cli/lib/type-generation/languages/kotlin.js.twig @@ -0,0 +1,53 @@ +/** @typedef {import('../attribute').Attribute} Attribute */ +const { AttributeType } = require('../attribute'); +const { LanguageMeta } = require("./language"); + +class Kotlin extends LanguageMeta { + getType(attribute) { + let type = ""; + switch (attribute.type) { + case AttributeType.STRING: + case AttributeType.EMAIL: + case AttributeType.DATETIME: + case AttributeType.ENUM: + type = "String"; + break; + case AttributeType.INTEGER: + type = "Int"; + break; + case AttributeType.FLOAT: + type = "Float"; + break; + case AttributeType.BOOLEAN: + type = "Boolean"; + break; + case AttributeType.RELATIONSHIP: + type = "Map"; + default: + throw new Error(`Unknown attribute type: ${attribute.type}`); + } + if (attribute.array) { + type = "List<" + type + ">"; + } + if (attribute.required) { + type += "?"; + } + return type; + } + + getTemplate() { + return `package io.appwrite.models + +data class <%- toPascalCase(collection.name) %>( +<% for (const attribute of collection.attributes) { -%> + val <%- toCamelCase(attribute.key) %>: <%- getType(attribute) %>, +<% } -%> +)`; + } + + getFileName(collection) { + return LanguageMeta.toPascalCase(collection.name) + ".kt"; + } +} + +module.exports = { Kotlin }; \ No newline at end of file diff --git a/templates/cli/lib/type-generation/languages/language.js.twig b/templates/cli/lib/type-generation/languages/language.js.twig index 86b758349..2b6c93d65 100644 --- a/templates/cli/lib/type-generation/languages/language.js.twig +++ b/templates/cli/lib/type-generation/languages/language.js.twig @@ -52,7 +52,7 @@ class LanguageMeta { } /** - * Should types be generated in a single file? + * Returns true if the language uses a single file for all types. * * @returns {boolean} */ @@ -82,20 +82,40 @@ class LanguageMeta { } } +const existsFiles = (...files) => + files.some((file) => fs.existsSync(path.join(process.cwd(), file))); + /** * @returns {string} */ function detectLanguage() { - if ( - fs.existsSync(path.join(process.cwd(), "package.json")) || - fs.existsSync(path.join(process.cwd(), "tsconfig.json")) || - fs.existsSync(path.join(process.cwd(), "deno.json")) - ) { + if (existsFiles("package.json", "tsconfig.json", "deno.json")) { return "ts"; } - if (fs.existsSync(path.join(process.cwd(), "composer.json"))) { + if (existsFiles("composer.json")) { return "php"; } + if (existsFiles("requirements.txt", "Pipfile", "pyproject.toml")) { + return "python"; + } + if (existsFiles("Gemfile", "Rakefile")) { + return "ruby"; + } + if (existsFiles("build.gradle.kts")) { + return "kotlin"; + } + if (existsFiles("build.gradle", "pom.xml")) { + return "java"; + } + if (existsFiles("*.csproj")) { + return "dotnet"; + } + if (existsFiles("Package.swift")) { + return "swift"; + } + if (existsFiles("pubspec.yaml")) { + return "dart"; + } throw new Error("Could not detect language, please specify with -l"); } diff --git a/templates/cli/lib/type-generation/languages/php.js.twig b/templates/cli/lib/type-generation/languages/php.js.twig index ec8f75a70..01a7bf930 100644 --- a/templates/cli/lib/type-generation/languages/php.js.twig +++ b/templates/cli/lib/type-generation/languages/php.js.twig @@ -44,7 +44,7 @@ use Appwrite\\Models\\Document; class <%- toPascalCase(collection.name) %> extends Document { <% for (const attribute of collection.attributes ){ -%> public <%- getType(attribute) %> $<%- toCamelCase(attribute.key) %>; -<% } %> +<% } -%> }`; } diff --git a/templates/cli/lib/type-generation/languages/python.js.twig b/templates/cli/lib/type-generation/languages/python.js.twig index d092dab09..f71f65e77 100644 --- a/templates/cli/lib/type-generation/languages/python.js.twig +++ b/templates/cli/lib/type-generation/languages/python.js.twig @@ -42,14 +42,13 @@ class Python extends LanguageMeta { getTemplate() { return `import typing - <% for (const collection of collections) { %> class <%- toPascalCase(collection.name) %>(typing.TypedDict): <% for (const attribute of collection.attributes) { -%> <%- toSnakeCase(attribute.key) %>: <%- getType(attribute) %> <% } -%> -<% } %>`; +<% } -%>`; } getFileName(_) { diff --git a/templates/cli/lib/type-generation/languages/swift.js.twig b/templates/cli/lib/type-generation/languages/swift.js.twig new file mode 100644 index 000000000..5262ea0df --- /dev/null +++ b/templates/cli/lib/type-generation/languages/swift.js.twig @@ -0,0 +1,59 @@ +/** @typedef {import('../attribute').Attribute} Attribute */ +const { AttributeType } = require('../attribute'); +const { LanguageMeta } = require("./language"); + +class Swift extends LanguageMeta { + getType(attribute) { + let type = ""; + switch (attribute.type) { + case AttributeType.STRING: + case AttributeType.EMAIL: + case AttributeType.DATETIME: + case AttributeType.ENUM: + type = "String"; + break; + case AttributeType.INTEGER: + type = "Int"; + break; + case AttributeType.FLOAT: + type = "Double"; + break; + case AttributeType.BOOLEAN: + type = "Bool"; + break; + case AttributeType.RELATIONSHIP: + return "Map"; + default: + throw new Error(`Unknown attribute type: ${attribute.type}`); + } + if (attribute.array) { + type = "[" + type + "]"; + } + if (attribute.required) { + type += "?"; + } + return type; + } + + getTemplate() { + return `import Foundation +import Codable + +public class <%- toPascalCase(collection.name) %>: Codable { +<% for (const attribute of collection.attributes) { -%> + public var <%- toCamelCase(attribute.key) %>: <%- getType(attribute) %> +<% } %> + enum CodingKeys: String, CodingKey { +<% for (const attribute of collection.attributes) { -%> + case <%- toCamelCase(attribute.key) %> +<% } -%> + } +}`; + } + + getFileName(collection) { + return LanguageMeta.toPascalCase(collection.name) + ".swift"; + } +} + +module.exports = { Swift }; \ No newline at end of file diff --git a/templates/cli/lib/type-generation/languages/typescript.js.twig b/templates/cli/lib/type-generation/languages/typescript.js.twig index b78d4570a..69e738bf2 100644 --- a/templates/cli/lib/type-generation/languages/typescript.js.twig +++ b/templates/cli/lib/type-generation/languages/typescript.js.twig @@ -58,14 +58,12 @@ class TypeScript extends LanguageMeta { getTemplate() { return `import { Models } from '${this._getAppwriteDependency()}'; - <% for (const collection of collections) { %> export type <%- toPascalCase(collection.name) %> = Models.Document & { <% for (const attribute of collection.attributes) { -%> <%- toCamelCase(attribute.key) %>: <%- getType(attribute) %>; <% } -%> } - <% } %>`; } From d3c2134b04a62b0e87c26d612bce38786eadc9b3 Mon Sep 17 00:00:00 2001 From: loks0n <22452787+loks0n@users.noreply.github.com> Date: Fri, 8 Mar 2024 11:01:01 +0100 Subject: [PATCH 15/44] feat: java --- src/SDK/Language/CLI.php | 5 ++ templates/cli/lib/commands/types.js.twig | 5 +- .../type-generation/languages/java.js.twig | 55 +++++++++++++++++++ 3 files changed, 64 insertions(+), 1 deletion(-) create mode 100644 templates/cli/lib/type-generation/languages/java.js.twig diff --git a/src/SDK/Language/CLI.php b/src/SDK/Language/CLI.php index e8de4555d..5c6abdc36 100644 --- a/src/SDK/Language/CLI.php +++ b/src/SDK/Language/CLI.php @@ -162,6 +162,11 @@ public function getFiles(): array 'destination' => 'lib/type-generation/languages/swift.js', 'template' => 'cli/lib/type-generation/languages/swift.js.twig', ], + [ + 'scope' => 'default', + 'destination' => 'lib/type-generation/languages/java.js', + 'template' => 'cli/lib/type-generation/languages/java.js.twig', + ], [ 'scope' => 'default', 'destination' => 'lib/questions.js', diff --git a/templates/cli/lib/commands/types.js.twig b/templates/cli/lib/commands/types.js.twig index 5643927ff..d7c1b4efc 100644 --- a/templates/cli/lib/commands/types.js.twig +++ b/templates/cli/lib/commands/types.js.twig @@ -10,6 +10,7 @@ const { TypeScript } = require("../type-generation/languages/typescript"); const { Python } = require("../type-generation/languages/python"); const { Kotlin } = require("../type-generation/languages/kotlin"); const { Swift } = require("../type-generation/languages/swift"); +const { Java } = require("../type-generation/languages/java"); /** * @param {string} language @@ -27,6 +28,8 @@ function createLanguageMeta(language) { return new Kotlin(); case "swift": return new Swift(); + case "java": + return new Java(); default: throw new Error(`Language '${language}' is not supported`); } @@ -49,7 +52,7 @@ const typesLanguageOption = new Option( "-l, --language ", "The language of the types" ) - .choices(["ts", "php", "python", "kotlin", "swift"]) + .choices(["ts", "php", "python", "kotlin", "swift", "java"]) .default("auto"); const typesCommand = actionRunner(async (rawOutputDirectory, {language}) => { diff --git a/templates/cli/lib/type-generation/languages/java.js.twig b/templates/cli/lib/type-generation/languages/java.js.twig new file mode 100644 index 000000000..a16e06540 --- /dev/null +++ b/templates/cli/lib/type-generation/languages/java.js.twig @@ -0,0 +1,55 @@ +/** @typedef {import('../attribute').Attribute} Attribute */ +const { AttributeType } = require('../attribute'); +const { LanguageMeta } = require("./language"); + +class Java extends LanguageMeta { + getType(attribute) { + let type = ""; + switch (attribute.type) { + case AttributeType.STRING: + case AttributeType.EMAIL: + case AttributeType.DATETIME: + case AttributeType.ENUM: + type = "String"; + break; + case AttributeType.INTEGER: + type = "Int"; + break; + case AttributeType.FLOAT: + type = "Float"; + break; + case AttributeType.BOOLEAN: + type = "Boolean"; + break; + case AttributeType.RELATIONSHIP: + type = "Map"; + default: + throw new Error(`Unknown attribute type: ${attribute.type}`); + } + if (attribute.array) { + type = "List<" + type + ">"; + } + if (attribute.required) { + type += "?"; + } + return type; + } + + getTemplate() { + return `package io.appwrite.models; + +import java.util.*; + +public record <%- toPascalCase(collection.name) %>( +<% for (const [index, attribute] of Object.entries(collection.attributes)) { -%> + <%= getType(attribute) %> <%= toCamelCase(attribute.key) %><%- index < collection.attributes.length - 1 ? ',' : '' %> +<% } -%> +) { }`; + } + + getFileName(collection) { + return LanguageMeta.toPascalCase(collection.name) + ".java"; + } +} + +module.exports = { Java }; \ No newline at end of file From 69e401f2e6014b0117cd3a07f98c59c8a2d39967 Mon Sep 17 00:00:00 2001 From: loks0n <22452787+loks0n@users.noreply.github.com> Date: Fri, 8 Mar 2024 11:54:02 +0100 Subject: [PATCH 16/44] feat: php fix --- templates/cli/lib/type-generation/languages/php.js.twig | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/templates/cli/lib/type-generation/languages/php.js.twig b/templates/cli/lib/type-generation/languages/php.js.twig index 01a7bf930..bb5074e24 100644 --- a/templates/cli/lib/type-generation/languages/php.js.twig +++ b/templates/cli/lib/type-generation/languages/php.js.twig @@ -4,6 +4,9 @@ const { LanguageMeta } = require("./language"); class PHP extends LanguageMeta { getType(attribute) { + if (attribute.array) { + return "array"; + } let type = "" switch (attribute.type) { case AttributeType.STRING: @@ -26,9 +29,6 @@ class PHP extends LanguageMeta { default: throw new Error(`Unknown attribute type: ${attribute.type}`); } - if (attribute.array) { - type += "[]"; - } if (attribute.required) { type += "|null"; } From 61e475dd3b0a2898d2e74bc63b7a8ffd8090f7b5 Mon Sep 17 00:00:00 2001 From: loks0n <22452787+loks0n@users.noreply.github.com> Date: Sat, 9 Mar 2024 11:07:31 +0100 Subject: [PATCH 17/44] feat: python models --- src/SDK/Language/Python.php | 70 ++++++++++++++++++- templates/python/package/models/model.py.twig | 37 ++++++++++ 2 files changed, 106 insertions(+), 1 deletion(-) create mode 100644 templates/python/package/models/model.py.twig diff --git a/src/SDK/Language/Python.php b/src/SDK/Language/Python.php index 891d0d67d..8cb5c9b25 100644 --- a/src/SDK/Language/Python.php +++ b/src/SDK/Language/Python.php @@ -200,13 +200,18 @@ public function getFiles(): array 'destination' => '.travis.yml', 'template' => 'python/.travis.yml.twig', ], + [ + 'scope' => 'definition', + 'destination' => '{{ spec.title | caseSnake}}/models/{{ definition.name | caseSnake }}.py', + 'template' => 'python/package/models/model.py.twig', + ], [ 'scope' => 'enum', 'destination' => '{{ spec.title | caseSnake}}/enums/{{ enum.name | caseSnake }}.py', 'template' => 'python/package/enums/enum.py.twig', ], ]; - } +} /** * @param array $parameter @@ -344,12 +349,75 @@ public function getParamExample(array $param): string return $output; } + protected function getPropertyType(array $property, array $spec, ?string $generic = 'T'): string + { + if (\array_key_exists('sub_schema', $property)) { + $type = $this->toPascalCase($property['sub_schema']); + + if ($this->hasGenericType($property['sub_schema'], $spec)) { + $type .= '[' . $generic . ']'; + } + + if ($property['type'] === 'array') { + $type = 'List[' . $type . ']'; + } + } else { + $type = $this->getTypeName($property); + } + + if (!$property['required']) { + $type = 'Optional[' . $type . '] = None'; + } + + return $type; + } + + protected function hasGenericType(?string $model, array $spec): string + { + if (empty($model) || $model === 'any') { + return false; + } + + $model = $spec['definitions'][$model]; + + if ($model['additionalProperties']) { + return true; + } + + foreach ($model['properties'] as $property) { + if (!\array_key_exists('sub_schema', $property) || !$property['sub_schema']) { + continue; + } + + return $this->hasGenericType($property['sub_schema'], $spec); + } + + return false; + } + + protected function getModelType(array $definition, array $spec, ?string $generic = 'T'): string + { + if ($this->hasGenericType($definition['name'], $spec)) { + return $this->toPascalCase($definition['name']) . '(Generic[' . $generic . '])'; + } + return $this->toPascalCase($definition['name']); + } + public function getFilters(): array { return [ new TwigFilter('caseEnumKey', function (string $value) { return $this->toUpperSnakeCase($value); }), + new TwigFilter('hasGenericType', function (string $model, array $spec) { + return $this->hasGenericType($model, $spec); + }), + new TwigFilter('modelType', function (array $definition, array $spec, ?string $generic = 'T') { + return $this->getModelType($definition, $spec, $generic); + }), + new TwigFilter('propertyType', function (array $property, array $spec, ?string $generic = 'T') { + return $this->getPropertyType($property, $spec, $generic); + }), ]; } } diff --git a/templates/python/package/models/model.py.twig b/templates/python/package/models/model.py.twig new file mode 100644 index 000000000..e02d2d8a5 --- /dev/null +++ b/templates/python/package/models/model.py.twig @@ -0,0 +1,37 @@ +from typing import Optional, List, Dict, Any, Union, TypeVar, Generic +{% for property in definition.properties %} +{%~ if property.sub_schema %} +from .{{ property.sub_schema | caseSnake }} import {{ property.sub_schema | caseUcfirst }} +{% endif -%} +{% endfor -%} + +{% if definition.additionalProperties %} +T = TypeVar('T') +{% else -%} +{% for property in definition.properties -%} +{% if property.sub_schema -%} +T = TypeVar('T') +{% endif -%} +{% endfor -%} +{% endif %} + +""" +{{ definition.description }} +""" +class {{ definition | modelType(spec) | raw }}: + {%~ for property in definition.properties %} + """ {{ property.description }} """ + {{ property.name | removeDollarSign }}: {{ property | propertyType(spec) | raw }} + + {%~ endfor %} + {%~ if definition.additionalProperties %} + data: T + {%~ endif %} + + def __init__(self, {% for property in definition.properties %}{{ property.name | removeDollarSign }}: {{ property | propertyType(spec) | raw }}{% if not loop.last %}, {% endif %}{% endfor %}{% if definition.additionalProperties %}, data: T{% endif %}): + {%~ for property in definition.properties %} + self.{{ property.name | removeDollarSign }} = {{ property.name | removeDollarSign }} + {%~ endfor %} + {%~ if definition.additionalProperties %} + self.data = data + {%~ endif %} \ No newline at end of file From 78ecd2aea4ea4eb3731b780995cbada754272d17 Mon Sep 17 00:00:00 2001 From: ChiragAgg5k Date: Tue, 20 May 2025 16:05:47 +0530 Subject: [PATCH 18/44] chore: whitespace fixes --- templates/cli/lib/type-generation/attribute.js.twig | 3 ++- templates/cli/lib/type-generation/collection.js.twig | 3 ++- templates/cli/lib/type-generation/languages/java.js.twig | 2 +- templates/cli/lib/type-generation/languages/kotlin.js.twig | 2 +- templates/cli/lib/type-generation/languages/php.js.twig | 2 +- templates/cli/lib/type-generation/languages/python.js.twig | 2 +- templates/cli/lib/type-generation/languages/swift.js.twig | 2 +- templates/cli/lib/type-generation/languages/typescript.js.twig | 2 +- templates/python/package/models/model.py.twig | 2 +- templates/web/src/models.ts.twig | 2 +- 10 files changed, 12 insertions(+), 10 deletions(-) diff --git a/templates/cli/lib/type-generation/attribute.js.twig b/templates/cli/lib/type-generation/attribute.js.twig index e357b0d40..63b681240 100644 --- a/templates/cli/lib/type-generation/attribute.js.twig +++ b/templates/cli/lib/type-generation/attribute.js.twig @@ -24,4 +24,5 @@ const AttributeType = { module.exports = { AttributeType, - }; \ No newline at end of file + }; + \ No newline at end of file diff --git a/templates/cli/lib/type-generation/collection.js.twig b/templates/cli/lib/type-generation/collection.js.twig index 6fcb0dd71..e28477168 100644 --- a/templates/cli/lib/type-generation/collection.js.twig +++ b/templates/cli/lib/type-generation/collection.js.twig @@ -10,4 +10,5 @@ * @property {boolean} documentSecurity - Indicates if document-level security is enabled for the collection. * @property {import('./attribute.js).Attribute[]} attributes - The attributes (fields) defined in the collection. * @property {unknown[]} indexes - The indexes defined on the collection for optimized query performance. - */ \ No newline at end of file + */ + \ No newline at end of file diff --git a/templates/cli/lib/type-generation/languages/java.js.twig b/templates/cli/lib/type-generation/languages/java.js.twig index a16e06540..b2e38013c 100644 --- a/templates/cli/lib/type-generation/languages/java.js.twig +++ b/templates/cli/lib/type-generation/languages/java.js.twig @@ -52,4 +52,4 @@ public record <%- toPascalCase(collection.name) %>( } } -module.exports = { Java }; \ No newline at end of file +module.exports = { Java }; diff --git a/templates/cli/lib/type-generation/languages/kotlin.js.twig b/templates/cli/lib/type-generation/languages/kotlin.js.twig index dec3052c9..7087e506b 100644 --- a/templates/cli/lib/type-generation/languages/kotlin.js.twig +++ b/templates/cli/lib/type-generation/languages/kotlin.js.twig @@ -50,4 +50,4 @@ data class <%- toPascalCase(collection.name) %>( } } -module.exports = { Kotlin }; \ No newline at end of file +module.exports = { Kotlin }; diff --git a/templates/cli/lib/type-generation/languages/php.js.twig b/templates/cli/lib/type-generation/languages/php.js.twig index bb5074e24..97b8883ca 100644 --- a/templates/cli/lib/type-generation/languages/php.js.twig +++ b/templates/cli/lib/type-generation/languages/php.js.twig @@ -53,4 +53,4 @@ class <%- toPascalCase(collection.name) %> extends Document { } } -module.exports = { PHP }; \ No newline at end of file +module.exports = { PHP }; diff --git a/templates/cli/lib/type-generation/languages/python.js.twig b/templates/cli/lib/type-generation/languages/python.js.twig index f71f65e77..e7dea9c51 100644 --- a/templates/cli/lib/type-generation/languages/python.js.twig +++ b/templates/cli/lib/type-generation/languages/python.js.twig @@ -56,4 +56,4 @@ class <%- toPascalCase(collection.name) %>(typing.TypedDict): } } -module.exports = { Python }; \ No newline at end of file +module.exports = { Python }; diff --git a/templates/cli/lib/type-generation/languages/swift.js.twig b/templates/cli/lib/type-generation/languages/swift.js.twig index 5262ea0df..96b626938 100644 --- a/templates/cli/lib/type-generation/languages/swift.js.twig +++ b/templates/cli/lib/type-generation/languages/swift.js.twig @@ -56,4 +56,4 @@ public class <%- toPascalCase(collection.name) %>: Codable { } } -module.exports = { Swift }; \ No newline at end of file +module.exports = { Swift }; diff --git a/templates/cli/lib/type-generation/languages/typescript.js.twig b/templates/cli/lib/type-generation/languages/typescript.js.twig index 69e738bf2..67c2cf8a7 100644 --- a/templates/cli/lib/type-generation/languages/typescript.js.twig +++ b/templates/cli/lib/type-generation/languages/typescript.js.twig @@ -72,4 +72,4 @@ export type <%- toPascalCase(collection.name) %> = Models.Document & { } } -module.exports = { TypeScript }; \ No newline at end of file +module.exports = { TypeScript }; diff --git a/templates/python/package/models/model.py.twig b/templates/python/package/models/model.py.twig index e02d2d8a5..5145b8143 100644 --- a/templates/python/package/models/model.py.twig +++ b/templates/python/package/models/model.py.twig @@ -34,4 +34,4 @@ class {{ definition | modelType(spec) | raw }}: {%~ endfor %} {%~ if definition.additionalProperties %} self.data = data - {%~ endif %} \ No newline at end of file + {%~ endif %} diff --git a/templates/web/src/models.ts.twig b/templates/web/src/models.ts.twig index d86acbb1a..9ce8b0c59 100644 --- a/templates/web/src/models.ts.twig +++ b/templates/web/src/models.ts.twig @@ -15,6 +15,6 @@ export namespace Models { {% endfor %} {% if definition.additionalProperties %} [key: string]: any; {% endif %} - } + } {% endfor %} } From 271a4d620b279130dfe48128265b47625d536a9d Mon Sep 17 00:00:00 2001 From: ChiragAgg5k Date: Tue, 20 May 2025 16:07:27 +0530 Subject: [PATCH 19/44] chore: formatting --- src/SDK/Language/CLI.php | 2 +- src/SDK/Language/Python.php | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/SDK/Language/CLI.php b/src/SDK/Language/CLI.php index 9b90d808e..75c360167 100644 --- a/src/SDK/Language/CLI.php +++ b/src/SDK/Language/CLI.php @@ -320,7 +320,7 @@ public function getFiles(): array 'scope' => 'default', 'destination' => 'lib/commands/organizations.js', 'template' => 'cli/lib/commands/organizations.js.twig', - ], + ], [ 'scope' => 'default', 'destination' => 'lib/commands/types.js', diff --git a/src/SDK/Language/Python.php b/src/SDK/Language/Python.php index 90f6f59a3..fb0a318d4 100644 --- a/src/SDK/Language/Python.php +++ b/src/SDK/Language/Python.php @@ -231,7 +231,7 @@ public function getFiles(): array 'template' => 'python/package/enums/__init__.py.twig', ], ]; -} + } /** * @param array $parameter From a96ed05f439a51863a254bf0936f6eaf8faebe02 Mon Sep 17 00:00:00 2001 From: ChiragAgg5k Date: Tue, 20 May 2025 16:10:12 +0530 Subject: [PATCH 20/44] chore: more whitespace stuff --- templates/cli/lib/type-generation/attribute.js.twig | 1 - templates/cli/lib/type-generation/collection.js.twig | 1 - 2 files changed, 2 deletions(-) diff --git a/templates/cli/lib/type-generation/attribute.js.twig b/templates/cli/lib/type-generation/attribute.js.twig index 63b681240..a3ece599c 100644 --- a/templates/cli/lib/type-generation/attribute.js.twig +++ b/templates/cli/lib/type-generation/attribute.js.twig @@ -25,4 +25,3 @@ const AttributeType = { module.exports = { AttributeType, }; - \ No newline at end of file diff --git a/templates/cli/lib/type-generation/collection.js.twig b/templates/cli/lib/type-generation/collection.js.twig index e28477168..39f807050 100644 --- a/templates/cli/lib/type-generation/collection.js.twig +++ b/templates/cli/lib/type-generation/collection.js.twig @@ -11,4 +11,3 @@ * @property {import('./attribute.js).Attribute[]} attributes - The attributes (fields) defined in the collection. * @property {unknown[]} indexes - The indexes defined on the collection for optimized query performance. */ - \ No newline at end of file From 734fbc292d8330d69b82f2d5adbed605a10a6c71 Mon Sep 17 00:00:00 2001 From: ChiragAgg5k Date: Tue, 20 May 2025 17:04:39 +0530 Subject: [PATCH 21/44] chore: remove python generation --- src/SDK/Language/Python.php | 78 ------------------- templates/cli/lib/commands/types.js.twig | 2 - .../type-generation/languages/python.js.twig | 59 -------------- 3 files changed, 139 deletions(-) delete mode 100644 templates/cli/lib/type-generation/languages/python.js.twig diff --git a/src/SDK/Language/Python.php b/src/SDK/Language/Python.php index fb0a318d4..555fdc787 100644 --- a/src/SDK/Language/Python.php +++ b/src/SDK/Language/Python.php @@ -205,21 +205,6 @@ public function getFiles(): array 'destination' => '.github/workflows/publish.yml', 'template' => 'python/.github/workflows/publish.yml.twig', ], - [ - 'scope' => 'default', - 'destination' => '.github/workflows/publish.yml', - 'template' => 'python/.github/workflows/publish.yml.twig', - ], - [ - 'scope' => 'definition', - 'destination' => '{{ spec.title | caseSnake}}/models/{{ definition.name | caseSnake }}.py', - 'template' => 'python/package/models/model.py.twig', - ], - [ - 'scope' => 'definition', - 'destination' => '{{ spec.title | caseSnake}}/models/{{ definition.name | caseSnake }}.py', - 'template' => 'python/package/models/model.py.twig', - ], [ 'scope' => 'enum', 'destination' => '{{ spec.title | caseSnake}}/enums/{{ enum.name | caseSnake }}.py', @@ -379,60 +364,6 @@ public function getParamExample(array $param): string return $output; } - protected function getPropertyType(array $property, array $spec, ?string $generic = 'T'): string - { - if (\array_key_exists('sub_schema', $property)) { - $type = $this->toPascalCase($property['sub_schema']); - - if ($this->hasGenericType($property['sub_schema'], $spec)) { - $type .= '[' . $generic . ']'; - } - - if ($property['type'] === 'array') { - $type = 'List[' . $type . ']'; - } - } else { - $type = $this->getTypeName($property); - } - - if (!$property['required']) { - $type = 'Optional[' . $type . '] = None'; - } - - return $type; - } - - protected function hasGenericType(?string $model, array $spec): string - { - if (empty($model) || $model === 'any') { - return false; - } - - $model = $spec['definitions'][$model]; - - if ($model['additionalProperties']) { - return true; - } - - foreach ($model['properties'] as $property) { - if (!\array_key_exists('sub_schema', $property) || !$property['sub_schema']) { - continue; - } - - return $this->hasGenericType($property['sub_schema'], $spec); - } - - return false; - } - - protected function getModelType(array $definition, array $spec, ?string $generic = 'T'): string - { - if ($this->hasGenericType($definition['name'], $spec)) { - return $this->toPascalCase($definition['name']) . '(Generic[' . $generic . '])'; - } - return $this->toPascalCase($definition['name']); - } - public function getFilters(): array { return [ @@ -442,15 +373,6 @@ public function getFilters(): array new TwigFilter('getPropertyType', function ($value, $method = []) { return $this->getTypeName($value, $method); }), - new TwigFilter('hasGenericType', function (string $model, array $spec) { - return $this->hasGenericType($model, $spec); - }), - new TwigFilter('modelType', function (array $definition, array $spec, ?string $generic = 'T') { - return $this->getModelType($definition, $spec, $generic); - }), - new TwigFilter('propertyType', function (array $property, array $spec, ?string $generic = 'T') { - return $this->getPropertyType($property, $spec, $generic); - }), ]; } } diff --git a/templates/cli/lib/commands/types.js.twig b/templates/cli/lib/commands/types.js.twig index d7c1b4efc..9af350e2e 100644 --- a/templates/cli/lib/commands/types.js.twig +++ b/templates/cli/lib/commands/types.js.twig @@ -22,8 +22,6 @@ function createLanguageMeta(language) { return new TypeScript(); case "php": return new PHP(); - case "python": - return new Python(); case "kotlin": return new Kotlin(); case "swift": diff --git a/templates/cli/lib/type-generation/languages/python.js.twig b/templates/cli/lib/type-generation/languages/python.js.twig deleted file mode 100644 index e7dea9c51..000000000 --- a/templates/cli/lib/type-generation/languages/python.js.twig +++ /dev/null @@ -1,59 +0,0 @@ -/** @typedef {import('../attribute').Attribute} Attribute */ -const { AttributeType } = require('../attribute'); -const { LanguageMeta } = require("./language"); - -class Python extends LanguageMeta { - getType(attribute) { - let type = "" - switch (attribute.type) { - case AttributeType.STRING: - case AttributeType.EMAIL: - case AttributeType.DATETIME: - case AttributeType.ENUM: - type = "str"; - break; - case AttributeType.INTEGER: - type = "int"; - break; - case AttributeType.FLOAT: - type = "float"; - break; - case AttributeType.BOOLEAN: - type = "bool"; - break; - case AttributeType.RELATIONSHIP: - type = "typing.Any"; - default: - throw new Error(`Unknown attribute type: ${attribute.type}`); - } - if (attribute.array) { - type = `typing.List[${type}]`; - } - if (attribute.required) { - type += " | None"; - } - return type; - } - - isSingleFile() { - return true; - } - - getTemplate() { - return `import typing - -<% for (const collection of collections) { %> -class <%- toPascalCase(collection.name) %>(typing.TypedDict): -<% for (const attribute of collection.attributes) { -%> - <%- toSnakeCase(attribute.key) %>: <%- getType(attribute) %> -<% } -%> - -<% } -%>`; - } - - getFileName(_) { - return "collections.py"; - } -} - -module.exports = { Python }; From d4dc6d74b3d7cef3b213c5dbba34e7d6d8c9b3b7 Mon Sep 17 00:00:00 2001 From: ChiragAgg5k Date: Tue, 20 May 2025 17:21:15 +0530 Subject: [PATCH 22/44] chore: work in progress dart --- src/SDK/Language/CLI.php | 15 ++-- templates/cli/lib/commands/types.js.twig | 5 +- .../cli/lib/type-generation/attribute.js.twig | 11 --- .../lib/type-generation/collection.js.twig | 13 ---- .../type-generation/languages/dart.js.twig | 72 +++++++++++++++++++ 5 files changed, 81 insertions(+), 35 deletions(-) delete mode 100644 templates/cli/lib/type-generation/collection.js.twig create mode 100644 templates/cli/lib/type-generation/languages/dart.js.twig diff --git a/src/SDK/Language/CLI.php b/src/SDK/Language/CLI.php index 75c360167..4435eca58 100644 --- a/src/SDK/Language/CLI.php +++ b/src/SDK/Language/CLI.php @@ -186,11 +186,6 @@ public function getFiles(): array 'destination' => 'lib/type-generation/attribute.js', 'template' => 'cli/lib/type-generation/attribute.js.twig', ], - [ - 'scope' => 'default', - 'destination' => 'lib/type-generation/collection.js', - 'template' => 'cli/lib/type-generation/collection.js.twig', - ], [ 'scope' => 'default', 'destination' => 'lib/type-generation/languages/language.js', @@ -206,11 +201,6 @@ public function getFiles(): array 'destination' => 'lib/type-generation/languages/typescript.js', 'template' => 'cli/lib/type-generation/languages/typescript.js.twig', ], - [ - 'scope' => 'default', - 'destination' => 'lib/type-generation/languages/python.js', - 'template' => 'cli/lib/type-generation/languages/python.js.twig', - ], [ 'scope' => 'default', 'destination' => 'lib/type-generation/languages/kotlin.js', @@ -226,6 +216,11 @@ public function getFiles(): array 'destination' => 'lib/type-generation/languages/java.js', 'template' => 'cli/lib/type-generation/languages/java.js.twig', ], + [ + 'scope' => 'default', + 'destination' => 'lib/type-generation/languages/dart.js', + 'template' => 'cli/lib/type-generation/languages/dart.js.twig', + ], [ 'scope' => 'default', 'destination' => 'lib/questions.js', diff --git a/templates/cli/lib/commands/types.js.twig b/templates/cli/lib/commands/types.js.twig index 9af350e2e..368155e44 100644 --- a/templates/cli/lib/commands/types.js.twig +++ b/templates/cli/lib/commands/types.js.twig @@ -11,6 +11,7 @@ const { Python } = require("../type-generation/languages/python"); const { Kotlin } = require("../type-generation/languages/kotlin"); const { Swift } = require("../type-generation/languages/swift"); const { Java } = require("../type-generation/languages/java"); +const { Dart } = require("../type-generation/languages/dart"); /** * @param {string} language @@ -28,6 +29,8 @@ function createLanguageMeta(language) { return new Swift(); case "java": return new Java(); + case "dart": + return new Dart(); default: throw new Error(`Language '${language}' is not supported`); } @@ -50,7 +53,7 @@ const typesLanguageOption = new Option( "-l, --language ", "The language of the types" ) - .choices(["ts", "php", "python", "kotlin", "swift", "java"]) + .choices(["ts", "php", "kotlin", "swift", "java", "dart"]) .default("auto"); const typesCommand = actionRunner(async (rawOutputDirectory, {language}) => { diff --git a/templates/cli/lib/type-generation/attribute.js.twig b/templates/cli/lib/type-generation/attribute.js.twig index a3ece599c..e92f0f026 100644 --- a/templates/cli/lib/type-generation/attribute.js.twig +++ b/templates/cli/lib/type-generation/attribute.js.twig @@ -11,17 +11,6 @@ const AttributeType = { RELATIONSHIP: "relationship", }; -/** - * @typedef {Object} Attribute - * @property {string} key - The unique identifier of the attribute. - * @property {"string"|"integer"|"float"|"boolean"|"datetime"|"email"|"ip"|"url"|"enum"|"relationship"} type - The type of the attribute. - * @property {string} status - The status of the attribute. - * @property {boolean} required - The required status of the attribute. - * @property {boolean} array - The array status of the attribute. - * @property {number} size - The size of the attribute. - * @property {string} default - The default value of the attribute. - */ - module.exports = { AttributeType, }; diff --git a/templates/cli/lib/type-generation/collection.js.twig b/templates/cli/lib/type-generation/collection.js.twig deleted file mode 100644 index 39f807050..000000000 --- a/templates/cli/lib/type-generation/collection.js.twig +++ /dev/null @@ -1,13 +0,0 @@ -/** - * Represents a collection within a database. - * - * @typedef {Object} Collection - * @property {string} $id - The unique identifier of the collection. - * @property {string[]} $permissions - The permissions for accessing the collection. - * @property {string} databaseId - The identifier of the database this collection belongs to. - * @property {string} name - The name of the collection. - * @property {boolean} enabled - Indicates if the collection is enabled. - * @property {boolean} documentSecurity - Indicates if document-level security is enabled for the collection. - * @property {import('./attribute.js).Attribute[]} attributes - The attributes (fields) defined in the collection. - * @property {unknown[]} indexes - The indexes defined on the collection for optimized query performance. - */ diff --git a/templates/cli/lib/type-generation/languages/dart.js.twig b/templates/cli/lib/type-generation/languages/dart.js.twig new file mode 100644 index 000000000..60ea662a2 --- /dev/null +++ b/templates/cli/lib/type-generation/languages/dart.js.twig @@ -0,0 +1,72 @@ +/** @typedef {import('../attribute').Attribute} Attribute */ +const { AttributeType } = require('../attribute'); +const { LanguageMeta } = require("./language"); + +class Dart extends LanguageMeta { + getType(attribute) { + let type = ""; + switch (attribute.type) { + case AttributeType.STRING: + case AttributeType.EMAIL: + case AttributeType.DATETIME: + case AttributeType.ENUM: + type = "String"; + break; + case AttributeType.INTEGER: + type = "int"; + break; + case AttributeType.FLOAT: + type = "double"; + break; + case AttributeType.BOOLEAN: + type = "bool"; + break; + case AttributeType.RELATIONSHIP: + type = "Map"; + break; + default: + throw new Error(`Unknown attribute type: ${attribute.type}`); + } + if (attribute.array) { + type = `List<${type}>`; + } + if (!attribute.required) { + type += "?"; + } + return type; + } + + getTemplate() { + return `import 'package:appwrite/models.dart'; + +class <%- toPascalCase(collection.name) %> extends Document { + <% for (const attribute of collection.attributes) { -%> + final <%- getType(attribute) %> <%- toCamelCase(attribute.key) %>; + <% } -%> + + <%- toPascalCase(collection.name) %>({ + required String $id, + required String $collectionId, + required String $databaseId, + required DateTime $createdAt, + required DateTime $updatedAt, + <% for (const attribute of collection.attributes) { -%> + required this.<%- toCamelCase(attribute.key) %>, + <% } -%> + }) : super( + $id: $id, + $collectionId: $collectionId, + $databaseId: $databaseId, + $createdAt: $createdAt, + $updatedAt: $updatedAt, + ); +} +`; + } + + getFileName(collection) { + return LanguageMeta.toSnakeCase(collection.name) + ".dart"; + } +} + +module.exports = { Dart }; \ No newline at end of file From e0ea6eca99060ec1d003fdcb2ca0a53f93d88315 Mon Sep 17 00:00:00 2001 From: ChiragAgg5k Date: Tue, 20 May 2025 19:13:49 +0530 Subject: [PATCH 23/44] chore: fix dart --- .../type-generation/languages/dart.js.twig | 29 +++++-------------- 1 file changed, 8 insertions(+), 21 deletions(-) diff --git a/templates/cli/lib/type-generation/languages/dart.js.twig b/templates/cli/lib/type-generation/languages/dart.js.twig index 60ea662a2..4539e8ffc 100644 --- a/templates/cli/lib/type-generation/languages/dart.js.twig +++ b/templates/cli/lib/type-generation/languages/dart.js.twig @@ -37,29 +37,16 @@ class Dart extends LanguageMeta { } getTemplate() { - return `import 'package:appwrite/models.dart'; + return `class <%= toPascalCase(collection.name) %> { +<% for (const [index, attribute] of Object.entries(collection.attributes)) { -%> + <%= getType(attribute) %> <%= toCamelCase(attribute.key) %>; +<% } -%> -class <%- toPascalCase(collection.name) %> extends Document { - <% for (const attribute of collection.attributes) { -%> - final <%- getType(attribute) %> <%- toCamelCase(attribute.key) %>; + <%= toPascalCase(collection.name) %>({ + <% for (const [index, attribute] of Object.entries(collection.attributes)) { -%> + <% if (attribute.required) { %>required <% } %>this.<%= toCamelCase(attribute.key) %>, <% } -%> - - <%- toPascalCase(collection.name) %>({ - required String $id, - required String $collectionId, - required String $databaseId, - required DateTime $createdAt, - required DateTime $updatedAt, - <% for (const attribute of collection.attributes) { -%> - required this.<%- toCamelCase(attribute.key) %>, - <% } -%> - }) : super( - $id: $id, - $collectionId: $collectionId, - $databaseId: $databaseId, - $createdAt: $createdAt, - $updatedAt: $updatedAt, - ); +}); } `; } From 737a136cf5c3dd4e38711ad08256bd0afecf4e72 Mon Sep 17 00:00:00 2001 From: ChiragAgg5k Date: Tue, 20 May 2025 19:20:21 +0530 Subject: [PATCH 24/44] chore: remove python models --- templates/python/package/models/model.py.twig | 37 ------------------- 1 file changed, 37 deletions(-) delete mode 100644 templates/python/package/models/model.py.twig diff --git a/templates/python/package/models/model.py.twig b/templates/python/package/models/model.py.twig deleted file mode 100644 index 5145b8143..000000000 --- a/templates/python/package/models/model.py.twig +++ /dev/null @@ -1,37 +0,0 @@ -from typing import Optional, List, Dict, Any, Union, TypeVar, Generic -{% for property in definition.properties %} -{%~ if property.sub_schema %} -from .{{ property.sub_schema | caseSnake }} import {{ property.sub_schema | caseUcfirst }} -{% endif -%} -{% endfor -%} - -{% if definition.additionalProperties %} -T = TypeVar('T') -{% else -%} -{% for property in definition.properties -%} -{% if property.sub_schema -%} -T = TypeVar('T') -{% endif -%} -{% endfor -%} -{% endif %} - -""" -{{ definition.description }} -""" -class {{ definition | modelType(spec) | raw }}: - {%~ for property in definition.properties %} - """ {{ property.description }} """ - {{ property.name | removeDollarSign }}: {{ property | propertyType(spec) | raw }} - - {%~ endfor %} - {%~ if definition.additionalProperties %} - data: T - {%~ endif %} - - def __init__(self, {% for property in definition.properties %}{{ property.name | removeDollarSign }}: {{ property | propertyType(spec) | raw }}{% if not loop.last %}, {% endif %}{% endfor %}{% if definition.additionalProperties %}, data: T{% endif %}): - {%~ for property in definition.properties %} - self.{{ property.name | removeDollarSign }} = {{ property.name | removeDollarSign }} - {%~ endfor %} - {%~ if definition.additionalProperties %} - self.data = data - {%~ endif %} From a75de52cba68d1e97a6ccd82d7d5b4865f840066 Mon Sep 17 00:00:00 2001 From: ChiragAgg5k Date: Tue, 20 May 2025 19:31:00 +0530 Subject: [PATCH 25/44] chore: review changes --- templates/cli/lib/commands/types.js.twig | 3 +-- templates/cli/lib/type-generation/languages/java.js.twig | 1 + templates/cli/lib/type-generation/languages/kotlin.js.twig | 1 + templates/cli/lib/type-generation/languages/php.js.twig | 1 + templates/cli/lib/type-generation/languages/swift.js.twig | 1 + templates/cli/lib/type-generation/languages/typescript.js.twig | 3 +++ 6 files changed, 8 insertions(+), 2 deletions(-) diff --git a/templates/cli/lib/commands/types.js.twig b/templates/cli/lib/commands/types.js.twig index 368155e44..b89a9fe03 100644 --- a/templates/cli/lib/commands/types.js.twig +++ b/templates/cli/lib/commands/types.js.twig @@ -7,7 +7,6 @@ const { localConfig } = require("../config"); const { success, log, actionRunner } = require("../parser"); const { PHP } = require("../type-generation/languages/php"); const { TypeScript } = require("../type-generation/languages/typescript"); -const { Python } = require("../type-generation/languages/python"); const { Kotlin } = require("../type-generation/languages/kotlin"); const { Swift } = require("../type-generation/languages/swift"); const { Java } = require("../type-generation/languages/java"); @@ -53,7 +52,7 @@ const typesLanguageOption = new Option( "-l, --language ", "The language of the types" ) - .choices(["ts", "php", "kotlin", "swift", "java", "dart"]) + .choices(["auto", "ts", "php", "kotlin", "swift", "java", "dart"]) .default("auto"); const typesCommand = actionRunner(async (rawOutputDirectory, {language}) => { diff --git a/templates/cli/lib/type-generation/languages/java.js.twig b/templates/cli/lib/type-generation/languages/java.js.twig index b2e38013c..093c30a06 100644 --- a/templates/cli/lib/type-generation/languages/java.js.twig +++ b/templates/cli/lib/type-generation/languages/java.js.twig @@ -23,6 +23,7 @@ class Java extends LanguageMeta { break; case AttributeType.RELATIONSHIP: type = "Map"; + break; default: throw new Error(`Unknown attribute type: ${attribute.type}`); } diff --git a/templates/cli/lib/type-generation/languages/kotlin.js.twig b/templates/cli/lib/type-generation/languages/kotlin.js.twig index 7087e506b..ed90f14c2 100644 --- a/templates/cli/lib/type-generation/languages/kotlin.js.twig +++ b/templates/cli/lib/type-generation/languages/kotlin.js.twig @@ -23,6 +23,7 @@ class Kotlin extends LanguageMeta { break; case AttributeType.RELATIONSHIP: type = "Map"; + break; default: throw new Error(`Unknown attribute type: ${attribute.type}`); } diff --git a/templates/cli/lib/type-generation/languages/php.js.twig b/templates/cli/lib/type-generation/languages/php.js.twig index 97b8883ca..0c4443f52 100644 --- a/templates/cli/lib/type-generation/languages/php.js.twig +++ b/templates/cli/lib/type-generation/languages/php.js.twig @@ -26,6 +26,7 @@ class PHP extends LanguageMeta { break; case AttributeType.RELATIONSHIP: type = "mixed"; + break; default: throw new Error(`Unknown attribute type: ${attribute.type}`); } diff --git a/templates/cli/lib/type-generation/languages/swift.js.twig b/templates/cli/lib/type-generation/languages/swift.js.twig index 96b626938..2f09845a8 100644 --- a/templates/cli/lib/type-generation/languages/swift.js.twig +++ b/templates/cli/lib/type-generation/languages/swift.js.twig @@ -23,6 +23,7 @@ class Swift extends LanguageMeta { break; case AttributeType.RELATIONSHIP: return "Map"; + break; default: throw new Error(`Unknown attribute type: ${attribute.type}`); } diff --git a/templates/cli/lib/type-generation/languages/typescript.js.twig b/templates/cli/lib/type-generation/languages/typescript.js.twig index 67c2cf8a7..d1925a913 100644 --- a/templates/cli/lib/type-generation/languages/typescript.js.twig +++ b/templates/cli/lib/type-generation/languages/typescript.js.twig @@ -13,6 +13,8 @@ class TypeScript extends LanguageMeta { case AttributeType.EMAIL: case AttributeType.DATETIME: case AttributeType.ENUM: + case AttributeType.IP: + case AttributeType.URL: type = "string"; break; case AttributeType.INTEGER: @@ -26,6 +28,7 @@ class TypeScript extends LanguageMeta { break; case AttributeType.RELATIONSHIP: type = "any"; + break; default: throw new Error(`Unknown attribute type: ${attribute.type}`); } From eb8bd877a802bb34667cac670c12c0438fd99ce8 Mon Sep 17 00:00:00 2001 From: ChiragAgg5k Date: Fri, 30 May 2025 14:02:23 +0530 Subject: [PATCH 26/44] fix: type generation for java with class based approach --- .../type-generation/languages/java.js.twig | 63 ++++++++++++++++--- 1 file changed, 53 insertions(+), 10 deletions(-) diff --git a/templates/cli/lib/type-generation/languages/java.js.twig b/templates/cli/lib/type-generation/languages/java.js.twig index 093c30a06..dc1ebcdc4 100644 --- a/templates/cli/lib/type-generation/languages/java.js.twig +++ b/templates/cli/lib/type-generation/languages/java.js.twig @@ -13,16 +13,16 @@ class Java extends LanguageMeta { type = "String"; break; case AttributeType.INTEGER: - type = "Int"; + type = "Integer"; break; case AttributeType.FLOAT: - type = "Float"; + type = "Double"; break; case AttributeType.BOOLEAN: type = "Boolean"; break; case AttributeType.RELATIONSHIP: - type = "Map"; + type = "Map"; break; default: throw new Error(`Unknown attribute type: ${attribute.type}`); @@ -30,9 +30,6 @@ class Java extends LanguageMeta { if (attribute.array) { type = "List<" + type + ">"; } - if (attribute.required) { - type += "?"; - } return type; } @@ -40,12 +37,58 @@ class Java extends LanguageMeta { return `package io.appwrite.models; import java.util.*; - -public record <%- toPascalCase(collection.name) %>( + +public class <%- toPascalCase(collection.name) %> { +<% for (const attribute of collection.attributes) { -%> + private <%= getType(attribute) %> <%= toCamelCase(attribute.key) %>; +<% } -%> + + public <%- toPascalCase(collection.name) %>() { + } + + public <%- toPascalCase(collection.name) %>( +<% for (const [index, attribute] of Object.entries(collection.attributes)) { -%> + <%= getType(attribute) %> <%= toCamelCase(attribute.key) %><%- index < collection.attributes.length - 1 ? ',' : '' %> +<% } -%> + ) { +<% for (const attribute of collection.attributes) { -%> + this.<%= toCamelCase(attribute.key) %> = <%= toCamelCase(attribute.key) %>; +<% } -%> + } + +<% for (const attribute of collection.attributes) { -%> + public <%= getType(attribute) %> get<%- toPascalCase(attribute.key) %>() { + return <%= toCamelCase(attribute.key) %>; + } + + public void set<%- toPascalCase(attribute.key) %>(<%= getType(attribute) %> <%= toCamelCase(attribute.key) %>) { + this.<%= toCamelCase(attribute.key) %> = <%= toCamelCase(attribute.key) %>; + } + +<% } -%> + @Override + public boolean equals(Object obj) { + if (this == obj) return true; + if (obj == null || getClass() != obj.getClass()) return false; + <%- toPascalCase(collection.name) %> that = (<%- toPascalCase(collection.name) %>) obj; + return Objects.equals(<%= collection.attributes.map(attr => toCamelCase(attr.key)).join(', ') %><%- collection.attributes.length > 1 ? ', that.' + collection.attributes.map(attr => toCamelCase(attr.key)).join(', that.') : collection.attributes.length === 1 ? ', that.' + toCamelCase(collection.attributes[0].key) : '' %>); + } + + @Override + public int hashCode() { + return Objects.hash(<%= collection.attributes.map(attr => toCamelCase(attr.key)).join(', ') %>); + } + + @Override + public String toString() { + return "<%- toPascalCase(collection.name) %>{" + <% for (const [index, attribute] of Object.entries(collection.attributes)) { -%> - <%= getType(attribute) %> <%= toCamelCase(attribute.key) %><%- index < collection.attributes.length - 1 ? ',' : '' %> + "<%= toCamelCase(attribute.key) %>=" + <%= toCamelCase(attribute.key) %> + <% } -%> -) { }`; + '}'; + } +} +`; } getFileName(collection) { From 65a27c3f78c9ba4b16b657494bc0131c651654e1 Mon Sep 17 00:00:00 2001 From: ChiragAgg5k Date: Fri, 30 May 2025 14:09:11 +0530 Subject: [PATCH 27/44] fix: compilation error in java --- templates/cli/lib/type-generation/languages/java.js.twig | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/templates/cli/lib/type-generation/languages/java.js.twig b/templates/cli/lib/type-generation/languages/java.js.twig index dc1ebcdc4..84205687c 100644 --- a/templates/cli/lib/type-generation/languages/java.js.twig +++ b/templates/cli/lib/type-generation/languages/java.js.twig @@ -71,7 +71,8 @@ public class <%- toPascalCase(collection.name) %> { if (this == obj) return true; if (obj == null || getClass() != obj.getClass()) return false; <%- toPascalCase(collection.name) %> that = (<%- toPascalCase(collection.name) %>) obj; - return Objects.equals(<%= collection.attributes.map(attr => toCamelCase(attr.key)).join(', ') %><%- collection.attributes.length > 1 ? ', that.' + collection.attributes.map(attr => toCamelCase(attr.key)).join(', that.') : collection.attributes.length === 1 ? ', that.' + toCamelCase(collection.attributes[0].key) : '' %>); + return <% collection.attributes.forEach((attr, index) => { %>Objects.equals(<%= toCamelCase(attr.key) %>, that.<%= toCamelCase(attr.key) %>)<% if (index < collection.attributes.length - 1) { %> && + <% } }); %>; } @Override From 1eab080cd7254d432669cac08630ace86e14678d Mon Sep 17 00:00:00 2001 From: ChiragAgg5k Date: Fri, 30 May 2025 14:12:04 +0530 Subject: [PATCH 28/44] fix: php and swift --- templates/cli/lib/type-generation/languages/php.js.twig | 4 +--- templates/cli/lib/type-generation/languages/swift.js.twig | 2 +- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/templates/cli/lib/type-generation/languages/php.js.twig b/templates/cli/lib/type-generation/languages/php.js.twig index 0c4443f52..221b95421 100644 --- a/templates/cli/lib/type-generation/languages/php.js.twig +++ b/templates/cli/lib/type-generation/languages/php.js.twig @@ -40,9 +40,7 @@ class PHP extends LanguageMeta { return ` extends Document { +class <%- toPascalCase(collection.name) %> { <% for (const attribute of collection.attributes ){ -%> public <%- getType(attribute) %> $<%- toCamelCase(attribute.key) %>; <% } -%> diff --git a/templates/cli/lib/type-generation/languages/swift.js.twig b/templates/cli/lib/type-generation/languages/swift.js.twig index 2f09845a8..0ffe3e43a 100644 --- a/templates/cli/lib/type-generation/languages/swift.js.twig +++ b/templates/cli/lib/type-generation/languages/swift.js.twig @@ -42,7 +42,7 @@ import Codable public class <%- toPascalCase(collection.name) %>: Codable { <% for (const attribute of collection.attributes) { -%> - public var <%- toCamelCase(attribute.key) %>: <%- getType(attribute) %> + public let <%- toCamelCase(attribute.key) %>: <%- getType(attribute) %> <% } %> enum CodingKeys: String, CodingKey { <% for (const attribute of collection.attributes) { -%> From 59a6674f2bac30273568782a0186ebcc07e152e9 Mon Sep 17 00:00:00 2001 From: ChiragAgg5k Date: Fri, 30 May 2025 15:33:15 +0530 Subject: [PATCH 29/44] chore: return relationship type in dart --- templates/cli/lib/type-generation/languages/dart.js.twig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/templates/cli/lib/type-generation/languages/dart.js.twig b/templates/cli/lib/type-generation/languages/dart.js.twig index 4539e8ffc..5df85a276 100644 --- a/templates/cli/lib/type-generation/languages/dart.js.twig +++ b/templates/cli/lib/type-generation/languages/dart.js.twig @@ -22,7 +22,7 @@ class Dart extends LanguageMeta { type = "bool"; break; case AttributeType.RELATIONSHIP: - type = "Map"; + type = LanguageMeta.toPascalCase(attribute.relatedCollection); break; default: throw new Error(`Unknown attribute type: ${attribute.type}`); From 4cabce309f768c00f146169b9e92df837f429233 Mon Sep 17 00:00:00 2001 From: ChiragAgg5k Date: Fri, 30 May 2025 15:43:05 +0530 Subject: [PATCH 30/44] chore: add javascript jsdocs --- src/SDK/Language/CLI.php | 5 ++ templates/cli/lib/commands/types.js.twig | 5 +- .../languages/javascript.js.twig | 79 +++++++++++++++++++ .../languages/language.js.twig | 5 +- 4 files changed, 92 insertions(+), 2 deletions(-) create mode 100644 templates/cli/lib/type-generation/languages/javascript.js.twig diff --git a/src/SDK/Language/CLI.php b/src/SDK/Language/CLI.php index 4435eca58..5aecfbe24 100644 --- a/src/SDK/Language/CLI.php +++ b/src/SDK/Language/CLI.php @@ -201,6 +201,11 @@ public function getFiles(): array 'destination' => 'lib/type-generation/languages/typescript.js', 'template' => 'cli/lib/type-generation/languages/typescript.js.twig', ], + [ + 'scope' => 'default', + 'destination' => 'lib/type-generation/languages/javascript.js', + 'template' => 'cli/lib/type-generation/languages/javascript.js.twig', + ], [ 'scope' => 'default', 'destination' => 'lib/type-generation/languages/kotlin.js', diff --git a/templates/cli/lib/commands/types.js.twig b/templates/cli/lib/commands/types.js.twig index b89a9fe03..d1110b7ff 100644 --- a/templates/cli/lib/commands/types.js.twig +++ b/templates/cli/lib/commands/types.js.twig @@ -11,6 +11,7 @@ const { Kotlin } = require("../type-generation/languages/kotlin"); const { Swift } = require("../type-generation/languages/swift"); const { Java } = require("../type-generation/languages/java"); const { Dart } = require("../type-generation/languages/dart"); +const { JavaScript } = require("../type-generation/languages/javascript"); /** * @param {string} language @@ -20,6 +21,8 @@ function createLanguageMeta(language) { switch (language) { case "ts": return new TypeScript(); + case "js": + return new JavaScript(); case "php": return new PHP(); case "kotlin": @@ -52,7 +55,7 @@ const typesLanguageOption = new Option( "-l, --language ", "The language of the types" ) - .choices(["auto", "ts", "php", "kotlin", "swift", "java", "dart"]) + .choices(["auto", "ts", "js", "php", "kotlin", "swift", "java", "dart"]) .default("auto"); const typesCommand = actionRunner(async (rawOutputDirectory, {language}) => { diff --git a/templates/cli/lib/type-generation/languages/javascript.js.twig b/templates/cli/lib/type-generation/languages/javascript.js.twig new file mode 100644 index 000000000..6c8bff010 --- /dev/null +++ b/templates/cli/lib/type-generation/languages/javascript.js.twig @@ -0,0 +1,79 @@ +/** @typedef {import('../attribute').Attribute} Attribute */ +const fs = require("fs"); +const path = require("path"); + +const { AttributeType } = require('../attribute'); +const { LanguageMeta } = require("./language"); + +class JavaScript extends LanguageMeta { + getType(attribute) { + let type = "" + switch (attribute.type) { + case AttributeType.STRING: + case AttributeType.EMAIL: + case AttributeType.DATETIME: + case AttributeType.ENUM: + case AttributeType.IP: + case AttributeType.URL: + type = "string"; + break; + case AttributeType.INTEGER: + type = "number"; + break; + case AttributeType.FLOAT: + type = "number"; + break; + case AttributeType.BOOLEAN: + type = "boolean"; + break; + case AttributeType.RELATIONSHIP: + type = "any"; + break; + default: + throw new Error(`Unknown attribute type: ${attribute.type}`); + } + if (attribute.array) { + type += "[]"; + } + if (!attribute.required) { + type += "|null"; + } + return type; + } + + isSingleFile() { + return true; + } + + _getAppwriteDependency() { + if (fs.existsSync(path.resolve(process.cwd(), 'package.json'))) { + const packageJsonRaw = fs.readFileSync(path.resolve(process.cwd(), 'package.json')); + const packageJson = JSON.parse(packageJsonRaw.toString('utf-8')); + return packageJson.dependencies['node-appwrite'] ? 'node-appwrite' : 'appwrite'; + } + + return "appwrite"; + } + + getTemplate() { + return `/** + * @typedef {import('${this._getAppwriteDependency()}').Models.Document} Document + */ + +<% for (const collection of collections) { %> +/** + * @typedef {Object} <%- toPascalCase(collection.name) %> +<% for (const attribute of collection.attributes) { -%> + * @property {<%- getType(attribute) %>} <%- toCamelCase(attribute.key) %> +<% } -%> + */ + +<% } %>`; + } + + getFileName(_) { + return "appwrite-types.js"; + } +} + +module.exports = { JavaScript }; \ No newline at end of file diff --git a/templates/cli/lib/type-generation/languages/language.js.twig b/templates/cli/lib/type-generation/languages/language.js.twig index 2b6c93d65..501cd8929 100644 --- a/templates/cli/lib/type-generation/languages/language.js.twig +++ b/templates/cli/lib/type-generation/languages/language.js.twig @@ -89,9 +89,12 @@ const existsFiles = (...files) => * @returns {string} */ function detectLanguage() { - if (existsFiles("package.json", "tsconfig.json", "deno.json")) { + if (existsFiles("tsconfig.json", "deno.json")) { return "ts"; } + if (existsFiles("package.json")) { + return "js"; + } if (existsFiles("composer.json")) { return "php"; } From 0d46a41d6892b0730c7c2d236879f46b63256eb1 Mon Sep 17 00:00:00 2001 From: ChiragAgg5k Date: Fri, 30 May 2025 15:44:14 +0530 Subject: [PATCH 31/44] chore: add undefined for options in js --- templates/cli/lib/type-generation/languages/javascript.js.twig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/templates/cli/lib/type-generation/languages/javascript.js.twig b/templates/cli/lib/type-generation/languages/javascript.js.twig index 6c8bff010..392149a22 100644 --- a/templates/cli/lib/type-generation/languages/javascript.js.twig +++ b/templates/cli/lib/type-generation/languages/javascript.js.twig @@ -36,7 +36,7 @@ class JavaScript extends LanguageMeta { type += "[]"; } if (!attribute.required) { - type += "|null"; + type += "|null|undefined"; } return type; } From 70b0e938426ebca3ac0a360f1845e089912259c8 Mon Sep 17 00:00:00 2001 From: ChiragAgg5k Date: Fri, 30 May 2025 15:47:26 +0530 Subject: [PATCH 32/44] fix: add constructor for php lang --- .../cli/lib/type-generation/languages/php.js.twig | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/templates/cli/lib/type-generation/languages/php.js.twig b/templates/cli/lib/type-generation/languages/php.js.twig index 221b95421..6145b3593 100644 --- a/templates/cli/lib/type-generation/languages/php.js.twig +++ b/templates/cli/lib/type-generation/languages/php.js.twig @@ -44,6 +44,20 @@ class <%- toPascalCase(collection.name) %> { <% for (const attribute of collection.attributes ){ -%> public <%- getType(attribute) %> $<%- toCamelCase(attribute.key) %>; <% } -%> + + public function __construct( +<% for (const attribute of collection.attributes ){ -%> +<% if (attribute.required) { -%> + <%- getType(attribute).replace('|null', '') %> $<%- toCamelCase(attribute.key) %><% if (collection.attributes.indexOf(attribute) < collection.attributes.length - 1) { %>,<% } %> +<% } else { -%> + ?<%- getType(attribute).replace('|null', '') %> $<%- toCamelCase(attribute.key) %> = null<% if (collection.attributes.indexOf(attribute) < collection.attributes.length - 1) { %>,<% } %> +<% } -%> +<% } -%> + ) { +<% for (const attribute of collection.attributes ){ -%> + $this-><%- toCamelCase(attribute.key) %> = $<%- toCamelCase(attribute.key) %>; +<% } -%> + } }`; } From 46904602c9b28e4038819052fea53fd0996f8e98 Mon Sep 17 00:00:00 2001 From: ChiragAgg5k Date: Fri, 30 May 2025 15:48:35 +0530 Subject: [PATCH 33/44] fix: required --- templates/cli/lib/type-generation/languages/kotlin.js.twig | 2 +- templates/cli/lib/type-generation/languages/php.js.twig | 2 +- templates/cli/lib/type-generation/languages/swift.js.twig | 2 +- templates/cli/lib/type-generation/languages/typescript.js.twig | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/templates/cli/lib/type-generation/languages/kotlin.js.twig b/templates/cli/lib/type-generation/languages/kotlin.js.twig index ed90f14c2..1c5b64e42 100644 --- a/templates/cli/lib/type-generation/languages/kotlin.js.twig +++ b/templates/cli/lib/type-generation/languages/kotlin.js.twig @@ -30,7 +30,7 @@ class Kotlin extends LanguageMeta { if (attribute.array) { type = "List<" + type + ">"; } - if (attribute.required) { + if (!attribute.required) { type += "?"; } return type; diff --git a/templates/cli/lib/type-generation/languages/php.js.twig b/templates/cli/lib/type-generation/languages/php.js.twig index 6145b3593..8bb1870ae 100644 --- a/templates/cli/lib/type-generation/languages/php.js.twig +++ b/templates/cli/lib/type-generation/languages/php.js.twig @@ -30,7 +30,7 @@ class PHP extends LanguageMeta { default: throw new Error(`Unknown attribute type: ${attribute.type}`); } - if (attribute.required) { + if (!attribute.required) { type += "|null"; } return type; diff --git a/templates/cli/lib/type-generation/languages/swift.js.twig b/templates/cli/lib/type-generation/languages/swift.js.twig index 0ffe3e43a..4136f7307 100644 --- a/templates/cli/lib/type-generation/languages/swift.js.twig +++ b/templates/cli/lib/type-generation/languages/swift.js.twig @@ -30,7 +30,7 @@ class Swift extends LanguageMeta { if (attribute.array) { type = "[" + type + "]"; } - if (attribute.required) { + if (!attribute.required) { type += "?"; } return type; diff --git a/templates/cli/lib/type-generation/languages/typescript.js.twig b/templates/cli/lib/type-generation/languages/typescript.js.twig index d1925a913..0bba4e950 100644 --- a/templates/cli/lib/type-generation/languages/typescript.js.twig +++ b/templates/cli/lib/type-generation/languages/typescript.js.twig @@ -35,7 +35,7 @@ class TypeScript extends LanguageMeta { if (attribute.array) { type += "[]"; } - if (attribute.required) { + if (!attribute.required) { type += " | null"; } return type; From 2e4b6435c52ad6132c1922601e109db94c80a0d9 Mon Sep 17 00:00:00 2001 From: ChiragAgg5k Date: Fri, 30 May 2025 15:54:49 +0530 Subject: [PATCH 34/44] feat: add getters and setters for php --- templates/cli/lib/commands/types.js.twig | 2 +- .../cli/lib/type-generation/languages/php.js.twig | 12 +++++++++++- 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/templates/cli/lib/commands/types.js.twig b/templates/cli/lib/commands/types.js.twig index d1110b7ff..db6dead66 100644 --- a/templates/cli/lib/commands/types.js.twig +++ b/templates/cli/lib/commands/types.js.twig @@ -114,7 +114,7 @@ const typesCommand = actionRunner(async (rawOutputDirectory, {language}) => { } } - success(); + success(`Generated types for all the listed collections`); }); const types = new Command("types") diff --git a/templates/cli/lib/type-generation/languages/php.js.twig b/templates/cli/lib/type-generation/languages/php.js.twig index 8bb1870ae..3990442f8 100644 --- a/templates/cli/lib/type-generation/languages/php.js.twig +++ b/templates/cli/lib/type-generation/languages/php.js.twig @@ -42,7 +42,7 @@ namespace Appwrite\\Models; class <%- toPascalCase(collection.name) %> { <% for (const attribute of collection.attributes ){ -%> - public <%- getType(attribute) %> $<%- toCamelCase(attribute.key) %>; + private <%- getType(attribute) %> $<%- toCamelCase(attribute.key) %>; <% } -%> public function __construct( @@ -58,6 +58,16 @@ class <%- toPascalCase(collection.name) %> { $this-><%- toCamelCase(attribute.key) %> = $<%- toCamelCase(attribute.key) %>; <% } -%> } + +<% for (const attribute of collection.attributes ){ -%> + public function get<%- toPascalCase(attribute.key) %>(): <%- getType(attribute) %> { + return $this-><%- toCamelCase(attribute.key) %>; + } + + public function set<%- toPascalCase(attribute.key) %>(<%- getType(attribute) %> $<%- toCamelCase(attribute.key) %>): void { + $this-><%- toCamelCase(attribute.key) %> = $<%- toCamelCase(attribute.key) %>; + } +<% } -%> }`; } From b8f07c0f9a995df1a13d49ffccfc49af3bc79fe3 Mon Sep 17 00:00:00 2001 From: ChiragAgg5k Date: Fri, 30 May 2025 16:06:47 +0530 Subject: [PATCH 35/44] feat: complete codable for swift --- .../type-generation/languages/swift.js.twig | 85 ++++++++++++++++++- 1 file changed, 83 insertions(+), 2 deletions(-) diff --git a/templates/cli/lib/type-generation/languages/swift.js.twig b/templates/cli/lib/type-generation/languages/swift.js.twig index 4136f7307..db1ca771c 100644 --- a/templates/cli/lib/type-generation/languages/swift.js.twig +++ b/templates/cli/lib/type-generation/languages/swift.js.twig @@ -38,7 +38,6 @@ class Swift extends LanguageMeta { getTemplate() { return `import Foundation -import Codable public class <%- toPascalCase(collection.name) %>: Codable { <% for (const attribute of collection.attributes) { -%> @@ -46,9 +45,91 @@ public class <%- toPascalCase(collection.name) %>: Codable { <% } %> enum CodingKeys: String, CodingKey { <% for (const attribute of collection.attributes) { -%> - case <%- toCamelCase(attribute.key) %> + case <%- toCamelCase(attribute.key) %> = "<%- attribute.key %>" <% } -%> } + + init( +<% for (const attribute of collection.attributes) { -%> + <%- toCamelCase(attribute.key) %>: <%- getType(attribute) %><% if (attribute !== collection.attributes[collection.attributes.length - 1]) { %>,<% } %> +<% } -%> + ) { +<% for (const attribute of collection.attributes) { -%> + self.<%- toCamelCase(attribute.key) %> = <%- toCamelCase(attribute.key) %> +<% } -%> + } + + public required init(from decoder: Decoder) throws { + let container = try decoder.container(keyedBy: CodingKeys.self) + +<% for (const attribute of collection.attributes) { -%> +<% if (attribute.required) { -%> + self.<%- toCamelCase(attribute.key) %> = try container.decode(<%- getType(attribute).replace('?', '') %>.self, forKey: .<%- toCamelCase(attribute.key) %>) +<% } else { -%> + self.<%- toCamelCase(attribute.key) %> = try container.decodeIfPresent(<%- getType(attribute).replace('?', '') %>.self, forKey: .<%- toCamelCase(attribute.key) %>) +<% } -%> +<% } -%> + } + + public func encode(to encoder: Encoder) throws { + var container = encoder.container(keyedBy: CodingKeys.self) + +<% for (const attribute of collection.attributes) { -%> +<% if (attribute.required) { -%> + try container.encode(<%- toCamelCase(attribute.key) %>, forKey: .<%- toCamelCase(attribute.key) %>) +<% } else { -%> + try container.encodeIfPresent(<%- toCamelCase(attribute.key) %>, forKey: .<%- toCamelCase(attribute.key) %>) +<% } -%> +<% } -%> + } + + public func toMap() -> [String: Any] { + return [ +<% for (const attribute of collection.attributes) { -%> +<% if (attribute.type === 'relationship') { -%> + "<%- attribute.key %>": <%- toCamelCase(attribute.key) %> as Any<% if (attribute !== collection.attributes[collection.attributes.length - 1]) { %>,<% } %> +<% } else if (attribute.array && attribute.type !== 'string' && attribute.type !== 'integer' && attribute.type !== 'float' && attribute.type !== 'boolean') { -%> + "<%- attribute.key %>": <%- toCamelCase(attribute.key) %>?.map { $0.toMap() } as Any<% if (attribute !== collection.attributes[collection.attributes.length - 1]) { %>,<% } %> +<% } else { -%> + "<%- attribute.key %>": <%- toCamelCase(attribute.key) %> as Any<% if (attribute !== collection.attributes[collection.attributes.length - 1]) { %>,<% } %> +<% } -%> +<% } -%> + ] + } + + public static func from(map: [String: Any]) -> <%- toPascalCase(collection.name) %> { + return <%- toPascalCase(collection.name) %>( +<% for (const attribute of collection.attributes) { -%> +<% if (attribute.type === 'relationship') { -%> + <%- toCamelCase(attribute.key) %>: map["<%- attribute.key %>"] as<% if (!attribute.required) { %>?<% } else { %>!<% } %> Map<% if (attribute !== collection.attributes[collection.attributes.length - 1]) { %>,<% } %> +<% } else if (attribute.array) { -%> +<% if (attribute.type === 'string') { -%> + <%- toCamelCase(attribute.key) %>: map["<%- attribute.key %>"] as<% if (!attribute.required) { %>?<% } else { %>!<% } %> [String]<% if (attribute !== collection.attributes[collection.attributes.length - 1]) { %>,<% } %> +<% } else if (attribute.type === 'integer') { -%> + <%- toCamelCase(attribute.key) %>: map["<%- attribute.key %>"] as<% if (!attribute.required) { %>?<% } else { %>!<% } %> [Int]<% if (attribute !== collection.attributes[collection.attributes.length - 1]) { %>,<% } %> +<% } else if (attribute.type === 'float') { -%> + <%- toCamelCase(attribute.key) %>: map["<%- attribute.key %>"] as<% if (!attribute.required) { %>?<% } else { %>!<% } %> [Double]<% if (attribute !== collection.attributes[collection.attributes.length - 1]) { %>,<% } %> +<% } else if (attribute.type === 'boolean') { -%> + <%- toCamelCase(attribute.key) %>: map["<%- attribute.key %>"] as<% if (!attribute.required) { %>?<% } else { %>!<% } %> [Bool]<% if (attribute !== collection.attributes[collection.attributes.length - 1]) { %>,<% } %> +<% } else { -%> + <%- toCamelCase(attribute.key) %>: (map["<%- attribute.key %>"] as<% if (!attribute.required) { %>?<% } else { %>!<% } %> [[String: Any]])<% if (!attribute.required) { %>?<% } %>.map { <%- toPascalCase(attribute.type) %>.from(map: $0) }<% if (attribute !== collection.attributes[collection.attributes.length - 1]) { %>,<% } %> +<% } -%> +<% } else { -%> +<% if (attribute.type === 'string' || attribute.type === 'email' || attribute.type === 'datetime' || attribute.type === 'enum') { -%> + <%- toCamelCase(attribute.key) %>: map["<%- attribute.key %>"] as<% if (!attribute.required) { %>?<% } else { %>!<% } %> String<% if (attribute !== collection.attributes[collection.attributes.length - 1]) { %>,<% } %> +<% } else if (attribute.type === 'integer') { -%> + <%- toCamelCase(attribute.key) %>: map["<%- attribute.key %>"] as<% if (!attribute.required) { %>?<% } else { %>!<% } %> Int<% if (attribute !== collection.attributes[collection.attributes.length - 1]) { %>,<% } %> +<% } else if (attribute.type === 'float') { -%> + <%- toCamelCase(attribute.key) %>: map["<%- attribute.key %>"] as<% if (!attribute.required) { %>?<% } else { %>!<% } %> Double<% if (attribute !== collection.attributes[collection.attributes.length - 1]) { %>,<% } %> +<% } else if (attribute.type === 'boolean') { -%> + <%- toCamelCase(attribute.key) %>: map["<%- attribute.key %>"] as<% if (!attribute.required) { %>?<% } else { %>!<% } %> Bool<% if (attribute !== collection.attributes[collection.attributes.length - 1]) { %>,<% } %> +<% } else { -%> + <%- toCamelCase(attribute.key) %>: <%- toPascalCase(attribute.type) %>.from(map: map["<%- attribute.key %>"] as! [String: Any])<% if (attribute !== collection.attributes[collection.attributes.length - 1]) { %>,<% } %> +<% } -%> +<% } -%> +<% } -%> + ) + } }`; } From ea25a5680896659534ec5fb9d93a364cdfe8443a Mon Sep 17 00:00:00 2001 From: ChiragAgg5k Date: Fri, 30 May 2025 18:16:55 +0530 Subject: [PATCH 36/44] chore: fix naming with spaces --- templates/cli/lib/type-generation/languages/language.js.twig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/templates/cli/lib/type-generation/languages/language.js.twig b/templates/cli/lib/type-generation/languages/language.js.twig index 501cd8929..96ba0bba2 100644 --- a/templates/cli/lib/type-generation/languages/language.js.twig +++ b/templates/cli/lib/type-generation/languages/language.js.twig @@ -31,7 +31,7 @@ class LanguageMeta { } static toCamelCase(string) { - return this.toKebabCase(string).replace(/-([a-z])/g, (g) => + return this.toKebabCase(string).replace(/-([a-z0-9])/g, (g) => g[1].toUpperCase() ); } From 7a74cc6098f1baeada98081228bc0a770c49d675 Mon Sep 17 00:00:00 2001 From: ChiragAgg5k Date: Fri, 30 May 2025 18:59:51 +0530 Subject: [PATCH 37/44] feat: fix relationship attribute mapping --- .../lib/type-generation/languages/dart.js.twig | 13 +++++++++++-- .../lib/type-generation/languages/java.js.twig | 18 +++++++++++++----- .../languages/javascript.js.twig | 5 ++++- .../type-generation/languages/kotlin.js.twig | 13 +++++++++++-- .../lib/type-generation/languages/php.js.twig | 11 ++++++++++- .../type-generation/languages/swift.js.twig | 7 +++++-- .../languages/typescript.js.twig | 5 ++++- 7 files changed, 58 insertions(+), 14 deletions(-) diff --git a/templates/cli/lib/type-generation/languages/dart.js.twig b/templates/cli/lib/type-generation/languages/dart.js.twig index 5df85a276..840e39266 100644 --- a/templates/cli/lib/type-generation/languages/dart.js.twig +++ b/templates/cli/lib/type-generation/languages/dart.js.twig @@ -23,6 +23,9 @@ class Dart extends LanguageMeta { break; case AttributeType.RELATIONSHIP: type = LanguageMeta.toPascalCase(attribute.relatedCollection); + if ((attribute.relationType === 'oneToMany' && attribute.side === 'parent') || attribute.relationType === 'manyToMany') { + type = `List<${type}>`; + } break; default: throw new Error(`Unknown attribute type: ${attribute.type}`); @@ -37,9 +40,15 @@ class Dart extends LanguageMeta { } getTemplate() { - return `class <%= toPascalCase(collection.name) %> { + return `<% for (const attribute of collection.attributes) { -%> +<% if (attribute.type === 'relationship') { -%> +import '<%- attribute.relatedCollection.toLowerCase() %>.dart'; + +<% } -%> +<% } -%> +class <%= toPascalCase(collection.name) %> { <% for (const [index, attribute] of Object.entries(collection.attributes)) { -%> - <%= getType(attribute) %> <%= toCamelCase(attribute.key) %>; + <%- getType(attribute) %> <%= toCamelCase(attribute.key) %>; <% } -%> <%= toPascalCase(collection.name) %>({ diff --git a/templates/cli/lib/type-generation/languages/java.js.twig b/templates/cli/lib/type-generation/languages/java.js.twig index 84205687c..f0cf106cb 100644 --- a/templates/cli/lib/type-generation/languages/java.js.twig +++ b/templates/cli/lib/type-generation/languages/java.js.twig @@ -22,7 +22,10 @@ class Java extends LanguageMeta { type = "Boolean"; break; case AttributeType.RELATIONSHIP: - type = "Map"; + type = LanguageMeta.toPascalCase(attribute.relatedCollection); + if ((attribute.relationType === 'oneToMany' && attribute.side === 'parent') || attribute.relationType === 'manyToMany') { + type = "List<" + type + ">"; + } break; default: throw new Error(`Unknown attribute type: ${attribute.type}`); @@ -37,10 +40,15 @@ class Java extends LanguageMeta { return `package io.appwrite.models; import java.util.*; +<% for (const attribute of collection.attributes) { -%> +<% if (attribute.type === 'relationship') { -%> +import <%- toPascalCase(attribute.relatedCollection) %>; +<% } -%> +<% } -%> public class <%- toPascalCase(collection.name) %> { <% for (const attribute of collection.attributes) { -%> - private <%= getType(attribute) %> <%= toCamelCase(attribute.key) %>; + private <%- getType(attribute) %> <%= toCamelCase(attribute.key) %>; <% } -%> public <%- toPascalCase(collection.name) %>() { @@ -48,7 +56,7 @@ public class <%- toPascalCase(collection.name) %> { public <%- toPascalCase(collection.name) %>( <% for (const [index, attribute] of Object.entries(collection.attributes)) { -%> - <%= getType(attribute) %> <%= toCamelCase(attribute.key) %><%- index < collection.attributes.length - 1 ? ',' : '' %> + <%- getType(attribute) %> <%= toCamelCase(attribute.key) %><%- index < collection.attributes.length - 1 ? ',' : '' %> <% } -%> ) { <% for (const attribute of collection.attributes) { -%> @@ -57,11 +65,11 @@ public class <%- toPascalCase(collection.name) %> { } <% for (const attribute of collection.attributes) { -%> - public <%= getType(attribute) %> get<%- toPascalCase(attribute.key) %>() { + public <%- getType(attribute) %> get<%- toPascalCase(attribute.key) %>() { return <%= toCamelCase(attribute.key) %>; } - public void set<%- toPascalCase(attribute.key) %>(<%= getType(attribute) %> <%= toCamelCase(attribute.key) %>) { + public void set<%- toPascalCase(attribute.key) %>(<%- getType(attribute) %> <%= toCamelCase(attribute.key) %>) { this.<%= toCamelCase(attribute.key) %> = <%= toCamelCase(attribute.key) %>; } diff --git a/templates/cli/lib/type-generation/languages/javascript.js.twig b/templates/cli/lib/type-generation/languages/javascript.js.twig index 392149a22..80d500fbe 100644 --- a/templates/cli/lib/type-generation/languages/javascript.js.twig +++ b/templates/cli/lib/type-generation/languages/javascript.js.twig @@ -27,7 +27,10 @@ class JavaScript extends LanguageMeta { type = "boolean"; break; case AttributeType.RELATIONSHIP: - type = "any"; + type = LanguageMeta.toPascalCase(attribute.relatedCollection); + if ((attribute.relationType === 'oneToMany' && attribute.side === 'parent') || attribute.relationType === 'manyToMany') { + type = `Array<${type}>`; + } break; default: throw new Error(`Unknown attribute type: ${attribute.type}`); diff --git a/templates/cli/lib/type-generation/languages/kotlin.js.twig b/templates/cli/lib/type-generation/languages/kotlin.js.twig index 1c5b64e42..d7d60ef3e 100644 --- a/templates/cli/lib/type-generation/languages/kotlin.js.twig +++ b/templates/cli/lib/type-generation/languages/kotlin.js.twig @@ -22,7 +22,10 @@ class Kotlin extends LanguageMeta { type = "Boolean"; break; case AttributeType.RELATIONSHIP: - type = "Map"; + type = LanguageMeta.toPascalCase(attribute.relatedCollection); + if ((attribute.relationType === 'oneToMany' && attribute.side === 'parent') || attribute.relationType === 'manyToMany') { + type = `List<${type}>`; + } break; default: throw new Error(`Unknown attribute type: ${attribute.type}`); @@ -38,7 +41,13 @@ class Kotlin extends LanguageMeta { getTemplate() { return `package io.appwrite.models - + +<% for (const attribute of collection.attributes) { -%> +<% if (attribute.type === 'relationship') { -%> +import <%- toPascalCase(attribute.relatedCollection) %> + +<% } -%> +<% } -%> data class <%- toPascalCase(collection.name) %>( <% for (const attribute of collection.attributes) { -%> val <%- toCamelCase(attribute.key) %>: <%- getType(attribute) %>, diff --git a/templates/cli/lib/type-generation/languages/php.js.twig b/templates/cli/lib/type-generation/languages/php.js.twig index 3990442f8..61b9fed79 100644 --- a/templates/cli/lib/type-generation/languages/php.js.twig +++ b/templates/cli/lib/type-generation/languages/php.js.twig @@ -25,7 +25,10 @@ class PHP extends LanguageMeta { type = "bool"; break; case AttributeType.RELATIONSHIP: - type = "mixed"; + type = LanguageMeta.toPascalCase(attribute.relatedCollection); + if ((attribute.relationType === 'oneToMany' && attribute.side === 'parent') || attribute.relationType === 'manyToMany') { + type = "array"; + } break; default: throw new Error(`Unknown attribute type: ${attribute.type}`); @@ -40,6 +43,12 @@ class PHP extends LanguageMeta { return ` +<% if (attribute.type === 'relationship' && !(attribute.relationType === 'manyToMany') && !(attribute.relationType === 'oneToMany' && attribute.side === 'parent')) { -%> +use Appwrite\\Models\\<%- toPascalCase(attribute.relatedCollection) %>; + +<% } -%> +<% } -%> class <%- toPascalCase(collection.name) %> { <% for (const attribute of collection.attributes ){ -%> private <%- getType(attribute) %> $<%- toCamelCase(attribute.key) %>; diff --git a/templates/cli/lib/type-generation/languages/swift.js.twig b/templates/cli/lib/type-generation/languages/swift.js.twig index db1ca771c..f8d8f9438 100644 --- a/templates/cli/lib/type-generation/languages/swift.js.twig +++ b/templates/cli/lib/type-generation/languages/swift.js.twig @@ -22,7 +22,10 @@ class Swift extends LanguageMeta { type = "Bool"; break; case AttributeType.RELATIONSHIP: - return "Map"; + type = LanguageMeta.toPascalCase(attribute.relatedCollection); + if ((attribute.relationType === 'oneToMany' && attribute.side === 'parent') || attribute.relationType === 'manyToMany') { + type = `[${type}]`; + } break; default: throw new Error(`Unknown attribute type: ${attribute.type}`); @@ -101,7 +104,7 @@ public class <%- toPascalCase(collection.name) %>: Codable { return <%- toPascalCase(collection.name) %>( <% for (const attribute of collection.attributes) { -%> <% if (attribute.type === 'relationship') { -%> - <%- toCamelCase(attribute.key) %>: map["<%- attribute.key %>"] as<% if (!attribute.required) { %>?<% } else { %>!<% } %> Map<% if (attribute !== collection.attributes[collection.attributes.length - 1]) { %>,<% } %> + <%- toCamelCase(attribute.key) %>: map["<%- attribute.key %>"] as<% if (!attribute.required) { %>?<% } else { %>!<% } %> <%- toPascalCase(attribute.relatedCollection) %><% if (attribute !== collection.attributes[collection.attributes.length - 1]) { %>,<% } %> <% } else if (attribute.array) { -%> <% if (attribute.type === 'string') { -%> <%- toCamelCase(attribute.key) %>: map["<%- attribute.key %>"] as<% if (!attribute.required) { %>?<% } else { %>!<% } %> [String]<% if (attribute !== collection.attributes[collection.attributes.length - 1]) { %>,<% } %> diff --git a/templates/cli/lib/type-generation/languages/typescript.js.twig b/templates/cli/lib/type-generation/languages/typescript.js.twig index 0bba4e950..70c7ba5b0 100644 --- a/templates/cli/lib/type-generation/languages/typescript.js.twig +++ b/templates/cli/lib/type-generation/languages/typescript.js.twig @@ -27,7 +27,10 @@ class TypeScript extends LanguageMeta { type = "boolean"; break; case AttributeType.RELATIONSHIP: - type = "any"; + type = LanguageMeta.toPascalCase(attribute.relatedCollection); + if ((attribute.relationType === 'oneToMany' && attribute.side === 'parent') || attribute.relationType === 'manyToMany') { + type = `${type}[]`; + } break; default: throw new Error(`Unknown attribute type: ${attribute.type}`); From 47e51e1fd9d1aec1da0269055d0ed3dd7f30e03a Mon Sep 17 00:00:00 2001 From: ChiragAgg5k Date: Fri, 30 May 2025 20:02:29 +0530 Subject: [PATCH 38/44] fix: missing relationship, use primitives in java --- templates/cli/lib/type-generation/languages/dart.js.twig | 2 +- templates/cli/lib/type-generation/languages/java.js.twig | 8 ++++---- .../cli/lib/type-generation/languages/javascript.js.twig | 2 +- .../cli/lib/type-generation/languages/kotlin.js.twig | 2 +- templates/cli/lib/type-generation/languages/php.js.twig | 2 +- templates/cli/lib/type-generation/languages/swift.js.twig | 2 +- .../cli/lib/type-generation/languages/typescript.js.twig | 2 +- 7 files changed, 10 insertions(+), 10 deletions(-) diff --git a/templates/cli/lib/type-generation/languages/dart.js.twig b/templates/cli/lib/type-generation/languages/dart.js.twig index 840e39266..cb3dee70b 100644 --- a/templates/cli/lib/type-generation/languages/dart.js.twig +++ b/templates/cli/lib/type-generation/languages/dart.js.twig @@ -23,7 +23,7 @@ class Dart extends LanguageMeta { break; case AttributeType.RELATIONSHIP: type = LanguageMeta.toPascalCase(attribute.relatedCollection); - if ((attribute.relationType === 'oneToMany' && attribute.side === 'parent') || attribute.relationType === 'manyToMany') { + if ((attribute.relationType === 'oneToMany' && attribute.side === 'parent') (attribute.relationType === 'manyToOne' && attribute.side === 'child') || || attribute.relationType === 'manyToMany') { type = `List<${type}>`; } break; diff --git a/templates/cli/lib/type-generation/languages/java.js.twig b/templates/cli/lib/type-generation/languages/java.js.twig index f0cf106cb..94ac59807 100644 --- a/templates/cli/lib/type-generation/languages/java.js.twig +++ b/templates/cli/lib/type-generation/languages/java.js.twig @@ -13,17 +13,17 @@ class Java extends LanguageMeta { type = "String"; break; case AttributeType.INTEGER: - type = "Integer"; + type = "int"; break; case AttributeType.FLOAT: - type = "Double"; + type = "double"; break; case AttributeType.BOOLEAN: - type = "Boolean"; + type = "boolean"; break; case AttributeType.RELATIONSHIP: type = LanguageMeta.toPascalCase(attribute.relatedCollection); - if ((attribute.relationType === 'oneToMany' && attribute.side === 'parent') || attribute.relationType === 'manyToMany') { + if ((attribute.relationType === 'oneToMany' && attribute.side === 'parent') || (attribute.relationType === 'manyToOne' && attribute.side === 'child') || attribute.relationType === 'manyToMany') { type = "List<" + type + ">"; } break; diff --git a/templates/cli/lib/type-generation/languages/javascript.js.twig b/templates/cli/lib/type-generation/languages/javascript.js.twig index 80d500fbe..08197895e 100644 --- a/templates/cli/lib/type-generation/languages/javascript.js.twig +++ b/templates/cli/lib/type-generation/languages/javascript.js.twig @@ -28,7 +28,7 @@ class JavaScript extends LanguageMeta { break; case AttributeType.RELATIONSHIP: type = LanguageMeta.toPascalCase(attribute.relatedCollection); - if ((attribute.relationType === 'oneToMany' && attribute.side === 'parent') || attribute.relationType === 'manyToMany') { + if ((attribute.relationType === 'oneToMany' && attribute.side === 'parent') || (attribute.relationType === 'manyToOne' && attribute.side === 'child') || attribute.relationType === 'manyToMany') { type = `Array<${type}>`; } break; diff --git a/templates/cli/lib/type-generation/languages/kotlin.js.twig b/templates/cli/lib/type-generation/languages/kotlin.js.twig index d7d60ef3e..3e3c1f866 100644 --- a/templates/cli/lib/type-generation/languages/kotlin.js.twig +++ b/templates/cli/lib/type-generation/languages/kotlin.js.twig @@ -23,7 +23,7 @@ class Kotlin extends LanguageMeta { break; case AttributeType.RELATIONSHIP: type = LanguageMeta.toPascalCase(attribute.relatedCollection); - if ((attribute.relationType === 'oneToMany' && attribute.side === 'parent') || attribute.relationType === 'manyToMany') { + if ((attribute.relationType === 'oneToMany' && attribute.side === 'parent') || (attribute.relationType === 'manyToOne' && attribute.side === 'child') || attribute.relationType === 'manyToMany') { type = `List<${type}>`; } break; diff --git a/templates/cli/lib/type-generation/languages/php.js.twig b/templates/cli/lib/type-generation/languages/php.js.twig index 61b9fed79..74a328951 100644 --- a/templates/cli/lib/type-generation/languages/php.js.twig +++ b/templates/cli/lib/type-generation/languages/php.js.twig @@ -26,7 +26,7 @@ class PHP extends LanguageMeta { break; case AttributeType.RELATIONSHIP: type = LanguageMeta.toPascalCase(attribute.relatedCollection); - if ((attribute.relationType === 'oneToMany' && attribute.side === 'parent') || attribute.relationType === 'manyToMany') { + if ((attribute.relationType === 'oneToMany' && attribute.side === 'parent') || (attribute.relationType === 'manyToOne' && attribute.side === 'child') || attribute.relationType === 'manyToMany') { type = "array"; } break; diff --git a/templates/cli/lib/type-generation/languages/swift.js.twig b/templates/cli/lib/type-generation/languages/swift.js.twig index f8d8f9438..624b600d6 100644 --- a/templates/cli/lib/type-generation/languages/swift.js.twig +++ b/templates/cli/lib/type-generation/languages/swift.js.twig @@ -23,7 +23,7 @@ class Swift extends LanguageMeta { break; case AttributeType.RELATIONSHIP: type = LanguageMeta.toPascalCase(attribute.relatedCollection); - if ((attribute.relationType === 'oneToMany' && attribute.side === 'parent') || attribute.relationType === 'manyToMany') { + if ((attribute.relationType === 'oneToMany' && attribute.side === 'parent') || (attribute.relationType === 'manyToOne' && attribute.side === 'child') || attribute.relationType === 'manyToMany') { type = `[${type}]`; } break; diff --git a/templates/cli/lib/type-generation/languages/typescript.js.twig b/templates/cli/lib/type-generation/languages/typescript.js.twig index 70c7ba5b0..fd43d3314 100644 --- a/templates/cli/lib/type-generation/languages/typescript.js.twig +++ b/templates/cli/lib/type-generation/languages/typescript.js.twig @@ -28,7 +28,7 @@ class TypeScript extends LanguageMeta { break; case AttributeType.RELATIONSHIP: type = LanguageMeta.toPascalCase(attribute.relatedCollection); - if ((attribute.relationType === 'oneToMany' && attribute.side === 'parent') || attribute.relationType === 'manyToMany') { + if ((attribute.relationType === 'oneToMany' && attribute.side === 'parent') || (attribute.relationType === 'manyToOne' && attribute.side === 'child') || attribute.relationType === 'manyToMany') { type = `${type}[]`; } break; From 0f601555b5ef650b1d3b4bd8dece6394fbd02bf6 Mon Sep 17 00:00:00 2001 From: ChiragAgg5k Date: Fri, 30 May 2025 20:19:50 +0530 Subject: [PATCH 39/44] feat: add support for enum --- .../lib/type-generation/languages/dart.js.twig | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/templates/cli/lib/type-generation/languages/dart.js.twig b/templates/cli/lib/type-generation/languages/dart.js.twig index cb3dee70b..47ce0a296 100644 --- a/templates/cli/lib/type-generation/languages/dart.js.twig +++ b/templates/cli/lib/type-generation/languages/dart.js.twig @@ -9,8 +9,10 @@ class Dart extends LanguageMeta { case AttributeType.STRING: case AttributeType.EMAIL: case AttributeType.DATETIME: - case AttributeType.ENUM: type = "String"; + if (attribute.format === AttributeType.ENUM) { + type = LanguageMeta.toPascalCase(attribute.key); + } break; case AttributeType.INTEGER: type = "int"; @@ -23,7 +25,7 @@ class Dart extends LanguageMeta { break; case AttributeType.RELATIONSHIP: type = LanguageMeta.toPascalCase(attribute.relatedCollection); - if ((attribute.relationType === 'oneToMany' && attribute.side === 'parent') (attribute.relationType === 'manyToOne' && attribute.side === 'child') || || attribute.relationType === 'manyToMany') { + if ((attribute.relationType === 'oneToMany' && attribute.side === 'parent') || (attribute.relationType === 'manyToOne' && attribute.side === 'child') || attribute.relationType === 'manyToMany') { type = `List<${type}>`; } break; @@ -44,6 +46,16 @@ class Dart extends LanguageMeta { <% if (attribute.type === 'relationship') { -%> import '<%- attribute.relatedCollection.toLowerCase() %>.dart'; +<% } -%> +<% } -%> +<% for (const attribute of collection.attributes) { -%> +<% if (attribute.format === 'enum') { -%> +enum <%- toPascalCase(attribute.key) %> { +<% for (const element of attribute.elements) { -%> + <%- toPascalCase(element) %>, +<% } -%> +} + <% } -%> <% } -%> class <%= toPascalCase(collection.name) %> { From 41282ce589ad0dd59a883cbe6988f4a7d517cddb Mon Sep 17 00:00:00 2001 From: ChiragAgg5k Date: Fri, 30 May 2025 20:37:55 +0530 Subject: [PATCH 40/44] feat: add java enum --- .../lib/type-generation/languages/dart.js.twig | 2 +- .../lib/type-generation/languages/java.js.twig | 15 ++++++++++++++- 2 files changed, 15 insertions(+), 2 deletions(-) diff --git a/templates/cli/lib/type-generation/languages/dart.js.twig b/templates/cli/lib/type-generation/languages/dart.js.twig index 47ce0a296..6e702a43b 100644 --- a/templates/cli/lib/type-generation/languages/dart.js.twig +++ b/templates/cli/lib/type-generation/languages/dart.js.twig @@ -52,7 +52,7 @@ import '<%- attribute.relatedCollection.toLowerCase() %>.dart'; <% if (attribute.format === 'enum') { -%> enum <%- toPascalCase(attribute.key) %> { <% for (const element of attribute.elements) { -%> - <%- toPascalCase(element) %>, + <%- element %>, <% } -%> } diff --git a/templates/cli/lib/type-generation/languages/java.js.twig b/templates/cli/lib/type-generation/languages/java.js.twig index 94ac59807..fbb184ddd 100644 --- a/templates/cli/lib/type-generation/languages/java.js.twig +++ b/templates/cli/lib/type-generation/languages/java.js.twig @@ -9,8 +9,10 @@ class Java extends LanguageMeta { case AttributeType.STRING: case AttributeType.EMAIL: case AttributeType.DATETIME: - case AttributeType.ENUM: type = "String"; + if (attribute.format === AttributeType.ENUM) { + type = LanguageMeta.toPascalCase(attribute.key); + } break; case AttributeType.INTEGER: type = "int"; @@ -47,6 +49,17 @@ import <%- toPascalCase(attribute.relatedCollection) %>; <% } -%> <% } -%> public class <%- toPascalCase(collection.name) %> { +<% for (const attribute of collection.attributes) { -%> +<% if (attribute.format === 'enum') { -%> + + public enum <%- toPascalCase(attribute.key) %> { +<% for (const [index, element] of Object.entries(attribute.elements)) { -%> + <%- element %><%- index < attribute.elements.length - 1 ? ',' : ';' %> +<% } -%> + } + +<% } -%> +<% } -%> <% for (const attribute of collection.attributes) { -%> private <%- getType(attribute) %> <%= toCamelCase(attribute.key) %>; <% } -%> From d9ca85a0a299f4c0336daec4c22b0ecd1fcadd3b Mon Sep 17 00:00:00 2001 From: ChiragAgg5k Date: Sat, 31 May 2025 13:11:01 +0530 Subject: [PATCH 41/44] chore: complete num for js and kotlin --- .../type-generation/languages/javascript.js.twig | 4 +++- .../lib/type-generation/languages/kotlin.js.twig | 14 +++++++++++++- 2 files changed, 16 insertions(+), 2 deletions(-) diff --git a/templates/cli/lib/type-generation/languages/javascript.js.twig b/templates/cli/lib/type-generation/languages/javascript.js.twig index 08197895e..f6d632e71 100644 --- a/templates/cli/lib/type-generation/languages/javascript.js.twig +++ b/templates/cli/lib/type-generation/languages/javascript.js.twig @@ -12,10 +12,12 @@ class JavaScript extends LanguageMeta { case AttributeType.STRING: case AttributeType.EMAIL: case AttributeType.DATETIME: - case AttributeType.ENUM: case AttributeType.IP: case AttributeType.URL: type = "string"; + if (attribute.format === AttributeType.ENUM) { + type = `"${attribute.elements.join('"|"')}"`; + } break; case AttributeType.INTEGER: type = "number"; diff --git a/templates/cli/lib/type-generation/languages/kotlin.js.twig b/templates/cli/lib/type-generation/languages/kotlin.js.twig index 3e3c1f866..85c380a43 100644 --- a/templates/cli/lib/type-generation/languages/kotlin.js.twig +++ b/templates/cli/lib/type-generation/languages/kotlin.js.twig @@ -9,8 +9,10 @@ class Kotlin extends LanguageMeta { case AttributeType.STRING: case AttributeType.EMAIL: case AttributeType.DATETIME: - case AttributeType.ENUM: type = "String"; + if (attribute.format === AttributeType.ENUM) { + type = LanguageMeta.toPascalCase(attribute.key); + } break; case AttributeType.INTEGER: type = "Int"; @@ -46,6 +48,16 @@ class Kotlin extends LanguageMeta { <% if (attribute.type === 'relationship') { -%> import <%- toPascalCase(attribute.relatedCollection) %> +<% } -%> +<% } -%> +<% for (const attribute of collection.attributes) { -%> +<% if (attribute.format === 'enum') { -%> +enum class <%- toPascalCase(attribute.key) %> { +<% for (const [index, element] of Object.entries(attribute.elements)) { -%> + <%- element %><%- index < attribute.elements.length - 1 ? ',' : '' %> +<% } -%> +} + <% } -%> <% } -%> data class <%- toPascalCase(collection.name) %>( From 5ffd5fa78d60f92bf0a879340b1f3178a0950acf Mon Sep 17 00:00:00 2001 From: ChiragAgg5k Date: Sat, 31 May 2025 18:10:07 +0530 Subject: [PATCH 42/44] chore: finish enum for php ts and swift --- .../lib/type-generation/languages/php.js.twig | 14 +++++++++++++- .../lib/type-generation/languages/swift.js.twig | 14 +++++++++++++- .../type-generation/languages/typescript.js.twig | 16 +++++++++++++++- 3 files changed, 41 insertions(+), 3 deletions(-) diff --git a/templates/cli/lib/type-generation/languages/php.js.twig b/templates/cli/lib/type-generation/languages/php.js.twig index 74a328951..6dd185397 100644 --- a/templates/cli/lib/type-generation/languages/php.js.twig +++ b/templates/cli/lib/type-generation/languages/php.js.twig @@ -12,8 +12,10 @@ class PHP extends LanguageMeta { case AttributeType.STRING: case AttributeType.EMAIL: case AttributeType.DATETIME: - case AttributeType.ENUM: type = "string"; + if (attribute.format === AttributeType.ENUM) { + type = LanguageMeta.toPascalCase(attribute.key); + } break; case AttributeType.INTEGER: type = "int"; @@ -47,6 +49,16 @@ namespace Appwrite\\Models; <% if (attribute.type === 'relationship' && !(attribute.relationType === 'manyToMany') && !(attribute.relationType === 'oneToMany' && attribute.side === 'parent')) { -%> use Appwrite\\Models\\<%- toPascalCase(attribute.relatedCollection) %>; +<% } -%> +<% } -%> +<% for (const attribute of collection.attributes) { -%> +<% if (attribute.format === 'enum') { -%> +enum <%- toPascalCase(attribute.key) %> { +<% for (const [index, element] of Object.entries(attribute.elements)) { -%> + case <%- element.toUpperCase() %> = '<%- element %>'; +<% } -%> +} + <% } -%> <% } -%> class <%- toPascalCase(collection.name) %> { diff --git a/templates/cli/lib/type-generation/languages/swift.js.twig b/templates/cli/lib/type-generation/languages/swift.js.twig index 624b600d6..67c64d89c 100644 --- a/templates/cli/lib/type-generation/languages/swift.js.twig +++ b/templates/cli/lib/type-generation/languages/swift.js.twig @@ -9,8 +9,10 @@ class Swift extends LanguageMeta { case AttributeType.STRING: case AttributeType.EMAIL: case AttributeType.DATETIME: - case AttributeType.ENUM: type = "String"; + if (attribute.format === AttributeType.ENUM) { + type = LanguageMeta.toPascalCase(attribute.key); + } break; case AttributeType.INTEGER: type = "Int"; @@ -42,6 +44,16 @@ class Swift extends LanguageMeta { getTemplate() { return `import Foundation +<% for (const attribute of collection.attributes) { -%> +<% if (attribute.format === 'enum') { -%> +public enum <%- toPascalCase(attribute.key) %>: String, Codable, CaseIterable { +<% for (const [index, element] of Object.entries(attribute.elements)) { -%> + case <%- element %> = "<%- element %>" +<% } -%> +} + +<% } -%> +<% } -%> public class <%- toPascalCase(collection.name) %>: Codable { <% for (const attribute of collection.attributes) { -%> public let <%- toCamelCase(attribute.key) %>: <%- getType(attribute) %> diff --git a/templates/cli/lib/type-generation/languages/typescript.js.twig b/templates/cli/lib/type-generation/languages/typescript.js.twig index fd43d3314..6c11267d2 100644 --- a/templates/cli/lib/type-generation/languages/typescript.js.twig +++ b/templates/cli/lib/type-generation/languages/typescript.js.twig @@ -12,10 +12,12 @@ class TypeScript extends LanguageMeta { case AttributeType.STRING: case AttributeType.EMAIL: case AttributeType.DATETIME: - case AttributeType.ENUM: case AttributeType.IP: case AttributeType.URL: type = "string"; + if (attribute.format === AttributeType.ENUM) { + type = LanguageMeta.toPascalCase(attribute.key); + } break; case AttributeType.INTEGER: type = "number"; @@ -64,6 +66,18 @@ class TypeScript extends LanguageMeta { getTemplate() { return `import { Models } from '${this._getAppwriteDependency()}'; + +<% for (const collection of collections) { -%> +<% for (const attribute of collection.attributes) { -%> +<% if (attribute.format === 'enum') { -%> +export enum <%- toPascalCase(attribute.key) %> { +<% for (const [index, element] of Object.entries(attribute.elements)) { -%> + <%- element.toUpperCase() %> = "<%- element %>", +<% } -%> +} +<% } -%> +<% } -%> +<% } -%> <% for (const collection of collections) { %> export type <%- toPascalCase(collection.name) %> = Models.Document & { <% for (const attribute of collection.attributes) { -%> From 41efeed5903c4be8afc50420400b0c5fda1ac4d1 Mon Sep 17 00:00:00 2001 From: Chirag Aggarwal Date: Sat, 31 May 2025 18:34:22 +0530 Subject: [PATCH 43/44] Update templates/cli/lib/type-generation/languages/java.js.twig Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- templates/cli/lib/type-generation/languages/java.js.twig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/templates/cli/lib/type-generation/languages/java.js.twig b/templates/cli/lib/type-generation/languages/java.js.twig index fbb184ddd..fb91be928 100644 --- a/templates/cli/lib/type-generation/languages/java.js.twig +++ b/templates/cli/lib/type-generation/languages/java.js.twig @@ -61,7 +61,7 @@ public class <%- toPascalCase(collection.name) %> { <% } -%> <% } -%> <% for (const attribute of collection.attributes) { -%> - private <%- getType(attribute) %> <%= toCamelCase(attribute.key) %>; + private <%- getType(attribute) %> <%- toCamelCase(attribute.key) %>; <% } -%> public <%- toPascalCase(collection.name) %>() { From 8a759ca5ded6bfbcc86bd194ee4ec3e8ed5e6559 Mon Sep 17 00:00:00 2001 From: ChiragAgg5k Date: Sat, 31 May 2025 20:52:54 +0530 Subject: [PATCH 44/44] feat: add factory and toMap methods for Dart model generation --- .../type-generation/languages/dart.js.twig | 72 +++++++++++++++++++ 1 file changed, 72 insertions(+) diff --git a/templates/cli/lib/type-generation/languages/dart.js.twig b/templates/cli/lib/type-generation/languages/dart.js.twig index 6e702a43b..65453e40e 100644 --- a/templates/cli/lib/type-generation/languages/dart.js.twig +++ b/templates/cli/lib/type-generation/languages/dart.js.twig @@ -68,6 +68,78 @@ class <%= toPascalCase(collection.name) %> { <% if (attribute.required) { %>required <% } %>this.<%= toCamelCase(attribute.key) %>, <% } -%> }); + + factory <%= toPascalCase(collection.name) %>.fromMap(Map map) { + return <%= toPascalCase(collection.name) %>( +<% for (const [index, attribute] of Object.entries(collection.attributes)) { -%> + <%= toCamelCase(attribute.key) %>: <% if (attribute.type === 'string' || attribute.type === 'email' || attribute.type === 'datetime') { -%> +<% if (attribute.format === 'enum') { -%> +<% if (attribute.array) { -%> +(map['<%= attribute.key %>'] as List?)?.map((e) => <%- toPascalCase(attribute.key) %>.values.firstWhere((element) => element.name == e)).toList()<% if (!attribute.required) { %> ?? []<% } -%> +<% } else { -%> +<% if (!attribute.required) { -%> +map['<%= attribute.key %>'] != null ? <%- toPascalCase(attribute.key) %>.values.where((e) => e.name == map['<%= attribute.key %>']).firstOrNull : null<% } else { -%> +<%- toPascalCase(attribute.key) %>.values.firstWhere((e) => e.name == map['<%= attribute.key %>'])<% } -%> +<% } -%> +<% } else { -%> +<% if (attribute.array) { -%> +List.from(map['<%= attribute.key %>'] ?? [])<% if (!attribute.required) { %> ?? []<% } -%> +<% } else { -%> +map['<%= attribute.key %>']<% if (!attribute.required) { %>?<% } %>.toString()<% if (!attribute.required) { %> ?? null<% } -%> +<% } -%> +<% } -%> +<% } else if (attribute.type === 'integer') { -%> +<% if (attribute.array) { -%> +List.from(map['<%= attribute.key %>'] ?? [])<% if (!attribute.required) { %> ?? []<% } -%> +<% } else { -%> +map['<%= attribute.key %>']<% if (!attribute.required) { %> ?? null<% } -%> +<% } -%> +<% } else if (attribute.type === 'float') { -%> +<% if (attribute.array) { -%> +List.from(map['<%= attribute.key %>'] ?? [])<% if (!attribute.required) { %> ?? []<% } -%> +<% } else { -%> +map['<%= attribute.key %>']<% if (!attribute.required) { %> ?? null<% } -%> +<% } -%> +<% } else if (attribute.type === 'boolean') { -%> +<% if (attribute.array) { -%> +List.from(map['<%= attribute.key %>'] ?? [])<% if (!attribute.required) { %> ?? []<% } -%> +<% } else { -%> +map['<%= attribute.key %>']<% if (!attribute.required) { %> ?? null<% } -%> +<% } -%> +<% } else if (attribute.type === 'relationship') { -%> +<% if ((attribute.relationType === 'oneToMany' && attribute.side === 'parent') || (attribute.relationType === 'manyToOne' && attribute.side === 'child') || attribute.relationType === 'manyToMany') { -%> +(map['<%= attribute.key %>'] as List?)?.map((e) => <%- toPascalCase(attribute.relatedCollection) %>.fromMap(e)).toList()<% if (!attribute.required) { %> ?? []<% } -%> +<% } else { -%> +<% if (!attribute.required) { -%> +map['<%= attribute.key %>'] != null ? <%- toPascalCase(attribute.relatedCollection) %>.fromMap(map['<%= attribute.key %>']) : null<% } else { -%> +<%- toPascalCase(attribute.relatedCollection) %>.fromMap(map['<%= attribute.key %>'])<% } -%> +<% } -%> +<% } -%>, +<% } -%> + ); + } + + Map toMap() { + return { +<% for (const [index, attribute] of Object.entries(collection.attributes)) { -%> + "<%= attribute.key %>": <% if (attribute.type === 'relationship') { -%> +<% if ((attribute.relationType === 'oneToMany' && attribute.side === 'parent') || (attribute.relationType === 'manyToOne' && attribute.side === 'child') || attribute.relationType === 'manyToMany') { -%> +<%= toCamelCase(attribute.key) %><% if (!attribute.required) { %>?<% } %>.map((e) => e.toMap()).toList()<% if (!attribute.required) { %> ?? []<% } -%> +<% } else { -%> +<%= toCamelCase(attribute.key) %><% if (!attribute.required) { %>?<% } %>.toMap()<% if (!attribute.required) { %> ?? {}<% } -%> +<% } -%> +<% } else if (attribute.format === 'enum') { -%> +<% if (attribute.array) { -%> +<%= toCamelCase(attribute.key) %><% if (!attribute.required) { %>?<% } %>.map((e) => e.name).toList()<% if (!attribute.required) { %> ?? []<% } -%> +<% } else { -%> +<%= toCamelCase(attribute.key) %><% if (!attribute.required) { %>?<% } %>.name<% if (!attribute.required) { %> ?? null<% } -%> +<% } -%> +<% } else { -%> +<%= toCamelCase(attribute.key) -%> +<% } -%>, +<% } -%> + }; + } } `; }