diff --git a/README.md b/README.md index 6746720..bfb12f1 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ # Appwrite Command Line SDK ![License](https://img.shields.io/github/license/appwrite/sdk-for-cli.svg?style=flat-square) -![Version](https://img.shields.io/badge/api%20version-0.15.1-blue.svg?style=flat-square) +![Version](https://img.shields.io/badge/api%20version-0.15.0-blue.svg?style=flat-square) [![Build Status](https://img.shields.io/travis/com/appwrite/sdk-generator?style=flat-square)](https://travis-ci.com/appwrite/sdk-generator) [![Twitter Account](https://img.shields.io/twitter/follow/appwrite?color=00acee&label=twitter&style=flat-square)](https://twitter.com/appwrite) [![Discord](https://img.shields.io/discord/564160730845151244?label=discord&style=flat-square)](https://appwrite.io/discord) @@ -29,7 +29,7 @@ Once the installation is complete, you can verify the install using ```sh $ appwrite -v -0.18.1 +0.18.2 ``` ### Install using prebuilt binaries @@ -58,7 +58,7 @@ $ iwr -useb https://appwrite.io/cli/install.ps1 | iex Once the installation completes, you can verify your install using ``` $ appwrite -v -0.18.1 +0.18.2 ``` ## Getting Started @@ -94,6 +94,12 @@ You can also fetch all the collections in your current project using appwrite init collection ``` +The CLI also comes with a convenient `--all` flag to perform both these steps at once. + +```sh +appwrite init --all +``` + * ### Creating and deploying cloud functions The CLI makes it extremely easy to create and deploy Appwrite's cloud functions. Initialise your new function using @@ -139,6 +145,12 @@ Similarly, you can deploy all your collections to your Appwrite server using appwrite deploy collections ``` +The `deploy` command also comes with a convenient `--all` flag to deploy all your functions and collections at once. + +```sh +appwrite deploy --all +``` + > ### Note > By default, requests to domains with self signed SSL certificates (or no certificates) are disabled. If you trust the domain, you can bypass the certificate validation using ```sh diff --git a/install.ps1 b/install.ps1 index 5a830f7..b540e8b 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/0.18.1/appwrite-cli-win-x64.exe" -$GITHUB_arm64_URL = "https://github.com/appwrite/sdk-for-cli/releases/download/0.18.1/appwrite-cli-win-arm64.exe" +$GITHUB_x64_URL = "https://github.com/appwrite/sdk-for-cli/releases/download/0.18.2/appwrite-cli-win-x64.exe" +$GITHUB_arm64_URL = "https://github.com/appwrite/sdk-for-cli/releases/download/0.18.2/appwrite-cli-win-arm64.exe" $APPWRITE_BINARY_NAME = "appwrite.exe" diff --git a/install.sh b/install.sh index 275df3c..fa3323a 100644 --- a/install.sh +++ b/install.sh @@ -97,7 +97,7 @@ printSuccess() { downloadBinary() { echo "[2/4] Downloading executable for $OS ($ARCH) ..." - GITHUB_LATEST_VERSION="0.18.1" + GITHUB_LATEST_VERSION="0.18.2" 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 7fe7f85..792500e 100644 --- a/lib/client.js +++ b/lib/client.js @@ -12,8 +12,8 @@ class Client { this.endpoint = 'https://HOSTNAME/v1'; this.headers = { 'content-type': '', - 'x-sdk-version': 'appwrite:cli:0.18.1', - 'User-Agent' : `AppwriteCLI/0.18.1 (${os.type()} ${os.version()}; ${os.arch()})`, + 'x-sdk-version': 'appwrite:cli:0.18.2', + 'User-Agent' : `AppwriteCLI/0.18.2 (${os.type()} ${os.version()}; ${os.arch()})`, 'X-Appwrite-Response-Format' : '0.15.0', }; } diff --git a/lib/commands/account.js b/lib/commands/account.js index c117d5a..474a2f2 100644 --- a/lib/commands/account.js +++ b/lib/commands/account.js @@ -784,7 +784,7 @@ account account .command(`updatePhone`) - .description(`Update the currently logged in user's phone number. After updating the phone number, the phone verification status will be reset. A confirmation SMS is not sent automatically, however you can use the [POST /account/verification/phone](/docs/client/account#accountCreatePhoneVerification) endpoint to send a confirmation SMS.`) + .description(`Update currently logged in user account phone number. After changing phone number, the user confirmation status will get reset. A new confirmation SMS is not sent automatically however you can use the phone confirmation endpoint again to send the confirmation SMS.`) .requiredOption(`--number `, `Phone number. Format this number with a leading '+' and a country code, e.g., +16175551212.`) .requiredOption(`--password `, `User password. Must be at least 8 chars.`) .action(actionRunner(accountUpdatePhone)) @@ -856,7 +856,7 @@ account account .command(`createOAuth2Session`) .description(`Allow the user to login to their account using the OAuth2 provider of their choice. Each OAuth2 provider should be enabled from the Appwrite console first. Use the success and failure arguments to provide a redirect URL's back to your app when login is completed. If there is already an active session, the new session will be attached to the logged-in account. If there are no active sessions, the server will attempt to look for a user with the same email address as the email received from the OAuth2 provider and attach the new session to the existing user. If no matching user is found - the server will create a new user.. `) - .requiredOption(`--provider `, `OAuth2 Provider. Currently, supported providers are: amazon, apple, auth0, autodesk, bitbucket, bitly, box, dailymotion, discord, dropbox, facebook, github, gitlab, google, linkedin, microsoft, notion, okta, paypal, paypalSandbox, salesforce, slack, spotify, stripe, tradeshift, tradeshiftBox, twitch, wordpress, yahoo, yammer, yandex, zoom.`) + .requiredOption(`--provider `, `OAuth2 Provider. Currently, supported providers are: amazon, apple, auth0, bitbucket, bitly, box, dailymotion, discord, dropbox, facebook, github, gitlab, google, linkedin, microsoft, notion, okta, paypal, paypalSandbox, salesforce, slack, spotify, stripe, tradeshift, tradeshiftBox, twitch, wordpress, yahoo, yammer, yandex, zoom.`) .option(`--success `, `URL to redirect back to your app after a successful login attempt. Only URLs from hostnames in your project platform list are allowed. This requirement helps to prevent an [open redirect](https://cheatsheetseries.owasp.org/cheatsheets/Unvalidated_Redirects_and_Forwards_Cheat_Sheet.html) attack against your project API.`) .option(`--failure `, `URL to redirect back to your app after a failed login attempt. Only URLs from hostnames in your project platform list are allowed. This requirement helps to prevent an [open redirect](https://cheatsheetseries.owasp.org/cheatsheets/Unvalidated_Redirects_and_Forwards_Cheat_Sheet.html) attack against your project API.`) .option(`--scopes `, `A list of custom OAuth2 scopes. Check each provider internal docs for a list of supported scopes. Maximum of 100 scopes are allowed, each 4096 characters long.`) @@ -864,14 +864,14 @@ account account .command(`createPhoneSession`) - .description(`Sends the user an SMS with a secret key for creating a session. Use the returned user ID and secret and submit a request to the [PUT /account/sessions/phone](/docs/client/account#accountUpdatePhoneSession) endpoint to complete the login process. The secret sent to the user's phone is valid for 15 minutes.`) + .description(`Sends the user a SMS with a secret key for creating a session. Use the returned user ID and the secret to submit a request to the [PUT /account/sessions/phone](/docs/client/account#accountUpdatePhoneSession) endpoint to complete the login process. The secret sent to the user's phone is valid for 15 minutes.`) .requiredOption(`--userId `, `Unique Id. Choose your own unique ID or pass the string "unique()" to auto generate it. Valid chars are a-z, A-Z, 0-9, period, hyphen, and underscore. Can't start with a special char. Max length is 36 chars.`) .requiredOption(`--number `, `Phone number. Format this number with a leading '+' and a country code, e.g., +16175551212.`) .action(actionRunner(accountCreatePhoneSession)) account .command(`updatePhoneSession`) - .description(`Use this endpoint to complete creating a session with SMS. Use the **userId** from the [createPhoneSession](/docs/client/account#accountCreatePhoneSession) endpoint and the **secret** received via SMS to successfully update and confirm the phone session.`) + .description(`Use this endpoint to complete creating the session with the Magic URL. Both the **userId** and **secret** arguments will be passed as query parameters to the redirect URL you have provided when sending your request to the [POST /account/sessions/magic-url](/docs/client/account#accountCreateMagicURLSession) endpoint. Please note that in order to avoid a [Redirect Attack](https://github.com/OWASP/CheatSheetSeries/blob/master/cheatsheets/Unvalidated_Redirects_and_Forwards_Cheat_Sheet.md) the only valid redirect URLs are the ones from domains you have set when adding your platforms in the console interface.`) .requiredOption(`--userId `, `User ID.`) .requiredOption(`--secret `, `Valid verification token.`) .action(actionRunner(accountUpdatePhoneSession)) @@ -914,7 +914,7 @@ account account .command(`createPhoneVerification`) - .description(`Use this endpoint to send a verification SMS to the currently logged in user. This endpoint is meant for use after updating a user's phone number using the [accountUpdatePhone](/docs/client/account#accountUpdatePhone) endpoint. Learn more about how to [complete the verification process](/docs/client/account#accountUpdatePhoneVerification). The verification code sent to the user's phone number is valid for 15 minutes.`) + .description(`Use this endpoint to send a verification message to your user's phone number to confirm they are the valid owners of that address. The provided secret should allow you to complete the verification process by verifying both the **userId** and **secret** parameters. Learn more about how to [complete the verification process](/docs/client/account#accountUpdatePhoneVerification). The verification link sent to the user's phone number is valid for 15 minutes.`) .action(actionRunner(accountCreatePhoneVerification)) account diff --git a/lib/commands/deploy.js b/lib/commands/deploy.js index 7175a36..a0d8051 100644 --- a/lib/commands/deploy.js +++ b/lib/commands/deploy.js @@ -6,37 +6,34 @@ const { questionsDeployFunctions, questionsGetEntrypoint, questionsDeployCollect const { actionRunner, success, log, error, commandDescriptions } = require("../parser"); const { functionsGet, functionsCreate, functionsUpdate, functionsCreateDeployment, functionsUpdateDeployment } = require('./functions'); const { - databasesGet, - databasesCreate, - databasesCreateBooleanAttribute, - databasesGetCollection, - databasesCreateCollection, - databasesCreateStringAttribute, - databasesCreateIntegerAttribute, - databasesCreateFloatAttribute, - databasesCreateEmailAttribute, - databasesCreateIndex, - databasesCreateUrlAttribute, - databasesCreateIpAttribute, - databasesCreateEnumAttribute, - databasesDeleteAttribute, - databasesListAttributes, - databasesListIndexes, - databasesDeleteIndex -} = require("./databases"); + databaseCreateBooleanAttribute, + databaseGetCollection, + databaseCreateCollection, + databaseCreateStringAttribute, + databaseCreateIntegerAttribute, + databaseCreateFloatAttribute, + databaseCreateEmailAttribute, + databaseCreateIndex, + databaseCreateUrlAttribute, + databaseCreateIpAttribute, + databaseCreateEnumAttribute, + databaseDeleteAttribute, + databaseListAttributes, + databaseListIndexes, + databaseDeleteIndex +} = require("./database"); const POOL_DEBOUNCE = 2000; // in milliseconds const POOL_MAX_DEBOUNCES = 30; const awaitPools = { - wipeAttributes: async (databaseId, collectionId, iteration = 1) => { + wipeAttributes: async (collectionId, iteration = 1) => { if (iteration > POOL_MAX_DEBOUNCES) { return false; } // TODO: Pagination? - const { attributes: remoteAttributes } = await databasesListAttributes({ - databaseId, + const { attributes: remoteAttributes } = await databaseListAttributes({ collectionId, limit: 100, parseOutput: false @@ -47,16 +44,15 @@ const awaitPools = { } await new Promise(resolve => setTimeout(resolve, POOL_DEBOUNCE)); - return await awaitPools.wipeAttributes(databaseId, collectionId, iteration + 1); + return await awaitPools.wipeAttributes(collectionId, iteration + 1); }, - wipeIndexes: async (databaseId, collectionId, iteration = 1) => { + wipeIndexes: async (collectionId, iteration = 1) => { if (iteration > POOL_MAX_DEBOUNCES) { return false; } // TODO: Pagination? - const { indexes: remoteIndexes } = await databasesListIndexes({ - databaseId, + const { indexes: remoteIndexes } = await databaseListIndexes({ collectionId, limit: 100, parseOutput: false @@ -67,16 +63,15 @@ const awaitPools = { } await new Promise(resolve => setTimeout(resolve, POOL_DEBOUNCE)); - return await awaitPools.wipeIndexes(databaseId, collectionId, iteration + 1); + return await awaitPools.wipeIndexes(collectionId, iteration + 1); }, - expectAttributes: async (databaseId, collectionId, attributeKeys, iteration = 1) => { + expectAttributes: async (collectionId, attributeKeys, iteration = 1) => { if (iteration > POOL_MAX_DEBOUNCES) { return false; } // TODO: Pagination? - const { attributes: remoteAttributes } = await databasesListAttributes({ - databaseId, + const { attributes: remoteAttributes } = await databaseListAttributes({ collectionId, limit: 100, parseOutput: false @@ -99,16 +94,15 @@ const awaitPools = { } await new Promise(resolve => setTimeout(resolve, POOL_DEBOUNCE)); - return await awaitPools.expectAttributes(databaseId, collectionId, attributeKeys, iteration + 1); + return await awaitPools.expectAttributes(collectionId, attributeKeys, iteration + 1); }, - expectIndexes: async (databaseId, collectionId, indexKeys, iteration = 1) => { + expectIndexes: async (collectionId, indexKeys, iteration = 1) => { if (iteration > POOL_MAX_DEBOUNCES) { return false; } // TODO: Pagination? - const { indexes: remoteIndexes } = await databasesListIndexes({ - databaseId, + const { indexes: remoteIndexes } = await databaseListIndexes({ collectionId, limit: 100, parseOutput: false @@ -131,49 +125,32 @@ const awaitPools = { } await new Promise(resolve => setTimeout(resolve, POOL_DEBOUNCE)); - return await awaitPools.expectIndexes(databaseId, collectionId, indexKeys, iteration + 1); + return await awaitPools.expectIndexes(collectionId, indexKeys, iteration + 1); }, } const deploy = new Command("deploy") .description(commandDescriptions['deploy']) - .action(actionRunner(async (_options, command) => { - command.help() - })); - -const deployFunction = async ({ functionId, all } = {}) => { - let response = {}; - - const functionIds = []; - - if(functionId) { - functionIds.push(functionId); - } else if(all) { - const functions = localConfig.getFunctions(); - if (functions.length === 0) { - throw new Error("No functions found in the current directory."); + .option("--all", "Flag to deploy collections and functions") + .action(actionRunner(async ({ all }, command) => { + if (all == undefined) { + command.help() } - functionIds.push(...functions.map((func, idx) => { - return func.$id; - })); - } - if(functionIds.length <= 0) { - const answers = await inquirer.prompt(questionsDeployFunctions); - functionIds.push(...answers.functions); - } + try { + await deployFunction(); + } catch (e) { + error(e.message); + } + await deployCollection() + })); - let functions = functionIds.map((id) => { - const functions = localConfig.getFunctions(); - const func = functions.find((f) => f.$id === id); +const deployFunction = async () => { + let response = {}; - if(!func) { - throw new Error("Function '" + id + "' not found.") - } + let answers = await inquirer.prompt(questionsDeployFunctions) + let functions = answers.functions.map((func) => JSONbig.parse(func)) - return func; - }); - for (let func of functions) { log(`Deploying function ${func.name} ( ${func['$id']} )`) @@ -253,13 +230,12 @@ const deployFunction = async ({ functionId, all } = {}) => { } } -const createAttribute = async (databaseId, collectionId, attribute) => { +const createAttribute = async (collectionId, attribute) => { switch (attribute.type) { case 'string': switch (attribute.format) { case 'email': - return await databasesCreateEmailAttribute({ - databaseId, + return await databaseCreateEmailAttribute({ collectionId, key: attribute.key, required: attribute.required, @@ -268,8 +244,7 @@ const createAttribute = async (databaseId, collectionId, attribute) => { parseOutput: false }) case 'url': - return await databasesCreateUrlAttribute({ - databaseId, + return await databaseCreateUrlAttribute({ collectionId, key: attribute.key, required: attribute.required, @@ -278,8 +253,7 @@ const createAttribute = async (databaseId, collectionId, attribute) => { parseOutput: false }) case 'ip': - return await databasesCreateIpAttribute({ - databaseId, + return await databaseCreateIpAttribute({ collectionId, key: attribute.key, required: attribute.required, @@ -288,8 +262,7 @@ const createAttribute = async (databaseId, collectionId, attribute) => { parseOutput: false }) case 'enum': - return await databasesCreateEnumAttribute({ - databaseId, + return await databaseCreateEnumAttribute({ collectionId, key: attribute.key, elements: attribute.elements, @@ -299,8 +272,7 @@ const createAttribute = async (databaseId, collectionId, attribute) => { parseOutput: false }) default: - return await databasesCreateStringAttribute({ - databaseId, + return await databaseCreateStringAttribute({ collectionId, key: attribute.key, size: attribute.size, @@ -312,8 +284,7 @@ const createAttribute = async (databaseId, collectionId, attribute) => { } case 'integer': - return await databasesCreateIntegerAttribute({ - databaseId, + return await databaseCreateIntegerAttribute({ collectionId, key: attribute.key, required: attribute.required, @@ -324,8 +295,7 @@ const createAttribute = async (databaseId, collectionId, attribute) => { parseOutput: false }) case 'double': - return databasesCreateFloatAttribute({ - databaseId, + return databaseCreateFloatAttribute({ collectionId, key: attribute.key, required: attribute.required, @@ -336,9 +306,7 @@ const createAttribute = async (databaseId, collectionId, attribute) => { parseOutput: false }) case 'boolean': - return databasesCreateBooleanAttribute({ - databaseId, - databaseId, + return databaseCreateBooleanAttribute({ collectionId, key: attribute.key, required: attribute.required, @@ -349,55 +317,15 @@ const createAttribute = async (databaseId, collectionId, attribute) => { } } -const deployCollection = async ({ all } = {}) => { +const deployCollection = async () => { let response = {}; - - let collectionIds = []; - const configCollections = localConfig.getCollections(); - - if(all) { - if (configCollections.length === 0) { - throw new Error("No collections found in the current directory. Run `appwrite init collection` to fetch all your collections."); - } - collectionIds.push(...configCollections.map((c) => c.$id)); - } - - if(collectionIds.length <= 0) { - let answers = await inquirer.prompt(questionsDeployCollections[0]) - collectionIds.push(...answers.collections); - } - - let collections = []; - - for(const collectionId of collectionIds) { - const idCollections = configCollections.filter((c) => c.$id === collectionId); - collections.push(...idCollections); - } + let answers = await inquirer.prompt(questionsDeployCollections[0]) + let collections = answers.collections.map((collection) => JSONbig.parse(collection)); for (let collection of collections) { log(`Deploying collection ${collection.name} ( ${collection['$id']} )`) - - let databaseId; - - try { - const database = await databasesGet({ - databaseId: collection.databaseId, - parseOutput: false, - }); - databaseId = database.$id; - } catch(err) { - log(`Database ${collection.databaseId} not found. Creating it now...`); - const database = await databasesCreate({ - databaseId: collection.databaseId, - name: collection.databaseId, - parseOutput: false, - }); - databaseId = database.$id; - } - try { - response = await databasesGetCollection({ - databaseId, + response = await databaseGetCollection({ collectionId: collection['$id'], parseOutput: false, }) @@ -412,55 +340,51 @@ const deployCollection = async ({ all } = {}) => { log(`Updating attributes ... `); // TODO: Pagination? - const { indexes: remoteIndexes } = await databasesListIndexes({ - databaseId, + const { indexes: remoteIndexes } = await databaseListIndexes({ collectionId: collection['$id'], limit: 100, parseOutput: false }); await Promise.all(remoteIndexes.map(async index => { - await databasesDeleteIndex({ - databaseId, + await databaseDeleteIndex({ collectionId: collection['$id'], key: index.key, parseOutput: false }); })); - const deleteIndexesPoolStatus = await awaitPools.wipeIndexes(databaseId, collection['$id']); + const deleteIndexesPoolStatus = await awaitPools.wipeIndexes(collection['$id']); if (!deleteIndexesPoolStatus) { throw new Error("Index deletion did not finish for too long."); } // TODO: Pagination? - const { attributes: remoteAttributes } = await databasesListAttributes({ - databaseId, + const { attributes: remoteAttributes } = await databaseListAttributes({ collectionId: collection['$id'], limit: 100, parseOutput: false }); await Promise.all(remoteAttributes.map(async attribute => { - await databasesDeleteAttribute({ - databaseId, + await databaseDeleteAttribute({ collectionId: collection['$id'], key: attribute.key, parseOutput: false }); })); - const deleteAttributesPoolStatus = await awaitPools.wipeAttributes(databaseId, collection['$id']); + const deleteAttributesPoolStatus = await awaitPools.wipeAttributes(collection['$id']); if (!deleteAttributesPoolStatus) { throw new Error("Attribute deletion did not finish for too long."); } await Promise.all(collection.attributes.map(async attribute => { - await createAttribute(databaseId, collection['$id'], attribute); + await createAttribute(collection['$id'], attribute); })); const attributeKeys = collection.attributes.map(attribute => attribute.key); - const createPoolStatus = await awaitPools.expectAttributes(databaseId, collection['$id'], attributeKeys); + const createPoolStatus = await awaitPools.expectAttributes(collection['$id'], attributeKeys); if (!createPoolStatus) { throw new Error("Attribute creation did not finish for too long."); } @@ -469,8 +393,7 @@ const deployCollection = async ({ all } = {}) => { log(`Creating indexes ...`) await Promise.all(collection.indexes.map(async index => { - await databasesCreateIndex({ - databaseId, + await databaseCreateIndex({ collectionId: collection['$id'], key: index.key, type: index.type, @@ -481,7 +404,7 @@ const deployCollection = async ({ all } = {}) => { })); const indexKeys = collection.indexes.map(attribute => attribute.key); - const indexPoolStatus = await awaitPools.expectIndexes(databaseId, collection['$id'], indexKeys); + const indexPoolStatus = await awaitPools.expectIndexes(collection['$id'], indexKeys); if (!indexPoolStatus) { throw new Error("Index creation did not finish for too long."); } @@ -490,8 +413,7 @@ const deployCollection = async ({ all } = {}) => { } catch (e) { if (e.code == 404) { log(`Collection ${collection.name} does not exist in the project. Creating ... `); - response = await databasesCreateCollection({ - databaseId, + response = await databaseCreateCollection({ collectionId: collection['$id'], name: collection.name, permission: collection.permission, @@ -502,11 +424,11 @@ const deployCollection = async ({ all } = {}) => { log(`Creating attributes ... `); await Promise.all(collection.attributes.map(async attribute => { - await createAttribute(databaseId, collection['$id'], attribute); + await createAttribute(collection['$id'], attribute); })); const attributeKeys = collection.attributes.map(attribute => attribute.key); - const attributePoolStatus = await awaitPools.expectAttributes(databaseId, collection['$id'], attributeKeys); + const attributePoolStatus = await awaitPools.expectAttributes(collection['$id'], attributeKeys); if (!attributePoolStatus) { throw new Error("Attribute creation did not finish for too long."); } @@ -515,8 +437,7 @@ const deployCollection = async ({ all } = {}) => { log(`Creating indexes ...`); await Promise.all(collection.indexes.map(async index => { - await databasesCreateIndex({ - databaseId, + await databaseCreateIndex({ collectionId: collection['$id'], key: index.key, type: index.type, @@ -527,7 +448,7 @@ const deployCollection = async ({ all } = {}) => { })); const indexKeys = collection.indexes.map(attribute => attribute.key); - const indexPoolStatus = await awaitPools.expectIndexes(databaseId, collection['$id'], indexKeys); + const indexPoolStatus = await awaitPools.expectIndexes(collection['$id'], indexKeys); if (!indexPoolStatus) { throw new Error("Index creation did not finish for too long."); } @@ -545,14 +466,11 @@ const deployCollection = async ({ all } = {}) => { deploy .command("function") .description("Deploy functions in the current directory.") - .option(`--functionId `, `Function ID`) - .option(`--all`, `Flag to deploy all functions`) .action(actionRunner(deployFunction)); deploy .command("collection") .description("Deploy collections in the current project.") - .option(`--all`, `Flag to deploy all functions`) .action(actionRunner(deployCollection)); module.exports = { diff --git a/lib/commands/generic.js b/lib/commands/generic.js index 5c827c0..f70c9bb 100644 --- a/lib/commands/generic.js +++ b/lib/commands/generic.js @@ -5,7 +5,7 @@ const { sdkForConsole } = require("../sdks"); const { globalConfig, localConfig } = require("../config"); const { actionRunner, success, parseBool, commandDescriptions, log, parse } = require("../parser"); const { questionsLogin } = require("../questions"); -const { accountCreateEmailSession, accountDeleteSession } = require("./account"); +const { accountCreateSession, accountDeleteSession } = require("./account"); const login = new Command("login") .description(commandDescriptions['login']) @@ -14,7 +14,7 @@ const login = new Command("login") let client = await sdkForConsole(false); - await accountCreateEmailSession({ + await accountCreateSession({ email: answers.email, password: answers.password, parseOutput: false, diff --git a/lib/commands/init.js b/lib/commands/init.js index fd75c80..002606d 100644 --- a/lib/commands/init.js +++ b/lib/commands/init.js @@ -6,16 +6,22 @@ const inquirer = require("inquirer"); const { teamsCreate } = require("./teams"); const { projectsCreate } = require("./projects"); const { functionsCreate } = require("./functions"); -const { databasesListCollections, databasesList } = require("./databases"); +const { databaseListCollections } = require("./database"); const { sdkForConsole } = require("../sdks"); const { localConfig } = require("../config"); -const { questionsInitProject, questionsInitFunction, questionsInitCollection } = require("../questions"); +const { questionsInitProject, questionsInitFunction } = require("../questions"); const { success, log, actionRunner, commandDescriptions } = require("../parser"); const init = new Command("init") .description(commandDescriptions['init']) - .action(actionRunner(async (_options, command) => { - command.help(); + .option("--all", "Flag to initialize projects and collection") + .action(actionRunner(async ({ all }, command) => { + if (all == undefined) { + command.help() + } + + await initProject(); + await initCollection() })); const initProject = async () => { @@ -34,7 +40,7 @@ const initProject = async () => { let teamId = response['$id']; response = await projectsCreate({ - projectId: answers.id, + projectId: 'unique()', name: answers.project, teamId, parseOutput: false @@ -48,7 +54,6 @@ const initProject = async () => { } const initFunction = async () => { - // TODO: Add CI/CD support (ID, name, runtime) let answers = await inquirer.prompt(questionsInitFunction) if (fs.existsSync(path.join(process.cwd(), 'functions', answers.name))) { @@ -60,7 +65,7 @@ const initFunction = async () => { } let response = await functionsCreate({ - functionId: answers.id, + functionId: 'unique()', name: answers.name, runtime: answers.runtime.id, parseOutput: false @@ -105,41 +110,20 @@ const initFunction = async () => { success(); } -const initCollection = async ({ all, databaseId } = {}) => { - const databaseIds = []; - - if(databaseId) { - databaseIds.push(databaseId); - } else if(all) { - let allDatabases = await databasesList({ - parseOutput: false - }) - - databaseIds.push(...allDatabases.databases.map((d) => d.$id)); - } - - if(databaseIds.length <= 0) { - let answers = await inquirer.prompt(questionsInitCollection) - if (!answers.databases) process.exit(1) - databaseIds.push(...answers.databases); - } - - for(const databaseId of databaseIds) { - // TODO: Pagination? - let response = await databasesListCollections({ - databaseId, - limit: 100, - parseOutput: false - }) +const initCollection = async () => { + // TODO: Pagination? + let response = await databaseListCollections({ + limit: 100, + parseOutput: false + }) - let collections = response.collections; - log(`Found ${collections.length} collections`); + let collections = response.collections; + log(`Found ${collections.length} collections`); - collections.forEach(async collection => { - log(`Fetching ${collection.name} ...`); - localConfig.addCollection(collection); - }); - } + collections.forEach(async collection => { + log(`Fetching ${collection.name} ...`); + localConfig.addCollection(collection); + }); success(); } @@ -157,8 +141,6 @@ init init .command("collection") .description("Initialise your Appwrite collections") - .option(`--databaseId `, `Database ID`) - .option(`--all`, `Flag to initialize all databases`) .action(actionRunner(initCollection)) module.exports = { diff --git a/lib/parser.js b/lib/parser.js index 2f906cd..7783d07 100644 --- a/lib/parser.js +++ b/lib/parser.js @@ -150,7 +150,7 @@ const logo = "\n _ _ _ ___ __ _____ const commandDescriptions = { "account": `The account command allows you to authenticate and manage a user account.`, "avatars": `The avatars command aims to help you complete everyday tasks related to your app image, icons, and avatars.`, - "databases": `The databases command allows you to create structured collections of documents, query and filter lists of documents.`, + "database": `The database command allows you to create structured collections of documents, query and filter lists of documents.`, "deploy": `The deploy command provides a convenient wrapper for deploying your functions and collections.`, "functions": `The functions command allows you view, create and manage your Cloud Functions.`, "health": `The health command allows you to both validate and monitor your Appwrite server's health.`, diff --git a/lib/questions.js b/lib/questions.js index a74a302..2cdabfa 100644 --- a/lib/questions.js +++ b/lib/questions.js @@ -1,7 +1,6 @@ const { localConfig } = require('./config'); const { projectsList } = require('./commands/projects'); const { functionsListRuntimes } = require('./commands/functions'); -const { databasesList } = require('./commands/databases'); const JSONbig = require("json-bigint")({ storeAsString: false }); const getIgnores = (runtime) => { @@ -109,15 +108,6 @@ const questionsInitProject = [ return answers.start == "new"; }, }, - { - type: "input", - name: "id", - message: "What ID would you like to have for your project?", - default: "myAwesomeProject", - when(answers) { - return answers.start == "new"; - }, - }, { type: "list", name: "project", @@ -156,12 +146,6 @@ const questionsInitFunction = [ message: "What would you like to name your function?", default: "My Awesome Function" }, - { - type: "input", - name: "id", - message: "What ID would you like to have for your function?", - default: "myAwesomeFunction" - }, { type: "list", name: "runtime", @@ -182,31 +166,6 @@ const questionsInitFunction = [ } ]; -const questionsInitCollection = [ - { - type: "checkbox", - name: "databases", - message: "From which database would you like to init collections?", - choices: async () => { - let response = await databasesList({ - parseOutput: false - }) - let databases = response["databases"] - - if(databases.length <= 0) { - throw new Error("No databases found. Please create one in project console.") - } - let choices = databases.map((database, idx) => { - return { - name: `${database.name} (${database.$id})`, - value: database.$id - } - }) - return choices; - } - } -]; - const questionsLogin = [ { type: "input", @@ -245,8 +204,8 @@ const questionsDeployFunctions = [ } let choices = functions.map((func, idx) => { return { - name: `${func.name} (${func.$id})`, - value: func.$id + name: `${func.name} (${func['$id']})`, + value: JSONbig.stringify(func) } }) return choices; @@ -267,7 +226,7 @@ const questionsDeployCollections = [ let choices = collections.map((collection, idx) => { return { name: `${collection.name} (${collection['$id']})`, - value: collection.$id + value: JSONbig.stringify(collection) } }) return choices; @@ -299,7 +258,6 @@ module.exports = { questionsInitProject, questionsLogin, questionsInitFunction, - questionsInitCollection, questionsDeployFunctions, questionsDeployCollections, questionsGetEntrypoint diff --git a/package.json b/package.json index b7096fe..2505295 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": "0.18.1", + "version": "0.18.2", "license": "BSD-3-Clause", "main": "index.js", "bin": {