diff --git a/README.md b/README.md index 64012ed..1a19247 100644 --- a/README.md +++ b/README.md @@ -29,7 +29,7 @@ Once the installation is complete, you can verify the install using ```sh $ appwrite -v -9.0.2 +9.1.0 ``` ### Install using prebuilt binaries @@ -60,7 +60,7 @@ $ scoop install https://raw.githubusercontent.com/appwrite/sdk-for-cli/master/sc Once the installation completes, you can verify your install using ``` $ appwrite -v -9.0.2 +9.1.0 ``` ## Getting Started diff --git a/docs/examples/databases/create-line-attribute.md b/docs/examples/databases/create-line-attribute.md new file mode 100644 index 0000000..f0d81ed --- /dev/null +++ b/docs/examples/databases/create-line-attribute.md @@ -0,0 +1,5 @@ +appwrite databases create-line-attribute \ + --database-id \ + --collection-id \ + --key '' \ + --required false diff --git a/docs/examples/databases/create-point-attribute.md b/docs/examples/databases/create-point-attribute.md new file mode 100644 index 0000000..926c731 --- /dev/null +++ b/docs/examples/databases/create-point-attribute.md @@ -0,0 +1,5 @@ +appwrite databases create-point-attribute \ + --database-id \ + --collection-id \ + --key '' \ + --required false diff --git a/docs/examples/databases/create-polygon-attribute.md b/docs/examples/databases/create-polygon-attribute.md new file mode 100644 index 0000000..f0a00c7 --- /dev/null +++ b/docs/examples/databases/create-polygon-attribute.md @@ -0,0 +1,5 @@ +appwrite databases create-polygon-attribute \ + --database-id \ + --collection-id \ + --key '' \ + --required false diff --git a/docs/examples/databases/update-line-attribute.md b/docs/examples/databases/update-line-attribute.md new file mode 100644 index 0000000..37059ec --- /dev/null +++ b/docs/examples/databases/update-line-attribute.md @@ -0,0 +1,5 @@ +appwrite databases update-line-attribute \ + --database-id \ + --collection-id \ + --key '' \ + --required false diff --git a/docs/examples/databases/update-point-attribute.md b/docs/examples/databases/update-point-attribute.md new file mode 100644 index 0000000..bcc983a --- /dev/null +++ b/docs/examples/databases/update-point-attribute.md @@ -0,0 +1,5 @@ +appwrite databases update-point-attribute \ + --database-id \ + --collection-id \ + --key '' \ + --required false diff --git a/docs/examples/databases/update-polygon-attribute.md b/docs/examples/databases/update-polygon-attribute.md new file mode 100644 index 0000000..6e1e100 --- /dev/null +++ b/docs/examples/databases/update-polygon-attribute.md @@ -0,0 +1,5 @@ +appwrite databases update-polygon-attribute \ + --database-id \ + --collection-id \ + --key '' \ + --required false diff --git a/docs/examples/tablesdb/create-line-column.md b/docs/examples/tablesdb/create-line-column.md new file mode 100644 index 0000000..cd6db93 --- /dev/null +++ b/docs/examples/tablesdb/create-line-column.md @@ -0,0 +1,5 @@ +appwrite tables-db create-line-column \ + --database-id \ + --table-id \ + --key '' \ + --required false diff --git a/docs/examples/tablesdb/create-point-column.md b/docs/examples/tablesdb/create-point-column.md new file mode 100644 index 0000000..0e7d232 --- /dev/null +++ b/docs/examples/tablesdb/create-point-column.md @@ -0,0 +1,5 @@ +appwrite tables-db create-point-column \ + --database-id \ + --table-id \ + --key '' \ + --required false diff --git a/docs/examples/tablesdb/create-polygon-column.md b/docs/examples/tablesdb/create-polygon-column.md new file mode 100644 index 0000000..060323b --- /dev/null +++ b/docs/examples/tablesdb/create-polygon-column.md @@ -0,0 +1,5 @@ +appwrite tables-db create-polygon-column \ + --database-id \ + --table-id \ + --key '' \ + --required false diff --git a/docs/examples/tablesdb/update-line-column.md b/docs/examples/tablesdb/update-line-column.md new file mode 100644 index 0000000..203ebd6 --- /dev/null +++ b/docs/examples/tablesdb/update-line-column.md @@ -0,0 +1,5 @@ +appwrite tables-db update-line-column \ + --database-id \ + --table-id \ + --key '' \ + --required false diff --git a/docs/examples/tablesdb/update-point-column.md b/docs/examples/tablesdb/update-point-column.md new file mode 100644 index 0000000..676a37e --- /dev/null +++ b/docs/examples/tablesdb/update-point-column.md @@ -0,0 +1,5 @@ +appwrite tables-db update-point-column \ + --database-id \ + --table-id \ + --key '' \ + --required false diff --git a/docs/examples/tablesdb/update-polygon-column.md b/docs/examples/tablesdb/update-polygon-column.md new file mode 100644 index 0000000..24e1f01 --- /dev/null +++ b/docs/examples/tablesdb/update-polygon-column.md @@ -0,0 +1,5 @@ +appwrite tables-db update-polygon-column \ + --database-id \ + --table-id \ + --key '' \ + --required false diff --git a/install.ps1 b/install.ps1 index d188ac9..57b3841 100644 --- a/install.ps1 +++ b/install.ps1 @@ -13,8 +13,8 @@ # You can use "View source" of this page to see the full script. # REPO -$GITHUB_x64_URL = "https://github.com/appwrite/sdk-for-cli/releases/download/9.0.2/appwrite-cli-win-x64.exe" -$GITHUB_arm64_URL = "https://github.com/appwrite/sdk-for-cli/releases/download/9.0.2/appwrite-cli-win-arm64.exe" +$GITHUB_x64_URL = "https://github.com/appwrite/sdk-for-cli/releases/download/9.1.0/appwrite-cli-win-x64.exe" +$GITHUB_arm64_URL = "https://github.com/appwrite/sdk-for-cli/releases/download/9.1.0/appwrite-cli-win-arm64.exe" $APPWRITE_BINARY_NAME = "appwrite.exe" diff --git a/install.sh b/install.sh index 4ee0edf..d76d22a 100644 --- a/install.sh +++ b/install.sh @@ -97,7 +97,7 @@ printSuccess() { downloadBinary() { echo "[2/4] Downloading executable for $OS ($ARCH) ..." - GITHUB_LATEST_VERSION="9.0.2" + GITHUB_LATEST_VERSION="9.1.0" GITHUB_FILE="appwrite-cli-${OS}-${ARCH}" GITHUB_URL="https://github.com/$GITHUB_REPOSITORY_NAME/releases/download/$GITHUB_LATEST_VERSION/$GITHUB_FILE" diff --git a/lib/client.js b/lib/client.js index 24961bc..ca621d0 100644 --- a/lib/client.js +++ b/lib/client.js @@ -16,8 +16,8 @@ class Client { 'x-sdk-name': 'Command Line', 'x-sdk-platform': 'console', 'x-sdk-language': 'cli', - 'x-sdk-version': '9.0.2', - 'user-agent' : `AppwriteCLI/9.0.2 (${os.type()} ${os.version()}; ${os.arch()})`, + 'x-sdk-version': '9.1.0', + 'user-agent' : `AppwriteCLI/9.1.0 (${os.type()} ${os.version()}; ${os.arch()})`, 'X-Appwrite-Response-Format' : '1.8.0', }; } diff --git a/lib/commands/databases.js b/lib/commands/databases.js index 65fae8c..d4dbdbc 100644 --- a/lib/commands/databases.js +++ b/lib/commands/databases.js @@ -1178,6 +1178,267 @@ const databasesUpdateIpAttribute = async ({databaseId,collectionId,key,required, return response; +} +/** + * @typedef {Object} DatabasesCreateLineAttributeRequestParams + * @property {string} databaseId Database ID. + * @property {string} collectionId Collection ID. You can create a new collection using the Database service [server integration](https://appwrite.io/docs/server/databases#databasesCreateCollection). + * @property {string} key Attribute Key. + * @property {boolean} required Is attribute required? + * @property {string} xdefault Default value for attribute when not provided, as JSON string. Cannot be set when attribute is required. + * @property {boolean} overrideForCli + * @property {boolean} parseOutput + * @property {libClient | undefined} sdk + */ + +/** + * @param {DatabasesCreateLineAttributeRequestParams} params + */ +const databasesCreateLineAttribute = async ({databaseId,collectionId,key,required,xdefault,parseOutput = true, overrideForCli = false, sdk = undefined}) => { + let client = !sdk ? await sdkForProject() : + sdk; + let apiPath = '/databases/{databaseId}/collections/{collectionId}/attributes/line'.replace('{databaseId}', databaseId).replace('{collectionId}', collectionId); + let payload = {}; + if (typeof key !== 'undefined') { + payload['key'] = key; + } + if (typeof required !== 'undefined') { + payload['required'] = required; + } + if (typeof xdefault !== 'undefined') { + payload['default'] = xdefault; + } + + let response = undefined; + + response = await client.call('post', apiPath, { + 'content-type': 'application/json', + }, payload); + + if (parseOutput) { + parse(response) + } + + return response; + +} +/** + * @typedef {Object} DatabasesUpdateLineAttributeRequestParams + * @property {string} databaseId Database ID. + * @property {string} collectionId Collection ID. You can create a new collection using the Database service [server integration](https://appwrite.io/docs/server/databases#createCollection). + * @property {string} key Attribute Key. + * @property {boolean} required Is attribute required? + * @property {string} xdefault Default value for attribute when not provided, as JSON string. Cannot be set when attribute is required. + * @property {string} newKey New attribute key. + * @property {boolean} overrideForCli + * @property {boolean} parseOutput + * @property {libClient | undefined} sdk + */ + +/** + * @param {DatabasesUpdateLineAttributeRequestParams} params + */ +const databasesUpdateLineAttribute = async ({databaseId,collectionId,key,required,xdefault,newKey,parseOutput = true, overrideForCli = false, sdk = undefined}) => { + let client = !sdk ? await sdkForProject() : + sdk; + let apiPath = '/databases/{databaseId}/collections/{collectionId}/attributes/line/{key}'.replace('{databaseId}', databaseId).replace('{collectionId}', collectionId).replace('{key}', key); + let payload = {}; + if (typeof required !== 'undefined') { + payload['required'] = required; + } + if (typeof xdefault !== 'undefined') { + payload['default'] = xdefault; + } + if (typeof newKey !== 'undefined') { + payload['newKey'] = newKey; + } + + let response = undefined; + + response = await client.call('patch', apiPath, { + 'content-type': 'application/json', + }, payload); + + if (parseOutput) { + parse(response) + } + + return response; + +} +/** + * @typedef {Object} DatabasesCreatePointAttributeRequestParams + * @property {string} databaseId Database ID. + * @property {string} collectionId Collection ID. You can create a new collection using the Database service [server integration](https://appwrite.io/docs/server/databases#databasesCreateCollection). + * @property {string} key Attribute Key. + * @property {boolean} required Is attribute required? + * @property {string} xdefault Default value for attribute when not provided, as JSON string. Cannot be set when attribute is required. + * @property {boolean} overrideForCli + * @property {boolean} parseOutput + * @property {libClient | undefined} sdk + */ + +/** + * @param {DatabasesCreatePointAttributeRequestParams} params + */ +const databasesCreatePointAttribute = async ({databaseId,collectionId,key,required,xdefault,parseOutput = true, overrideForCli = false, sdk = undefined}) => { + let client = !sdk ? await sdkForProject() : + sdk; + let apiPath = '/databases/{databaseId}/collections/{collectionId}/attributes/point'.replace('{databaseId}', databaseId).replace('{collectionId}', collectionId); + let payload = {}; + if (typeof key !== 'undefined') { + payload['key'] = key; + } + if (typeof required !== 'undefined') { + payload['required'] = required; + } + if (typeof xdefault !== 'undefined') { + payload['default'] = xdefault; + } + + let response = undefined; + + response = await client.call('post', apiPath, { + 'content-type': 'application/json', + }, payload); + + if (parseOutput) { + parse(response) + } + + return response; + +} +/** + * @typedef {Object} DatabasesUpdatePointAttributeRequestParams + * @property {string} databaseId Database ID. + * @property {string} collectionId Collection ID. You can create a new collection using the Database service [server integration](https://appwrite.io/docs/server/databases#createCollection). + * @property {string} key Attribute Key. + * @property {boolean} required Is attribute required? + * @property {string} xdefault Default value for attribute when not provided, as JSON string. Cannot be set when attribute is required. + * @property {string} newKey New attribute key. + * @property {boolean} overrideForCli + * @property {boolean} parseOutput + * @property {libClient | undefined} sdk + */ + +/** + * @param {DatabasesUpdatePointAttributeRequestParams} params + */ +const databasesUpdatePointAttribute = async ({databaseId,collectionId,key,required,xdefault,newKey,parseOutput = true, overrideForCli = false, sdk = undefined}) => { + let client = !sdk ? await sdkForProject() : + sdk; + let apiPath = '/databases/{databaseId}/collections/{collectionId}/attributes/point/{key}'.replace('{databaseId}', databaseId).replace('{collectionId}', collectionId).replace('{key}', key); + let payload = {}; + if (typeof required !== 'undefined') { + payload['required'] = required; + } + if (typeof xdefault !== 'undefined') { + payload['default'] = xdefault; + } + if (typeof newKey !== 'undefined') { + payload['newKey'] = newKey; + } + + let response = undefined; + + response = await client.call('patch', apiPath, { + 'content-type': 'application/json', + }, payload); + + if (parseOutput) { + parse(response) + } + + return response; + +} +/** + * @typedef {Object} DatabasesCreatePolygonAttributeRequestParams + * @property {string} databaseId Database ID. + * @property {string} collectionId Collection ID. You can create a new collection using the Database service [server integration](https://appwrite.io/docs/server/databases#databasesCreateCollection). + * @property {string} key Attribute Key. + * @property {boolean} required Is attribute required? + * @property {string} xdefault Default value for attribute when not provided, as JSON string. Cannot be set when attribute is required. + * @property {boolean} overrideForCli + * @property {boolean} parseOutput + * @property {libClient | undefined} sdk + */ + +/** + * @param {DatabasesCreatePolygonAttributeRequestParams} params + */ +const databasesCreatePolygonAttribute = async ({databaseId,collectionId,key,required,xdefault,parseOutput = true, overrideForCli = false, sdk = undefined}) => { + let client = !sdk ? await sdkForProject() : + sdk; + let apiPath = '/databases/{databaseId}/collections/{collectionId}/attributes/polygon'.replace('{databaseId}', databaseId).replace('{collectionId}', collectionId); + let payload = {}; + if (typeof key !== 'undefined') { + payload['key'] = key; + } + if (typeof required !== 'undefined') { + payload['required'] = required; + } + if (typeof xdefault !== 'undefined') { + payload['default'] = xdefault; + } + + let response = undefined; + + response = await client.call('post', apiPath, { + 'content-type': 'application/json', + }, payload); + + if (parseOutput) { + parse(response) + } + + return response; + +} +/** + * @typedef {Object} DatabasesUpdatePolygonAttributeRequestParams + * @property {string} databaseId Database ID. + * @property {string} collectionId Collection ID. You can create a new collection using the Database service [server integration](https://appwrite.io/docs/server/databases#createCollection). + * @property {string} key Attribute Key. + * @property {boolean} required Is attribute required? + * @property {string} xdefault Default value for attribute when not provided, as JSON string. Cannot be set when attribute is required. + * @property {string} newKey New attribute key. + * @property {boolean} overrideForCli + * @property {boolean} parseOutput + * @property {libClient | undefined} sdk + */ + +/** + * @param {DatabasesUpdatePolygonAttributeRequestParams} params + */ +const databasesUpdatePolygonAttribute = async ({databaseId,collectionId,key,required,xdefault,newKey,parseOutput = true, overrideForCli = false, sdk = undefined}) => { + let client = !sdk ? await sdkForProject() : + sdk; + let apiPath = '/databases/{databaseId}/collections/{collectionId}/attributes/polygon/{key}'.replace('{databaseId}', databaseId).replace('{collectionId}', collectionId).replace('{key}', key); + let payload = {}; + if (typeof required !== 'undefined') { + payload['required'] = required; + } + if (typeof xdefault !== 'undefined') { + payload['default'] = xdefault; + } + if (typeof newKey !== 'undefined') { + payload['newKey'] = newKey; + } + + let response = undefined; + + response = await client.call('patch', apiPath, { + 'content-type': 'application/json', + }, payload); + + if (parseOutput) { + parse(response) + } + + return response; + } /** * @typedef {Object} DatabasesCreateRelationshipAttributeRequestParams @@ -2583,6 +2844,69 @@ databases .option(`--new-key `, `New Attribute Key.`) .action(actionRunner(databasesUpdateIpAttribute)) +databases + .command(`create-line-attribute`) + .description(`[**DEPRECATED** - This command is deprecated. Please use 'tables-db create-line-column' instead] Create a geometric line attribute.`) + .requiredOption(`--database-id `, `Database ID.`) + .requiredOption(`--collection-id `, `Collection ID. You can create a new collection using the Database service [server integration](https://appwrite.io/docs/server/databases#databasesCreateCollection).`) + .requiredOption(`--key `, `Attribute Key.`) + .requiredOption(`--required [value]`, `Is attribute required?`, (value) => value === undefined ? true : parseBool(value)) + .option(`--xdefault `, `Default value for attribute when not provided, as JSON string. Cannot be set when attribute is required.`) + .action(actionRunner(databasesCreateLineAttribute)) + +databases + .command(`update-line-attribute`) + .description(`[**DEPRECATED** - This command is deprecated. Please use 'tables-db update-line-column' instead] Update a line attribute. Changing the 'default' value will not update already existing documents.`) + .requiredOption(`--database-id `, `Database ID.`) + .requiredOption(`--collection-id `, `Collection ID. You can create a new collection using the Database service [server integration](https://appwrite.io/docs/server/databases#createCollection).`) + .requiredOption(`--key `, `Attribute Key.`) + .requiredOption(`--required [value]`, `Is attribute required?`, (value) => value === undefined ? true : parseBool(value)) + .option(`--xdefault `, `Default value for attribute when not provided, as JSON string. Cannot be set when attribute is required.`) + .option(`--new-key `, `New attribute key.`) + .action(actionRunner(databasesUpdateLineAttribute)) + +databases + .command(`create-point-attribute`) + .description(`[**DEPRECATED** - This command is deprecated. Please use 'tables-db create-point-column' instead] Create a geometric 2d point attribute.`) + .requiredOption(`--database-id `, `Database ID.`) + .requiredOption(`--collection-id `, `Collection ID. You can create a new collection using the Database service [server integration](https://appwrite.io/docs/server/databases#databasesCreateCollection).`) + .requiredOption(`--key `, `Attribute Key.`) + .requiredOption(`--required [value]`, `Is attribute required?`, (value) => value === undefined ? true : parseBool(value)) + .option(`--xdefault `, `Default value for attribute when not provided, as JSON string. Cannot be set when attribute is required.`) + .action(actionRunner(databasesCreatePointAttribute)) + +databases + .command(`update-point-attribute`) + .description(`[**DEPRECATED** - This command is deprecated. Please use 'tables-db update-point-column' instead] Update a point attribute. Changing the 'default' value will not update already existing documents.`) + .requiredOption(`--database-id `, `Database ID.`) + .requiredOption(`--collection-id `, `Collection ID. You can create a new collection using the Database service [server integration](https://appwrite.io/docs/server/databases#createCollection).`) + .requiredOption(`--key `, `Attribute Key.`) + .requiredOption(`--required [value]`, `Is attribute required?`, (value) => value === undefined ? true : parseBool(value)) + .option(`--xdefault `, `Default value for attribute when not provided, as JSON string. Cannot be set when attribute is required.`) + .option(`--new-key `, `New attribute key.`) + .action(actionRunner(databasesUpdatePointAttribute)) + +databases + .command(`create-polygon-attribute`) + .description(`[**DEPRECATED** - This command is deprecated. Please use 'tables-db create-polygon-column' instead] Create a geometric polygon attribute.`) + .requiredOption(`--database-id `, `Database ID.`) + .requiredOption(`--collection-id `, `Collection ID. You can create a new collection using the Database service [server integration](https://appwrite.io/docs/server/databases#databasesCreateCollection).`) + .requiredOption(`--key `, `Attribute Key.`) + .requiredOption(`--required [value]`, `Is attribute required?`, (value) => value === undefined ? true : parseBool(value)) + .option(`--xdefault `, `Default value for attribute when not provided, as JSON string. Cannot be set when attribute is required.`) + .action(actionRunner(databasesCreatePolygonAttribute)) + +databases + .command(`update-polygon-attribute`) + .description(`[**DEPRECATED** - This command is deprecated. Please use 'tables-db update-polygon-column' instead] Update a polygon attribute. Changing the 'default' value will not update already existing documents.`) + .requiredOption(`--database-id `, `Database ID.`) + .requiredOption(`--collection-id `, `Collection ID. You can create a new collection using the Database service [server integration](https://appwrite.io/docs/server/databases#createCollection).`) + .requiredOption(`--key `, `Attribute Key.`) + .requiredOption(`--required [value]`, `Is attribute required?`, (value) => value === undefined ? true : parseBool(value)) + .option(`--xdefault `, `Default value for attribute when not provided, as JSON string. Cannot be set when attribute is required.`) + .option(`--new-key `, `New attribute key.`) + .action(actionRunner(databasesUpdatePolygonAttribute)) + databases .command(`create-relationship-attribute`) .description(`[**DEPRECATED** - This command is deprecated. Please use 'tables-db create-relationship-column' instead] Create relationship attribute. [Learn more about relationship attributes](https://appwrite.io/docs/databases-relationships#relationship-attributes). `) @@ -2885,6 +3209,12 @@ module.exports = { databasesUpdateIntegerAttribute, databasesCreateIpAttribute, databasesUpdateIpAttribute, + databasesCreateLineAttribute, + databasesUpdateLineAttribute, + databasesCreatePointAttribute, + databasesUpdatePointAttribute, + databasesCreatePolygonAttribute, + databasesUpdatePolygonAttribute, databasesCreateRelationshipAttribute, databasesCreateStringAttribute, databasesUpdateStringAttribute, diff --git a/lib/commands/functions.js b/lib/commands/functions.js index cf42c5a..e6c0236 100644 --- a/lib/commands/functions.js +++ b/lib/commands/functions.js @@ -1082,7 +1082,7 @@ const functionsListExecutions = async ({functionId,queries,parseOutput = true, o * @property {string} body HTTP body of execution. Default value is empty string. * @property {boolean} async Execute code in the background. Default value is false. * @property {string} xpath HTTP path of execution. Path can include query params. Default value is / - * @property {ExecutionMethod} method HTTP method of execution. Default value is GET. + * @property {ExecutionMethod} method HTTP method of execution. Default value is POST. * @property {object} headers HTTP headers of execution. Defaults to empty. * @property {string} scheduledAt Scheduled execution time in [ISO 8601](https://www.iso.org/iso-8601-date-and-time-format.html) format. DateTime value must be in future with precision in minutes. * @property {boolean} overrideForCli @@ -1606,7 +1606,7 @@ functions .option(`--body `, `HTTP body of execution. Default value is empty string.`) .option(`--async [value]`, `Execute code in the background. Default value is false.`, (value) => value === undefined ? true : parseBool(value)) .option(`--xpath `, `HTTP path of execution. Path can include query params. Default value is /`) - .option(`--method `, `HTTP method of execution. Default value is GET.`) + .option(`--method `, `HTTP method of execution. Default value is POST.`) .option(`--headers `, `HTTP headers of execution. Defaults to empty.`) .option(`--scheduled-at `, `Scheduled execution time in [ISO 8601](https://www.iso.org/iso-8601-date-and-time-format.html) format. DateTime value must be in future with precision in minutes.`) .action(actionRunner(functionsCreateExecution)) diff --git a/lib/commands/generic.js b/lib/commands/generic.js index 0e2741a..3f8c08e 100644 --- a/lib/commands/generic.js +++ b/lib/commands/generic.js @@ -5,8 +5,8 @@ const { sdkForConsole } = require("../sdks"); const { globalConfig, localConfig } = require("../config"); const { actionRunner, success, parseBool, commandDescriptions, error, parse, hint, log, drawTable, cliConfig } = require("../parser"); const ID = require("../id"); -const { questionsLogin, questionsLogout, questionsListFactors, questionsMfaChallenge } = require("../questions"); -const { accountUpdateMfaChallenge, accountCreateMfaChallenge, accountGet, accountCreateEmailPasswordSession, accountDeleteSession } = require("./account"); +const { questionsLogin, questionsLogout, questionsListFactors, questionsMFAChallenge } = require("../questions"); +const { accountUpdateMFAChallenge, accountCreateMFAChallenge, accountGet, accountCreateEmailPasswordSession, accountDeleteSession } = require("./account"); const DEFAULT_ENDPOINT = 'https://cloud.appwrite.io/v1'; @@ -71,15 +71,15 @@ const loginCommand = async ({ email, password, endpoint, mfa, code }) => { if (error.response === 'user_more_factors_required') { const { factor } = mfa ? { factor: mfa } : await inquirer.prompt(questionsListFactors); - const challenge = await accountCreateMfaChallenge({ + const challenge = await accountCreateMFAChallenge({ factor, parseOutput: false, sdk: client }); - const { otp } = code ? { otp: code } : await inquirer.prompt(questionsMfaChallenge); + const { otp } = code ? { otp: code } : await inquirer.prompt(questionsMFAChallenge); - await accountUpdateMfaChallenge({ + await accountUpdateMFAChallenge({ challengeId: challenge.$id, otp, parseOutput: false, diff --git a/lib/commands/init.js b/lib/commands/init.js index 7cbfd43..7d59ba0 100644 --- a/lib/commands/init.js +++ b/lib/commands/init.js @@ -124,7 +124,9 @@ const initProject = async ({ organizationId, projectId, projectName } = {}) => { if(answers.autopull) { cliConfig.all = true; cliConfig.force = true; - await pullResources(); + await pullResources({ + skipDeprecated: true + }); } else { log("You can run 'appwrite pull all' to synchronize all of your existing resources."); } diff --git a/lib/commands/pull.js b/lib/commands/pull.js index a88adfd..ac395aa 100644 --- a/lib/commands/pull.js +++ b/lib/commands/pull.js @@ -16,7 +16,9 @@ const { paginate } = require("../paginate"); const { questionsPullCollection, questionsPullFunctions, questionsPullFunctionsCode, questionsPullSites, questionsPullSitesCode, questionsPullResources } = require("../questions"); const { cliConfig, success, log, warn, actionRunner, commandDescriptions } = require("../parser"); -const pullResources = async () => { +const pullResources = async ({ + skipDeprecated = false +} = {}) => { const actions = { settings: pullSettings, functions: pullFunctions, @@ -28,6 +30,10 @@ const pullResources = async () => { messages: pullMessagingTopic } + if (skipDeprecated) { + delete actions.collections; + } + if (cliConfig.all) { for (let action of Object.values(actions)) { cliConfig.all = true; @@ -372,7 +378,7 @@ const pullTable = async () => { total++; log(`Pulling all tables from ${chalk.bold(database['name'])} database ...`); - localConfig.addDatabase(database); + localConfig.addTablesDB(database); const { tables } = await paginate(tablesDBListTables, { databaseId, diff --git a/lib/commands/push.js b/lib/commands/push.js index ff59bbc..899d537 100644 --- a/lib/commands/push.js +++ b/lib/commands/push.js @@ -51,7 +51,9 @@ const { } = require("./databases"); const { tablesDBGet, - tablesDBGetTable + tablesDBGetTable, + tablesDBUpdateTable, + tablesDBCreateTable } = require("./tables-db"); const { storageGetBucket, storageUpdateBucket, storageCreateBucket @@ -892,7 +894,7 @@ const createIndexes = async (indexes, collection) => { ); if (!result) { - throw new Error("Index creation timed out."); + throw new Error('Index creation timed out.'); } success(`Created ${indexes.length} indexes`); @@ -916,6 +918,25 @@ const createAttributes = async (attributes, collection) => { success(`Created ${attributes.length} attributes`); } +const createColumns = async (columns, table) => { + for (let column of columns) { + if (column.side !== 'child') { + await createAttribute(table['databaseId'], table['$id'], column); + } + } + + const result = await awaitPools.expectAttributes( + table['databaseId'], + table['$id'], + table.columns.filter(column => column.side !== 'child').map(column => column.key) + ); + + if (!result) { + throw new Error(`Column creation timed out.`); + } + + success(`Created ${columns.length} columns`); +} const pushResources = async () => { const actions = { @@ -1711,7 +1732,7 @@ const pushTable = async ({ returnOnZero, attempts } = { returnOnZero: false }) = // Parallel db actions await Promise.all(databases.map(async (databaseId) => { - const localDatabase = localConfig.getDatabase(databaseId); + const localDatabase = localConfig.getTablesDB(databaseId); try { const database = await tablesDBGet({ @@ -1753,11 +1774,10 @@ const pushTable = async ({ returnOnZero, attempts } = { returnOnZero: false }) = }); if (remoteTable.name !== table.name) { - await databasesUpdateTable({ + await tablesDBUpdateTable({ databaseId: table['databaseId'], tableId: table['$id'], name: table.name, - name: table.name, parseOutput: false }) @@ -1770,7 +1790,7 @@ const pushTable = async ({ returnOnZero, attempts } = { returnOnZero: false }) = (e) { if (Number(e.code) === 404) { log(`Table ${table.name} does not exist in the project. Creating ... `); - await databasesCreateTable({ + await tablesDBCreateTable({ databaseId: table['databaseId'], tableId: table['$id'], name: table.name, @@ -1796,13 +1816,12 @@ const pushTable = async ({ returnOnZero, attempts } = { returnOnZero: false }) = if ((Array.isArray(columns) && columns.length <= 0) && (Array.isArray(indexes) && indexes.length <= 0)) { continue; } - } log(`Pushing table ${table.name} ( ${table['databaseId']} - ${table['$id']} ) attributes`) try { - await createAttributes(columns, table) + await createColumns(columns, table) } catch (e) { throw e; } @@ -1900,7 +1919,6 @@ const pushCollection = async ({ returnOnZero, attempts } = { returnOnZero: false databaseId: collection['databaseId'], collectionId: collection['$id'], name: collection.name, - name: collection.name, parseOutput: false }) diff --git a/lib/commands/tables-db.js b/lib/commands/tables-db.js index 46d2280..1325902 100644 --- a/lib/commands/tables-db.js +++ b/lib/commands/tables-db.js @@ -1178,6 +1178,267 @@ const tablesDBUpdateIpColumn = async ({databaseId,tableId,key,required,xdefault, return response; +} +/** + * @typedef {Object} TablesDBCreateLineColumnRequestParams + * @property {string} databaseId Database ID. + * @property {string} tableId Table ID. You can create a new table using the TablesDB service [server integration](https://appwrite.io/docs/server/tablesdb#tablesDBCreate). + * @property {string} key Column Key. + * @property {boolean} required Is column required? + * @property {string} xdefault Default value for column when not provided, as JSON string. Cannot be set when column is required. + * @property {boolean} overrideForCli + * @property {boolean} parseOutput + * @property {libClient | undefined} sdk + */ + +/** + * @param {TablesDBCreateLineColumnRequestParams} params + */ +const tablesDBCreateLineColumn = async ({databaseId,tableId,key,required,xdefault,parseOutput = true, overrideForCli = false, sdk = undefined}) => { + let client = !sdk ? await sdkForProject() : + sdk; + let apiPath = '/tablesdb/{databaseId}/tables/{tableId}/columns/line'.replace('{databaseId}', databaseId).replace('{tableId}', tableId); + let payload = {}; + if (typeof key !== 'undefined') { + payload['key'] = key; + } + if (typeof required !== 'undefined') { + payload['required'] = required; + } + if (typeof xdefault !== 'undefined') { + payload['default'] = xdefault; + } + + let response = undefined; + + response = await client.call('post', apiPath, { + 'content-type': 'application/json', + }, payload); + + if (parseOutput) { + parse(response) + } + + return response; + +} +/** + * @typedef {Object} TablesDBUpdateLineColumnRequestParams + * @property {string} databaseId Database ID. + * @property {string} tableId Table ID. You can create a new table using the TablesDB service [server integration](https://appwrite.io/docs/server/tablesdb#tablesDBCreate). + * @property {string} key Column Key. + * @property {boolean} required Is column required? + * @property {string} xdefault Default value for column when not provided, as JSON string. Cannot be set when column is required. + * @property {string} newKey New Column Key. + * @property {boolean} overrideForCli + * @property {boolean} parseOutput + * @property {libClient | undefined} sdk + */ + +/** + * @param {TablesDBUpdateLineColumnRequestParams} params + */ +const tablesDBUpdateLineColumn = async ({databaseId,tableId,key,required,xdefault,newKey,parseOutput = true, overrideForCli = false, sdk = undefined}) => { + let client = !sdk ? await sdkForProject() : + sdk; + let apiPath = '/tablesdb/{databaseId}/tables/{tableId}/columns/line/{key}'.replace('{databaseId}', databaseId).replace('{tableId}', tableId).replace('{key}', key); + let payload = {}; + if (typeof required !== 'undefined') { + payload['required'] = required; + } + if (typeof xdefault !== 'undefined') { + payload['default'] = xdefault; + } + if (typeof newKey !== 'undefined') { + payload['newKey'] = newKey; + } + + let response = undefined; + + response = await client.call('patch', apiPath, { + 'content-type': 'application/json', + }, payload); + + if (parseOutput) { + parse(response) + } + + return response; + +} +/** + * @typedef {Object} TablesDBCreatePointColumnRequestParams + * @property {string} databaseId Database ID. + * @property {string} tableId Table ID. You can create a new table using the TablesDB service [server integration](https://appwrite.io/docs/server/tablesdb#tablesDBCreate). + * @property {string} key Column Key. + * @property {boolean} required Is column required? + * @property {string} xdefault Default value for column when not provided, as JSON string. Cannot be set when column is required. + * @property {boolean} overrideForCli + * @property {boolean} parseOutput + * @property {libClient | undefined} sdk + */ + +/** + * @param {TablesDBCreatePointColumnRequestParams} params + */ +const tablesDBCreatePointColumn = async ({databaseId,tableId,key,required,xdefault,parseOutput = true, overrideForCli = false, sdk = undefined}) => { + let client = !sdk ? await sdkForProject() : + sdk; + let apiPath = '/tablesdb/{databaseId}/tables/{tableId}/columns/point'.replace('{databaseId}', databaseId).replace('{tableId}', tableId); + let payload = {}; + if (typeof key !== 'undefined') { + payload['key'] = key; + } + if (typeof required !== 'undefined') { + payload['required'] = required; + } + if (typeof xdefault !== 'undefined') { + payload['default'] = xdefault; + } + + let response = undefined; + + response = await client.call('post', apiPath, { + 'content-type': 'application/json', + }, payload); + + if (parseOutput) { + parse(response) + } + + return response; + +} +/** + * @typedef {Object} TablesDBUpdatePointColumnRequestParams + * @property {string} databaseId Database ID. + * @property {string} tableId Table ID. You can create a new table using the TablesDB service [server integration](https://appwrite.io/docs/server/tablesdb#tablesDBCreate). + * @property {string} key Column Key. + * @property {boolean} required Is column required? + * @property {string} xdefault Default value for column when not provided, as JSON string. Cannot be set when column is required. + * @property {string} newKey New Column Key. + * @property {boolean} overrideForCli + * @property {boolean} parseOutput + * @property {libClient | undefined} sdk + */ + +/** + * @param {TablesDBUpdatePointColumnRequestParams} params + */ +const tablesDBUpdatePointColumn = async ({databaseId,tableId,key,required,xdefault,newKey,parseOutput = true, overrideForCli = false, sdk = undefined}) => { + let client = !sdk ? await sdkForProject() : + sdk; + let apiPath = '/tablesdb/{databaseId}/tables/{tableId}/columns/point/{key}'.replace('{databaseId}', databaseId).replace('{tableId}', tableId).replace('{key}', key); + let payload = {}; + if (typeof required !== 'undefined') { + payload['required'] = required; + } + if (typeof xdefault !== 'undefined') { + payload['default'] = xdefault; + } + if (typeof newKey !== 'undefined') { + payload['newKey'] = newKey; + } + + let response = undefined; + + response = await client.call('patch', apiPath, { + 'content-type': 'application/json', + }, payload); + + if (parseOutput) { + parse(response) + } + + return response; + +} +/** + * @typedef {Object} TablesDBCreatePolygonColumnRequestParams + * @property {string} databaseId Database ID. + * @property {string} tableId Table ID. You can create a new table using the TablesDB service [server integration](https://appwrite.io/docs/server/tablesdb#tablesDBCreate). + * @property {string} key Column Key. + * @property {boolean} required Is column required? + * @property {string} xdefault Default value for column when not provided, as JSON string. Cannot be set when column is required. + * @property {boolean} overrideForCli + * @property {boolean} parseOutput + * @property {libClient | undefined} sdk + */ + +/** + * @param {TablesDBCreatePolygonColumnRequestParams} params + */ +const tablesDBCreatePolygonColumn = async ({databaseId,tableId,key,required,xdefault,parseOutput = true, overrideForCli = false, sdk = undefined}) => { + let client = !sdk ? await sdkForProject() : + sdk; + let apiPath = '/tablesdb/{databaseId}/tables/{tableId}/columns/polygon'.replace('{databaseId}', databaseId).replace('{tableId}', tableId); + let payload = {}; + if (typeof key !== 'undefined') { + payload['key'] = key; + } + if (typeof required !== 'undefined') { + payload['required'] = required; + } + if (typeof xdefault !== 'undefined') { + payload['default'] = xdefault; + } + + let response = undefined; + + response = await client.call('post', apiPath, { + 'content-type': 'application/json', + }, payload); + + if (parseOutput) { + parse(response) + } + + return response; + +} +/** + * @typedef {Object} TablesDBUpdatePolygonColumnRequestParams + * @property {string} databaseId Database ID. + * @property {string} tableId Table ID. You can create a new table using the TablesDB service [server integration](https://appwrite.io/docs/server/tablesdb#tablesDBCreate). + * @property {string} key Column Key. + * @property {boolean} required Is column required? + * @property {string} xdefault Default value for column when not provided, as JSON string. Cannot be set when column is required. + * @property {string} newKey New Column Key. + * @property {boolean} overrideForCli + * @property {boolean} parseOutput + * @property {libClient | undefined} sdk + */ + +/** + * @param {TablesDBUpdatePolygonColumnRequestParams} params + */ +const tablesDBUpdatePolygonColumn = async ({databaseId,tableId,key,required,xdefault,newKey,parseOutput = true, overrideForCli = false, sdk = undefined}) => { + let client = !sdk ? await sdkForProject() : + sdk; + let apiPath = '/tablesdb/{databaseId}/tables/{tableId}/columns/polygon/{key}'.replace('{databaseId}', databaseId).replace('{tableId}', tableId).replace('{key}', key); + let payload = {}; + if (typeof required !== 'undefined') { + payload['required'] = required; + } + if (typeof xdefault !== 'undefined') { + payload['default'] = xdefault; + } + if (typeof newKey !== 'undefined') { + payload['newKey'] = newKey; + } + + let response = undefined; + + response = await client.call('patch', apiPath, { + 'content-type': 'application/json', + }, payload); + + if (parseOutput) { + parse(response) + } + + return response; + } /** * @typedef {Object} TablesDBCreateRelationshipColumnRequestParams @@ -2566,6 +2827,69 @@ tablesDB .option(`--new-key `, `New Column Key.`) .action(actionRunner(tablesDBUpdateIpColumn)) +tablesDB + .command(`create-line-column`) + .description(`Create a geometric line attribute.`) + .requiredOption(`--database-id `, `Database ID.`) + .requiredOption(`--table-id `, `Table ID. You can create a new table using the TablesDB service [server integration](https://appwrite.io/docs/server/tablesdb#tablesDBCreate).`) + .requiredOption(`--key `, `Column Key.`) + .requiredOption(`--required [value]`, `Is column required?`, (value) => value === undefined ? true : parseBool(value)) + .option(`--xdefault `, `Default value for column when not provided, as JSON string. Cannot be set when column is required.`) + .action(actionRunner(tablesDBCreateLineColumn)) + +tablesDB + .command(`update-line-column`) + .description(`Update a line column. Changing the 'default' value will not update already existing documents.`) + .requiredOption(`--database-id `, `Database ID.`) + .requiredOption(`--table-id `, `Table ID. You can create a new table using the TablesDB service [server integration](https://appwrite.io/docs/server/tablesdb#tablesDBCreate).`) + .requiredOption(`--key `, `Column Key.`) + .requiredOption(`--required [value]`, `Is column required?`, (value) => value === undefined ? true : parseBool(value)) + .option(`--xdefault `, `Default value for column when not provided, as JSON string. Cannot be set when column is required.`) + .option(`--new-key `, `New Column Key.`) + .action(actionRunner(tablesDBUpdateLineColumn)) + +tablesDB + .command(`create-point-column`) + .description(`Create a geometric point attribute.`) + .requiredOption(`--database-id `, `Database ID.`) + .requiredOption(`--table-id `, `Table ID. You can create a new table using the TablesDB service [server integration](https://appwrite.io/docs/server/tablesdb#tablesDBCreate).`) + .requiredOption(`--key `, `Column Key.`) + .requiredOption(`--required [value]`, `Is column required?`, (value) => value === undefined ? true : parseBool(value)) + .option(`--xdefault `, `Default value for column when not provided, as JSON string. Cannot be set when column is required.`) + .action(actionRunner(tablesDBCreatePointColumn)) + +tablesDB + .command(`update-point-column`) + .description(`Update a point column. Changing the 'default' value will not update already existing documents.`) + .requiredOption(`--database-id `, `Database ID.`) + .requiredOption(`--table-id `, `Table ID. You can create a new table using the TablesDB service [server integration](https://appwrite.io/docs/server/tablesdb#tablesDBCreate).`) + .requiredOption(`--key `, `Column Key.`) + .requiredOption(`--required [value]`, `Is column required?`, (value) => value === undefined ? true : parseBool(value)) + .option(`--xdefault `, `Default value for column when not provided, as JSON string. Cannot be set when column is required.`) + .option(`--new-key `, `New Column Key.`) + .action(actionRunner(tablesDBUpdatePointColumn)) + +tablesDB + .command(`create-polygon-column`) + .description(`Create a geometric polygon attribute.`) + .requiredOption(`--database-id `, `Database ID.`) + .requiredOption(`--table-id `, `Table ID. You can create a new table using the TablesDB service [server integration](https://appwrite.io/docs/server/tablesdb#tablesDBCreate).`) + .requiredOption(`--key `, `Column Key.`) + .requiredOption(`--required [value]`, `Is column required?`, (value) => value === undefined ? true : parseBool(value)) + .option(`--xdefault `, `Default value for column when not provided, as JSON string. Cannot be set when column is required.`) + .action(actionRunner(tablesDBCreatePolygonColumn)) + +tablesDB + .command(`update-polygon-column`) + .description(`Update a polygon column. Changing the 'default' value will not update already existing documents.`) + .requiredOption(`--database-id `, `Database ID.`) + .requiredOption(`--table-id `, `Table ID. You can create a new table using the TablesDB service [server integration](https://appwrite.io/docs/server/tablesdb#tablesDBCreate).`) + .requiredOption(`--key `, `Column Key.`) + .requiredOption(`--required [value]`, `Is column required?`, (value) => value === undefined ? true : parseBool(value)) + .option(`--xdefault `, `Default value for column when not provided, as JSON string. Cannot be set when column is required.`) + .option(`--new-key `, `New Column Key.`) + .action(actionRunner(tablesDBUpdatePolygonColumn)) + tablesDB .command(`create-relationship-column`) .description(`Create relationship column. [Learn more about relationship columns](https://appwrite.io/docs/databases-relationships#relationship-columns). `) @@ -2865,6 +3189,12 @@ module.exports = { tablesDBUpdateIntegerColumn, tablesDBCreateIpColumn, tablesDBUpdateIpColumn, + tablesDBCreateLineColumn, + tablesDBUpdateLineColumn, + tablesDBCreatePointColumn, + tablesDBUpdatePointColumn, + tablesDBCreatePolygonColumn, + tablesDBUpdatePolygonColumn, tablesDBCreateRelationshipColumn, tablesDBCreateStringColumn, tablesDBUpdateStringColumn, diff --git a/lib/commands/types.js b/lib/commands/types.js index 16beecb..78428f1 100644 --- a/lib/commands/types.js +++ b/lib/commands/types.js @@ -12,6 +12,7 @@ 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"); +const { CSharp } = require("../type-generation/languages/csharp"); /** * @param {string} language @@ -33,6 +34,8 @@ function createLanguageMeta(language) { return new Java(); case "dart": return new Dart(); + case "cs": + return new CSharp(); default: throw new Error(`Language '${language}' is not supported`); } @@ -55,7 +58,7 @@ const typesLanguageOption = new Option( "-l, --language ", "The language of the types" ) - .choices(["auto", "ts", "js", "php", "kotlin", "swift", "java", "dart"]) + .choices(["auto", "ts", "js", "php", "kotlin", "swift", "java", "dart", "cs"]) .default("auto"); const typesStrictOption = new Option( @@ -97,22 +100,58 @@ const typesCommand = actionRunner(async (rawOutputDirectory, {language, strict}) fs.mkdirSync(outputDirectory, { recursive: true }); } - const collections = localConfig.getCollections(); - if (collections.length === 0) { - const configFileName = path.basename(localConfig.path); - throw new Error(`No collections found in configuration. Make sure ${configFileName} exists and contains collections.`); + // Try tables first, fallback to collections + let tables = localConfig.getTables(); + let collections = []; + let dataSource = 'tables'; + + if (tables.length === 0) { + collections = localConfig.getCollections(); + dataSource = 'collections'; + + if (collections.length === 0) { + const configFileName = path.basename(localConfig.path); + throw new Error(`No tables or collections found in configuration. Make sure ${configFileName} exists and contains tables or collections.`); + } + } + + // Use tables if available, otherwise use collections + let dataItems = tables.length > 0 ? tables : collections; + const itemType = tables.length > 0 ? 'tables' : 'collections'; + + // Normalize tables data: rename 'columns' to 'attributes' for template compatibility + if (tables.length > 0) { + dataItems = dataItems.map(table => { + const { columns, ...rest } = table; + return { + ...rest, + attributes: (columns || []).map(column => { + if (column.relatedTable) { + const { relatedTable, ...columnRest } = column; + return { + ...columnRest, + relatedCollection: relatedTable + }; + } + return column; + }) + }; + }); } - log(`Found ${collections.length} collections: ${collections.map(c => c.name).join(", ")}`); + log(`Found ${dataItems.length} ${itemType}: ${dataItems.map(c => c.name).join(", ")}`); + + // Use columns if available, otherwise use attributes + const resourceType = tables.length > 0 ? 'columns' : 'attributes'; - const totalAttributes = collections.reduce((count, collection) => count + collection.attributes.length, 0); - log(`Found ${totalAttributes} attributes across all collections`); + const totalAttributes = dataItems.reduce((count, item) => count + (item.attributes || []).length, 0); + log(`Found ${totalAttributes} ${resourceType} across all ${itemType}`); const templater = ejs.compile(meta.getTemplate()); if (meta.isSingleFile()) { const content = templater({ - collections, + collections: dataItems, strict, ...templateHelpers, getType: meta.getType, @@ -123,23 +162,23 @@ const typesCommand = actionRunner(async (rawOutputDirectory, {language, strict}) fs.writeFileSync(destination, content); log(`Added types to ${destination}`); } else { - for (const collection of collections) { + for (const item of dataItems) { const content = templater({ - collections, - collection, + collections: dataItems, + collection: item, strict, ...templateHelpers, getType: meta.getType, }); - const destination = path.join(outputDirectory, meta.getFileName(collection)); + const destination = path.join(outputDirectory, meta.getFileName(item)); fs.writeFileSync(destination, content); - log(`Added types for ${collection.name} to ${destination}`); + log(`Added types for ${item.name} to ${destination}`); } } - success(`Generated types for all the listed collections`); + success(`Generated types for all the listed ${itemType}`); }); const types = new Command("types") diff --git a/lib/config.js b/lib/config.js index a67976a..4dbb397 100644 --- a/lib/config.js +++ b/lib/config.js @@ -55,7 +55,7 @@ const KeysColumns = new Set([ // enum "elements", // relationship - "relatedCollection", + "relatedTable", "relationType", "twoWay", "twoWayKey", @@ -150,6 +150,47 @@ class Config { toString() { return JSONbig.stringify(this.data, null, 4); } + + _getDBEntities(entityType) { + if (!this.has(entityType)) { + return []; + } + return this.get(entityType); + } + + _getDBEntity(entityType, $id) { + if (!this.has(entityType)) { + return {}; + } + + let entities = this.get(entityType); + for (let i = 0; i < entities.length; i++) { + if (entities[i]['$id'] == $id) { + return entities[i]; + } + } + + return {}; + } + + _addDBEntity(entityType, props, keysSet, nestedKeys = {}) { + props = whitelistKeys(props, keysSet, nestedKeys); + + if (!this.has(entityType)) { + this.set(entityType, []); + } + + let entities = this.get(entityType); + for (let i = 0; i < entities.length; i++) { + if (entities[i]['$id'] == props['$id']) { + entities[i] = props; + this.set(entityType, entities); + return; + } + } + entities.push(props); + this.set(entityType, entities); + } } class Local extends Config { @@ -464,45 +505,28 @@ class Local extends Config { this.set("topics", topics); } - getDatabases() { - if (!this.has("databases")) { - return []; - } - return this.get("databases"); + getTablesDBs() { + return this._getDBEntities("tablesDB"); } - getDatabase($id) { - if (!this.has("databases")) { - return {}; - } + getTablesDB($id) { + return this._getDBEntity("tablesDB", $id); + } + + addTablesDB(props) { + this._addDBEntity("tablesDB", props, KeysDatabase); + } - let databases = this.get("databases"); - for (let i = 0; i < databases.length; i++) { - if (databases[i]['$id'] == $id) { - return databases[i]; - } - } + getDatabases() { + return this._getDBEntities("databases"); + } - return {}; + getDatabase($id) { + return this._getDBEntity("databases", $id); } addDatabase(props) { - props = whitelistKeys(props, KeysDatabase); - - if (!this.has("databases")) { - this.set("databases", []); - } - - let databases = this.get("databases"); - for (let i = 0; i < databases.length; i++) { - if (databases[i]['$id'] == props['$id']) { - databases[i] = props; - this.set("databases", databases); - return; - } - } - databases.push(props); - this.set("databases", databases); + this._addDBEntity("databases", props, KeysDatabase); } getTeams() { diff --git a/lib/parser.js b/lib/parser.js index b440b7f..f9ea7f3 100644 --- a/lib/parser.js +++ b/lib/parser.js @@ -122,7 +122,7 @@ const parseError = (err) => { } catch { } - const version = '9.0.2'; + const version = '9.1.0'; const stepsToReproduce = `Running \`appwrite ${cliConfig.reportData.data.args.join(' ')}\``; const yourEnvironment = `CLI version: ${version}\nOperation System: ${os.type()}\nAppwrite version: ${appwriteVersion}\nIs Cloud: ${isCloud()}`; diff --git a/lib/questions.js b/lib/questions.js index 07492be..7bfb84e 100644 --- a/lib/questions.js +++ b/lib/questions.js @@ -5,7 +5,7 @@ const { projectsList } = require('./commands/projects'); const { organizationsList } = require('./commands/organizations'); const { teamsList } = require('./commands/teams'); const { functionsListRuntimes, functionsListSpecifications, functionsList } = require('./commands/functions'); -const { accountListMfaFactors } = require("./commands/account"); +const { accountListMFAFactors } = require("./commands/account"); const { sdkForConsole } = require("./sdks"); const { validateRequired } = require("./validations"); const { paginate } = require('./paginate'); @@ -259,7 +259,7 @@ const questionsPullResources = [ { name: `Buckets ${chalk.blackBright(`(Storage)`)}`, value: 'buckets' }, { name: `Teams ${chalk.blackBright(`(Auth)`)}`, value: 'teams' }, { name: `Topics ${chalk.blackBright(`(Messaging)`)}`, value: 'messages' }, - { name: `Collections ${chalk.blackBright(`(Database)`)}`, value: 'collections' } + { name: `Collections ${chalk.blackBright(`(Legacy Databases)`)}`, value: 'collections' } ] } ] @@ -671,7 +671,7 @@ const questionsPushResources = [ { name: `Buckets ${chalk.blackBright(`(Storage)`)}`, value: 'buckets' }, { name: `Teams ${chalk.blackBright(`(Auth)`)}`, value: 'teams' }, { name: `Topics ${chalk.blackBright(`(Messaging)`)}`, value: 'messages' }, - { name: `Collections ${chalk.blackBright(`(Database)`)}`, value: 'collections' } + { name: `Collections ${chalk.blackBright(`(Legacy Databases)`)}`, value: 'collections' } ] } ]; @@ -877,7 +877,7 @@ const questionsListFactors = [ message: "Your account is protected by multi-factor authentication. Please choose one for verification.", choices: async () => { let client = await sdkForConsole(false); - const factors = await accountListMfaFactors({ + const factors = await accountListMFAFactors({ sdk: client, parseOutput: false }); @@ -906,7 +906,7 @@ const questionsListFactors = [ } ]; -const questionsMfaChallenge = [ +const questionsMFAChallenge = [ { type: "input", name: "otp", @@ -1019,7 +1019,7 @@ module.exports = { questionsPushTeams, questionsGetEntrypoint, questionsListFactors, - questionsMfaChallenge, + questionsMFAChallenge, questionsRunFunctions, questionGetEndpoint, questionsInitResources, diff --git a/lib/type-generation/languages/csharp.js b/lib/type-generation/languages/csharp.js new file mode 100644 index 0000000..1df7c7c --- /dev/null +++ b/lib/type-generation/languages/csharp.js @@ -0,0 +1,170 @@ +/** @typedef {import('../attribute').Attribute} Attribute */ +const { AttributeType } = require('../attribute'); +const { LanguageMeta } = require("./language"); + +class CSharp extends LanguageMeta { + getType(attribute, collections) { + let type = ""; + switch (attribute.type) { + case AttributeType.STRING: + case AttributeType.EMAIL: + case AttributeType.DATETIME: + type = "string"; + if (attribute.format === AttributeType.ENUM) { + type = LanguageMeta.toPascalCase(attribute.key); + } + break; + case AttributeType.INTEGER: + type = "long"; + break; + case AttributeType.FLOAT: + type = "double"; + break; + case AttributeType.BOOLEAN: + type = "bool"; + break; + case AttributeType.RELATIONSHIP: + const relatedCollection = collections.find(c => c.$id === attribute.relatedCollection); + if (!relatedCollection) { + throw new Error(`Related collection with ID '${attribute.relatedCollection}' not found.`); + } + type = LanguageMeta.toPascalCase(relatedCollection.name); + if ((attribute.relationType === 'oneToMany' && attribute.side === 'parent') || (attribute.relationType === 'manyToOne' && attribute.side === 'child') || attribute.relationType === 'manyToMany') { + type = `List<${type}>`; + } + break; + default: + throw new Error(`Unknown attribute type: ${attribute.type}`); + } + if (attribute.array) { + type = `List<${type}>`; + } + if (!attribute.required) { + type += "?"; + } + return type; + } + + getTemplate() { + return `/// This file is auto-generated by the Appwrite CLI. +/// You can regenerate it by running \`appwrite ${process.argv.slice(2).join(' ')}\`. + +#nullable enable +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text.Json.Serialization; + +namespace Appwrite.Models +{ +<% for (const attribute of collection.attributes) { -%> +<% if (attribute.format === 'enum') { -%> + +public enum <%- toPascalCase(attribute.key) %> { +<% for (const [index, element] of Object.entries(attribute.elements) ) { -%> + [JsonPropertyName("<%- element %>")] + <%- toPascalCase(element) %><% if (index < attribute.elements.length - 1) { %>,<% } %> +<% } -%> +} +<% } -%> +<% } %> +public class <%= toPascalCase(collection.name) %> +{ +<% for (const [index, attribute] of Object.entries(collection.attributes)) { -%> + [JsonPropertyName("<%- attribute.key %>")] + public <%- getType(attribute, collections) %> <%= toPascalCase(attribute.key) %> { get; private set; } + +<% } -%> + + public <%= toPascalCase(collection.name) %>( +<% for (const [index, attribute] of Object.entries(collection.attributes)) { -%> + <%- getType(attribute, collections) %> <%= toCamelCase(attribute.key) %><% if (index < collection.attributes.length - 1) { %>,<% } %> +<% } -%> + ) + { +<% for (const [index, attribute] of Object.entries(collection.attributes)) { -%> + <%= toPascalCase(attribute.key) %> = <%= toCamelCase(attribute.key) %>; +<% } -%> + } + + public static <%= toPascalCase(collection.name) %> From(Dictionary map) => new <%= toPascalCase(collection.name) %>( +<% for (const [index, attribute] of Object.entries(collection.attributes)) { -%> + <%- toCamelCase(attribute.key) %>: <% + // ENUM + if (attribute.format === 'enum') { + if (attribute.array) { + -%>((IEnumerable)map["<%- attribute.key %>"]).Select(e => Enum.Parse>(e.ToString()!, true)).ToList()<% + } else { + -%>Enum.Parse>(map["<%- attribute.key %>"].ToString()!, true)<% + } + // RELATIONSHIP + } else if (attribute.type === 'relationship') { + const relatedClass = toPascalCase(collections.find(c => c.$id === attribute.relatedCollection).name); + if ((attribute.relationType === 'oneToMany' && attribute.side === 'parent') || (attribute.relationType === 'manyToOne' && attribute.side === 'child') || attribute.relationType === 'manyToMany' || attribute.array) { + -%>((IEnumerable)map["<%- attribute.key %>"]).Select(it => Models.<%- relatedClass %>.From((Dictionary)it)).ToList()<% + } else { + -%>Models.<%- relatedClass %>.From((Dictionary)map["<%- attribute.key %>"])<% + } + // ARRAY TYPES + } else if (attribute.array) { + if (attribute.type === 'string' || attribute.type === 'datetime' || attribute.type === 'email') { + -%>((IEnumerable)map["<%- attribute.key %>"]).Select(x => x?.ToString())<%- attribute.required ? '.Where(x => x != null)' : '' %>.ToList()!<% + } else if (attribute.type === 'integer') { + -%>((IEnumerable)map["<%- attribute.key %>"]).Select(x => <%- !attribute.required ? 'x == null ? (long?)null : ' : '' %>Convert.ToInt64(x)).ToList()<% + } else if (attribute.type === 'double') { + -%>((IEnumerable)map["<%- attribute.key %>"]).Select(x => <%- !attribute.required ? 'x == null ? (double?)null : ' : '' %>Convert.ToDouble(x)).ToList()<% + } else if (attribute.type === 'boolean') { + -%>((IEnumerable)map["<%- attribute.key %>"]).Select(x => <%- !attribute.required ? 'x == null ? (bool?)null : ' : '' %>(bool)x).ToList()<% + } + // SINGLE VALUE TYPES + } else if (attribute.type === 'integer') { + -%><%- !attribute.required ? 'map["' + attribute.key + '"] == null ? null : ' : '' %>Convert.ToInt64(map["<%- attribute.key %>"])<% + } else if (attribute.type === 'double') { + -%><%- !attribute.required ? 'map["' + attribute.key + '"] == null ? null : ' : '' %>Convert.ToDouble(map["<%- attribute.key %>"])<% + } else if (attribute.type === 'boolean') { + -%>(<%- getType(attribute, collections) %>)map["<%- attribute.key %>"]<% + } else if (attribute.type === 'string' || attribute.type === 'datetime' || attribute.type === 'email') { + -%>map["<%- attribute.key %>"]<%- !attribute.required ? '?' : '' %>.ToString()<%- attribute.required ? '!' : ''%><% + } else { + -%>default<% + } + -%><% if (index < collection.attributes.length - 1) { %>,<% } %> +<% } -%> + ); + + public Dictionary ToMap() => new Dictionary() + { +<% for (const [index, attribute] of Object.entries(collection.attributes)) { -%> + { "<%- attribute.key %>", <% + // ENUM + if (attribute.format === 'enum') { + if (attribute.array) { + -%><%= toPascalCase(attribute.key) %>?.Select(e => e.ToString()).ToList()<% + } else { + -%><%= toPascalCase(attribute.key) %>?.ToString()<% + } + // RELATIONSHIP + } else if (attribute.type === 'relationship') { + if ((attribute.relationType === 'oneToMany' && attribute.side === 'parent') || (attribute.relationType === 'manyToOne' && attribute.side === 'child') || attribute.relationType === 'manyToMany' || attribute.array) { + -%><%= toPascalCase(attribute.key) %>?.Select(e => e.ToMap()).ToList()<% + } else { + -%><%= toPascalCase(attribute.key) %>?.ToMap()<% + } + // OTHER + } else { + -%><%= toPascalCase(attribute.key) %><% + } + -%> }<% if (index < collection.attributes.length - 1) { %>,<% } %> +<% } -%> + }; +} +} +`; + } + + getFileName(collection) { + return LanguageMeta.toPascalCase(collection.name) + ".cs"; + } +} + +module.exports = { CSharp }; diff --git a/lib/type-generation/languages/javascript.js b/lib/type-generation/languages/javascript.js index d5e8487..5c40f29 100644 --- a/lib/type-generation/languages/javascript.js +++ b/lib/type-generation/languages/javascript.js @@ -66,7 +66,7 @@ class JavaScript extends LanguageMeta { getTemplate() { return `/** - * @typedef {import('${this._getAppwriteDependency()}').Models.Document} Document + * @typedef {import('${this._getAppwriteDependency()}').Models.Row} Row */ // This file is auto-generated by the Appwrite CLI. @@ -83,7 +83,7 @@ class JavaScript extends LanguageMeta { <% } -%> <% } -%> <% for (const [index, collection] of Object.entries(collections)) { %>/** - * @typedef {Document & { + * @typedef {Row & { <% for (const attribute of collection.attributes) { -%> * <%- strict ? toCamelCase(attribute.key) : attribute.key %>: <%- getType(attribute, collections) %>; <% } -%> diff --git a/lib/type-generation/languages/typescript.js b/lib/type-generation/languages/typescript.js index 09146d1..69285dd 100644 --- a/lib/type-generation/languages/typescript.js +++ b/lib/type-generation/languages/typescript.js @@ -88,7 +88,7 @@ export enum <%- toPascalCase(attribute.key) %> { <% } -%> <% } -%> <% for (const [index, collection] of Object.entries(collections)) { -%> -export type <%- toPascalCase(collection.name) %> = Models.Document & { +export type <%- toPascalCase(collection.name) %> = Models.Row & { <% for (const attribute of collection.attributes) { -%> <%- strict ? toCamelCase(attribute.key) : attribute.key %>: <%- getType(attribute, collections) %>; <% } -%> diff --git a/package.json b/package.json index fe86e16..d8c9089 100644 --- a/package.json +++ b/package.json @@ -2,7 +2,7 @@ "name": "appwrite-cli", "homepage": "https://appwrite.io/support", "description": "Appwrite is an open-source self-hosted backend server that abstract and simplify complex and repetitive development tasks behind a very simple REST API", - "version": "9.0.2", + "version": "9.1.0", "license": "BSD-3-Clause", "main": "index.js", "bin": { diff --git a/scoop/appwrite.config.json b/scoop/appwrite.config.json index 776a3d4..165cd7a 100644 --- a/scoop/appwrite.config.json +++ b/scoop/appwrite.config.json @@ -1,12 +1,12 @@ { "$schema": "https://raw.githubusercontent.com/ScoopInstaller/Scoop/master/schema.json", - "version": "9.0.2", + "version": "9.1.0", "description": "The Appwrite CLI is a command-line application that allows you to interact with Appwrite and perform server-side tasks using your terminal.", "homepage": "https://github.com/appwrite/sdk-for-cli", "license": "BSD-3-Clause", "architecture": { "64bit": { - "url": "https://github.com/appwrite/sdk-for-cli/releases/download/9.0.2/appwrite-cli-win-x64.exe", + "url": "https://github.com/appwrite/sdk-for-cli/releases/download/9.1.0/appwrite-cli-win-x64.exe", "bin": [ [ "appwrite-cli-win-x64.exe", @@ -15,7 +15,7 @@ ] }, "arm64": { - "url": "https://github.com/appwrite/sdk-for-cli/releases/download/9.0.2/appwrite-cli-win-arm64.exe", + "url": "https://github.com/appwrite/sdk-for-cli/releases/download/9.1.0/appwrite-cli-win-arm64.exe", "bin": [ [ "appwrite-cli-win-arm64.exe",