@mittwald/cli 1.0.0-alpha.37 → 1.0.0-alpha.38
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 +8 -8
- package/dist/BaseCommand.js +4 -0
- package/dist/commands/database/mysql/create.d.ts +1 -0
- package/dist/commands/database/mysql/create.js +34 -29
- package/dist/commands/database/mysql/create.test.d.ts +1 -0
- package/dist/commands/database/mysql/create.test.js +100 -0
- package/dist/commands/database/mysql/delete.js +1 -1
- package/dist/commands/database/mysql/dump.js +1 -1
- package/dist/commands/database/mysql/get.js +1 -1
- package/dist/commands/database/mysql/phpmyadmin.js +1 -1
- package/dist/commands/database/mysql/port-forward.js +1 -1
- package/dist/commands/database/mysql/shell.js +1 -1
- package/dist/commands/project/create.js +0 -2
- package/dist/lib/api_consistency.d.ts +2 -0
- package/dist/lib/api_consistency.js +21 -0
- package/dist/lib/api_retry.d.ts +2 -0
- package/dist/lib/api_retry.js +29 -0
- package/dist/lib/app/Installer.js +2 -2
- package/dist/lib/app/install.d.ts +1 -1
- package/dist/lib/app/install.js +3 -4
- package/dist/lib/app/wait.d.ts +1 -1
- package/dist/lib/app/wait.js +1 -3
- package/dist/lib/database/mysql/flags.d.ts +1 -2
- package/dist/lib/database/mysql/flags.js +7 -17
- package/package.json +6 -7
package/README.md
CHANGED
|
@@ -2280,7 +2280,7 @@ USAGE
|
|
|
2280
2280
|
$ mw database mysql delete DATABASE-ID [-q] [-f]
|
|
2281
2281
|
|
|
2282
2282
|
ARGUMENTS
|
|
2283
|
-
DATABASE-ID The ID of the database
|
|
2283
|
+
DATABASE-ID The ID or name of the database
|
|
2284
2284
|
|
|
2285
2285
|
FLAGS
|
|
2286
2286
|
-f, --force Do not ask for confirmation
|
|
@@ -2305,7 +2305,7 @@ USAGE
|
|
|
2305
2305
|
$ mw database mysql dump DATABASE-ID -o <value> [-q] [-p <value>] [--ssh-user <value>] [--temporary-user] [--gzip]
|
|
2306
2306
|
|
|
2307
2307
|
ARGUMENTS
|
|
2308
|
-
DATABASE-ID The ID of the database
|
|
2308
|
+
DATABASE-ID The ID or name of the database
|
|
2309
2309
|
|
|
2310
2310
|
FLAGS
|
|
2311
2311
|
-o, --output=<value> (required) the output file to write the dump to ("-" for stdout)
|
|
@@ -2365,7 +2365,7 @@ USAGE
|
|
|
2365
2365
|
$ mw database mysql get DATABASE-ID [-o json|yaml | | ]
|
|
2366
2366
|
|
|
2367
2367
|
ARGUMENTS
|
|
2368
|
-
DATABASE-ID The ID of the database
|
|
2368
|
+
DATABASE-ID The ID or name of the database
|
|
2369
2369
|
|
|
2370
2370
|
FLAGS
|
|
2371
2371
|
-o, --output=<option> output in a more machine friendly format
|
|
@@ -2415,7 +2415,7 @@ USAGE
|
|
|
2415
2415
|
$ mw database mysql phpmyadmin DATABASE-ID
|
|
2416
2416
|
|
|
2417
2417
|
ARGUMENTS
|
|
2418
|
-
DATABASE-ID The ID of the database
|
|
2418
|
+
DATABASE-ID The ID or name of the database
|
|
2419
2419
|
```
|
|
2420
2420
|
|
|
2421
2421
|
## `mw database mysql port-forward DATABASE-ID`
|
|
@@ -2427,7 +2427,7 @@ USAGE
|
|
|
2427
2427
|
$ mw database mysql port-forward DATABASE-ID [-q] [--ssh-user <value>] [--port <value>]
|
|
2428
2428
|
|
|
2429
2429
|
ARGUMENTS
|
|
2430
|
-
DATABASE-ID The ID of the database
|
|
2430
|
+
DATABASE-ID The ID or name of the database
|
|
2431
2431
|
|
|
2432
2432
|
FLAGS
|
|
2433
2433
|
-q, --quiet suppress process output and only display a machine-readable summary.
|
|
@@ -2457,7 +2457,7 @@ USAGE
|
|
|
2457
2457
|
$ mw database mysql shell DATABASE-ID [-q] [-p <value>]
|
|
2458
2458
|
|
|
2459
2459
|
ARGUMENTS
|
|
2460
|
-
DATABASE-ID The ID of the database
|
|
2460
|
+
DATABASE-ID The ID or name of the database
|
|
2461
2461
|
|
|
2462
2462
|
FLAGS
|
|
2463
2463
|
-p, --mysql-password=<value> the password to use for the MySQL user (env: MYSQL_PWD)
|
|
@@ -3123,7 +3123,7 @@ DESCRIPTION
|
|
|
3123
3123
|
Display help for mw.
|
|
3124
3124
|
```
|
|
3125
3125
|
|
|
3126
|
-
_See code: [@oclif/plugin-help](https://github.com/oclif/plugin-help/blob/v6.0.
|
|
3126
|
+
_See code: [@oclif/plugin-help](https://github.com/oclif/plugin-help/blob/v6.0.15/src/commands/help.ts)_
|
|
3127
3127
|
|
|
3128
3128
|
## `mw login reset`
|
|
3129
3129
|
|
|
@@ -4517,7 +4517,7 @@ EXAMPLES
|
|
|
4517
4517
|
$ mw update --available
|
|
4518
4518
|
```
|
|
4519
4519
|
|
|
4520
|
-
_See code: [@oclif/plugin-update](https://github.com/oclif/plugin-update/blob/v4.1.
|
|
4520
|
+
_See code: [@oclif/plugin-update](https://github.com/oclif/plugin-update/blob/v4.1.16/src/commands/update.ts)_
|
|
4521
4521
|
|
|
4522
4522
|
## `mw user api-token create`
|
|
4523
4523
|
|
package/dist/BaseCommand.js
CHANGED
|
@@ -2,6 +2,8 @@ import { Command } from "@oclif/core";
|
|
|
2
2
|
import * as fs from "fs/promises";
|
|
3
3
|
import * as path from "path";
|
|
4
4
|
import { MittwaldAPIV2Client } from "@mittwald/api-client";
|
|
5
|
+
import { configureAxiosRetry } from "./lib/api_retry.js";
|
|
6
|
+
import { configureConsistencyHandling } from "./lib/api_consistency.js";
|
|
5
7
|
export class BaseCommand extends Command {
|
|
6
8
|
authenticationRequired = true;
|
|
7
9
|
apiClient = MittwaldAPIV2Client.newUnauthenticated();
|
|
@@ -15,6 +17,8 @@ export class BaseCommand extends Command {
|
|
|
15
17
|
this.apiClient = MittwaldAPIV2Client.newWithToken(token);
|
|
16
18
|
this.apiClient.axios.defaults.headers["User-Agent"] =
|
|
17
19
|
`mittwald-cli/${this.config.version}`;
|
|
20
|
+
configureAxiosRetry(this.apiClient.axios);
|
|
21
|
+
configureConsistencyHandling(this.apiClient.axios);
|
|
18
22
|
}
|
|
19
23
|
}
|
|
20
24
|
getTokenFilename() {
|
|
@@ -18,6 +18,7 @@ export declare class Create extends ExecRenderBaseCommand<typeof Create, Result>
|
|
|
18
18
|
"project-id": import("@oclif/core/lib/interfaces/parser.js").OptionFlag<string>;
|
|
19
19
|
};
|
|
20
20
|
protected exec(): Promise<Result>;
|
|
21
|
+
private createMySQLDatabase;
|
|
21
22
|
private getPassword;
|
|
22
23
|
protected render({ databaseId }: Result): ReactNode;
|
|
23
24
|
}
|
|
@@ -49,46 +49,48 @@ export class Create extends ExecRenderBaseCommand {
|
|
|
49
49
|
const projectId = await this.withProjectId(Create);
|
|
50
50
|
const { description, version, collation, "character-set": characterSet, "user-external": externalAccess, "user-access-level": accessLevel, } = this.flags;
|
|
51
51
|
const password = await this.getPassword(p);
|
|
52
|
-
const db = await
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
},
|
|
64
|
-
},
|
|
65
|
-
user: {
|
|
66
|
-
password,
|
|
67
|
-
externalAccess,
|
|
68
|
-
accessLevel: accessLevel,
|
|
69
|
-
},
|
|
70
|
-
},
|
|
71
|
-
});
|
|
72
|
-
assertStatus(r, 201);
|
|
73
|
-
return r.data;
|
|
52
|
+
const db = await this.createMySQLDatabase(p, projectId, {
|
|
53
|
+
description,
|
|
54
|
+
version,
|
|
55
|
+
characterSettings: {
|
|
56
|
+
collation,
|
|
57
|
+
characterSet,
|
|
58
|
+
},
|
|
59
|
+
}, {
|
|
60
|
+
password,
|
|
61
|
+
externalAccess,
|
|
62
|
+
accessLevel: accessLevel,
|
|
74
63
|
});
|
|
75
64
|
const database = await p.runStep("fetching database", async () => {
|
|
76
|
-
const
|
|
65
|
+
const response = await this.apiClient.database.getMysqlDatabase({
|
|
77
66
|
mysqlDatabaseId: db.id,
|
|
78
67
|
});
|
|
79
|
-
assertStatus(
|
|
80
|
-
return
|
|
68
|
+
assertStatus(response, 200);
|
|
69
|
+
return response.data;
|
|
81
70
|
});
|
|
82
71
|
const user = await p.runStep("fetching user", async () => {
|
|
83
|
-
const
|
|
72
|
+
const response = await this.apiClient.database.getMysqlUser({
|
|
84
73
|
mysqlUserId: db.userId,
|
|
85
74
|
});
|
|
86
|
-
assertStatus(
|
|
87
|
-
return
|
|
75
|
+
assertStatus(response, 200);
|
|
76
|
+
return response.data;
|
|
88
77
|
});
|
|
89
|
-
p.complete(
|
|
78
|
+
await p.complete(_jsx(DatabaseCreateSuccess, { database: database, user: user }));
|
|
90
79
|
return { databaseId: db.id, userId: db.userId };
|
|
91
80
|
}
|
|
81
|
+
async createMySQLDatabase(p, projectId, database, user) {
|
|
82
|
+
return await p.runStep("creating MySQL database", async () => {
|
|
83
|
+
const r = await this.apiClient.database.createMysqlDatabase({
|
|
84
|
+
projectId,
|
|
85
|
+
data: {
|
|
86
|
+
database: { projectId, ...database },
|
|
87
|
+
user,
|
|
88
|
+
},
|
|
89
|
+
});
|
|
90
|
+
assertStatus(r, 201);
|
|
91
|
+
return r.data;
|
|
92
|
+
});
|
|
93
|
+
}
|
|
92
94
|
async getPassword(p) {
|
|
93
95
|
if (this.flags["user-password"]) {
|
|
94
96
|
return this.flags["user-password"];
|
|
@@ -101,3 +103,6 @@ export class Create extends ExecRenderBaseCommand {
|
|
|
101
103
|
}
|
|
102
104
|
}
|
|
103
105
|
}
|
|
106
|
+
function DatabaseCreateSuccess({ database, user, }) {
|
|
107
|
+
return (_jsxs(Success, { children: ["The database ", _jsx(Value, { children: database.name }), " and the user", " ", _jsx(Value, { children: user.name }), " were successfully created."] }));
|
|
108
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
import { expect, test } from "@oclif/test";
|
|
2
|
+
describe("database:mysql:create", () => {
|
|
3
|
+
const projectId = "339d6458-839f-4809-a03d-78700069690c";
|
|
4
|
+
const databaseId = "83e0cb85-dcf7-4968-8646-87a63980ae91";
|
|
5
|
+
const userId = "a8c1eb2a-aa4d-4daf-8e21-9d91d56559ca";
|
|
6
|
+
const password = "secret";
|
|
7
|
+
const description = "Test";
|
|
8
|
+
const createFlags = [
|
|
9
|
+
"database mysql create",
|
|
10
|
+
"--project-id",
|
|
11
|
+
projectId,
|
|
12
|
+
"--version",
|
|
13
|
+
"8.0",
|
|
14
|
+
"--description",
|
|
15
|
+
description,
|
|
16
|
+
"--user-password",
|
|
17
|
+
password,
|
|
18
|
+
];
|
|
19
|
+
test
|
|
20
|
+
.nock("https://api.mittwald.de", (api) => {
|
|
21
|
+
api.get(`/v2/projects/${projectId}`).reply(200, {
|
|
22
|
+
id: projectId,
|
|
23
|
+
});
|
|
24
|
+
api
|
|
25
|
+
.post(`/v2/projects/${projectId}/mysql-databases`, {
|
|
26
|
+
database: {
|
|
27
|
+
projectId,
|
|
28
|
+
description,
|
|
29
|
+
version: "8.0",
|
|
30
|
+
characterSettings: {
|
|
31
|
+
collation: "utf8mb4_unicode_ci",
|
|
32
|
+
characterSet: "utf8mb4",
|
|
33
|
+
},
|
|
34
|
+
},
|
|
35
|
+
user: {
|
|
36
|
+
password,
|
|
37
|
+
externalAccess: false,
|
|
38
|
+
accessLevel: "full",
|
|
39
|
+
},
|
|
40
|
+
})
|
|
41
|
+
.reply(201, { id: databaseId, userId });
|
|
42
|
+
api.get(`/v2/mysql-databases/${databaseId}`).reply(200, {
|
|
43
|
+
id: databaseId,
|
|
44
|
+
name: "mysql_xxxxxx",
|
|
45
|
+
});
|
|
46
|
+
api.get(`/v2/mysql-users/${userId}`).reply(200, {
|
|
47
|
+
id: userId,
|
|
48
|
+
name: "dbu_xxxxxx",
|
|
49
|
+
});
|
|
50
|
+
})
|
|
51
|
+
.env({ MITTWALD_API_TOKEN: "foo" })
|
|
52
|
+
.stdout()
|
|
53
|
+
.command(createFlags)
|
|
54
|
+
.it("creates a database and prints database and user name", (ctx) => {
|
|
55
|
+
// cannot match on exact output, because linebreaks
|
|
56
|
+
expect(ctx.stdout).to.contain("The database mysql_xxxxxx");
|
|
57
|
+
expect(ctx.stdout).to.contain("the user dbu_xxxxxx");
|
|
58
|
+
});
|
|
59
|
+
test
|
|
60
|
+
.nock("https://api.mittwald.de", (api) => {
|
|
61
|
+
api.get(`/v2/projects/${projectId}`).reply(200, {
|
|
62
|
+
id: projectId,
|
|
63
|
+
});
|
|
64
|
+
api
|
|
65
|
+
.post(`/v2/projects/${projectId}/mysql-databases`, {
|
|
66
|
+
database: {
|
|
67
|
+
projectId,
|
|
68
|
+
description: "Test",
|
|
69
|
+
version: "8.0",
|
|
70
|
+
characterSettings: {
|
|
71
|
+
collation: "utf8mb4_unicode_ci",
|
|
72
|
+
characterSet: "utf8mb4",
|
|
73
|
+
},
|
|
74
|
+
},
|
|
75
|
+
user: {
|
|
76
|
+
password: "secret",
|
|
77
|
+
externalAccess: false,
|
|
78
|
+
accessLevel: "full",
|
|
79
|
+
},
|
|
80
|
+
})
|
|
81
|
+
.reply(201, { id: databaseId, userId });
|
|
82
|
+
api.get(`/v2/mysql-databases/${databaseId}`).reply(200, {
|
|
83
|
+
id: databaseId,
|
|
84
|
+
name: "mysql_xxxxxx",
|
|
85
|
+
});
|
|
86
|
+
api.get(`/v2/mysql-users/${userId}`).times(3).reply(403);
|
|
87
|
+
api.get(`/v2/mysql-users/${userId}`).reply(200, {
|
|
88
|
+
id: userId,
|
|
89
|
+
name: "dbu_xxxxxx",
|
|
90
|
+
});
|
|
91
|
+
})
|
|
92
|
+
.env({ MITTWALD_API_TOKEN: "foo" })
|
|
93
|
+
.stdout()
|
|
94
|
+
.command(createFlags)
|
|
95
|
+
.it("retries fetching user until successful", (ctx) => {
|
|
96
|
+
// cannot match on exact output, because linebreaks
|
|
97
|
+
expect(ctx.stdout).to.contain("The database mysql_xxxxxx");
|
|
98
|
+
expect(ctx.stdout).to.contain("the user dbu_xxxxxx");
|
|
99
|
+
});
|
|
100
|
+
});
|
|
@@ -8,7 +8,7 @@ export default class Delete extends DeleteBaseCommand {
|
|
|
8
8
|
static flags = { ...DeleteBaseCommand.baseFlags };
|
|
9
9
|
static args = { ...mysqlArgs };
|
|
10
10
|
async deleteResource() {
|
|
11
|
-
const mysqlDatabaseId = await withMySQLId(this.apiClient, this.flags, this.args
|
|
11
|
+
const mysqlDatabaseId = await withMySQLId(this.apiClient, this.flags, this.args);
|
|
12
12
|
const response = await this.apiClient.database.deleteMysqlDatabase({
|
|
13
13
|
mysqlDatabaseId,
|
|
14
14
|
});
|
|
@@ -43,7 +43,7 @@ export class Dump extends ExecRenderBaseCommand {
|
|
|
43
43
|
};
|
|
44
44
|
static args = { ...mysqlArgs };
|
|
45
45
|
async exec() {
|
|
46
|
-
const databaseId = await withMySQLId(this.apiClient, this.flags, this.args
|
|
46
|
+
const databaseId = await withMySQLId(this.apiClient, this.flags, this.args);
|
|
47
47
|
const p = makeProcessRenderer(this.flags, "Dumping a MySQL database");
|
|
48
48
|
const connectionDetails = await getConnectionDetailsWithPassword(this.apiClient, databaseId, p, this.flags);
|
|
49
49
|
if (this.flags["temporary-user"]) {
|
|
@@ -7,7 +7,7 @@ export class Get extends GetBaseCommand {
|
|
|
7
7
|
};
|
|
8
8
|
static args = { ...mysqlArgs };
|
|
9
9
|
async getData() {
|
|
10
|
-
const mysqlDatabaseId = await withMySQLId(this.apiClient, this.flags, this.args
|
|
10
|
+
const mysqlDatabaseId = await withMySQLId(this.apiClient, this.flags, this.args);
|
|
11
11
|
return await this.apiClient.database.getMysqlDatabase({
|
|
12
12
|
mysqlDatabaseId,
|
|
13
13
|
});
|
|
@@ -7,7 +7,7 @@ export class PhpMyAdmin extends BaseCommand {
|
|
|
7
7
|
static args = { ...mysqlArgs };
|
|
8
8
|
async run() {
|
|
9
9
|
const { flags, args } = await this.parse(PhpMyAdmin);
|
|
10
|
-
const databaseId = await withMySQLId(this.apiClient, flags, args
|
|
10
|
+
const databaseId = await withMySQLId(this.apiClient, flags, args);
|
|
11
11
|
const users = await this.apiClient.database.listMysqlUsers({
|
|
12
12
|
mysqlDatabaseId: databaseId,
|
|
13
13
|
});
|
|
@@ -20,7 +20,7 @@ export class PortForward extends ExecRenderBaseCommand {
|
|
|
20
20
|
};
|
|
21
21
|
static args = { ...mysqlArgs };
|
|
22
22
|
async exec() {
|
|
23
|
-
const databaseId = await withMySQLId(this.apiClient, this.flags, this.args
|
|
23
|
+
const databaseId = await withMySQLId(this.apiClient, this.flags, this.args);
|
|
24
24
|
const p = makeProcessRenderer(this.flags, "Port-forwarding a MySQL database");
|
|
25
25
|
const { sshUser, sshHost, hostname, database } = await getConnectionDetails(this.apiClient, databaseId, this.flags["ssh-user"], p);
|
|
26
26
|
const { port } = this.flags;
|
|
@@ -13,7 +13,7 @@ export class Shell extends ExecRenderBaseCommand {
|
|
|
13
13
|
};
|
|
14
14
|
static args = { ...mysqlArgs };
|
|
15
15
|
async exec() {
|
|
16
|
-
const databaseId = await withMySQLId(this.apiClient, this.flags, this.args
|
|
16
|
+
const databaseId = await withMySQLId(this.apiClient, this.flags, this.args);
|
|
17
17
|
const p = makeProcessRenderer(this.flags, "Starting a MySQL shell");
|
|
18
18
|
const { sshUser, sshHost, user, hostname, database, password } = await getConnectionDetailsWithPassword(this.apiClient, databaseId, p, this.flags);
|
|
19
19
|
p.complete(_jsx(Text, { children: "Starting MySQL shell -- get ready..." }));
|
|
@@ -35,7 +35,6 @@ export default class Create extends ExecRenderBaseCommand {
|
|
|
35
35
|
data: { description },
|
|
36
36
|
});
|
|
37
37
|
assertStatus(result, 201);
|
|
38
|
-
const eventId = result.headers["etag"];
|
|
39
38
|
stepCreating.complete();
|
|
40
39
|
process.addInfo(_jsxs(Text, { children: ["project ID: ", _jsx(Value, { children: result.data.id })] }));
|
|
41
40
|
if (flags.wait) {
|
|
@@ -43,7 +42,6 @@ export default class Create extends ExecRenderBaseCommand {
|
|
|
43
42
|
await waitUntil(async () => {
|
|
44
43
|
const projectResponse = await this.apiClient.project.getProject({
|
|
45
44
|
projectId: result.data.id,
|
|
46
|
-
headers: { "if-event-reached": eventId },
|
|
47
45
|
});
|
|
48
46
|
if (projectResponse.status === 200 &&
|
|
49
47
|
projectResponse.data.readiness === "ready") {
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import debug from "debug";
|
|
2
|
+
const d = debug("mw:api-consistency");
|
|
3
|
+
export function configureConsistencyHandling(axios) {
|
|
4
|
+
let lastEventId = undefined;
|
|
5
|
+
axios.interceptors.request.use((config) => {
|
|
6
|
+
if (lastEventId !== undefined) {
|
|
7
|
+
d("setting if-event-reached to %o", lastEventId);
|
|
8
|
+
config.headers["if-event-reached"] = lastEventId;
|
|
9
|
+
}
|
|
10
|
+
return config;
|
|
11
|
+
});
|
|
12
|
+
axios.interceptors.response.use((response) => {
|
|
13
|
+
const isMutatingRequest = ["post", "put", "delete", "patch"].indexOf(response.config?.method?.toLowerCase() ?? "") >= 0;
|
|
14
|
+
const headers = response.headers;
|
|
15
|
+
if (headers.has("etag") && isMutatingRequest) {
|
|
16
|
+
d("setting last event id to %o after mutating request", headers.get("etag"));
|
|
17
|
+
lastEventId = headers.get("etag");
|
|
18
|
+
}
|
|
19
|
+
return response;
|
|
20
|
+
});
|
|
21
|
+
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import debug from "debug";
|
|
2
|
+
import axiosRetry from "axios-retry";
|
|
3
|
+
const d = debug("mw:api-retry");
|
|
4
|
+
export function configureAxiosRetry(axios) {
|
|
5
|
+
axios.interceptors.request.use((config) => {
|
|
6
|
+
return {
|
|
7
|
+
...config,
|
|
8
|
+
validateStatus: (status) => status < 300,
|
|
9
|
+
};
|
|
10
|
+
});
|
|
11
|
+
axiosRetry(axios, {
|
|
12
|
+
retries: 10,
|
|
13
|
+
retryDelay: axiosRetry.exponentialDelay,
|
|
14
|
+
onRetry(count, error) {
|
|
15
|
+
d("retrying request after %d attempts; error: %o", count, error.message);
|
|
16
|
+
},
|
|
17
|
+
retryCondition(error) {
|
|
18
|
+
if (error.code === "ERR_FR_TOO_MANY_REDIRECTS") {
|
|
19
|
+
return false;
|
|
20
|
+
}
|
|
21
|
+
if (axiosRetry.isNetworkOrIdempotentRequestError(error)) {
|
|
22
|
+
return true;
|
|
23
|
+
}
|
|
24
|
+
const isSafeRequest = error.config?.method?.toLowerCase() === "get";
|
|
25
|
+
const isAccessDenied = error.response?.status === 403;
|
|
26
|
+
return isSafeRequest && isAccessDenied;
|
|
27
|
+
},
|
|
28
|
+
});
|
|
29
|
+
}
|
|
@@ -33,10 +33,10 @@ export class AppInstaller {
|
|
|
33
33
|
const projectId = await withProjectId(apiClient, "flag", flags, args, config);
|
|
34
34
|
await autofillFlags(apiClient, process, this.appSupportedFlags, flags, projectId, this.appName);
|
|
35
35
|
const appVersion = await normalizeToAppVersionUuid(apiClient, "version" in flags ? flags.version : "latest", process, this.appId);
|
|
36
|
-
const
|
|
36
|
+
const appInstallationId = await triggerAppInstallation(apiClient, process, projectId, flags, appVersion);
|
|
37
37
|
let successText;
|
|
38
38
|
if (flags.wait) {
|
|
39
|
-
await waitUntilAppIsInstalled(apiClient, process, appInstallationId
|
|
39
|
+
await waitUntilAppIsInstalled(apiClient, process, appInstallationId);
|
|
40
40
|
successText = `Your ${this.appName} installation is now complete. Have fun! 🎉`;
|
|
41
41
|
}
|
|
42
42
|
else {
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { MittwaldAPIV2, MittwaldAPIV2Client } from "@mittwald/api-client";
|
|
2
2
|
import { ProcessRenderer } from "../../rendering/process/process.js";
|
|
3
3
|
type AppAppVersion = MittwaldAPIV2.Components.Schemas.AppAppVersion;
|
|
4
|
-
export declare function triggerAppInstallation(apiClient: MittwaldAPIV2Client, process: ProcessRenderer, projectId: string, flags: Record<string, string>, appVersion: AppAppVersion): Promise<string
|
|
4
|
+
export declare function triggerAppInstallation(apiClient: MittwaldAPIV2Client, process: ProcessRenderer, projectId: string, flags: Record<string, string>, appVersion: AppAppVersion): Promise<string>;
|
|
5
5
|
export {};
|
package/dist/lib/app/install.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { assertStatus } from "@mittwald/api-client-commons";
|
|
2
2
|
export async function triggerAppInstallation(apiClient, process, projectId, flags, appVersion) {
|
|
3
|
-
const
|
|
3
|
+
const appInstallationId = await process.runStep("starting installation", async () => {
|
|
4
4
|
const result = await apiClient.app.requestAppinstallation({
|
|
5
5
|
projectId,
|
|
6
6
|
data: {
|
|
@@ -14,7 +14,7 @@ export async function triggerAppInstallation(apiClient, process, projectId, flag
|
|
|
14
14
|
},
|
|
15
15
|
});
|
|
16
16
|
assertStatus(result, 201);
|
|
17
|
-
return
|
|
17
|
+
return result.data.id;
|
|
18
18
|
});
|
|
19
19
|
await process.runStep("waiting for installation to be retrievable", async () => {
|
|
20
20
|
for (let attempts = 0; attempts < 10; attempts++) {
|
|
@@ -31,7 +31,6 @@ export async function triggerAppInstallation(apiClient, process, projectId, flag
|
|
|
31
31
|
await process.runStep("setting document root", async () => {
|
|
32
32
|
const result = await apiClient.app.patchAppinstallation({
|
|
33
33
|
appInstallationId,
|
|
34
|
-
headers: { "if-event-reached": eventId },
|
|
35
34
|
data: {
|
|
36
35
|
customDocumentRoot: flags["document-root"],
|
|
37
36
|
},
|
|
@@ -39,5 +38,5 @@ export async function triggerAppInstallation(apiClient, process, projectId, flag
|
|
|
39
38
|
assertStatus(result, 204);
|
|
40
39
|
});
|
|
41
40
|
}
|
|
42
|
-
return
|
|
41
|
+
return appInstallationId;
|
|
43
42
|
}
|
package/dist/lib/app/wait.d.ts
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
import { MittwaldAPIV2Client } from "@mittwald/api-client";
|
|
2
2
|
import { ProcessRenderer } from "../../rendering/process/process.js";
|
|
3
|
-
export declare function waitUntilAppIsInstalled(apiClient: MittwaldAPIV2Client, process: ProcessRenderer, appInstallationId: string
|
|
3
|
+
export declare function waitUntilAppIsInstalled(apiClient: MittwaldAPIV2Client, process: ProcessRenderer, appInstallationId: string): Promise<void>;
|
package/dist/lib/app/wait.js
CHANGED
|
@@ -1,13 +1,11 @@
|
|
|
1
1
|
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
2
|
import { waitUntil } from "../wait.js";
|
|
3
3
|
import { Text } from "ink";
|
|
4
|
-
export async function waitUntilAppIsInstalled(apiClient, process, appInstallationId
|
|
4
|
+
export async function waitUntilAppIsInstalled(apiClient, process, appInstallationId) {
|
|
5
5
|
const stepWaiting = process.addStep(_jsx(Text, { children: "waiting for app installation to be ready" }));
|
|
6
6
|
await waitUntil(async () => {
|
|
7
7
|
const installationResponse = await apiClient.app.getAppinstallation({
|
|
8
8
|
appInstallationId,
|
|
9
|
-
// TODO: Remove once @mittwald/api-client supports this
|
|
10
|
-
headers: { "if-event-reached": eventId }, // eslint-disable-line
|
|
11
9
|
});
|
|
12
10
|
if (installationResponse.status === 200 &&
|
|
13
11
|
installationResponse.data.appVersion.current ==
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import { Config } from "@oclif/core";
|
|
2
1
|
import { MittwaldAPIV2Client } from "@mittwald/api-client";
|
|
3
2
|
import { ArgOutput, FlagOutput } from "@oclif/core/lib/interfaces/parser.js";
|
|
4
3
|
export declare const mysqlConnectionFlags: {
|
|
@@ -7,4 +6,4 @@ export declare const mysqlConnectionFlags: {
|
|
|
7
6
|
export declare const mysqlArgs: {
|
|
8
7
|
"database-id": import("@oclif/core/lib/interfaces/parser.js").Arg<string, Record<string, unknown>>;
|
|
9
8
|
};
|
|
10
|
-
export declare function withMySQLId(apiClient: MittwaldAPIV2Client, flags: FlagOutput, args: ArgOutput
|
|
9
|
+
export declare function withMySQLId(apiClient: MittwaldAPIV2Client, flags: FlagOutput, args: ArgOutput): Promise<string>;
|
|
@@ -1,6 +1,4 @@
|
|
|
1
1
|
import { Args, Flags } from "@oclif/core";
|
|
2
|
-
import { isUuid } from "../../../normalize_id.js";
|
|
3
|
-
import { withProjectId } from "../../project/flags.js";
|
|
4
2
|
import { assertStatus } from "@mittwald/api-client-commons";
|
|
5
3
|
export const mysqlConnectionFlags = {
|
|
6
4
|
"mysql-password": Flags.string({
|
|
@@ -17,7 +15,7 @@ NOTE: This is a security risk, as the password will be visible in the process li
|
|
|
17
15
|
};
|
|
18
16
|
export const mysqlArgs = {
|
|
19
17
|
"database-id": Args.string({
|
|
20
|
-
description: "The ID of the database
|
|
18
|
+
description: "The ID or name of the database",
|
|
21
19
|
required: true,
|
|
22
20
|
}),
|
|
23
21
|
};
|
|
@@ -30,19 +28,11 @@ function getIdCandidate(flags, args) {
|
|
|
30
28
|
}
|
|
31
29
|
throw new Error("No ID given");
|
|
32
30
|
}
|
|
33
|
-
export async function withMySQLId(apiClient, flags, args
|
|
34
|
-
const
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
}
|
|
38
|
-
const projectId = await withProjectId(apiClient, "flag", flags, args, cfg);
|
|
39
|
-
const databases = await apiClient.database.listMysqlDatabases({
|
|
40
|
-
projectId,
|
|
31
|
+
export async function withMySQLId(apiClient, flags, args) {
|
|
32
|
+
const mysqlDatabaseId = getIdCandidate(flags, args);
|
|
33
|
+
const response = await apiClient.database.getMysqlDatabase({
|
|
34
|
+
mysqlDatabaseId,
|
|
41
35
|
});
|
|
42
|
-
assertStatus(
|
|
43
|
-
|
|
44
|
-
if (!database) {
|
|
45
|
-
throw new Error(`No database with name "${candidate}" found`);
|
|
46
|
-
}
|
|
47
|
-
return database.id;
|
|
36
|
+
assertStatus(response, 200);
|
|
37
|
+
return response.data.id;
|
|
48
38
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mittwald/cli",
|
|
3
|
-
"version": "1.0.0-alpha.
|
|
3
|
+
"version": "1.0.0-alpha.38",
|
|
4
4
|
"description": "Hand-crafted CLI for the mittwald API",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": {
|
|
@@ -32,25 +32,24 @@
|
|
|
32
32
|
"post:generate": "yarn run -T compile && yarn run -T compile:cjs",
|
|
33
33
|
"test": "yarn test:format && yarn test:licenses && yarn test:unit",
|
|
34
34
|
"test:format": "yarn lint && yarn format --check",
|
|
35
|
-
"test:unit": "mocha --forbid-only \"src/**/*.test.ts\"",
|
|
36
35
|
"test:licenses": "yarn license-check --summary --unknown --failOn 'UNLICENSED;UNKNOWN'",
|
|
37
|
-
"test:readme": "yarn generate:readme && git diff --exit-code README.md"
|
|
36
|
+
"test:readme": "yarn generate:readme && git diff --exit-code README.md",
|
|
37
|
+
"test:unit": "mocha --forbid-only \"src/**/*.test.ts\""
|
|
38
38
|
},
|
|
39
39
|
"files": [
|
|
40
40
|
".deps",
|
|
41
|
-
"
|
|
42
|
-
"
|
|
41
|
+
"bin",
|
|
42
|
+
"dist/**/*.{js,d.ts}"
|
|
43
43
|
],
|
|
44
44
|
"dependencies": {
|
|
45
45
|
"@mittwald/api-client": "^4.9.0",
|
|
46
|
-
"@mittwald/api-client-commons": "^4.2.2",
|
|
47
46
|
"@mittwald/react-use-promise": "^2.1.2",
|
|
48
47
|
"@oclif/core": "^3.18.1",
|
|
49
48
|
"@oclif/plugin-autocomplete": "^3.0.3",
|
|
50
49
|
"@oclif/plugin-help": "^6.0.5",
|
|
51
50
|
"@oclif/plugin-update": "^4.1.3",
|
|
52
51
|
"@oclif/plugin-warn-if-update-available": "^3.0.2",
|
|
53
|
-
"axios": "^
|
|
52
|
+
"axios-retry": "^4.0.0",
|
|
54
53
|
"chalk": "^5.3.0",
|
|
55
54
|
"date-fns": "^3.2.0",
|
|
56
55
|
"humanize-string": "^3.0.0",
|