appwrite-cli 0.18.1 → 0.18.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +15 -3
- package/install.ps1 +2 -2
- package/install.sh +1 -1
- package/lib/client.js +2 -2
- package/lib/commands/account.js +5 -5
- package/lib/commands/deploy.js +71 -153
- package/lib/commands/generic.js +2 -2
- package/lib/commands/init.js +24 -42
- package/lib/parser.js +1 -1
- package/lib/questions.js +3 -45
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
# Appwrite Command Line SDK
|
|
2
2
|
|
|
3
3
|

|
|
4
|
-

|
|
5
5
|
[](https://travis-ci.com/appwrite/sdk-generator)
|
|
6
6
|
[](https://twitter.com/appwrite)
|
|
7
7
|
[](https://appwrite.io/discord)
|
|
@@ -29,7 +29,7 @@ Once the installation is complete, you can verify the install using
|
|
|
29
29
|
|
|
30
30
|
```sh
|
|
31
31
|
$ appwrite -v
|
|
32
|
-
0.18.
|
|
32
|
+
0.18.2
|
|
33
33
|
```
|
|
34
34
|
|
|
35
35
|
### Install using prebuilt binaries
|
|
@@ -58,7 +58,7 @@ $ iwr -useb https://appwrite.io/cli/install.ps1 | iex
|
|
|
58
58
|
Once the installation completes, you can verify your install using
|
|
59
59
|
```
|
|
60
60
|
$ appwrite -v
|
|
61
|
-
0.18.
|
|
61
|
+
0.18.2
|
|
62
62
|
```
|
|
63
63
|
|
|
64
64
|
## Getting Started
|
|
@@ -94,6 +94,12 @@ You can also fetch all the collections in your current project using
|
|
|
94
94
|
appwrite init collection
|
|
95
95
|
```
|
|
96
96
|
|
|
97
|
+
The CLI also comes with a convenient `--all` flag to perform both these steps at once.
|
|
98
|
+
|
|
99
|
+
```sh
|
|
100
|
+
appwrite init --all
|
|
101
|
+
```
|
|
102
|
+
|
|
97
103
|
* ### Creating and deploying cloud functions
|
|
98
104
|
|
|
99
105
|
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
|
|
|
139
145
|
appwrite deploy collections
|
|
140
146
|
```
|
|
141
147
|
|
|
148
|
+
The `deploy` command also comes with a convenient `--all` flag to deploy all your functions and collections at once.
|
|
149
|
+
|
|
150
|
+
```sh
|
|
151
|
+
appwrite deploy --all
|
|
152
|
+
```
|
|
153
|
+
|
|
142
154
|
> ### Note
|
|
143
155
|
> 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
|
|
144
156
|
```sh
|
package/install.ps1
CHANGED
|
@@ -13,8 +13,8 @@
|
|
|
13
13
|
# You can use "View source" of this page to see the full script.
|
|
14
14
|
|
|
15
15
|
# REPO
|
|
16
|
-
$GITHUB_x64_URL = "https://github.com/appwrite/sdk-for-cli/releases/download/0.18.
|
|
17
|
-
$GITHUB_arm64_URL = "https://github.com/appwrite/sdk-for-cli/releases/download/0.18.
|
|
16
|
+
$GITHUB_x64_URL = "https://github.com/appwrite/sdk-for-cli/releases/download/0.18.2/appwrite-cli-win-x64.exe"
|
|
17
|
+
$GITHUB_arm64_URL = "https://github.com/appwrite/sdk-for-cli/releases/download/0.18.2/appwrite-cli-win-arm64.exe"
|
|
18
18
|
|
|
19
19
|
$APPWRITE_BINARY_NAME = "appwrite.exe"
|
|
20
20
|
|
package/install.sh
CHANGED
|
@@ -97,7 +97,7 @@ printSuccess() {
|
|
|
97
97
|
downloadBinary() {
|
|
98
98
|
echo "[2/4] Downloading executable for $OS ($ARCH) ..."
|
|
99
99
|
|
|
100
|
-
GITHUB_LATEST_VERSION="0.18.
|
|
100
|
+
GITHUB_LATEST_VERSION="0.18.2"
|
|
101
101
|
GITHUB_FILE="appwrite-cli-${OS}-${ARCH}"
|
|
102
102
|
GITHUB_URL="https://github.com/$GITHUB_REPOSITORY_NAME/releases/download/$GITHUB_LATEST_VERSION/$GITHUB_FILE"
|
|
103
103
|
|
package/lib/client.js
CHANGED
|
@@ -12,8 +12,8 @@ class Client {
|
|
|
12
12
|
this.endpoint = 'https://HOSTNAME/v1';
|
|
13
13
|
this.headers = {
|
|
14
14
|
'content-type': '',
|
|
15
|
-
'x-sdk-version': 'appwrite:cli:0.18.
|
|
16
|
-
'User-Agent' : `AppwriteCLI/0.18.
|
|
15
|
+
'x-sdk-version': 'appwrite:cli:0.18.2',
|
|
16
|
+
'User-Agent' : `AppwriteCLI/0.18.2 (${os.type()} ${os.version()}; ${os.arch()})`,
|
|
17
17
|
'X-Appwrite-Response-Format' : '0.15.0',
|
|
18
18
|
};
|
|
19
19
|
}
|
package/lib/commands/account.js
CHANGED
|
@@ -784,7 +784,7 @@ account
|
|
|
784
784
|
|
|
785
785
|
account
|
|
786
786
|
.command(`updatePhone`)
|
|
787
|
-
.description(`Update
|
|
787
|
+
.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.`)
|
|
788
788
|
.requiredOption(`--number <number>`, `Phone number. Format this number with a leading '+' and a country code, e.g., +16175551212.`)
|
|
789
789
|
.requiredOption(`--password <password>`, `User password. Must be at least 8 chars.`)
|
|
790
790
|
.action(actionRunner(accountUpdatePhone))
|
|
@@ -856,7 +856,7 @@ account
|
|
|
856
856
|
account
|
|
857
857
|
.command(`createOAuth2Session`)
|
|
858
858
|
.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.. `)
|
|
859
|
-
.requiredOption(`--provider <provider>`, `OAuth2 Provider. Currently, supported providers are: amazon, apple, auth0,
|
|
859
|
+
.requiredOption(`--provider <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.`)
|
|
860
860
|
.option(`--success <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.`)
|
|
861
861
|
.option(`--failure <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.`)
|
|
862
862
|
.option(`--scopes <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
|
|
|
864
864
|
|
|
865
865
|
account
|
|
866
866
|
.command(`createPhoneSession`)
|
|
867
|
-
.description(`Sends the user
|
|
867
|
+
.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.`)
|
|
868
868
|
.requiredOption(`--userId <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.`)
|
|
869
869
|
.requiredOption(`--number <number>`, `Phone number. Format this number with a leading '+' and a country code, e.g., +16175551212.`)
|
|
870
870
|
.action(actionRunner(accountCreatePhoneSession))
|
|
871
871
|
|
|
872
872
|
account
|
|
873
873
|
.command(`updatePhoneSession`)
|
|
874
|
-
.description(`Use this endpoint to complete creating
|
|
874
|
+
.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.`)
|
|
875
875
|
.requiredOption(`--userId <userId>`, `User ID.`)
|
|
876
876
|
.requiredOption(`--secret <secret>`, `Valid verification token.`)
|
|
877
877
|
.action(actionRunner(accountUpdatePhoneSession))
|
|
@@ -914,7 +914,7 @@ account
|
|
|
914
914
|
|
|
915
915
|
account
|
|
916
916
|
.command(`createPhoneVerification`)
|
|
917
|
-
.description(`Use this endpoint to send a verification
|
|
917
|
+
.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.`)
|
|
918
918
|
.action(actionRunner(accountCreatePhoneVerification))
|
|
919
919
|
|
|
920
920
|
account
|
package/lib/commands/deploy.js
CHANGED
|
@@ -6,37 +6,34 @@ const { questionsDeployFunctions, questionsGetEntrypoint, questionsDeployCollect
|
|
|
6
6
|
const { actionRunner, success, log, error, commandDescriptions } = require("../parser");
|
|
7
7
|
const { functionsGet, functionsCreate, functionsUpdate, functionsCreateDeployment, functionsUpdateDeployment } = require('./functions');
|
|
8
8
|
const {
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
databasesDeleteIndex
|
|
26
|
-
} = require("./databases");
|
|
9
|
+
databaseCreateBooleanAttribute,
|
|
10
|
+
databaseGetCollection,
|
|
11
|
+
databaseCreateCollection,
|
|
12
|
+
databaseCreateStringAttribute,
|
|
13
|
+
databaseCreateIntegerAttribute,
|
|
14
|
+
databaseCreateFloatAttribute,
|
|
15
|
+
databaseCreateEmailAttribute,
|
|
16
|
+
databaseCreateIndex,
|
|
17
|
+
databaseCreateUrlAttribute,
|
|
18
|
+
databaseCreateIpAttribute,
|
|
19
|
+
databaseCreateEnumAttribute,
|
|
20
|
+
databaseDeleteAttribute,
|
|
21
|
+
databaseListAttributes,
|
|
22
|
+
databaseListIndexes,
|
|
23
|
+
databaseDeleteIndex
|
|
24
|
+
} = require("./database");
|
|
27
25
|
|
|
28
26
|
const POOL_DEBOUNCE = 2000; // in milliseconds
|
|
29
27
|
const POOL_MAX_DEBOUNCES = 30;
|
|
30
28
|
|
|
31
29
|
const awaitPools = {
|
|
32
|
-
wipeAttributes: async (
|
|
30
|
+
wipeAttributes: async (collectionId, iteration = 1) => {
|
|
33
31
|
if (iteration > POOL_MAX_DEBOUNCES) {
|
|
34
32
|
return false;
|
|
35
33
|
}
|
|
36
34
|
|
|
37
35
|
// TODO: Pagination?
|
|
38
|
-
const { attributes: remoteAttributes } = await
|
|
39
|
-
databaseId,
|
|
36
|
+
const { attributes: remoteAttributes } = await databaseListAttributes({
|
|
40
37
|
collectionId,
|
|
41
38
|
limit: 100,
|
|
42
39
|
parseOutput: false
|
|
@@ -47,16 +44,15 @@ const awaitPools = {
|
|
|
47
44
|
}
|
|
48
45
|
|
|
49
46
|
await new Promise(resolve => setTimeout(resolve, POOL_DEBOUNCE));
|
|
50
|
-
return await awaitPools.wipeAttributes(
|
|
47
|
+
return await awaitPools.wipeAttributes(collectionId, iteration + 1);
|
|
51
48
|
},
|
|
52
|
-
wipeIndexes: async (
|
|
49
|
+
wipeIndexes: async (collectionId, iteration = 1) => {
|
|
53
50
|
if (iteration > POOL_MAX_DEBOUNCES) {
|
|
54
51
|
return false;
|
|
55
52
|
}
|
|
56
53
|
|
|
57
54
|
// TODO: Pagination?
|
|
58
|
-
const { indexes: remoteIndexes } = await
|
|
59
|
-
databaseId,
|
|
55
|
+
const { indexes: remoteIndexes } = await databaseListIndexes({
|
|
60
56
|
collectionId,
|
|
61
57
|
limit: 100,
|
|
62
58
|
parseOutput: false
|
|
@@ -67,16 +63,15 @@ const awaitPools = {
|
|
|
67
63
|
}
|
|
68
64
|
|
|
69
65
|
await new Promise(resolve => setTimeout(resolve, POOL_DEBOUNCE));
|
|
70
|
-
return await awaitPools.wipeIndexes(
|
|
66
|
+
return await awaitPools.wipeIndexes(collectionId, iteration + 1);
|
|
71
67
|
},
|
|
72
|
-
expectAttributes: async (
|
|
68
|
+
expectAttributes: async (collectionId, attributeKeys, iteration = 1) => {
|
|
73
69
|
if (iteration > POOL_MAX_DEBOUNCES) {
|
|
74
70
|
return false;
|
|
75
71
|
}
|
|
76
72
|
|
|
77
73
|
// TODO: Pagination?
|
|
78
|
-
const { attributes: remoteAttributes } = await
|
|
79
|
-
databaseId,
|
|
74
|
+
const { attributes: remoteAttributes } = await databaseListAttributes({
|
|
80
75
|
collectionId,
|
|
81
76
|
limit: 100,
|
|
82
77
|
parseOutput: false
|
|
@@ -99,16 +94,15 @@ const awaitPools = {
|
|
|
99
94
|
}
|
|
100
95
|
|
|
101
96
|
await new Promise(resolve => setTimeout(resolve, POOL_DEBOUNCE));
|
|
102
|
-
return await awaitPools.expectAttributes(
|
|
97
|
+
return await awaitPools.expectAttributes(collectionId, attributeKeys, iteration + 1);
|
|
103
98
|
},
|
|
104
|
-
expectIndexes: async (
|
|
99
|
+
expectIndexes: async (collectionId, indexKeys, iteration = 1) => {
|
|
105
100
|
if (iteration > POOL_MAX_DEBOUNCES) {
|
|
106
101
|
return false;
|
|
107
102
|
}
|
|
108
103
|
|
|
109
104
|
// TODO: Pagination?
|
|
110
|
-
const { indexes: remoteIndexes } = await
|
|
111
|
-
databaseId,
|
|
105
|
+
const { indexes: remoteIndexes } = await databaseListIndexes({
|
|
112
106
|
collectionId,
|
|
113
107
|
limit: 100,
|
|
114
108
|
parseOutput: false
|
|
@@ -131,49 +125,32 @@ const awaitPools = {
|
|
|
131
125
|
}
|
|
132
126
|
|
|
133
127
|
await new Promise(resolve => setTimeout(resolve, POOL_DEBOUNCE));
|
|
134
|
-
return await awaitPools.expectIndexes(
|
|
128
|
+
return await awaitPools.expectIndexes(collectionId, indexKeys, iteration + 1);
|
|
135
129
|
},
|
|
136
130
|
}
|
|
137
131
|
|
|
138
132
|
const deploy = new Command("deploy")
|
|
139
133
|
.description(commandDescriptions['deploy'])
|
|
140
|
-
.
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
const deployFunction = async ({ functionId, all } = {}) => {
|
|
145
|
-
let response = {};
|
|
146
|
-
|
|
147
|
-
const functionIds = [];
|
|
148
|
-
|
|
149
|
-
if(functionId) {
|
|
150
|
-
functionIds.push(functionId);
|
|
151
|
-
} else if(all) {
|
|
152
|
-
const functions = localConfig.getFunctions();
|
|
153
|
-
if (functions.length === 0) {
|
|
154
|
-
throw new Error("No functions found in the current directory.");
|
|
134
|
+
.option("--all", "Flag to deploy collections and functions")
|
|
135
|
+
.action(actionRunner(async ({ all }, command) => {
|
|
136
|
+
if (all == undefined) {
|
|
137
|
+
command.help()
|
|
155
138
|
}
|
|
156
|
-
functionIds.push(...functions.map((func, idx) => {
|
|
157
|
-
return func.$id;
|
|
158
|
-
}));
|
|
159
|
-
}
|
|
160
139
|
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
140
|
+
try {
|
|
141
|
+
await deployFunction();
|
|
142
|
+
} catch (e) {
|
|
143
|
+
error(e.message);
|
|
144
|
+
}
|
|
145
|
+
await deployCollection()
|
|
146
|
+
}));
|
|
165
147
|
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
const func = functions.find((f) => f.$id === id);
|
|
148
|
+
const deployFunction = async () => {
|
|
149
|
+
let response = {};
|
|
169
150
|
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
}
|
|
151
|
+
let answers = await inquirer.prompt(questionsDeployFunctions)
|
|
152
|
+
let functions = answers.functions.map((func) => JSONbig.parse(func))
|
|
173
153
|
|
|
174
|
-
return func;
|
|
175
|
-
});
|
|
176
|
-
|
|
177
154
|
for (let func of functions) {
|
|
178
155
|
log(`Deploying function ${func.name} ( ${func['$id']} )`)
|
|
179
156
|
|
|
@@ -253,13 +230,12 @@ const deployFunction = async ({ functionId, all } = {}) => {
|
|
|
253
230
|
}
|
|
254
231
|
}
|
|
255
232
|
|
|
256
|
-
const createAttribute = async (
|
|
233
|
+
const createAttribute = async (collectionId, attribute) => {
|
|
257
234
|
switch (attribute.type) {
|
|
258
235
|
case 'string':
|
|
259
236
|
switch (attribute.format) {
|
|
260
237
|
case 'email':
|
|
261
|
-
return await
|
|
262
|
-
databaseId,
|
|
238
|
+
return await databaseCreateEmailAttribute({
|
|
263
239
|
collectionId,
|
|
264
240
|
key: attribute.key,
|
|
265
241
|
required: attribute.required,
|
|
@@ -268,8 +244,7 @@ const createAttribute = async (databaseId, collectionId, attribute) => {
|
|
|
268
244
|
parseOutput: false
|
|
269
245
|
})
|
|
270
246
|
case 'url':
|
|
271
|
-
return await
|
|
272
|
-
databaseId,
|
|
247
|
+
return await databaseCreateUrlAttribute({
|
|
273
248
|
collectionId,
|
|
274
249
|
key: attribute.key,
|
|
275
250
|
required: attribute.required,
|
|
@@ -278,8 +253,7 @@ const createAttribute = async (databaseId, collectionId, attribute) => {
|
|
|
278
253
|
parseOutput: false
|
|
279
254
|
})
|
|
280
255
|
case 'ip':
|
|
281
|
-
return await
|
|
282
|
-
databaseId,
|
|
256
|
+
return await databaseCreateIpAttribute({
|
|
283
257
|
collectionId,
|
|
284
258
|
key: attribute.key,
|
|
285
259
|
required: attribute.required,
|
|
@@ -288,8 +262,7 @@ const createAttribute = async (databaseId, collectionId, attribute) => {
|
|
|
288
262
|
parseOutput: false
|
|
289
263
|
})
|
|
290
264
|
case 'enum':
|
|
291
|
-
return await
|
|
292
|
-
databaseId,
|
|
265
|
+
return await databaseCreateEnumAttribute({
|
|
293
266
|
collectionId,
|
|
294
267
|
key: attribute.key,
|
|
295
268
|
elements: attribute.elements,
|
|
@@ -299,8 +272,7 @@ const createAttribute = async (databaseId, collectionId, attribute) => {
|
|
|
299
272
|
parseOutput: false
|
|
300
273
|
})
|
|
301
274
|
default:
|
|
302
|
-
return await
|
|
303
|
-
databaseId,
|
|
275
|
+
return await databaseCreateStringAttribute({
|
|
304
276
|
collectionId,
|
|
305
277
|
key: attribute.key,
|
|
306
278
|
size: attribute.size,
|
|
@@ -312,8 +284,7 @@ const createAttribute = async (databaseId, collectionId, attribute) => {
|
|
|
312
284
|
|
|
313
285
|
}
|
|
314
286
|
case 'integer':
|
|
315
|
-
return await
|
|
316
|
-
databaseId,
|
|
287
|
+
return await databaseCreateIntegerAttribute({
|
|
317
288
|
collectionId,
|
|
318
289
|
key: attribute.key,
|
|
319
290
|
required: attribute.required,
|
|
@@ -324,8 +295,7 @@ const createAttribute = async (databaseId, collectionId, attribute) => {
|
|
|
324
295
|
parseOutput: false
|
|
325
296
|
})
|
|
326
297
|
case 'double':
|
|
327
|
-
return
|
|
328
|
-
databaseId,
|
|
298
|
+
return databaseCreateFloatAttribute({
|
|
329
299
|
collectionId,
|
|
330
300
|
key: attribute.key,
|
|
331
301
|
required: attribute.required,
|
|
@@ -336,9 +306,7 @@ const createAttribute = async (databaseId, collectionId, attribute) => {
|
|
|
336
306
|
parseOutput: false
|
|
337
307
|
})
|
|
338
308
|
case 'boolean':
|
|
339
|
-
return
|
|
340
|
-
databaseId,
|
|
341
|
-
databaseId,
|
|
309
|
+
return databaseCreateBooleanAttribute({
|
|
342
310
|
collectionId,
|
|
343
311
|
key: attribute.key,
|
|
344
312
|
required: attribute.required,
|
|
@@ -349,55 +317,15 @@ const createAttribute = async (databaseId, collectionId, attribute) => {
|
|
|
349
317
|
}
|
|
350
318
|
}
|
|
351
319
|
|
|
352
|
-
const deployCollection = async (
|
|
320
|
+
const deployCollection = async () => {
|
|
353
321
|
let response = {};
|
|
354
|
-
|
|
355
|
-
let
|
|
356
|
-
const configCollections = localConfig.getCollections();
|
|
357
|
-
|
|
358
|
-
if(all) {
|
|
359
|
-
if (configCollections.length === 0) {
|
|
360
|
-
throw new Error("No collections found in the current directory. Run `appwrite init collection` to fetch all your collections.");
|
|
361
|
-
}
|
|
362
|
-
collectionIds.push(...configCollections.map((c) => c.$id));
|
|
363
|
-
}
|
|
364
|
-
|
|
365
|
-
if(collectionIds.length <= 0) {
|
|
366
|
-
let answers = await inquirer.prompt(questionsDeployCollections[0])
|
|
367
|
-
collectionIds.push(...answers.collections);
|
|
368
|
-
}
|
|
369
|
-
|
|
370
|
-
let collections = [];
|
|
371
|
-
|
|
372
|
-
for(const collectionId of collectionIds) {
|
|
373
|
-
const idCollections = configCollections.filter((c) => c.$id === collectionId);
|
|
374
|
-
collections.push(...idCollections);
|
|
375
|
-
}
|
|
322
|
+
let answers = await inquirer.prompt(questionsDeployCollections[0])
|
|
323
|
+
let collections = answers.collections.map((collection) => JSONbig.parse(collection));
|
|
376
324
|
|
|
377
325
|
for (let collection of collections) {
|
|
378
326
|
log(`Deploying collection ${collection.name} ( ${collection['$id']} )`)
|
|
379
|
-
|
|
380
|
-
let databaseId;
|
|
381
|
-
|
|
382
|
-
try {
|
|
383
|
-
const database = await databasesGet({
|
|
384
|
-
databaseId: collection.databaseId,
|
|
385
|
-
parseOutput: false,
|
|
386
|
-
});
|
|
387
|
-
databaseId = database.$id;
|
|
388
|
-
} catch(err) {
|
|
389
|
-
log(`Database ${collection.databaseId} not found. Creating it now...`);
|
|
390
|
-
const database = await databasesCreate({
|
|
391
|
-
databaseId: collection.databaseId,
|
|
392
|
-
name: collection.databaseId,
|
|
393
|
-
parseOutput: false,
|
|
394
|
-
});
|
|
395
|
-
databaseId = database.$id;
|
|
396
|
-
}
|
|
397
|
-
|
|
398
327
|
try {
|
|
399
|
-
response = await
|
|
400
|
-
databaseId,
|
|
328
|
+
response = await databaseGetCollection({
|
|
401
329
|
collectionId: collection['$id'],
|
|
402
330
|
parseOutput: false,
|
|
403
331
|
})
|
|
@@ -412,55 +340,51 @@ const deployCollection = async ({ all } = {}) => {
|
|
|
412
340
|
log(`Updating attributes ... `);
|
|
413
341
|
|
|
414
342
|
// TODO: Pagination?
|
|
415
|
-
const { indexes: remoteIndexes } = await
|
|
416
|
-
databaseId,
|
|
343
|
+
const { indexes: remoteIndexes } = await databaseListIndexes({
|
|
417
344
|
collectionId: collection['$id'],
|
|
418
345
|
limit: 100,
|
|
419
346
|
parseOutput: false
|
|
420
347
|
});
|
|
421
348
|
|
|
422
349
|
await Promise.all(remoteIndexes.map(async index => {
|
|
423
|
-
await
|
|
424
|
-
databaseId,
|
|
350
|
+
await databaseDeleteIndex({
|
|
425
351
|
collectionId: collection['$id'],
|
|
426
352
|
key: index.key,
|
|
427
353
|
parseOutput: false
|
|
428
354
|
});
|
|
429
355
|
}));
|
|
430
356
|
|
|
431
|
-
const deleteIndexesPoolStatus = await awaitPools.wipeIndexes(
|
|
357
|
+
const deleteIndexesPoolStatus = await awaitPools.wipeIndexes(collection['$id']);
|
|
432
358
|
if (!deleteIndexesPoolStatus) {
|
|
433
359
|
throw new Error("Index deletion did not finish for too long.");
|
|
434
360
|
}
|
|
435
361
|
|
|
436
362
|
// TODO: Pagination?
|
|
437
|
-
const { attributes: remoteAttributes } = await
|
|
438
|
-
databaseId,
|
|
363
|
+
const { attributes: remoteAttributes } = await databaseListAttributes({
|
|
439
364
|
collectionId: collection['$id'],
|
|
440
365
|
limit: 100,
|
|
441
366
|
parseOutput: false
|
|
442
367
|
});
|
|
443
368
|
|
|
444
369
|
await Promise.all(remoteAttributes.map(async attribute => {
|
|
445
|
-
await
|
|
446
|
-
databaseId,
|
|
370
|
+
await databaseDeleteAttribute({
|
|
447
371
|
collectionId: collection['$id'],
|
|
448
372
|
key: attribute.key,
|
|
449
373
|
parseOutput: false
|
|
450
374
|
});
|
|
451
375
|
}));
|
|
452
376
|
|
|
453
|
-
const deleteAttributesPoolStatus = await awaitPools.wipeAttributes(
|
|
377
|
+
const deleteAttributesPoolStatus = await awaitPools.wipeAttributes(collection['$id']);
|
|
454
378
|
if (!deleteAttributesPoolStatus) {
|
|
455
379
|
throw new Error("Attribute deletion did not finish for too long.");
|
|
456
380
|
}
|
|
457
381
|
|
|
458
382
|
await Promise.all(collection.attributes.map(async attribute => {
|
|
459
|
-
await createAttribute(
|
|
383
|
+
await createAttribute(collection['$id'], attribute);
|
|
460
384
|
}));
|
|
461
385
|
|
|
462
386
|
const attributeKeys = collection.attributes.map(attribute => attribute.key);
|
|
463
|
-
const createPoolStatus = await awaitPools.expectAttributes(
|
|
387
|
+
const createPoolStatus = await awaitPools.expectAttributes(collection['$id'], attributeKeys);
|
|
464
388
|
if (!createPoolStatus) {
|
|
465
389
|
throw new Error("Attribute creation did not finish for too long.");
|
|
466
390
|
}
|
|
@@ -469,8 +393,7 @@ const deployCollection = async ({ all } = {}) => {
|
|
|
469
393
|
|
|
470
394
|
log(`Creating indexes ...`)
|
|
471
395
|
await Promise.all(collection.indexes.map(async index => {
|
|
472
|
-
await
|
|
473
|
-
databaseId,
|
|
396
|
+
await databaseCreateIndex({
|
|
474
397
|
collectionId: collection['$id'],
|
|
475
398
|
key: index.key,
|
|
476
399
|
type: index.type,
|
|
@@ -481,7 +404,7 @@ const deployCollection = async ({ all } = {}) => {
|
|
|
481
404
|
}));
|
|
482
405
|
|
|
483
406
|
const indexKeys = collection.indexes.map(attribute => attribute.key);
|
|
484
|
-
const indexPoolStatus = await awaitPools.expectIndexes(
|
|
407
|
+
const indexPoolStatus = await awaitPools.expectIndexes(collection['$id'], indexKeys);
|
|
485
408
|
if (!indexPoolStatus) {
|
|
486
409
|
throw new Error("Index creation did not finish for too long.");
|
|
487
410
|
}
|
|
@@ -490,8 +413,7 @@ const deployCollection = async ({ all } = {}) => {
|
|
|
490
413
|
} catch (e) {
|
|
491
414
|
if (e.code == 404) {
|
|
492
415
|
log(`Collection ${collection.name} does not exist in the project. Creating ... `);
|
|
493
|
-
response = await
|
|
494
|
-
databaseId,
|
|
416
|
+
response = await databaseCreateCollection({
|
|
495
417
|
collectionId: collection['$id'],
|
|
496
418
|
name: collection.name,
|
|
497
419
|
permission: collection.permission,
|
|
@@ -502,11 +424,11 @@ const deployCollection = async ({ all } = {}) => {
|
|
|
502
424
|
|
|
503
425
|
log(`Creating attributes ... `);
|
|
504
426
|
await Promise.all(collection.attributes.map(async attribute => {
|
|
505
|
-
await createAttribute(
|
|
427
|
+
await createAttribute(collection['$id'], attribute);
|
|
506
428
|
}));
|
|
507
429
|
|
|
508
430
|
const attributeKeys = collection.attributes.map(attribute => attribute.key);
|
|
509
|
-
const attributePoolStatus = await awaitPools.expectAttributes(
|
|
431
|
+
const attributePoolStatus = await awaitPools.expectAttributes(collection['$id'], attributeKeys);
|
|
510
432
|
if (!attributePoolStatus) {
|
|
511
433
|
throw new Error("Attribute creation did not finish for too long.");
|
|
512
434
|
}
|
|
@@ -515,8 +437,7 @@ const deployCollection = async ({ all } = {}) => {
|
|
|
515
437
|
|
|
516
438
|
log(`Creating indexes ...`);
|
|
517
439
|
await Promise.all(collection.indexes.map(async index => {
|
|
518
|
-
await
|
|
519
|
-
databaseId,
|
|
440
|
+
await databaseCreateIndex({
|
|
520
441
|
collectionId: collection['$id'],
|
|
521
442
|
key: index.key,
|
|
522
443
|
type: index.type,
|
|
@@ -527,7 +448,7 @@ const deployCollection = async ({ all } = {}) => {
|
|
|
527
448
|
}));
|
|
528
449
|
|
|
529
450
|
const indexKeys = collection.indexes.map(attribute => attribute.key);
|
|
530
|
-
const indexPoolStatus = await awaitPools.expectIndexes(
|
|
451
|
+
const indexPoolStatus = await awaitPools.expectIndexes(collection['$id'], indexKeys);
|
|
531
452
|
if (!indexPoolStatus) {
|
|
532
453
|
throw new Error("Index creation did not finish for too long.");
|
|
533
454
|
}
|
|
@@ -545,14 +466,11 @@ const deployCollection = async ({ all } = {}) => {
|
|
|
545
466
|
deploy
|
|
546
467
|
.command("function")
|
|
547
468
|
.description("Deploy functions in the current directory.")
|
|
548
|
-
.option(`--functionId <functionId>`, `Function ID`)
|
|
549
|
-
.option(`--all`, `Flag to deploy all functions`)
|
|
550
469
|
.action(actionRunner(deployFunction));
|
|
551
470
|
|
|
552
471
|
deploy
|
|
553
472
|
.command("collection")
|
|
554
473
|
.description("Deploy collections in the current project.")
|
|
555
|
-
.option(`--all`, `Flag to deploy all functions`)
|
|
556
474
|
.action(actionRunner(deployCollection));
|
|
557
475
|
|
|
558
476
|
module.exports = {
|
package/lib/commands/generic.js
CHANGED
|
@@ -5,7 +5,7 @@ const { sdkForConsole } = require("../sdks");
|
|
|
5
5
|
const { globalConfig, localConfig } = require("../config");
|
|
6
6
|
const { actionRunner, success, parseBool, commandDescriptions, log, parse } = require("../parser");
|
|
7
7
|
const { questionsLogin } = require("../questions");
|
|
8
|
-
const {
|
|
8
|
+
const { accountCreateSession, accountDeleteSession } = require("./account");
|
|
9
9
|
|
|
10
10
|
const login = new Command("login")
|
|
11
11
|
.description(commandDescriptions['login'])
|
|
@@ -14,7 +14,7 @@ const login = new Command("login")
|
|
|
14
14
|
|
|
15
15
|
let client = await sdkForConsole(false);
|
|
16
16
|
|
|
17
|
-
await
|
|
17
|
+
await accountCreateSession({
|
|
18
18
|
email: answers.email,
|
|
19
19
|
password: answers.password,
|
|
20
20
|
parseOutput: false,
|
package/lib/commands/init.js
CHANGED
|
@@ -6,16 +6,22 @@ const inquirer = require("inquirer");
|
|
|
6
6
|
const { teamsCreate } = require("./teams");
|
|
7
7
|
const { projectsCreate } = require("./projects");
|
|
8
8
|
const { functionsCreate } = require("./functions");
|
|
9
|
-
const {
|
|
9
|
+
const { databaseListCollections } = require("./database");
|
|
10
10
|
const { sdkForConsole } = require("../sdks");
|
|
11
11
|
const { localConfig } = require("../config");
|
|
12
|
-
const { questionsInitProject, questionsInitFunction
|
|
12
|
+
const { questionsInitProject, questionsInitFunction } = require("../questions");
|
|
13
13
|
const { success, log, actionRunner, commandDescriptions } = require("../parser");
|
|
14
14
|
|
|
15
15
|
const init = new Command("init")
|
|
16
16
|
.description(commandDescriptions['init'])
|
|
17
|
-
.
|
|
18
|
-
|
|
17
|
+
.option("--all", "Flag to initialize projects and collection")
|
|
18
|
+
.action(actionRunner(async ({ all }, command) => {
|
|
19
|
+
if (all == undefined) {
|
|
20
|
+
command.help()
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
await initProject();
|
|
24
|
+
await initCollection()
|
|
19
25
|
}));
|
|
20
26
|
|
|
21
27
|
const initProject = async () => {
|
|
@@ -34,7 +40,7 @@ const initProject = async () => {
|
|
|
34
40
|
|
|
35
41
|
let teamId = response['$id'];
|
|
36
42
|
response = await projectsCreate({
|
|
37
|
-
projectId:
|
|
43
|
+
projectId: 'unique()',
|
|
38
44
|
name: answers.project,
|
|
39
45
|
teamId,
|
|
40
46
|
parseOutput: false
|
|
@@ -48,7 +54,6 @@ const initProject = async () => {
|
|
|
48
54
|
}
|
|
49
55
|
|
|
50
56
|
const initFunction = async () => {
|
|
51
|
-
// TODO: Add CI/CD support (ID, name, runtime)
|
|
52
57
|
let answers = await inquirer.prompt(questionsInitFunction)
|
|
53
58
|
|
|
54
59
|
if (fs.existsSync(path.join(process.cwd(), 'functions', answers.name))) {
|
|
@@ -60,7 +65,7 @@ const initFunction = async () => {
|
|
|
60
65
|
}
|
|
61
66
|
|
|
62
67
|
let response = await functionsCreate({
|
|
63
|
-
functionId:
|
|
68
|
+
functionId: 'unique()',
|
|
64
69
|
name: answers.name,
|
|
65
70
|
runtime: answers.runtime.id,
|
|
66
71
|
parseOutput: false
|
|
@@ -105,41 +110,20 @@ const initFunction = async () => {
|
|
|
105
110
|
success();
|
|
106
111
|
}
|
|
107
112
|
|
|
108
|
-
const initCollection = async (
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
}
|
|
114
|
-
let allDatabases = await databasesList({
|
|
115
|
-
parseOutput: false
|
|
116
|
-
})
|
|
117
|
-
|
|
118
|
-
databaseIds.push(...allDatabases.databases.map((d) => d.$id));
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
if(databaseIds.length <= 0) {
|
|
122
|
-
let answers = await inquirer.prompt(questionsInitCollection)
|
|
123
|
-
if (!answers.databases) process.exit(1)
|
|
124
|
-
databaseIds.push(...answers.databases);
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
for(const databaseId of databaseIds) {
|
|
128
|
-
// TODO: Pagination?
|
|
129
|
-
let response = await databasesListCollections({
|
|
130
|
-
databaseId,
|
|
131
|
-
limit: 100,
|
|
132
|
-
parseOutput: false
|
|
133
|
-
})
|
|
113
|
+
const initCollection = async () => {
|
|
114
|
+
// TODO: Pagination?
|
|
115
|
+
let response = await databaseListCollections({
|
|
116
|
+
limit: 100,
|
|
117
|
+
parseOutput: false
|
|
118
|
+
})
|
|
134
119
|
|
|
135
|
-
|
|
136
|
-
|
|
120
|
+
let collections = response.collections;
|
|
121
|
+
log(`Found ${collections.length} collections`);
|
|
137
122
|
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
}
|
|
123
|
+
collections.forEach(async collection => {
|
|
124
|
+
log(`Fetching ${collection.name} ...`);
|
|
125
|
+
localConfig.addCollection(collection);
|
|
126
|
+
});
|
|
143
127
|
|
|
144
128
|
success();
|
|
145
129
|
}
|
|
@@ -157,8 +141,6 @@ init
|
|
|
157
141
|
init
|
|
158
142
|
.command("collection")
|
|
159
143
|
.description("Initialise your Appwrite collections")
|
|
160
|
-
.option(`--databaseId <databaseId>`, `Database ID`)
|
|
161
|
-
.option(`--all`, `Flag to initialize all databases`)
|
|
162
144
|
.action(actionRunner(initCollection))
|
|
163
145
|
|
|
164
146
|
module.exports = {
|
package/lib/parser.js
CHANGED
|
@@ -150,7 +150,7 @@ const logo = "\n _ _ _ ___ __ _____
|
|
|
150
150
|
const commandDescriptions = {
|
|
151
151
|
"account": `The account command allows you to authenticate and manage a user account.`,
|
|
152
152
|
"avatars": `The avatars command aims to help you complete everyday tasks related to your app image, icons, and avatars.`,
|
|
153
|
-
"
|
|
153
|
+
"database": `The database command allows you to create structured collections of documents, query and filter lists of documents.`,
|
|
154
154
|
"deploy": `The deploy command provides a convenient wrapper for deploying your functions and collections.`,
|
|
155
155
|
"functions": `The functions command allows you view, create and manage your Cloud Functions.`,
|
|
156
156
|
"health": `The health command allows you to both validate and monitor your Appwrite server's health.`,
|
package/lib/questions.js
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
const { localConfig } = require('./config');
|
|
2
2
|
const { projectsList } = require('./commands/projects');
|
|
3
3
|
const { functionsListRuntimes } = require('./commands/functions');
|
|
4
|
-
const { databasesList } = require('./commands/databases');
|
|
5
4
|
const JSONbig = require("json-bigint")({ storeAsString: false });
|
|
6
5
|
|
|
7
6
|
const getIgnores = (runtime) => {
|
|
@@ -109,15 +108,6 @@ const questionsInitProject = [
|
|
|
109
108
|
return answers.start == "new";
|
|
110
109
|
},
|
|
111
110
|
},
|
|
112
|
-
{
|
|
113
|
-
type: "input",
|
|
114
|
-
name: "id",
|
|
115
|
-
message: "What ID would you like to have for your project?",
|
|
116
|
-
default: "myAwesomeProject",
|
|
117
|
-
when(answers) {
|
|
118
|
-
return answers.start == "new";
|
|
119
|
-
},
|
|
120
|
-
},
|
|
121
111
|
{
|
|
122
112
|
type: "list",
|
|
123
113
|
name: "project",
|
|
@@ -156,12 +146,6 @@ const questionsInitFunction = [
|
|
|
156
146
|
message: "What would you like to name your function?",
|
|
157
147
|
default: "My Awesome Function"
|
|
158
148
|
},
|
|
159
|
-
{
|
|
160
|
-
type: "input",
|
|
161
|
-
name: "id",
|
|
162
|
-
message: "What ID would you like to have for your function?",
|
|
163
|
-
default: "myAwesomeFunction"
|
|
164
|
-
},
|
|
165
149
|
{
|
|
166
150
|
type: "list",
|
|
167
151
|
name: "runtime",
|
|
@@ -182,31 +166,6 @@ const questionsInitFunction = [
|
|
|
182
166
|
}
|
|
183
167
|
];
|
|
184
168
|
|
|
185
|
-
const questionsInitCollection = [
|
|
186
|
-
{
|
|
187
|
-
type: "checkbox",
|
|
188
|
-
name: "databases",
|
|
189
|
-
message: "From which database would you like to init collections?",
|
|
190
|
-
choices: async () => {
|
|
191
|
-
let response = await databasesList({
|
|
192
|
-
parseOutput: false
|
|
193
|
-
})
|
|
194
|
-
let databases = response["databases"]
|
|
195
|
-
|
|
196
|
-
if(databases.length <= 0) {
|
|
197
|
-
throw new Error("No databases found. Please create one in project console.")
|
|
198
|
-
}
|
|
199
|
-
let choices = databases.map((database, idx) => {
|
|
200
|
-
return {
|
|
201
|
-
name: `${database.name} (${database.$id})`,
|
|
202
|
-
value: database.$id
|
|
203
|
-
}
|
|
204
|
-
})
|
|
205
|
-
return choices;
|
|
206
|
-
}
|
|
207
|
-
}
|
|
208
|
-
];
|
|
209
|
-
|
|
210
169
|
const questionsLogin = [
|
|
211
170
|
{
|
|
212
171
|
type: "input",
|
|
@@ -245,8 +204,8 @@ const questionsDeployFunctions = [
|
|
|
245
204
|
}
|
|
246
205
|
let choices = functions.map((func, idx) => {
|
|
247
206
|
return {
|
|
248
|
-
name: `${func.name} (${func
|
|
249
|
-
value: func
|
|
207
|
+
name: `${func.name} (${func['$id']})`,
|
|
208
|
+
value: JSONbig.stringify(func)
|
|
250
209
|
}
|
|
251
210
|
})
|
|
252
211
|
return choices;
|
|
@@ -267,7 +226,7 @@ const questionsDeployCollections = [
|
|
|
267
226
|
let choices = collections.map((collection, idx) => {
|
|
268
227
|
return {
|
|
269
228
|
name: `${collection.name} (${collection['$id']})`,
|
|
270
|
-
value: collection
|
|
229
|
+
value: JSONbig.stringify(collection)
|
|
271
230
|
}
|
|
272
231
|
})
|
|
273
232
|
return choices;
|
|
@@ -299,7 +258,6 @@ module.exports = {
|
|
|
299
258
|
questionsInitProject,
|
|
300
259
|
questionsLogin,
|
|
301
260
|
questionsInitFunction,
|
|
302
|
-
questionsInitCollection,
|
|
303
261
|
questionsDeployFunctions,
|
|
304
262
|
questionsDeployCollections,
|
|
305
263
|
questionsGetEntrypoint
|
package/package.json
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"name": "appwrite-cli",
|
|
3
3
|
"homepage": "https://appwrite.io/support",
|
|
4
4
|
"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",
|
|
5
|
-
"version": "0.18.
|
|
5
|
+
"version": "0.18.2",
|
|
6
6
|
"license": "BSD-3-Clause",
|
|
7
7
|
"main": "index.js",
|
|
8
8
|
"bin": {
|