@credibledata/cred-cli 0.1.3 → 0.1.5
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/.prettierignore +2 -1
- package/README.md +0 -2
- package/bun.lock +182 -283
- package/dist/client/api.js +804 -111
- package/dist/client/api.js.map +1 -1
- package/dist/clientconfiguration.js +55 -13
- package/dist/clientconfiguration.js.map +1 -1
- package/dist/clientconfiguration.spec.js +305 -16
- package/dist/clientconfiguration.spec.js.map +1 -1
- package/dist/commands/add.js +6 -3
- package/dist/commands/add.js.map +1 -1
- package/dist/commands/connection/createconnections.js +18 -3
- package/dist/commands/connection/createconnections.js.map +1 -1
- package/dist/commands/connection/createconnections.spec.js +219 -0
- package/dist/commands/connection/createconnections.spec.js.map +1 -0
- package/dist/commands/connection/deleteconnection.spec.js +130 -0
- package/dist/commands/connection/deleteconnection.spec.js.map +1 -0
- package/dist/commands/connection/listconnections.js +9 -3
- package/dist/commands/connection/listconnections.js.map +1 -1
- package/dist/commands/connection/listconnections.spec.js +117 -0
- package/dist/commands/connection/listconnections.spec.js.map +1 -0
- package/dist/commands/group/addmember.js +2 -0
- package/dist/commands/group/addmember.js.map +1 -1
- package/dist/commands/group/addmember.spec.js +148 -0
- package/dist/commands/group/addmember.spec.js.map +1 -0
- package/dist/commands/group/creategroup.spec.js +122 -0
- package/dist/commands/group/creategroup.spec.js.map +1 -0
- package/dist/commands/group/deletegroup.spec.js +104 -0
- package/dist/commands/group/deletegroup.spec.js.map +1 -0
- package/dist/commands/group/getgroup.spec.js +111 -0
- package/dist/commands/group/getgroup.spec.js.map +1 -0
- package/dist/commands/group/listgroups.spec.js +118 -0
- package/dist/commands/group/listgroups.spec.js.map +1 -0
- package/dist/commands/group/removemember.js +1 -0
- package/dist/commands/group/removemember.js.map +1 -1
- package/dist/commands/group/removemember.spec.js +138 -0
- package/dist/commands/group/removemember.spec.js.map +1 -0
- package/dist/commands/groupaccesstoken/createToken.js +6 -2
- package/dist/commands/groupaccesstoken/createToken.js.map +1 -1
- package/dist/commands/groupaccesstoken/createToken.spec.js +127 -0
- package/dist/commands/groupaccesstoken/createToken.spec.js.map +1 -0
- package/dist/commands/groupaccesstoken/deleteToken.js.map +1 -1
- package/dist/commands/groupaccesstoken/deleteToken.spec.js +103 -0
- package/dist/commands/groupaccesstoken/deleteToken.spec.js.map +1 -0
- package/dist/commands/groupaccesstoken/groupContainer.js.map +1 -1
- package/dist/commands/groupaccesstoken/listTokens.js.map +1 -1
- package/dist/commands/groupaccesstoken/listTokens.spec.js +115 -0
- package/dist/commands/groupaccesstoken/listTokens.spec.js.map +1 -0
- package/dist/commands/list.js +10 -3
- package/dist/commands/list.js.map +1 -1
- package/dist/commands/misc/login.js +146 -22
- package/dist/commands/misc/login.js.map +1 -1
- package/dist/commands/misc/login.spec.js +111 -6
- package/dist/commands/misc/login.spec.js.map +1 -1
- package/dist/commands/misc/logout.spec.js +30 -0
- package/dist/commands/misc/logout.spec.js.map +1 -0
- package/dist/commands/misc/loopbackOAuth.js +110 -0
- package/dist/commands/misc/loopbackOAuth.js.map +1 -0
- package/dist/commands/misc/set-access-token.js.map +1 -1
- package/dist/commands/misc/set-access-token.spec.js +65 -0
- package/dist/commands/misc/set-access-token.spec.js.map +1 -0
- package/dist/commands/package/deletepackage.spec.js +136 -0
- package/dist/commands/package/deletepackage.spec.js.map +1 -0
- package/dist/commands/package/listpackages.js +10 -5
- package/dist/commands/package/listpackages.js.map +1 -1
- package/dist/commands/package/listpackages.spec.js +129 -0
- package/dist/commands/package/listpackages.spec.js.map +1 -0
- package/dist/commands/package/publish.js +23 -0
- package/dist/commands/package/publish.js.map +1 -1
- package/dist/commands/package/publish.spec.js +511 -0
- package/dist/commands/package/publish.spec.js.map +1 -0
- package/dist/commands/package/updatepackage.spec.js +50 -0
- package/dist/commands/package/updatepackage.spec.js.map +1 -1
- package/dist/commands/project/createproject.js +11 -1
- package/dist/commands/project/createproject.js.map +1 -1
- package/dist/commands/project/createproject.spec.js +154 -0
- package/dist/commands/project/createproject.spec.js.map +1 -0
- package/dist/commands/project/deleteproject.spec.js +106 -0
- package/dist/commands/project/deleteproject.spec.js.map +1 -0
- package/dist/commands/project/getproject.spec.js +99 -0
- package/dist/commands/project/getproject.spec.js.map +1 -0
- package/dist/commands/project/listprojects.js +13 -7
- package/dist/commands/project/listprojects.js.map +1 -1
- package/dist/commands/project/listprojects.spec.js +111 -0
- package/dist/commands/project/listprojects.spec.js.map +1 -0
- package/dist/commands/project/setproject.spec.js +58 -0
- package/dist/commands/project/setproject.spec.js.map +1 -0
- package/dist/commands/project/updateproject.js +68 -0
- package/dist/commands/project/updateproject.js.map +1 -0
- package/dist/commands/project/updateproject.spec.js +129 -0
- package/dist/commands/project/updateproject.spec.js.map +1 -0
- package/dist/commands/rm.js.map +1 -1
- package/dist/commands/update.js +13 -0
- package/dist/commands/update.js.map +1 -1
- package/dist/commands/version/listversions.spec.js +121 -0
- package/dist/commands/version/listversions.spec.js.map +1 -0
- package/dist/common/UserGroupId.spec.js +92 -0
- package/dist/common/UserGroupId.spec.js.map +1 -0
- package/dist/constants.js +49 -13
- package/dist/constants.js.map +1 -1
- package/dist/index.js +40 -18
- package/dist/index.js.map +1 -1
- package/dist/logger.spec.js +125 -0
- package/dist/logger.spec.js.map +1 -0
- package/dist/polyfills/file.polyfill.js.map +1 -1
- package/dist/utils.js +59 -0
- package/dist/utils.js.map +1 -1
- package/dist/utils.spec.js +158 -0
- package/dist/utils.spec.js.map +1 -0
- package/docker/Dockerfile.ci +11 -3
- package/eslint.config.mjs +45 -0
- package/generate-client.sh +5 -0
- package/package.json +24 -16
- package/src/client/.openapi-generator/FILES +1 -0
- package/src/client/api.ts +1999 -696
- package/src/clientconfiguration.spec.ts +394 -20
- package/src/clientconfiguration.ts +74 -15
- package/src/commands/add.ts +15 -6
- package/src/commands/connection/createconnections.spec.ts +403 -0
- package/src/commands/connection/createconnections.ts +30 -5
- package/src/commands/connection/deleteconnection.spec.ts +189 -0
- package/src/commands/connection/listconnections.spec.ts +132 -0
- package/src/commands/connection/listconnections.ts +22 -3
- package/src/commands/group/addmember.spec.ts +257 -0
- package/src/commands/group/addmember.ts +2 -0
- package/src/commands/group/creategroup.spec.ts +146 -0
- package/src/commands/group/deletegroup.spec.ts +120 -0
- package/src/commands/group/getgroup.spec.ts +129 -0
- package/src/commands/group/listgroups.spec.ts +137 -0
- package/src/commands/group/removemember.spec.ts +224 -0
- package/src/commands/group/removemember.ts +1 -0
- package/src/commands/groupaccesstoken/createToken.spec.ts +211 -0
- package/src/commands/groupaccesstoken/createToken.ts +55 -51
- package/src/commands/groupaccesstoken/deleteToken.spec.ts +147 -0
- package/src/commands/groupaccesstoken/deleteToken.ts +16 -11
- package/src/commands/groupaccesstoken/groupContainer.ts +51 -51
- package/src/commands/groupaccesstoken/listTokens.spec.ts +147 -0
- package/src/commands/groupaccesstoken/listTokens.ts +16 -11
- package/src/commands/list.ts +50 -9
- package/src/commands/misc/login.spec.ts +91 -6
- package/src/commands/misc/login.ts +208 -25
- package/src/commands/misc/logout.spec.ts +32 -0
- package/src/commands/misc/loopbackOAuth.ts +127 -0
- package/src/commands/misc/set-access-token.spec.ts +95 -0
- package/src/commands/misc/set-access-token.ts +16 -16
- package/src/commands/package/deletepackage.spec.ts +164 -0
- package/src/commands/package/listpackages.spec.ts +147 -0
- package/src/commands/package/listpackages.ts +28 -5
- package/src/commands/package/publish.spec.ts +597 -0
- package/src/commands/package/publish.ts +32 -0
- package/src/commands/package/updatepackage.spec.ts +98 -0
- package/src/commands/project/createproject.spec.ts +247 -0
- package/src/commands/project/createproject.ts +12 -1
- package/src/commands/project/deleteproject.spec.ts +123 -0
- package/src/commands/project/getproject.spec.ts +109 -0
- package/src/commands/project/listprojects.spec.ts +121 -0
- package/src/commands/project/listprojects.ts +33 -7
- package/src/commands/project/setproject.spec.ts +63 -0
- package/src/commands/project/updateproject.spec.ts +217 -0
- package/src/commands/project/updateproject.ts +82 -0
- package/src/commands/rm.ts +1 -6
- package/src/commands/update.ts +24 -1
- package/src/commands/version/listversions.spec.ts +138 -0
- package/src/common/UserGroupId.spec.ts +117 -0
- package/src/constants.ts +59 -13
- package/src/index.ts +46 -20
- package/src/logger.spec.ts +158 -0
- package/src/polyfills/file.polyfill.ts +1 -0
- package/src/utils.spec.ts +135 -0
- package/src/utils.ts +81 -0
- package/tsconfig.json +15 -18
- package/.eslintrc +0 -33
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
import { expect } from "chai";
|
|
2
|
+
import sinon from "sinon";
|
|
3
|
+
import { VersionsApi } from "../../client/api";
|
|
4
|
+
import { ClientConfiguration } from "../../clientconfiguration";
|
|
5
|
+
import { logger } from "../../logger";
|
|
6
|
+
import { prettifyJson } from "../../utils";
|
|
7
|
+
import { listVersions } from "./listversions";
|
|
8
|
+
|
|
9
|
+
describe("list-versions", () => {
|
|
10
|
+
let sandbox: sinon.SinonSandbox;
|
|
11
|
+
let listVersionsStub: sinon.SinonStub;
|
|
12
|
+
let loggerStub: sinon.SinonStubbedInstance<typeof logger>;
|
|
13
|
+
let consoleLogStub: sinon.SinonStub;
|
|
14
|
+
|
|
15
|
+
beforeEach(() => {
|
|
16
|
+
sandbox = sinon.createSandbox();
|
|
17
|
+
listVersionsStub = sandbox.stub();
|
|
18
|
+
loggerStub = sandbox.stub(logger);
|
|
19
|
+
consoleLogStub = sandbox.stub(console, "log");
|
|
20
|
+
|
|
21
|
+
sandbox
|
|
22
|
+
.stub(ClientConfiguration.prototype, "getOrganization")
|
|
23
|
+
.returns("test-org");
|
|
24
|
+
sandbox
|
|
25
|
+
.stub(ClientConfiguration.prototype, "getProject")
|
|
26
|
+
.returns("test-project");
|
|
27
|
+
sandbox
|
|
28
|
+
.stub(ClientConfiguration.prototype, "doesConfigExists")
|
|
29
|
+
.returns(true);
|
|
30
|
+
sandbox
|
|
31
|
+
.stub(ClientConfiguration.prototype, "getHeaders")
|
|
32
|
+
.returns({ Authorization: "Bearer test-token" });
|
|
33
|
+
|
|
34
|
+
sandbox
|
|
35
|
+
.stub(VersionsApi.prototype, "listVersions")
|
|
36
|
+
.callsFake(listVersionsStub);
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
afterEach(() => {
|
|
40
|
+
sandbox.restore();
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
it("should list versions successfully", async () => {
|
|
44
|
+
const mockVersions = [
|
|
45
|
+
{ id: "1.0.0", archiveStatus: "active" },
|
|
46
|
+
{ id: "2.0.0", archiveStatus: "active" },
|
|
47
|
+
];
|
|
48
|
+
const mockResponse = {
|
|
49
|
+
data: mockVersions,
|
|
50
|
+
};
|
|
51
|
+
listVersionsStub.resolves(mockResponse);
|
|
52
|
+
|
|
53
|
+
await listVersions("http://test-server", "/test-package", false);
|
|
54
|
+
|
|
55
|
+
expect(listVersionsStub.calledOnce).to.be.true;
|
|
56
|
+
expect(listVersionsStub.firstCall.args[0]).to.equal("test-org");
|
|
57
|
+
expect(listVersionsStub.firstCall.args[1]).to.equal("test-project");
|
|
58
|
+
expect(listVersionsStub.firstCall.args[2]).to.equal("test-package");
|
|
59
|
+
expect(listVersionsStub.firstCall.args[3]).to.deep.include({
|
|
60
|
+
baseURL: "http://test-server",
|
|
61
|
+
headers: { Authorization: "Bearer test-token" },
|
|
62
|
+
});
|
|
63
|
+
expect(consoleLogStub.calledWith(prettifyJson(mockVersions))).to.be.true;
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
it("should handle package path without leading slash", async () => {
|
|
67
|
+
const mockVersions = [{ id: "1.0.0" }];
|
|
68
|
+
const mockResponse = {
|
|
69
|
+
data: mockVersions,
|
|
70
|
+
};
|
|
71
|
+
listVersionsStub.resolves(mockResponse);
|
|
72
|
+
|
|
73
|
+
await listVersions("http://test-server", "test-package", false);
|
|
74
|
+
|
|
75
|
+
expect(listVersionsStub.firstCall.args[2]).to.equal("test-package");
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
it("should handle missing config", async () => {
|
|
79
|
+
sandbox.restore();
|
|
80
|
+
sandbox = sinon.createSandbox();
|
|
81
|
+
listVersionsStub = sandbox.stub();
|
|
82
|
+
loggerStub = sandbox.stub(logger);
|
|
83
|
+
consoleLogStub = sandbox.stub(console, "log");
|
|
84
|
+
|
|
85
|
+
sandbox
|
|
86
|
+
.stub(ClientConfiguration.prototype, "doesConfigExists")
|
|
87
|
+
.returns(false);
|
|
88
|
+
sandbox
|
|
89
|
+
.stub(VersionsApi.prototype, "listVersions")
|
|
90
|
+
.callsFake(listVersionsStub);
|
|
91
|
+
|
|
92
|
+
await listVersions("http://test-server", "/test-package", false);
|
|
93
|
+
|
|
94
|
+
expect(listVersionsStub.called).to.be.false;
|
|
95
|
+
expect(
|
|
96
|
+
loggerStub.error.calledWith(
|
|
97
|
+
sinon.match("Please, make sure that you are logged in"),
|
|
98
|
+
),
|
|
99
|
+
).to.be.true;
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
it("should handle missing project", async () => {
|
|
103
|
+
sandbox.restore();
|
|
104
|
+
sandbox = sinon.createSandbox();
|
|
105
|
+
listVersionsStub = sandbox.stub();
|
|
106
|
+
loggerStub = sandbox.stub(logger);
|
|
107
|
+
consoleLogStub = sandbox.stub(console, "log");
|
|
108
|
+
|
|
109
|
+
sandbox
|
|
110
|
+
.stub(ClientConfiguration.prototype, "doesConfigExists")
|
|
111
|
+
.returns(true);
|
|
112
|
+
sandbox.stub(ClientConfiguration.prototype, "getProject").returns("");
|
|
113
|
+
sandbox
|
|
114
|
+
.stub(VersionsApi.prototype, "listVersions")
|
|
115
|
+
.callsFake(listVersionsStub);
|
|
116
|
+
|
|
117
|
+
await listVersions("http://test-server", "/test-package", false);
|
|
118
|
+
|
|
119
|
+
expect(listVersionsStub.called).to.be.false;
|
|
120
|
+
expect(
|
|
121
|
+
loggerStub.error.calledWith(
|
|
122
|
+
sinon.match("Please, make sure to set a project"),
|
|
123
|
+
),
|
|
124
|
+
).to.be.true;
|
|
125
|
+
});
|
|
126
|
+
|
|
127
|
+
it("should handle API errors", async () => {
|
|
128
|
+
const mockError = new Error("API Error");
|
|
129
|
+
listVersionsStub.rejects(mockError);
|
|
130
|
+
|
|
131
|
+
await listVersions("http://test-server", "/test-package", true);
|
|
132
|
+
|
|
133
|
+
expect(listVersionsStub.calledOnce).to.be.true;
|
|
134
|
+
expect(
|
|
135
|
+
loggerStub.error.calledWith(sinon.match("Failed to list versions")),
|
|
136
|
+
).to.be.true;
|
|
137
|
+
});
|
|
138
|
+
});
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
import { expect } from "chai";
|
|
2
|
+
import { ResourceType, UserGroupId } from "./UserGroupId";
|
|
3
|
+
|
|
4
|
+
describe("UserGroupId", () => {
|
|
5
|
+
describe("constructor", () => {
|
|
6
|
+
it("should parse a valid user id", () => {
|
|
7
|
+
const id = new UserGroupId("user:john");
|
|
8
|
+
expect(id.getResourceType()).to.equal(ResourceType.USER);
|
|
9
|
+
expect(id.getResourceName()).to.equal("john");
|
|
10
|
+
expect(id.getValue()).to.equal("user:john");
|
|
11
|
+
});
|
|
12
|
+
|
|
13
|
+
it("should parse a valid group id", () => {
|
|
14
|
+
const id = new UserGroupId("group:admins");
|
|
15
|
+
expect(id.getResourceType()).to.equal(ResourceType.GROUP);
|
|
16
|
+
expect(id.getResourceName()).to.equal("admins");
|
|
17
|
+
expect(id.getValue()).to.equal("group:admins");
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
it("should trim whitespace from input", () => {
|
|
21
|
+
const id = new UserGroupId(" user:john ");
|
|
22
|
+
expect(id.getValue()).to.equal("user:john");
|
|
23
|
+
expect(id.getResourceName()).to.equal("john");
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
it("should throw for null or empty input", () => {
|
|
27
|
+
expect(() => new UserGroupId("")).to.throw("cannot be null or empty");
|
|
28
|
+
expect(() => new UserGroupId(" ")).to.throw(
|
|
29
|
+
"cannot be null or empty",
|
|
30
|
+
);
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
it("should throw for invalid format", () => {
|
|
34
|
+
expect(() => new UserGroupId("invalid")).to.throw(
|
|
35
|
+
"Invalid UserGroupId format",
|
|
36
|
+
);
|
|
37
|
+
expect(() => new UserGroupId("user:")).to.throw(
|
|
38
|
+
"Invalid UserGroupId format",
|
|
39
|
+
);
|
|
40
|
+
expect(() => new UserGroupId("foo:bar")).to.throw(
|
|
41
|
+
"Invalid UserGroupId format",
|
|
42
|
+
);
|
|
43
|
+
expect(() => new UserGroupId("user:a:b")).to.throw(
|
|
44
|
+
"Invalid UserGroupId format",
|
|
45
|
+
);
|
|
46
|
+
});
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
describe("static factory methods", () => {
|
|
50
|
+
it("forPublicAccess should create user:* id", () => {
|
|
51
|
+
const id = UserGroupId.forPublicAccess();
|
|
52
|
+
expect(id.getResourceType()).to.equal(ResourceType.USER);
|
|
53
|
+
expect(id.getResourceName()).to.equal("*");
|
|
54
|
+
expect(id.getValue()).to.equal("user:*");
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
it("forUser should create a user id", () => {
|
|
58
|
+
const id = UserGroupId.forUser("alice");
|
|
59
|
+
expect(id.getResourceType()).to.equal(ResourceType.USER);
|
|
60
|
+
expect(id.getResourceName()).to.equal("alice");
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
it("forUser should throw for undefined user name", () => {
|
|
64
|
+
expect(() => UserGroupId.forUser(undefined)).to.throw(
|
|
65
|
+
"cannot be undefined",
|
|
66
|
+
);
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
it("forGroup should create a group id", () => {
|
|
70
|
+
const id = UserGroupId.forGroup("developers");
|
|
71
|
+
expect(id.getResourceType()).to.equal(ResourceType.GROUP);
|
|
72
|
+
expect(id.getResourceName()).to.equal("developers");
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
it("create should build from ResourceType and name", () => {
|
|
76
|
+
const userId = UserGroupId.create(ResourceType.USER, "bob");
|
|
77
|
+
expect(userId.getResourceType()).to.equal(ResourceType.USER);
|
|
78
|
+
expect(userId.getResourceName()).to.equal("bob");
|
|
79
|
+
|
|
80
|
+
const groupId = UserGroupId.create(ResourceType.GROUP, "team");
|
|
81
|
+
expect(groupId.getResourceType()).to.equal(ResourceType.GROUP);
|
|
82
|
+
expect(groupId.getResourceName()).to.equal("team");
|
|
83
|
+
});
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
describe("serialization", () => {
|
|
87
|
+
it("toString should return original value", () => {
|
|
88
|
+
const id = new UserGroupId("user:john");
|
|
89
|
+
expect(id.toString()).to.equal("user:john");
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
it("toJSON should return original value for JSON serialization", () => {
|
|
93
|
+
const id = new UserGroupId("group:admins");
|
|
94
|
+
expect(id.toJSON()).to.equal("group:admins");
|
|
95
|
+
expect(JSON.stringify(id)).to.equal('"group:admins"');
|
|
96
|
+
});
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
describe("isPublicAccess", () => {
|
|
100
|
+
it("should return false for regular user ids", () => {
|
|
101
|
+
const id = new UserGroupId("user:john");
|
|
102
|
+
expect(id.isPublicAccess()).to.be.false;
|
|
103
|
+
});
|
|
104
|
+
|
|
105
|
+
it("should return false for group ids", () => {
|
|
106
|
+
const id = new UserGroupId("group:admins");
|
|
107
|
+
expect(id.isPublicAccess()).to.be.false;
|
|
108
|
+
});
|
|
109
|
+
|
|
110
|
+
it("should return false for user:* (checks originalValue not resourceName)", () => {
|
|
111
|
+
// isPublicAccess checks this.originalValue === "*" which is never true
|
|
112
|
+
// since originalValue is the full string "user:*"
|
|
113
|
+
const id = UserGroupId.forPublicAccess();
|
|
114
|
+
expect(id.isPublicAccess()).to.be.false;
|
|
115
|
+
});
|
|
116
|
+
});
|
|
117
|
+
});
|
package/src/constants.ts
CHANGED
|
@@ -3,16 +3,23 @@ export type Environment =
|
|
|
3
3
|
| "staging"
|
|
4
4
|
| "dev"
|
|
5
5
|
| "localhost"
|
|
6
|
-
| "e2e-test"
|
|
6
|
+
| "e2e-test"
|
|
7
|
+
| "videoamp";
|
|
7
8
|
|
|
8
9
|
export const controlPlanesByEnv: Record<Environment, string> = {
|
|
9
|
-
production: "https://admin.
|
|
10
|
-
dev: "https://dev-admin.
|
|
11
|
-
staging: "https://staging-admin.
|
|
10
|
+
production: "https://admin.credibledata.com/api/v0",
|
|
11
|
+
dev: "https://dev-admin.credibledata.com/api/v0",
|
|
12
|
+
staging: "https://staging-admin.credibledata.com/api/v0",
|
|
12
13
|
localhost: "http://admin.ms2.localhost/api/v0",
|
|
13
14
|
"e2e-test": "http://admin.ms2.localhost/api/v0",
|
|
15
|
+
videoamp: "https://admin.videoamp-aws-us-east-1.credibledata.com/api/v0",
|
|
14
16
|
};
|
|
15
17
|
|
|
18
|
+
/** Environments that must have an object in `~/.cred` `configPerEnvironment` (avoids undefined on write). */
|
|
19
|
+
export const ALL_ENVIRONMENTS = Object.keys(
|
|
20
|
+
controlPlanesByEnv,
|
|
21
|
+
) as Environment[];
|
|
22
|
+
|
|
16
23
|
export const auth0ConfigsByEnv: Record<
|
|
17
24
|
Environment,
|
|
18
25
|
{
|
|
@@ -23,34 +30,73 @@ export const auth0ConfigsByEnv: Record<
|
|
|
23
30
|
}
|
|
24
31
|
> = {
|
|
25
32
|
production: {
|
|
26
|
-
domain: "https://
|
|
33
|
+
domain: "https://auth.credibledata.com",
|
|
27
34
|
clientId: "ge8JL5TJNwqy7bygAaJkD92KdluqEZgO",
|
|
28
35
|
audience: "https://ms2data.us.auth0.com/api/v2/",
|
|
29
36
|
},
|
|
30
37
|
dev: {
|
|
31
|
-
domain: "https://dev-
|
|
38
|
+
domain: "https://dev-auth.credibledata.com",
|
|
32
39
|
clientId: "rr6mzJNj9b104MjCc9xWKPPehq1MJx0L",
|
|
33
40
|
audience: "https://dev-ohaafogw5p8uyx1m.us.auth0.com/api/v2/",
|
|
34
41
|
},
|
|
35
42
|
staging: {
|
|
36
|
-
domain: "https://dev-
|
|
43
|
+
domain: "https://dev-auth.credibledata.com",
|
|
37
44
|
clientId: "rr6mzJNj9b104MjCc9xWKPPehq1MJx0L",
|
|
38
45
|
audience: "https://dev-ohaafogw5p8uyx1m.us.auth0.com/api/v2/",
|
|
39
46
|
},
|
|
40
47
|
localhost: {
|
|
41
|
-
domain: "https://dev-
|
|
48
|
+
domain: "https://dev-auth.credibledata.com",
|
|
42
49
|
clientId: "rr6mzJNj9b104MjCc9xWKPPehq1MJx0L",
|
|
43
50
|
audience: "https://dev-ohaafogw5p8uyx1m.us.auth0.com/api/v2/",
|
|
44
51
|
},
|
|
45
52
|
"e2e-test": {
|
|
46
|
-
domain: "https://dev-
|
|
53
|
+
domain: "https://dev-auth.credibledata.com",
|
|
47
54
|
clientId: "atd8n8dEiGUiNRM7Geuvg41fi5s8ZKqY",
|
|
48
55
|
clientSecret:
|
|
49
56
|
"ZrEvs86WLqKO9EPp4y_cOvUn3NQT1qc3XC8111f6DBt9oyaclV5sWHz6Nkyo_bEF",
|
|
50
57
|
audience: "https://dev-ohaafogw5p8uyx1m.us.auth0.com/api/v2/",
|
|
51
58
|
},
|
|
59
|
+
videoamp: {
|
|
60
|
+
domain: "https://auth.credibledata.com",
|
|
61
|
+
clientId: "ge8JL5TJNwqy7bygAaJkD92KdluqEZgO",
|
|
62
|
+
audience: "https://ms2data.us.auth0.com/api/v2/",
|
|
63
|
+
},
|
|
52
64
|
};
|
|
53
65
|
|
|
66
|
+
/**
|
|
67
|
+
* `cred login <organizationName>` → Auth0 Organization ID for org-branded Universal Login.
|
|
68
|
+
*
|
|
69
|
+
* Auth0 documents the `organization` parameter on `/authorize` for Organizations
|
|
70
|
+
* (https://auth0.com/docs/manage-users/organizations/configure-organizations/use-org-name-authentication-api).
|
|
71
|
+
* The Device Authorization Flow documents only `client_id`, `scope`, and `audience` on
|
|
72
|
+
* `POST /oauth/device/code` — not `organization`
|
|
73
|
+
* (https://auth0.com/docs/get-started/authentication-and-authorization-flow/device-authorization-flow/call-your-api-using-the-device-authorization-flow#device-code-parameters),
|
|
74
|
+
* so mapped orgs use Authorization Code + PKCE with `organization` on `/authorize` and
|
|
75
|
+
* {@link AUTH_CLI_LOOPBACK_REDIRECT_URI}.
|
|
76
|
+
*
|
|
77
|
+
* Align with app `ORGANIZATION_HOST_ORG_ID_MAP` / `getAuth0OrganizationAuthorizationParams`.
|
|
78
|
+
*
|
|
79
|
+
* Entries are added when PKCE + Auth0 org id is ready for that CLI name; until then the map may
|
|
80
|
+
* stay empty and `login.spec` stubs {@link getAuth0OrganizationIdForCliLogin} for PKCE coverage.
|
|
81
|
+
*/
|
|
82
|
+
export const AUTH0_ORGANIZATION_IDS_BY_CLI_ORG_NAME: Record<string, string> = {
|
|
83
|
+
// videoamp: "org_nMRKafeSuxOiuJeG",
|
|
84
|
+
};
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* Allowlisted on the Auth0 Native application (CLI) as an Allowed Callback URL for PKCE logins
|
|
88
|
+
* that pass `organization` on `/authorize` (see Auth0 Organizations + Authentication API docs above).
|
|
89
|
+
*/
|
|
90
|
+
export const AUTH_CLI_LOOPBACK_REDIRECT_URI = "http://127.0.0.1:18240/callback";
|
|
91
|
+
|
|
92
|
+
export function getAuth0OrganizationIdForCliLogin(
|
|
93
|
+
organizationName: string,
|
|
94
|
+
): string | undefined {
|
|
95
|
+
return AUTH0_ORGANIZATION_IDS_BY_CLI_ORG_NAME[
|
|
96
|
+
organizationName.toLowerCase()
|
|
97
|
+
];
|
|
98
|
+
}
|
|
99
|
+
|
|
54
100
|
// Time constants in ms
|
|
55
101
|
export const SECOND = 1000;
|
|
56
102
|
export const MINUTE = SECOND * 60;
|
|
@@ -61,10 +107,10 @@ export const DAY = HOUR * 24;
|
|
|
61
107
|
export const LOGIN_TIMEOUT_MS = MINUTE * 5;
|
|
62
108
|
|
|
63
109
|
export const ASCII_TITLE = `
|
|
64
|
-
_____ _ _ _ _ _____ _ _____
|
|
110
|
+
_____ _ _ _ _ _____ _ _____
|
|
65
111
|
/ ____| | (_) | | | / ____| | |_ _|
|
|
66
|
-
| | _ __ ___ __| |_| |__ | | ___ | | | | | |
|
|
67
|
-
| | | '__/ _ \\/ _\` | | '_ \\| |/ _ \\ | | | | | |
|
|
68
|
-
| |____| | | __/ (_| | | |_) | | __/ | |____| |____ _| |_
|
|
112
|
+
| | _ __ ___ __| |_| |__ | | ___ | | | | | |
|
|
113
|
+
| | | '__/ _ \\/ _\` | | '_ \\| |/ _ \\ | | | | | |
|
|
114
|
+
| |____| | | __/ (_| | | |_) | | __/ | |____| |____ _| |_
|
|
69
115
|
\\_____|_| \\___|\\__,_|_|_.__/|_|\\___| \\_____|______|_____|
|
|
70
116
|
`;
|
package/src/index.ts
CHANGED
|
@@ -25,19 +25,25 @@ import { ASCII_TITLE, controlPlanesByEnv } from "./constants";
|
|
|
25
25
|
|
|
26
26
|
const clientConfig = new ClientConfiguration();
|
|
27
27
|
|
|
28
|
-
const
|
|
29
|
-
|
|
28
|
+
const getActiveEnvironment = () => {
|
|
29
|
+
return clientConfig.getEnvironment();
|
|
30
|
+
};
|
|
31
|
+
axios.defaults.baseURL = controlPlanesByEnv[getActiveEnvironment()];
|
|
30
32
|
|
|
31
33
|
const cli = program
|
|
32
|
-
.version(
|
|
34
|
+
.version(
|
|
35
|
+
cliPackage.version,
|
|
36
|
+
"-V, --cli-version",
|
|
37
|
+
"Output the CLI version number",
|
|
38
|
+
)
|
|
33
39
|
.description(kleur.green(ASCII_TITLE));
|
|
34
40
|
|
|
35
41
|
// Nested CRUD commands for resource management
|
|
36
|
-
registerGetCommands(cli,
|
|
37
|
-
registerListCommands(cli,
|
|
38
|
-
registerUpdateCommands(cli,
|
|
39
|
-
registerRmCommands(cli,
|
|
40
|
-
registerAddCommands(cli,
|
|
42
|
+
registerGetCommands(cli, getActiveEnvironment());
|
|
43
|
+
registerListCommands(cli, getActiveEnvironment());
|
|
44
|
+
registerUpdateCommands(cli, getActiveEnvironment());
|
|
45
|
+
registerRmCommands(cli, getActiveEnvironment());
|
|
46
|
+
registerAddCommands(cli, getActiveEnvironment());
|
|
41
47
|
registerSetCommands(cli);
|
|
42
48
|
|
|
43
49
|
// Root commands
|
|
@@ -58,7 +64,7 @@ cli.command("publish")
|
|
|
58
64
|
)
|
|
59
65
|
.action((options) => {
|
|
60
66
|
publish(
|
|
61
|
-
controlPlanesByEnv[
|
|
67
|
+
controlPlanesByEnv[getActiveEnvironment()],
|
|
62
68
|
options.replicationCount,
|
|
63
69
|
options.debug,
|
|
64
70
|
options.yes,
|
|
@@ -77,7 +83,7 @@ cli.command("archive")
|
|
|
77
83
|
.option("-y, --yes", "Skip confirmation.")
|
|
78
84
|
.action((packageName, versionId, options) => {
|
|
79
85
|
archiveVersion(
|
|
80
|
-
controlPlanesByEnv[
|
|
86
|
+
controlPlanesByEnv[getActiveEnvironment()],
|
|
81
87
|
packageName,
|
|
82
88
|
versionId,
|
|
83
89
|
options.debug,
|
|
@@ -96,7 +102,7 @@ cli.command("unarchive")
|
|
|
96
102
|
.option("-y, --yes", "Skip confirmation.")
|
|
97
103
|
.action((packageName, versionId, options) => {
|
|
98
104
|
unarchiveVersion(
|
|
99
|
-
controlPlanesByEnv[
|
|
105
|
+
controlPlanesByEnv[getActiveEnvironment()],
|
|
100
106
|
packageName,
|
|
101
107
|
versionId,
|
|
102
108
|
options.debug,
|
|
@@ -108,7 +114,11 @@ cli.command("login")
|
|
|
108
114
|
.description("Login into Credible service.")
|
|
109
115
|
.argument("<organizationName>", "The organization to login to.")
|
|
110
116
|
.action((organizationName) => {
|
|
111
|
-
|
|
117
|
+
// TODO: Uncomment this when aws videoamp cluster is ready
|
|
118
|
+
// if (organizationName === "videoamp") {
|
|
119
|
+
// clientConfig.setEnvironment("videoamp" as Environment);
|
|
120
|
+
// }
|
|
121
|
+
login(organizationName, controlPlanesByEnv[getActiveEnvironment()]);
|
|
112
122
|
});
|
|
113
123
|
|
|
114
124
|
cli.command("logout")
|
|
@@ -130,12 +140,26 @@ cli.command("status")
|
|
|
130
140
|
config.printStatus();
|
|
131
141
|
});
|
|
132
142
|
|
|
133
|
-
const complete = omelette("
|
|
143
|
+
const complete = omelette("credible").tree({
|
|
134
144
|
// CRUD commands
|
|
135
145
|
get: ["project", "group"],
|
|
136
|
-
ls: [
|
|
137
|
-
|
|
138
|
-
|
|
146
|
+
ls: [
|
|
147
|
+
"project",
|
|
148
|
+
"connection",
|
|
149
|
+
"package",
|
|
150
|
+
"version",
|
|
151
|
+
"group",
|
|
152
|
+
"group-access-token",
|
|
153
|
+
],
|
|
154
|
+
update: ["project", "package"],
|
|
155
|
+
rm: [
|
|
156
|
+
"project",
|
|
157
|
+
"connection",
|
|
158
|
+
"package",
|
|
159
|
+
"group",
|
|
160
|
+
"member",
|
|
161
|
+
"group-access-token",
|
|
162
|
+
],
|
|
139
163
|
add: ["project", "connection", "group", "member", "group-access-token"],
|
|
140
164
|
// Misc commands
|
|
141
165
|
set: ["project"],
|
|
@@ -152,16 +176,16 @@ complete.onAsync("$3", async ({ reply, line }) => {
|
|
|
152
176
|
const projectRelatedCommands = ["set project", "get project", "rm project"];
|
|
153
177
|
if (projectRelatedCommands.some((cmd) => line.includes(cmd))) {
|
|
154
178
|
reply(
|
|
155
|
-
listProjects(controlPlanesByEnv[
|
|
156
|
-
(
|
|
179
|
+
listProjects(controlPlanesByEnv[getActiveEnvironment()], false).then(
|
|
180
|
+
(result) => result.projects.map((project) => project.name),
|
|
157
181
|
),
|
|
158
182
|
);
|
|
159
183
|
}
|
|
160
184
|
const packageRelatedCommands = ["rm package", "ls version"];
|
|
161
185
|
if (packageRelatedCommands.some((cmd) => line.includes(cmd))) {
|
|
162
186
|
reply(
|
|
163
|
-
listPackages(controlPlanesByEnv[
|
|
164
|
-
(
|
|
187
|
+
listPackages(controlPlanesByEnv[getActiveEnvironment()], false).then(
|
|
188
|
+
(result) => result.packages.map((_package) => _package.name),
|
|
165
189
|
),
|
|
166
190
|
);
|
|
167
191
|
}
|
|
@@ -173,11 +197,13 @@ complete.next(() => {
|
|
|
173
197
|
|
|
174
198
|
if (~process.argv.indexOf("--cleanup")) {
|
|
175
199
|
complete.cleanupShellInitFile();
|
|
200
|
+
process.exit(0);
|
|
176
201
|
}
|
|
177
202
|
|
|
178
203
|
if (~process.argv.indexOf("--install")) {
|
|
179
204
|
complete.setupShellInitFile();
|
|
180
205
|
console.log("CLI installed successfully!");
|
|
206
|
+
process.exit(0);
|
|
181
207
|
}
|
|
182
208
|
|
|
183
209
|
complete.init();
|
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
import { expect } from "chai";
|
|
2
|
+
import sinon from "sinon";
|
|
3
|
+
import { AxiosError } from "axios";
|
|
4
|
+
import { logger, logAxiosError } from "./logger";
|
|
5
|
+
|
|
6
|
+
describe("logAxiosError", () => {
|
|
7
|
+
let loggerStub: sinon.SinonStubbedInstance<typeof logger>;
|
|
8
|
+
let sandbox: sinon.SinonSandbox;
|
|
9
|
+
|
|
10
|
+
beforeEach(() => {
|
|
11
|
+
sandbox = sinon.createSandbox();
|
|
12
|
+
loggerStub = sandbox.stub(logger);
|
|
13
|
+
});
|
|
14
|
+
|
|
15
|
+
afterEach(() => {
|
|
16
|
+
sandbox.restore();
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
it("should log 401 Unauthorized error", () => {
|
|
20
|
+
const error = {
|
|
21
|
+
response: {
|
|
22
|
+
status: 401,
|
|
23
|
+
data: null,
|
|
24
|
+
},
|
|
25
|
+
request: { path: "/api/test" },
|
|
26
|
+
isAxiosError: true,
|
|
27
|
+
} as unknown as AxiosError;
|
|
28
|
+
|
|
29
|
+
logAxiosError(error, false);
|
|
30
|
+
|
|
31
|
+
expect(loggerStub.error.calledWith(sinon.match("Unauthorized"))).to.be
|
|
32
|
+
.true;
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
it("should log 403 Forbidden error", () => {
|
|
36
|
+
const error = {
|
|
37
|
+
response: {
|
|
38
|
+
status: 403,
|
|
39
|
+
data: null,
|
|
40
|
+
},
|
|
41
|
+
request: { path: "/api/test" },
|
|
42
|
+
isAxiosError: true,
|
|
43
|
+
} as unknown as AxiosError;
|
|
44
|
+
|
|
45
|
+
logAxiosError(error, false);
|
|
46
|
+
|
|
47
|
+
expect(loggerStub.error.calledWith(sinon.match("Forbidden"))).to.be.true;
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
it("should log 500 Internal Server Error", () => {
|
|
51
|
+
const error = {
|
|
52
|
+
response: {
|
|
53
|
+
status: 500,
|
|
54
|
+
data: null,
|
|
55
|
+
},
|
|
56
|
+
request: { path: "/api/test" },
|
|
57
|
+
isAxiosError: true,
|
|
58
|
+
} as unknown as AxiosError;
|
|
59
|
+
|
|
60
|
+
logAxiosError(error, false);
|
|
61
|
+
|
|
62
|
+
expect(loggerStub.error.calledWith(sinon.match("Internal Server Error")))
|
|
63
|
+
.to.be.true;
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
it("should log response data when present", () => {
|
|
67
|
+
const error = {
|
|
68
|
+
response: {
|
|
69
|
+
status: 400,
|
|
70
|
+
data: "Bad request details",
|
|
71
|
+
},
|
|
72
|
+
request: { path: "/api/test" },
|
|
73
|
+
isAxiosError: true,
|
|
74
|
+
} as unknown as AxiosError;
|
|
75
|
+
|
|
76
|
+
logAxiosError(error, false);
|
|
77
|
+
|
|
78
|
+
expect(loggerStub.error.calledWith(sinon.match("Bad request details"))).to
|
|
79
|
+
.be.true;
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
it("should log debug info with status and path when debug is true", () => {
|
|
83
|
+
const error = {
|
|
84
|
+
response: {
|
|
85
|
+
status: 401,
|
|
86
|
+
data: null,
|
|
87
|
+
},
|
|
88
|
+
request: { path: "/api/test" },
|
|
89
|
+
isAxiosError: true,
|
|
90
|
+
} as unknown as AxiosError;
|
|
91
|
+
|
|
92
|
+
logAxiosError(error, true);
|
|
93
|
+
|
|
94
|
+
expect(loggerStub.error.calledWith(sinon.match("Error 401 at /api/test")))
|
|
95
|
+
.to.be.true;
|
|
96
|
+
});
|
|
97
|
+
|
|
98
|
+
it("should not log debug info when debug is false", () => {
|
|
99
|
+
const error = {
|
|
100
|
+
response: {
|
|
101
|
+
status: 401,
|
|
102
|
+
data: null,
|
|
103
|
+
},
|
|
104
|
+
request: { path: "/api/test" },
|
|
105
|
+
isAxiosError: true,
|
|
106
|
+
} as unknown as AxiosError;
|
|
107
|
+
|
|
108
|
+
logAxiosError(error, false);
|
|
109
|
+
|
|
110
|
+
expect(loggerStub.error.calledWith(sinon.match("Error 401 at"))).to.be
|
|
111
|
+
.false;
|
|
112
|
+
});
|
|
113
|
+
|
|
114
|
+
it("should handle request error with no response", () => {
|
|
115
|
+
const error = {
|
|
116
|
+
response: undefined,
|
|
117
|
+
request: {
|
|
118
|
+
_options: {
|
|
119
|
+
method: "GET",
|
|
120
|
+
path: "/api/resource",
|
|
121
|
+
hostname: "example.com",
|
|
122
|
+
port: 443,
|
|
123
|
+
},
|
|
124
|
+
},
|
|
125
|
+
code: "ECONNREFUSED",
|
|
126
|
+
message: "Connection refused",
|
|
127
|
+
isAxiosError: true,
|
|
128
|
+
} as unknown as AxiosError;
|
|
129
|
+
|
|
130
|
+
logAxiosError(error, false);
|
|
131
|
+
|
|
132
|
+
expect(
|
|
133
|
+
loggerStub.error.calledWith(
|
|
134
|
+
sinon.match(
|
|
135
|
+
"Failed to GET resource /api/resource on example.com:443",
|
|
136
|
+
),
|
|
137
|
+
),
|
|
138
|
+
).to.be.true;
|
|
139
|
+
});
|
|
140
|
+
|
|
141
|
+
it("should handle setup error with no request or response", () => {
|
|
142
|
+
const error = {
|
|
143
|
+
response: undefined,
|
|
144
|
+
request: undefined,
|
|
145
|
+
message: "Invalid URL",
|
|
146
|
+
isAxiosError: true,
|
|
147
|
+
} as unknown as AxiosError;
|
|
148
|
+
|
|
149
|
+
logAxiosError(error, false);
|
|
150
|
+
|
|
151
|
+
expect(
|
|
152
|
+
(loggerStub.error as sinon.SinonStub).calledWith(
|
|
153
|
+
"Error",
|
|
154
|
+
"Invalid URL",
|
|
155
|
+
),
|
|
156
|
+
).to.be.true;
|
|
157
|
+
});
|
|
158
|
+
});
|