@platforma-sdk/pl-cli 0.7.69 → 0.7.71
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/bin/run.mjs +5 -7
- package/dist/base_command.d.ts +31 -42
- package/dist/cli.d.ts +3 -0
- package/dist/cli.js +181 -247
- package/dist/cli.js.map +1 -1
- package/dist/cmd/admin/copy-project.d.ts +2 -16
- package/dist/cmd/project/delete.d.ts +2 -18
- package/dist/cmd/project/duplicate.d.ts +2 -19
- package/dist/cmd/project/info.d.ts +2 -17
- package/dist/cmd/project/list.d.ts +2 -14
- package/dist/cmd/project/rename.d.ts +2 -18
- package/dist/cmd-opts.d.ts +7 -17
- package/package.json +5 -25
- package/src/base_command.ts +58 -78
- package/src/cli.ts +32 -0
- package/src/cmd/admin/copy-project.ts +75 -76
- package/src/cmd/project/delete.ts +45 -48
- package/src/cmd/project/duplicate.ts +52 -59
- package/src/cmd/project/info.ts +40 -42
- package/src/cmd/project/list.ts +44 -39
- package/src/cmd/project/rename.ts +23 -30
- package/src/cmd-opts.ts +37 -54
- package/dist/cmd/index.d.ts +0 -14
- package/src/cmd/index.ts +0 -17
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { Command, Option } from "commander";
|
|
2
2
|
import { field, resourceIdToString } from "@milaboratories/pl-client";
|
|
3
3
|
import { ProjectMetaKey } from "@milaboratories/pl-middle-layer";
|
|
4
4
|
import { randomUUID } from "node:crypto";
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
5
|
+
import { connectClient } from "../../base_command";
|
|
6
|
+
import { addOptions, GlobalOptions, AdminAuthOptions } from "../../cmd-opts";
|
|
7
7
|
import {
|
|
8
8
|
resolveProject,
|
|
9
9
|
deduplicateName,
|
|
@@ -13,91 +13,90 @@ import {
|
|
|
13
13
|
} from "../../project_ops";
|
|
14
14
|
import { outputJson, outputText } from "../../output";
|
|
15
15
|
|
|
16
|
-
export default
|
|
17
|
-
|
|
18
|
-
"Copy a project from one user to another. Requires admin/controller credentials."
|
|
16
|
+
export default function adminCopyProjectCommand(): Command {
|
|
17
|
+
const cmd = new Command("copy-project").description(
|
|
18
|
+
"Copy a project from one user to another. Requires admin/controller credentials.",
|
|
19
|
+
);
|
|
19
20
|
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
"target-user"
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
"auto-rename": Flags.boolean({
|
|
40
|
-
summary: "Auto-rename on collision (default: true)",
|
|
41
|
-
default: true,
|
|
42
|
-
allowNo: true,
|
|
43
|
-
}),
|
|
44
|
-
};
|
|
21
|
+
addOptions(cmd, GlobalOptions(), AdminAuthOptions());
|
|
22
|
+
cmd.addOption(
|
|
23
|
+
new Option(
|
|
24
|
+
"--source-user <user>",
|
|
25
|
+
"Username of the source project owner",
|
|
26
|
+
).makeOptionMandatory(),
|
|
27
|
+
);
|
|
28
|
+
cmd.addOption(
|
|
29
|
+
new Option("--source-project <project>", "Source project ID or label").makeOptionMandatory(),
|
|
30
|
+
);
|
|
31
|
+
cmd.option(
|
|
32
|
+
"--target-user <user>",
|
|
33
|
+
"Username of the target user (defaults to source-user for same-user copy)",
|
|
34
|
+
);
|
|
35
|
+
cmd.option("-n, --name <name>", "Name for the copied project");
|
|
36
|
+
cmd.addOption(
|
|
37
|
+
new Option("--auto-rename", "Auto-rename on collision (default: true)").default(true),
|
|
38
|
+
);
|
|
39
|
+
cmd.option("--no-auto-rename", "Fail on name collision instead of auto-renaming");
|
|
45
40
|
|
|
46
|
-
|
|
47
|
-
const
|
|
48
|
-
|
|
41
|
+
cmd.action(async (flags) => {
|
|
42
|
+
const pl = await connectClient(flags);
|
|
43
|
+
try {
|
|
44
|
+
const targetUser = flags.targetUser ?? flags.sourceUser;
|
|
49
45
|
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
flags["source-project"],
|
|
57
|
-
);
|
|
46
|
+
const source = await navigateToUserRoot(pl, flags.sourceUser);
|
|
47
|
+
const { rid: sourceRid } = await resolveProject(
|
|
48
|
+
pl,
|
|
49
|
+
source.projectListRid,
|
|
50
|
+
flags.sourceProject,
|
|
51
|
+
);
|
|
58
52
|
|
|
59
|
-
|
|
60
|
-
|
|
53
|
+
const target =
|
|
54
|
+
targetUser === flags.sourceUser ? source : await navigateToUserRoot(pl, targetUser);
|
|
61
55
|
|
|
62
|
-
|
|
56
|
+
const newId = randomUUID();
|
|
63
57
|
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
58
|
+
const result = await pl.withWriteTx("adminCopyProject", async (tx) => {
|
|
59
|
+
const sourceMetaStr = await tx.getKValueString(sourceRid, ProjectMetaKey);
|
|
60
|
+
const sourceMeta = JSON.parse(sourceMetaStr);
|
|
61
|
+
const sourceLabel: string = sourceMeta.label;
|
|
68
62
|
|
|
69
|
-
|
|
63
|
+
const existingLabels = await getExistingLabelsInTx(tx, target.projectListRid);
|
|
70
64
|
|
|
71
|
-
|
|
65
|
+
let newLabel: string = flags.name ?? sourceLabel;
|
|
72
66
|
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
67
|
+
if (existingLabels.includes(newLabel)) {
|
|
68
|
+
if (!flags.autoRename) {
|
|
69
|
+
throw new Error(`Project name "${newLabel}" already exists for target user.`);
|
|
70
|
+
}
|
|
71
|
+
newLabel = deduplicateName(flags.name ?? sourceLabel, existingLabels);
|
|
76
72
|
}
|
|
77
|
-
newLabel = deduplicateName(flags.name ?? sourceLabel, existingLabels);
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
const newPrj = await duplicateProject(tx, sourceRid, { label: newLabel });
|
|
81
|
-
tx.createField(field(target.projectListRid, newId), "Dynamic", newPrj);
|
|
82
|
-
await tx.commit();
|
|
83
73
|
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
});
|
|
74
|
+
const newPrj = await duplicateProject(tx, sourceRid, { label: newLabel });
|
|
75
|
+
tx.createField(field(target.projectListRid, newId), "Dynamic", newPrj);
|
|
76
|
+
await tx.commit();
|
|
88
77
|
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
id:
|
|
92
|
-
rid: resourceIdToString(result.rid),
|
|
93
|
-
label: result.label,
|
|
94
|
-
sourceUser: flags["source-user"],
|
|
95
|
-
targetUser,
|
|
78
|
+
const prjResourceId = await newPrj.globalId;
|
|
79
|
+
const prjId = resourceIdToString(prjResourceId);
|
|
80
|
+
return { id: prjId, rid: prjResourceId, label: newLabel };
|
|
96
81
|
});
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
82
|
+
|
|
83
|
+
if (flags.format === "json") {
|
|
84
|
+
outputJson({
|
|
85
|
+
id: result.id,
|
|
86
|
+
rid: resourceIdToString(result.rid),
|
|
87
|
+
label: result.label,
|
|
88
|
+
sourceUser: flags.sourceUser,
|
|
89
|
+
targetUser,
|
|
90
|
+
});
|
|
91
|
+
} else {
|
|
92
|
+
outputText(
|
|
93
|
+
`Copied project from ${flags.sourceUser} to ${targetUser} as "${result.label}" (id: ${result.id}, rid: ${resourceIdToString(result.rid)})`,
|
|
94
|
+
);
|
|
95
|
+
}
|
|
96
|
+
} finally {
|
|
97
|
+
await pl.close();
|
|
101
98
|
}
|
|
102
|
-
}
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
return cmd;
|
|
103
102
|
}
|
|
@@ -1,58 +1,55 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { Command } from "commander";
|
|
2
2
|
import { createInterface } from "node:readline";
|
|
3
|
-
import {
|
|
3
|
+
import { connect } from "../../base_command";
|
|
4
|
+
import { addOptions, GlobalOptions, UserAuthOptions, AdminTargetOptions } from "../../cmd-opts";
|
|
4
5
|
import { resolveProject, deleteProject, getProjectInfo } from "../../project_ops";
|
|
5
6
|
import { outputJson, outputText } from "../../output";
|
|
6
7
|
|
|
7
|
-
export default
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
resolve,
|
|
39
|
-
);
|
|
40
|
-
});
|
|
41
|
-
if (answer.toLowerCase() !== "y") {
|
|
42
|
-
outputText("Aborted.");
|
|
43
|
-
return;
|
|
8
|
+
export default function projectDeleteCommand(): Command {
|
|
9
|
+
const cmd = new Command("delete").description(
|
|
10
|
+
"Delete a project. This permanently destroys all project data.",
|
|
11
|
+
);
|
|
12
|
+
|
|
13
|
+
cmd.argument("<project>", "Project ID or label");
|
|
14
|
+
addOptions(cmd, GlobalOptions(), UserAuthOptions(), AdminTargetOptions());
|
|
15
|
+
cmd.option("--force", "Skip confirmation", false);
|
|
16
|
+
|
|
17
|
+
cmd.action(async (project: string, flags) => {
|
|
18
|
+
const { pl, projectListRid } = await connect(flags);
|
|
19
|
+
try {
|
|
20
|
+
const { id, rid, fieldName } = await resolveProject(pl, projectListRid, project);
|
|
21
|
+
|
|
22
|
+
const info = await getProjectInfo(pl, id, rid);
|
|
23
|
+
|
|
24
|
+
if (!flags.force) {
|
|
25
|
+
const rl = createInterface({ input: process.stdin, output: process.stderr });
|
|
26
|
+
try {
|
|
27
|
+
const answer = await new Promise<string>((resolve) => {
|
|
28
|
+
rl.question(
|
|
29
|
+
`Delete project "${info.label}" (${info.blockCount} blocks)? [y/N] `,
|
|
30
|
+
resolve,
|
|
31
|
+
);
|
|
32
|
+
});
|
|
33
|
+
if (answer.toLowerCase() !== "y") {
|
|
34
|
+
outputText("Aborted.");
|
|
35
|
+
return;
|
|
36
|
+
}
|
|
37
|
+
} finally {
|
|
38
|
+
rl.close();
|
|
44
39
|
}
|
|
45
|
-
} finally {
|
|
46
|
-
rl.close();
|
|
47
40
|
}
|
|
48
|
-
}
|
|
49
41
|
|
|
50
|
-
|
|
42
|
+
await deleteProject(pl, projectListRid, fieldName);
|
|
51
43
|
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
44
|
+
if (flags.format === "json") {
|
|
45
|
+
outputJson({ deleted: true, id, label: info.label });
|
|
46
|
+
} else {
|
|
47
|
+
outputText(`Deleted project "${info.label}"`);
|
|
48
|
+
}
|
|
49
|
+
} finally {
|
|
50
|
+
await pl.close();
|
|
56
51
|
}
|
|
57
|
-
}
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
return cmd;
|
|
58
55
|
}
|
|
@@ -1,8 +1,9 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { Command, Option } from "commander";
|
|
2
2
|
import { field, resourceIdToString } from "@milaboratories/pl-client";
|
|
3
3
|
import { ProjectMetaKey } from "@milaboratories/pl-middle-layer";
|
|
4
4
|
import { randomUUID } from "node:crypto";
|
|
5
|
-
import {
|
|
5
|
+
import { connect } from "../../base_command";
|
|
6
|
+
import { addOptions, GlobalOptions, UserAuthOptions, AdminTargetOptions } from "../../cmd-opts";
|
|
6
7
|
import {
|
|
7
8
|
resolveProject,
|
|
8
9
|
deduplicateName,
|
|
@@ -11,72 +12,64 @@ import {
|
|
|
11
12
|
} from "../../project_ops";
|
|
12
13
|
import { outputJson, outputText } from "../../output";
|
|
13
14
|
|
|
14
|
-
export default
|
|
15
|
-
|
|
16
|
-
"Duplicate a project within the same user. Auto-renames on collision by default."
|
|
15
|
+
export default function projectDuplicateCommand(): Command {
|
|
16
|
+
const cmd = new Command("duplicate").description(
|
|
17
|
+
"Duplicate a project within the same user. Auto-renames on collision by default.",
|
|
18
|
+
);
|
|
17
19
|
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
20
|
+
cmd.argument("<project>", "Project ID or label");
|
|
21
|
+
addOptions(cmd, GlobalOptions(), UserAuthOptions(), AdminTargetOptions());
|
|
22
|
+
cmd.option("-n, --name <name>", "Name for the duplicate");
|
|
23
|
+
cmd.addOption(
|
|
24
|
+
new Option("--auto-rename", "Auto-rename on collision (default: true)").default(true),
|
|
25
|
+
);
|
|
26
|
+
cmd.option("--no-auto-rename", "Fail on name collision instead of auto-renaming");
|
|
24
27
|
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
summary: "Name for the duplicate",
|
|
30
|
-
helpValue: "<name>",
|
|
31
|
-
}),
|
|
32
|
-
"auto-rename": Flags.boolean({
|
|
33
|
-
summary: "Auto-rename on collision (default: true)",
|
|
34
|
-
default: true,
|
|
35
|
-
allowNo: true,
|
|
36
|
-
}),
|
|
37
|
-
};
|
|
28
|
+
cmd.action(async (project: string, flags) => {
|
|
29
|
+
const { pl, projectListRid } = await connect(flags);
|
|
30
|
+
try {
|
|
31
|
+
const { rid: sourceRid } = await resolveProject(pl, projectListRid, project);
|
|
38
32
|
|
|
39
|
-
|
|
40
|
-
const { args, flags } = await this.parse(ProjectDuplicate);
|
|
41
|
-
const { pl, projectListRid } = await this.connect(flags);
|
|
33
|
+
const newId = randomUUID();
|
|
42
34
|
|
|
43
|
-
|
|
35
|
+
const result = await pl.withWriteTx("duplicateProject", async (tx) => {
|
|
36
|
+
const sourceMetaStr = await tx.getKValueString(sourceRid, ProjectMetaKey);
|
|
37
|
+
const sourceMeta = JSON.parse(sourceMetaStr);
|
|
38
|
+
const sourceLabel: string = sourceMeta.label;
|
|
44
39
|
|
|
45
|
-
|
|
40
|
+
const existingLabels = await getExistingLabelsInTx(tx, projectListRid);
|
|
46
41
|
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
throw new Error(`Project name "${flags.name}" already exists.`);
|
|
42
|
+
// Compute new label
|
|
43
|
+
let newLabel: string;
|
|
44
|
+
if (flags.name) {
|
|
45
|
+
if (!flags.autoRename && existingLabels.includes(flags.name)) {
|
|
46
|
+
throw new Error(`Project name "${flags.name}" already exists.`);
|
|
47
|
+
}
|
|
48
|
+
newLabel = existingLabels.includes(flags.name)
|
|
49
|
+
? deduplicateName(flags.name, existingLabels)
|
|
50
|
+
: flags.name;
|
|
51
|
+
} else {
|
|
52
|
+
newLabel = deduplicateName(sourceLabel, existingLabels);
|
|
59
53
|
}
|
|
60
|
-
newLabel = existingLabels.includes(flags.name)
|
|
61
|
-
? deduplicateName(flags.name, existingLabels)
|
|
62
|
-
: flags.name;
|
|
63
|
-
} else {
|
|
64
|
-
newLabel = deduplicateName(sourceLabel, existingLabels);
|
|
65
|
-
}
|
|
66
54
|
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
55
|
+
const newPrj = await duplicateProject(tx, sourceRid, { label: newLabel });
|
|
56
|
+
tx.createField(field(projectListRid, newId), "Dynamic", newPrj);
|
|
57
|
+
await tx.commit();
|
|
70
58
|
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
59
|
+
const projectResourceId = await newPrj.globalId;
|
|
60
|
+
const projectId = resourceIdToString(projectResourceId);
|
|
61
|
+
return { id: projectId, rid: projectResourceId, label: newLabel };
|
|
62
|
+
});
|
|
75
63
|
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
64
|
+
if (flags.format === "json") {
|
|
65
|
+
outputJson({ id: result.id, rid: resourceIdToString(result.rid), label: result.label });
|
|
66
|
+
} else {
|
|
67
|
+
outputText(`Duplicated project as "${result.label}" (id: ${result.id})`);
|
|
68
|
+
}
|
|
69
|
+
} finally {
|
|
70
|
+
await pl.close();
|
|
80
71
|
}
|
|
81
|
-
}
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
return cmd;
|
|
82
75
|
}
|
package/src/cmd/project/info.ts
CHANGED
|
@@ -1,53 +1,51 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
1
|
+
import { Command } from "commander";
|
|
2
|
+
import { connect } from "../../base_command";
|
|
3
|
+
import { addOptions, GlobalOptions, UserAuthOptions, AdminTargetOptions } from "../../cmd-opts";
|
|
3
4
|
import { resolveProject, getProjectInfo } from "../../project_ops";
|
|
4
5
|
import { formatDate, outputJson, outputText } from "../../output";
|
|
5
6
|
import { resourceIdToString } from "@milaboratories/pl-client";
|
|
6
7
|
|
|
7
|
-
export default
|
|
8
|
-
|
|
8
|
+
export default function projectInfoCommand(): Command {
|
|
9
|
+
const cmd = new Command("info").description("Show detailed information about a project.");
|
|
9
10
|
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
description: "Project ID or label",
|
|
13
|
-
required: true,
|
|
14
|
-
}),
|
|
15
|
-
};
|
|
11
|
+
cmd.argument("<project>", "Project ID or label");
|
|
12
|
+
addOptions(cmd, GlobalOptions(), UserAuthOptions(), AdminTargetOptions());
|
|
16
13
|
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
14
|
+
cmd.action(async (project: string, flags) => {
|
|
15
|
+
const { pl, projectListRid } = await connect(flags);
|
|
16
|
+
try {
|
|
17
|
+
const { id, rid } = await resolveProject(pl, projectListRid, project);
|
|
18
|
+
const info = await getProjectInfo(pl, id, rid);
|
|
20
19
|
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
outputText(`Schema: ${info.schemaVersion ?? "(unknown)"}`);
|
|
43
|
-
outputText(`Blocks: ${info.blockCount}`);
|
|
44
|
-
if (info.blockIds.length > 0) {
|
|
45
|
-
for (const bid of info.blockIds) {
|
|
46
|
-
outputText(` - ${bid}`);
|
|
20
|
+
if (flags.format === "json") {
|
|
21
|
+
outputJson({
|
|
22
|
+
id: info.id,
|
|
23
|
+
rid: resourceIdToString(info.rid),
|
|
24
|
+
label: info.label,
|
|
25
|
+
schemaVersion: info.schemaVersion,
|
|
26
|
+
blockCount: info.blockCount,
|
|
27
|
+
blockIds: info.blockIds,
|
|
28
|
+
created: info.created.toISOString(),
|
|
29
|
+
lastModified: info.lastModified.toISOString(),
|
|
30
|
+
});
|
|
31
|
+
} else {
|
|
32
|
+
outputText(`Project: ${info.label}`);
|
|
33
|
+
outputText(`ID: ${info.id}`);
|
|
34
|
+
outputText(`RID: ${resourceIdToString(info.rid)}`);
|
|
35
|
+
outputText(`Schema: ${info.schemaVersion ?? "(unknown)"}`);
|
|
36
|
+
outputText(`Blocks: ${info.blockCount}`);
|
|
37
|
+
if (info.blockIds.length > 0) {
|
|
38
|
+
for (const bid of info.blockIds) {
|
|
39
|
+
outputText(` - ${bid}`);
|
|
40
|
+
}
|
|
47
41
|
}
|
|
42
|
+
outputText(`Created: ${formatDate(info.created)}`);
|
|
43
|
+
outputText(`Last Modified: ${formatDate(info.lastModified)}`);
|
|
48
44
|
}
|
|
49
|
-
|
|
50
|
-
|
|
45
|
+
} finally {
|
|
46
|
+
await pl.close();
|
|
51
47
|
}
|
|
52
|
-
}
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
return cmd;
|
|
53
51
|
}
|
package/src/cmd/project/list.ts
CHANGED
|
@@ -1,49 +1,54 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { Command } from "commander";
|
|
2
|
+
import { connect } from "../../base_command";
|
|
3
|
+
import { addOptions, GlobalOptions, UserAuthOptions, AdminTargetOptions } from "../../cmd-opts";
|
|
2
4
|
import { listProjects } from "../../project_ops";
|
|
3
5
|
import { formatTable, formatDate, outputJson, outputText } from "../../output";
|
|
4
6
|
import { resourceIdToString } from "@milaboratories/pl-client";
|
|
5
7
|
|
|
6
|
-
export default
|
|
7
|
-
|
|
8
|
+
export default function projectListCommand(): Command {
|
|
9
|
+
const cmd = new Command("list").description("List all projects for the authenticated user.");
|
|
8
10
|
|
|
9
|
-
|
|
10
|
-
...PlCommand.baseFlags,
|
|
11
|
-
};
|
|
11
|
+
addOptions(cmd, GlobalOptions(), UserAuthOptions(), AdminTargetOptions());
|
|
12
12
|
|
|
13
|
-
|
|
14
|
-
const {
|
|
15
|
-
|
|
16
|
-
|
|
13
|
+
cmd.action(async (flags) => {
|
|
14
|
+
const { pl, projectListRid } = await connect(flags);
|
|
15
|
+
try {
|
|
16
|
+
const projects = await listProjects(pl, projectListRid);
|
|
17
17
|
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
18
|
+
if (flags.format === "json") {
|
|
19
|
+
outputJson(
|
|
20
|
+
projects.map((p) => ({
|
|
21
|
+
id: p.id,
|
|
22
|
+
rid: resourceIdToString(p.rid),
|
|
23
|
+
label: p.label,
|
|
24
|
+
created: p.created.toISOString(),
|
|
25
|
+
lastModified: p.lastModified.toISOString(),
|
|
26
|
+
})),
|
|
27
|
+
);
|
|
28
|
+
} else {
|
|
29
|
+
if (projects.length === 0) {
|
|
30
|
+
outputText("No projects found.");
|
|
31
|
+
return;
|
|
32
|
+
}
|
|
33
33
|
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
34
|
+
outputText(
|
|
35
|
+
formatTable(
|
|
36
|
+
["ID", "RID", "LABEL", "CREATED", "LAST MODIFIED"],
|
|
37
|
+
projects.map((p) => [
|
|
38
|
+
p.id,
|
|
39
|
+
resourceIdToString(p.rid),
|
|
40
|
+
p.label,
|
|
41
|
+
formatDate(p.created),
|
|
42
|
+
formatDate(p.lastModified),
|
|
43
|
+
]),
|
|
44
|
+
),
|
|
45
|
+
);
|
|
46
|
+
outputText(`\n${projects.length} project(s)`);
|
|
47
|
+
}
|
|
48
|
+
} finally {
|
|
49
|
+
await pl.close();
|
|
47
50
|
}
|
|
48
|
-
}
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
return cmd;
|
|
49
54
|
}
|