@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.
Files changed (172) hide show
  1. package/.prettierignore +2 -1
  2. package/README.md +0 -2
  3. package/bun.lock +182 -283
  4. package/dist/client/api.js +804 -111
  5. package/dist/client/api.js.map +1 -1
  6. package/dist/clientconfiguration.js +55 -13
  7. package/dist/clientconfiguration.js.map +1 -1
  8. package/dist/clientconfiguration.spec.js +305 -16
  9. package/dist/clientconfiguration.spec.js.map +1 -1
  10. package/dist/commands/add.js +6 -3
  11. package/dist/commands/add.js.map +1 -1
  12. package/dist/commands/connection/createconnections.js +18 -3
  13. package/dist/commands/connection/createconnections.js.map +1 -1
  14. package/dist/commands/connection/createconnections.spec.js +219 -0
  15. package/dist/commands/connection/createconnections.spec.js.map +1 -0
  16. package/dist/commands/connection/deleteconnection.spec.js +130 -0
  17. package/dist/commands/connection/deleteconnection.spec.js.map +1 -0
  18. package/dist/commands/connection/listconnections.js +9 -3
  19. package/dist/commands/connection/listconnections.js.map +1 -1
  20. package/dist/commands/connection/listconnections.spec.js +117 -0
  21. package/dist/commands/connection/listconnections.spec.js.map +1 -0
  22. package/dist/commands/group/addmember.js +2 -0
  23. package/dist/commands/group/addmember.js.map +1 -1
  24. package/dist/commands/group/addmember.spec.js +148 -0
  25. package/dist/commands/group/addmember.spec.js.map +1 -0
  26. package/dist/commands/group/creategroup.spec.js +122 -0
  27. package/dist/commands/group/creategroup.spec.js.map +1 -0
  28. package/dist/commands/group/deletegroup.spec.js +104 -0
  29. package/dist/commands/group/deletegroup.spec.js.map +1 -0
  30. package/dist/commands/group/getgroup.spec.js +111 -0
  31. package/dist/commands/group/getgroup.spec.js.map +1 -0
  32. package/dist/commands/group/listgroups.spec.js +118 -0
  33. package/dist/commands/group/listgroups.spec.js.map +1 -0
  34. package/dist/commands/group/removemember.js +1 -0
  35. package/dist/commands/group/removemember.js.map +1 -1
  36. package/dist/commands/group/removemember.spec.js +138 -0
  37. package/dist/commands/group/removemember.spec.js.map +1 -0
  38. package/dist/commands/groupaccesstoken/createToken.js +6 -2
  39. package/dist/commands/groupaccesstoken/createToken.js.map +1 -1
  40. package/dist/commands/groupaccesstoken/createToken.spec.js +127 -0
  41. package/dist/commands/groupaccesstoken/createToken.spec.js.map +1 -0
  42. package/dist/commands/groupaccesstoken/deleteToken.js.map +1 -1
  43. package/dist/commands/groupaccesstoken/deleteToken.spec.js +103 -0
  44. package/dist/commands/groupaccesstoken/deleteToken.spec.js.map +1 -0
  45. package/dist/commands/groupaccesstoken/groupContainer.js.map +1 -1
  46. package/dist/commands/groupaccesstoken/listTokens.js.map +1 -1
  47. package/dist/commands/groupaccesstoken/listTokens.spec.js +115 -0
  48. package/dist/commands/groupaccesstoken/listTokens.spec.js.map +1 -0
  49. package/dist/commands/list.js +10 -3
  50. package/dist/commands/list.js.map +1 -1
  51. package/dist/commands/misc/login.js +146 -22
  52. package/dist/commands/misc/login.js.map +1 -1
  53. package/dist/commands/misc/login.spec.js +111 -6
  54. package/dist/commands/misc/login.spec.js.map +1 -1
  55. package/dist/commands/misc/logout.spec.js +30 -0
  56. package/dist/commands/misc/logout.spec.js.map +1 -0
  57. package/dist/commands/misc/loopbackOAuth.js +110 -0
  58. package/dist/commands/misc/loopbackOAuth.js.map +1 -0
  59. package/dist/commands/misc/set-access-token.js.map +1 -1
  60. package/dist/commands/misc/set-access-token.spec.js +65 -0
  61. package/dist/commands/misc/set-access-token.spec.js.map +1 -0
  62. package/dist/commands/package/deletepackage.spec.js +136 -0
  63. package/dist/commands/package/deletepackage.spec.js.map +1 -0
  64. package/dist/commands/package/listpackages.js +10 -5
  65. package/dist/commands/package/listpackages.js.map +1 -1
  66. package/dist/commands/package/listpackages.spec.js +129 -0
  67. package/dist/commands/package/listpackages.spec.js.map +1 -0
  68. package/dist/commands/package/publish.js +23 -0
  69. package/dist/commands/package/publish.js.map +1 -1
  70. package/dist/commands/package/publish.spec.js +511 -0
  71. package/dist/commands/package/publish.spec.js.map +1 -0
  72. package/dist/commands/package/updatepackage.spec.js +50 -0
  73. package/dist/commands/package/updatepackage.spec.js.map +1 -1
  74. package/dist/commands/project/createproject.js +11 -1
  75. package/dist/commands/project/createproject.js.map +1 -1
  76. package/dist/commands/project/createproject.spec.js +154 -0
  77. package/dist/commands/project/createproject.spec.js.map +1 -0
  78. package/dist/commands/project/deleteproject.spec.js +106 -0
  79. package/dist/commands/project/deleteproject.spec.js.map +1 -0
  80. package/dist/commands/project/getproject.spec.js +99 -0
  81. package/dist/commands/project/getproject.spec.js.map +1 -0
  82. package/dist/commands/project/listprojects.js +13 -7
  83. package/dist/commands/project/listprojects.js.map +1 -1
  84. package/dist/commands/project/listprojects.spec.js +111 -0
  85. package/dist/commands/project/listprojects.spec.js.map +1 -0
  86. package/dist/commands/project/setproject.spec.js +58 -0
  87. package/dist/commands/project/setproject.spec.js.map +1 -0
  88. package/dist/commands/project/updateproject.js +68 -0
  89. package/dist/commands/project/updateproject.js.map +1 -0
  90. package/dist/commands/project/updateproject.spec.js +129 -0
  91. package/dist/commands/project/updateproject.spec.js.map +1 -0
  92. package/dist/commands/rm.js.map +1 -1
  93. package/dist/commands/update.js +13 -0
  94. package/dist/commands/update.js.map +1 -1
  95. package/dist/commands/version/listversions.spec.js +121 -0
  96. package/dist/commands/version/listversions.spec.js.map +1 -0
  97. package/dist/common/UserGroupId.spec.js +92 -0
  98. package/dist/common/UserGroupId.spec.js.map +1 -0
  99. package/dist/constants.js +49 -13
  100. package/dist/constants.js.map +1 -1
  101. package/dist/index.js +40 -18
  102. package/dist/index.js.map +1 -1
  103. package/dist/logger.spec.js +125 -0
  104. package/dist/logger.spec.js.map +1 -0
  105. package/dist/polyfills/file.polyfill.js.map +1 -1
  106. package/dist/utils.js +59 -0
  107. package/dist/utils.js.map +1 -1
  108. package/dist/utils.spec.js +158 -0
  109. package/dist/utils.spec.js.map +1 -0
  110. package/docker/Dockerfile.ci +11 -3
  111. package/eslint.config.mjs +45 -0
  112. package/generate-client.sh +5 -0
  113. package/package.json +24 -16
  114. package/src/client/.openapi-generator/FILES +1 -0
  115. package/src/client/api.ts +1999 -696
  116. package/src/clientconfiguration.spec.ts +394 -20
  117. package/src/clientconfiguration.ts +74 -15
  118. package/src/commands/add.ts +15 -6
  119. package/src/commands/connection/createconnections.spec.ts +403 -0
  120. package/src/commands/connection/createconnections.ts +30 -5
  121. package/src/commands/connection/deleteconnection.spec.ts +189 -0
  122. package/src/commands/connection/listconnections.spec.ts +132 -0
  123. package/src/commands/connection/listconnections.ts +22 -3
  124. package/src/commands/group/addmember.spec.ts +257 -0
  125. package/src/commands/group/addmember.ts +2 -0
  126. package/src/commands/group/creategroup.spec.ts +146 -0
  127. package/src/commands/group/deletegroup.spec.ts +120 -0
  128. package/src/commands/group/getgroup.spec.ts +129 -0
  129. package/src/commands/group/listgroups.spec.ts +137 -0
  130. package/src/commands/group/removemember.spec.ts +224 -0
  131. package/src/commands/group/removemember.ts +1 -0
  132. package/src/commands/groupaccesstoken/createToken.spec.ts +211 -0
  133. package/src/commands/groupaccesstoken/createToken.ts +55 -51
  134. package/src/commands/groupaccesstoken/deleteToken.spec.ts +147 -0
  135. package/src/commands/groupaccesstoken/deleteToken.ts +16 -11
  136. package/src/commands/groupaccesstoken/groupContainer.ts +51 -51
  137. package/src/commands/groupaccesstoken/listTokens.spec.ts +147 -0
  138. package/src/commands/groupaccesstoken/listTokens.ts +16 -11
  139. package/src/commands/list.ts +50 -9
  140. package/src/commands/misc/login.spec.ts +91 -6
  141. package/src/commands/misc/login.ts +208 -25
  142. package/src/commands/misc/logout.spec.ts +32 -0
  143. package/src/commands/misc/loopbackOAuth.ts +127 -0
  144. package/src/commands/misc/set-access-token.spec.ts +95 -0
  145. package/src/commands/misc/set-access-token.ts +16 -16
  146. package/src/commands/package/deletepackage.spec.ts +164 -0
  147. package/src/commands/package/listpackages.spec.ts +147 -0
  148. package/src/commands/package/listpackages.ts +28 -5
  149. package/src/commands/package/publish.spec.ts +597 -0
  150. package/src/commands/package/publish.ts +32 -0
  151. package/src/commands/package/updatepackage.spec.ts +98 -0
  152. package/src/commands/project/createproject.spec.ts +247 -0
  153. package/src/commands/project/createproject.ts +12 -1
  154. package/src/commands/project/deleteproject.spec.ts +123 -0
  155. package/src/commands/project/getproject.spec.ts +109 -0
  156. package/src/commands/project/listprojects.spec.ts +121 -0
  157. package/src/commands/project/listprojects.ts +33 -7
  158. package/src/commands/project/setproject.spec.ts +63 -0
  159. package/src/commands/project/updateproject.spec.ts +217 -0
  160. package/src/commands/project/updateproject.ts +82 -0
  161. package/src/commands/rm.ts +1 -6
  162. package/src/commands/update.ts +24 -1
  163. package/src/commands/version/listversions.spec.ts +138 -0
  164. package/src/common/UserGroupId.spec.ts +117 -0
  165. package/src/constants.ts +59 -13
  166. package/src/index.ts +46 -20
  167. package/src/logger.spec.ts +158 -0
  168. package/src/polyfills/file.polyfill.ts +1 -0
  169. package/src/utils.spec.ts +135 -0
  170. package/src/utils.ts +81 -0
  171. package/tsconfig.json +15 -18
  172. 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.ms2.co/api/v0",
10
- dev: "https://dev-admin.ms2.co/api/v0",
11
- staging: "https://staging-admin.ms2.co/api/v0",
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://ms2data.us.auth0.com",
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-ohaafogw5p8uyx1m.us.auth0.com",
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-ohaafogw5p8uyx1m.us.auth0.com",
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-ohaafogw5p8uyx1m.us.auth0.com",
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-ohaafogw5p8uyx1m.us.auth0.com",
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 activeEnvironment = clientConfig.getEnvironment();
29
- axios.defaults.baseURL = controlPlanesByEnv[activeEnvironment];
28
+ const getActiveEnvironment = () => {
29
+ return clientConfig.getEnvironment();
30
+ };
31
+ axios.defaults.baseURL = controlPlanesByEnv[getActiveEnvironment()];
30
32
 
31
33
  const cli = program
32
- .version(cliPackage.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, activeEnvironment);
37
- registerListCommands(cli, activeEnvironment);
38
- registerUpdateCommands(cli, activeEnvironment);
39
- registerRmCommands(cli, activeEnvironment);
40
- registerAddCommands(cli, activeEnvironment);
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[activeEnvironment],
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[activeEnvironment],
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[activeEnvironment],
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
- login(organizationName);
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("cred").tree({
143
+ const complete = omelette("credible").tree({
134
144
  // CRUD commands
135
145
  get: ["project", "group"],
136
- ls: ["project", "connection", "package", "version", "group", "group-access-token"],
137
- update: ["package"],
138
- rm: ["project", "connection", "package", "group", "member", "group-access-token"],
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[activeEnvironment], false).then(
156
- (projects) => projects.map((project) => project.name),
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[activeEnvironment], false).then(
164
- (packages) => packages.map((_package) => _package.name),
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
+ });
@@ -1,3 +1,4 @@
1
+ /* eslint-disable @typescript-eslint/no-unused-vars -- reserved for future globalThis polyfill */
1
2
  import { File, Blob } from "formdata-node";
2
3
 
3
4
  // TODO: Polyfill leads to errors when pushing packages.