Skip to content

Commit 8eac05a

Browse files
committed
Merge branch 'refs/heads/feat-report-error' into feat-cli-sudo
# Conflicts: # templates/cli/lib/commands/pull.js.twig # templates/cli/lib/commands/push.js.twig
2 parents ff9bfb4 + 94b8dd4 commit 8eac05a

File tree

15 files changed

+1044
-243
lines changed

15 files changed

+1044
-243
lines changed

src/SDK/Language.php

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,15 @@ public function getFilters(): array
8484
return [];
8585
}
8686

87+
/**
88+
* Language specific functions.
89+
* @return array
90+
*/
91+
public function getFunctions(): array
92+
{
93+
return [];
94+
}
95+
8796
protected function toPascalCase(string $value): string
8897
{
8998
return \ucfirst($this->toCamelCase($value));

src/SDK/Language/CLI.php

Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,66 @@
22

33
namespace Appwrite\SDK\Language;
44

5+
use Twig\TwigFunction;
6+
57
class CLI extends Node
68
{
9+
/**
10+
* List of functions to ignore for console preview.
11+
* @var array
12+
*/
13+
private $consoleIgnoreFunctions = [
14+
'listidentities',
15+
'listmfafactors',
16+
'getprefs',
17+
'getsession',
18+
'getattribute',
19+
'listdocumentlogs',
20+
'getindex',
21+
'listcollectionlogs',
22+
'getcollectionusage',
23+
'listlogs',
24+
'listruntimes',
25+
'getusage',
26+
'getusage',
27+
'listvariables',
28+
'getvariable',
29+
'listproviderlogs',
30+
'listsubscriberlogs',
31+
'getsubscriber',
32+
'listtopiclogs',
33+
'getemailtemplate',
34+
'getsmstemplate',
35+
'getfiledownload',
36+
'getfilepreview',
37+
'getfileview',
38+
'getusage',
39+
'listlogs',
40+
'getprefs',
41+
'getusage',
42+
'listlogs',
43+
'getmembership',
44+
'listmemberships',
45+
'listmfafactors',
46+
'getmfarecoverycodes',
47+
'getprefs',
48+
'listtargets',
49+
'gettarget',
50+
];
51+
52+
/**
53+
* List of SDK services to ignore for console preview.
54+
* @var array
55+
*/
56+
private $consoleIgnoreServices = [
57+
'health',
58+
'migrations',
59+
'locale',
60+
'avatars',
61+
'project',
62+
'proxy',
63+
'vcs'
64+
];
765
/**
866
* @var array
967
*/
@@ -172,6 +230,11 @@ public function getFiles(): array
172230
'destination' => 'lib/utils.js',
173231
'template' => 'cli/lib/utils.js.twig',
174232
],
233+
[
234+
'scope' => 'default',
235+
'destination' => 'lib/commands/init.js',
236+
'template' => 'cli/lib/commands/init.js.twig',
237+
],
175238
[
176239
'scope' => 'default',
177240
'destination' => 'lib/commands/pull.js',
@@ -285,4 +348,18 @@ public function getParamExample(array $param): string
285348

286349
return $output;
287350
}
351+
352+
/**
353+
* Language specific filters.
354+
* @return array
355+
*/
356+
public function getFunctions(): array
357+
{
358+
return [
359+
/** Return true if the entered service->method is enabled for a console preview link */
360+
new TwigFunction('methodHaveConsolePreview', fn($method, $service) => preg_match('/^([Gg]et|[Ll]ist)/', $method)
361+
&& !in_array(strtolower($method), $this->consoleIgnoreFunctions)
362+
&& !in_array($service, $this->consoleIgnoreServices)),
363+
];
364+
}
288365
}

src/SDK/SDK.php

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,13 @@ public function __construct(Language $language, Spec $spec)
8585
'debug' => true
8686
]);
8787

88+
/**
89+
* Add language-specific functions
90+
*/
91+
foreach ($this->language->getFunctions() as $function) {
92+
$this->twig->addFunction($function);
93+
}
94+
8895
/**
8996
* Add language specific filters
9097
*/

templates/cli/base/params.twig

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -75,8 +75,10 @@
7575
{% endif %}
7676
{% endfor %}
7777
{% if method.type == 'location' %}
78-
payload['project'] = localConfig.getProject().projectId
79-
payload['key'] = globalConfig.getKey();
80-
const queryParams = new URLSearchParams(payload);
81-
apiPath = `${globalConfig.getEndpoint()}${apiPath}?${queryParams.toString()}`;
78+
if (!overrideForCli) {
79+
payload['project'] = localConfig.getProject().projectId
80+
payload['key'] = globalConfig.getKey();
81+
const queryParams = new URLSearchParams(payload);
82+
apiPath = `${globalConfig.getEndpoint()}${apiPath}?${queryParams.toString()}`;
83+
}
8284
{% endif %}

templates/cli/base/requests/api.twig

Lines changed: 19 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -9,13 +9,27 @@
99
{% endfor %}
1010
}, payload{% if method.type == 'location' %}, 'arraybuffer'{% endif %});
1111

12-
{% if method.type == 'location' %}
13-
fs.writeFileSync(destination, response);
12+
{%~ if method.type == 'location' %}
13+
if (overrideForCli) {
14+
response = Buffer.from(response);
15+
}
1416

15-
{% endif %}
17+
fs.writeFileSync(destination, response);
18+
{%~ endif %}
1619
if (parseOutput) {
20+
{%~ if methodHaveConsolePreview(method.name,service.name) %}
21+
if(console) {
22+
showConsoleLink('{{service.name}}', '{{ method.name }}',open
23+
{%- for parameter in method.parameters.path -%}{%- set param = (parameter.name | caseCamel | escapeKeyword) -%}{%- if param ends with 'Id' -%}, {{ param }} {%- endif -%}{%- endfor -%}
24+
);
25+
} else {
26+
parse(response)
27+
success()
28+
}
29+
{%~ else %}
1730
parse(response)
1831
success()
32+
{%~ endif %}
1933
}
20-
21-
return response;
34+
35+
return response;

templates/cli/index.js.twig

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ const { commandDescriptions, cliConfig } = require("./lib/parser");
1212
const { client } = require("./lib/commands/generic");
1313
{% if sdk.test != "true" %}
1414
const { login, logout, whoami } = require("./lib/commands/generic");
15+
const { init } = require("./lib/commands/init");
1516
const { pull } = require("./lib/commands/pull");
1617
const { push } = require("./lib/commands/push");
1718
{% endif %}
@@ -55,6 +56,7 @@ program
5556
{% if sdk.test != "true" %}
5657
.addCommand(whoami)
5758
.addCommand(login)
59+
.addCommand(init)
5860
.addCommand(pull)
5961
.addCommand(push)
6062
.addCommand(logout)

templates/cli/lib/commands/command.js.twig

Lines changed: 23 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ const tar = require("tar");
44
const ignore = require("ignore");
55
const { promisify } = require('util');
66
const libClient = require('../client.js');
7-
const { getAllFiles } = require('../utils.js');
7+
const { getAllFiles, showConsoleLink } = require('../utils.js');
88
const { Command } = require('commander');
99
const { sdkForProject, sdkForConsole } = require('../sdks')
1010
const { parse, actionRunner, parseInteger, parseBool, commandDescriptions, success, log } = require('../parser')
@@ -45,6 +45,7 @@ const {{ service.name | caseLower }} = new Command("{{ service.name | caseLower
4545
{% for parameter in method.parameters.all %}
4646
* @property {{ "{" }}{{ parameter | typeName }}{{ "}" }} {{ parameter.name | caseCamel | escapeKeyword }} {{ parameter.description | replace({'`':'\''}) | replace({'\n':' '}) | replace({'\n \n':' '}) }}
4747
{% endfor %}
48+
* @property {boolean} overrideForCli
4849
* @property {boolean} parseOutput
4950
* @property {libClient | undefined} sdk
5051
{% if 'multipart/form-data' in method.consumes %}
@@ -58,8 +59,22 @@ const {{ service.name | caseLower }} = new Command("{{ service.name | caseLower
5859
/**
5960
* @param {{ "{" }}{{ service.name | caseUcfirst }}{{ method.name | caseUcfirst }}RequestParams{{ "}" }} params
6061
*/
61-
const {{ service.name | caseLower }}{{ method.name | caseUcfirst }} = async ({ {% for parameter in method.parameters.all %}{{ parameter.name | caseCamel | escapeKeyword }}, {% endfor %}parseOutput = true, sdk = undefined{% if 'multipart/form-data' in method.consumes %}, onProgress = () => {}{% endif %}{% if method.type == 'location' %}, destination{% endif %}}) => {
62-
let client = !sdk ? await {% if service.name == "projects" %}sdkForConsole(){% else %}sdkForProject(){% endif %} : sdk;
62+
{%~ block decleration -%}
63+
const {{ service.name | caseLower }}{{ method.name | caseUcfirst }} = async ({
64+
{%- for parameter in method.parameters.all -%}
65+
{{ parameter.name | caseCamel | escapeKeyword }},
66+
{%- endfor -%}
67+
68+
{%- block baseParams -%}parseOutput = true, overrideForCli = false, sdk = undefined {%- endblock -%}
69+
70+
{%- if 'multipart/form-data' in method.consumes -%},onProgress = () => {}{%- endif -%}
71+
72+
{%- if method.type == 'location' -%}, destination{%- endif -%}
73+
{% if methodHaveConsolePreview(method.name,service.name) %}, console, open{%- endif -%}
74+
}) => {
75+
{%~ endblock %}
76+
let client = !sdk ? await {% if service.name == "projects" %}sdkForConsole(){% else %}sdkForProject(){% endif %} :
77+
sdk;
6378
let apiPath = '{{ method.path }}'{% for parameter in method.parameters.path %}.replace('{{ '{' }}{{ parameter.name | caseCamel }}{{ '}' }}', {{ parameter.name | caseCamel | escapeKeyword }}){% endfor %};
6479
{{ include ('cli/base/params.twig') }}
6580
{% if 'multipart/form-data' in method.consumes %}
@@ -80,6 +95,10 @@ const {{ service.name | caseLower }}{{ method.name | caseUcfirst }} = async ({ {
8095
{% if method.type == 'location' %}
8196
.requiredOption(`--destination <path>`, `output file path.`)
8297
{% endif %}
98+
{% if methodHaveConsolePreview(method.name,service.name) %}
99+
.option(`--console`, `Get the resource console url`)
100+
.option(`--open`, `Use with '--console' to open the using default browser`)
101+
{% endif %}
83102
{% endautoescape %}
84103
.action(actionRunner({{ service.name | caseLower }}{{ method.name | caseUcfirst }}))
85104

@@ -90,4 +109,4 @@ module.exports = {
90109
{{ service.name | caseLower }}{{ method.name | caseUcfirst }}{% if not loop.last %},{% endif %}
91110

92111
{% endfor %}
93-
};
112+
};

templates/cli/lib/commands/generic.js.twig

Lines changed: 55 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,58 @@ const { actionRunner, success, parseBool, commandDescriptions, error, parse, dra
88
const { questionsLogin, questionsListFactors, questionsMfaChallenge } = require("../questions");
99
const { accountUpdateMfaChallenge, accountCreateMfaChallenge, accountGet, accountCreateEmailPasswordSession, accountDeleteSession } = require("./account");
1010

11+
const loginCommand = async () => {
12+
const answers = await inquirer.prompt(questionsLogin)
13+
14+
let client = await sdkForConsole(false);
15+
16+
await accountCreateEmailPasswordSession({
17+
email: answers.email,
18+
password: answers.password,
19+
parseOutput: false,
20+
sdk: client
21+
})
22+
23+
client.setCookie(globalConfig.getCookie());
24+
25+
let account;
26+
27+
try {
28+
account = await accountGet({
29+
sdk: client,
30+
parseOutput: false
31+
});
32+
} catch(error) {
33+
if (error.response === 'user_more_factors_required') {
34+
const { factor } = await inquirer.prompt(questionsListFactors);
35+
36+
const challenge = await accountCreateMfaChallenge({
37+
factor,
38+
parseOutput: false,
39+
sdk: client
40+
});
41+
42+
const { otp } = await inquirer.prompt(questionsMfaChallenge);
43+
44+
await accountUpdateMfaChallenge({
45+
challengeId: challenge.$id,
46+
otp,
47+
parseOutput: false,
48+
sdk: client
49+
});
50+
51+
account = await accountGet({
52+
sdk: client,
53+
parseOutput: false
54+
});
55+
} else {
56+
throw error;
57+
}
58+
}
59+
60+
success("Signed in as user with ID: " + account.$id);
61+
};
62+
1163
const whoami = new Command("whoami")
1264
.description(commandDescriptions['whoami'])
1365
.option("-j, --json", "Output in JSON format")
@@ -48,62 +100,13 @@ const whoami = new Command("whoami")
48100
drawTable(data)
49101
}));
50102

103+
51104
const login = new Command("login")
52105
.description(commandDescriptions['login'])
53106
.configureHelp({
54107
helpWidth: process.stdout.columns || 80
55108
})
56-
.action(actionRunner(async () => {
57-
const answers = await inquirer.prompt(questionsLogin)
58-
59-
let client = await sdkForConsole(false);
60-
61-
await accountCreateEmailPasswordSession({
62-
email: answers.email,
63-
password: answers.password,
64-
parseOutput: false,
65-
sdk: client
66-
})
67-
68-
client.setCookie(globalConfig.getCookie());
69-
70-
let account;
71-
72-
try {
73-
account = await accountGet({
74-
sdk: client,
75-
parseOutput: false
76-
});
77-
} catch (error) {
78-
if (error.response === 'user_more_factors_required') {
79-
const { factor } = await inquirer.prompt(questionsListFactors);
80-
81-
const challenge = await accountCreateMfaChallenge({
82-
factor,
83-
parseOutput: false,
84-
sdk: client
85-
});
86-
87-
const { otp } = await inquirer.prompt(questionsMfaChallenge);
88-
89-
await accountUpdateMfaChallenge({
90-
challengeId: challenge.$id,
91-
otp,
92-
parseOutput: false,
93-
sdk: client
94-
});
95-
96-
account = await accountGet({
97-
sdk: client,
98-
parseOutput: false
99-
});
100-
} else {
101-
throw error;
102-
}
103-
}
104-
105-
success("Signed in as user with ID: " + account.$id);
106-
}));
109+
.action(actionRunner(loginCommand));
107110

108111
const logout = new Command("logout")
109112
.description(commandDescriptions['logout'])
@@ -199,6 +202,7 @@ const client = new Command("client")
199202

200203
module.exports = {
201204
{% if sdk.test != "true" %}
205+
loginCommand,
202206
whoami,
203207
login,
204208
logout,

0 commit comments

Comments
 (0)