@credibledata/cred-cli 0.1.0

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 (170) hide show
  1. package/.eslintrc +33 -0
  2. package/.github/workflows/build.yml +23 -0
  3. package/.mocharc.json +10 -0
  4. package/.prettierignore +1 -0
  5. package/.prettierrc +4 -0
  6. package/README.md +40 -0
  7. package/bun.lock +1259 -0
  8. package/dist/client/api.js +7022 -0
  9. package/dist/client/api.js.map +1 -0
  10. package/dist/client/base.js +69 -0
  11. package/dist/client/base.js.map +1 -0
  12. package/dist/client/common.js +162 -0
  13. package/dist/client/common.js.map +1 -0
  14. package/dist/client/configuration.js +44 -0
  15. package/dist/client/configuration.js.map +1 -0
  16. package/dist/client/index.js +32 -0
  17. package/dist/client/index.js.map +1 -0
  18. package/dist/clientconfiguration.js +163 -0
  19. package/dist/clientconfiguration.js.map +1 -0
  20. package/dist/clientconfiguration.spec.js +92 -0
  21. package/dist/clientconfiguration.spec.js.map +1 -0
  22. package/dist/commands/add.js +61 -0
  23. package/dist/commands/add.js.map +1 -0
  24. package/dist/commands/connection/createconnections.js +62 -0
  25. package/dist/commands/connection/createconnections.js.map +1 -0
  26. package/dist/commands/connection/deleteconnection.js +52 -0
  27. package/dist/commands/connection/deleteconnection.js.map +1 -0
  28. package/dist/commands/connection/listconnections.js +44 -0
  29. package/dist/commands/connection/listconnections.js.map +1 -0
  30. package/dist/commands/get.js +28 -0
  31. package/dist/commands/get.js.map +1 -0
  32. package/dist/commands/group/addmember.js +73 -0
  33. package/dist/commands/group/addmember.js.map +1 -0
  34. package/dist/commands/group/creategroup.js +55 -0
  35. package/dist/commands/group/creategroup.js.map +1 -0
  36. package/dist/commands/group/deletegroup.js +48 -0
  37. package/dist/commands/group/deletegroup.js.map +1 -0
  38. package/dist/commands/group/getgroup.js +50 -0
  39. package/dist/commands/group/getgroup.js.map +1 -0
  40. package/dist/commands/group/listgroups.js +56 -0
  41. package/dist/commands/group/listgroups.js.map +1 -0
  42. package/dist/commands/group/removemember.js +68 -0
  43. package/dist/commands/group/removemember.js.map +1 -0
  44. package/dist/commands/groupaccesstoken/createToken.js +49 -0
  45. package/dist/commands/groupaccesstoken/createToken.js.map +1 -0
  46. package/dist/commands/groupaccesstoken/deleteToken.js +24 -0
  47. package/dist/commands/groupaccesstoken/deleteToken.js.map +1 -0
  48. package/dist/commands/groupaccesstoken/groupContainer.js +71 -0
  49. package/dist/commands/groupaccesstoken/groupContainer.js.map +1 -0
  50. package/dist/commands/groupaccesstoken/listTokens.js +25 -0
  51. package/dist/commands/groupaccesstoken/listTokens.js.map +1 -0
  52. package/dist/commands/list.js +68 -0
  53. package/dist/commands/list.js.map +1 -0
  54. package/dist/commands/misc/login.js +166 -0
  55. package/dist/commands/misc/login.js.map +1 -0
  56. package/dist/commands/misc/login.spec.js +138 -0
  57. package/dist/commands/misc/login.spec.js.map +1 -0
  58. package/dist/commands/misc/logout.js +11 -0
  59. package/dist/commands/misc/logout.js.map +1 -0
  60. package/dist/commands/misc/set-access-token.js +27 -0
  61. package/dist/commands/misc/set-access-token.js.map +1 -0
  62. package/dist/commands/package/deletepackage.js +54 -0
  63. package/dist/commands/package/deletepackage.js.map +1 -0
  64. package/dist/commands/package/listpackages.js +52 -0
  65. package/dist/commands/package/listpackages.js.map +1 -0
  66. package/dist/commands/package/publish.js +134 -0
  67. package/dist/commands/package/publish.js.map +1 -0
  68. package/dist/commands/package/updatepackage.js +75 -0
  69. package/dist/commands/package/updatepackage.js.map +1 -0
  70. package/dist/commands/package/updatepackage.spec.js +139 -0
  71. package/dist/commands/package/updatepackage.spec.js.map +1 -0
  72. package/dist/commands/project/createproject.js +65 -0
  73. package/dist/commands/project/createproject.js.map +1 -0
  74. package/dist/commands/project/deleteproject.js +49 -0
  75. package/dist/commands/project/deleteproject.js.map +1 -0
  76. package/dist/commands/project/getproject.js +44 -0
  77. package/dist/commands/project/getproject.js.map +1 -0
  78. package/dist/commands/project/listprojects.js +46 -0
  79. package/dist/commands/project/listprojects.js.map +1 -0
  80. package/dist/commands/project/setproject.js +26 -0
  81. package/dist/commands/project/setproject.js.map +1 -0
  82. package/dist/commands/rm.js +68 -0
  83. package/dist/commands/rm.js.map +1 -0
  84. package/dist/commands/set.js +17 -0
  85. package/dist/commands/set.js.map +1 -0
  86. package/dist/commands/update.js +29 -0
  87. package/dist/commands/update.js.map +1 -0
  88. package/dist/commands/version/archive-unarchive-version.js +79 -0
  89. package/dist/commands/version/archive-unarchive-version.js.map +1 -0
  90. package/dist/commands/version/archive-unarchive-version.spec.js +244 -0
  91. package/dist/commands/version/archive-unarchive-version.spec.js.map +1 -0
  92. package/dist/commands/version/listversions.js +46 -0
  93. package/dist/commands/version/listversions.js.map +1 -0
  94. package/dist/common/UserGroupId.js +112 -0
  95. package/dist/common/UserGroupId.js.map +1 -0
  96. package/dist/constants.js +54 -0
  97. package/dist/constants.js.map +1 -0
  98. package/dist/index.js +137 -0
  99. package/dist/index.js.map +1 -0
  100. package/dist/logger.js +77 -0
  101. package/dist/logger.js.map +1 -0
  102. package/dist/polyfills/file.polyfill.js +6 -0
  103. package/dist/polyfills/file.polyfill.js.map +1 -0
  104. package/dist/utils.js +51 -0
  105. package/dist/utils.js.map +1 -0
  106. package/dist/validator.js +53 -0
  107. package/dist/validator.js.map +1 -0
  108. package/dist/validator.spec.js +41 -0
  109. package/dist/validator.spec.js.map +1 -0
  110. package/docker/Dockerfile.ci +10 -0
  111. package/docker-compose.yml +18 -0
  112. package/mocha.setup.ts +4 -0
  113. package/openapitools.json +7 -0
  114. package/package.json +67 -0
  115. package/src/client/.openapi-generator/FILES +8 -0
  116. package/src/client/.openapi-generator/VERSION +1 -0
  117. package/src/client/.openapi-generator-ignore +23 -0
  118. package/src/client/api.ts +8408 -0
  119. package/src/client/base.ts +86 -0
  120. package/src/client/common.ts +150 -0
  121. package/src/client/configuration.ts +110 -0
  122. package/src/client/git_push.sh +57 -0
  123. package/src/client/index.ts +18 -0
  124. package/src/clientconfiguration.spec.ts +116 -0
  125. package/src/clientconfiguration.ts +204 -0
  126. package/src/commands/add.ts +106 -0
  127. package/src/commands/connection/createconnections.ts +67 -0
  128. package/src/commands/connection/deleteconnection.ts +57 -0
  129. package/src/commands/connection/listconnections.ts +39 -0
  130. package/src/commands/get.ts +31 -0
  131. package/src/commands/group/addmember.ts +83 -0
  132. package/src/commands/group/creategroup.ts +54 -0
  133. package/src/commands/group/deletegroup.ts +45 -0
  134. package/src/commands/group/getgroup.ts +47 -0
  135. package/src/commands/group/listgroups.ts +45 -0
  136. package/src/commands/group/removemember.ts +74 -0
  137. package/src/commands/groupaccesstoken/createToken.ts +68 -0
  138. package/src/commands/groupaccesstoken/deleteToken.ts +24 -0
  139. package/src/commands/groupaccesstoken/groupContainer.ts +83 -0
  140. package/src/commands/groupaccesstoken/listTokens.ts +24 -0
  141. package/src/commands/list.ts +90 -0
  142. package/src/commands/misc/login.spec.ts +128 -0
  143. package/src/commands/misc/login.ts +142 -0
  144. package/src/commands/misc/logout.ts +9 -0
  145. package/src/commands/misc/set-access-token.ts +32 -0
  146. package/src/commands/package/deletepackage.ts +58 -0
  147. package/src/commands/package/listpackages.ts +44 -0
  148. package/src/commands/package/publish.ts +179 -0
  149. package/src/commands/package/updatepackage.spec.ts +221 -0
  150. package/src/commands/package/updatepackage.ts +95 -0
  151. package/src/commands/project/createproject.ts +59 -0
  152. package/src/commands/project/deleteproject.ts +48 -0
  153. package/src/commands/project/getproject.ts +43 -0
  154. package/src/commands/project/listprojects.ts +36 -0
  155. package/src/commands/project/setproject.ts +14 -0
  156. package/src/commands/rm.ts +106 -0
  157. package/src/commands/set.ts +16 -0
  158. package/src/commands/update.ts +41 -0
  159. package/src/commands/version/archive-unarchive-version.spec.ts +456 -0
  160. package/src/commands/version/archive-unarchive-version.ts +111 -0
  161. package/src/commands/version/listversions.ts +45 -0
  162. package/src/common/UserGroupId.ts +137 -0
  163. package/src/constants.ts +70 -0
  164. package/src/index.ts +183 -0
  165. package/src/logger.ts +86 -0
  166. package/src/polyfills/file.polyfill.ts +5 -0
  167. package/src/utils.ts +34 -0
  168. package/src/validator.spec.ts +51 -0
  169. package/src/validator.ts +44 -0
  170. package/tsconfig.json +20 -0
@@ -0,0 +1,47 @@
1
+ import { GroupsApi } from "../../client/api";
2
+ import { Configuration } from "../../client/configuration";
3
+ import { ClientConfiguration } from "../../clientconfiguration";
4
+ import { logger } from "../../logger";
5
+ import { printOutError } from "../../utils";
6
+
7
+ const groupsApi = new GroupsApi(new Configuration());
8
+
9
+ export async function getGroup(
10
+ controlPlane: string,
11
+ groupName: string,
12
+ debug: boolean,
13
+ ) {
14
+ const clientConfig = new ClientConfiguration();
15
+
16
+ if (!clientConfig.doesConfigExists()) {
17
+ logger.error("Please, make sure that you are logged in");
18
+ return;
19
+ }
20
+
21
+ const organizationName = clientConfig.getOrganization();
22
+ if (!organizationName) {
23
+ logger.error("No organization set. Please login or set an organization.");
24
+ return;
25
+ }
26
+
27
+ logger.info(
28
+ `Getting group ${groupName} from organization ${organizationName}`,
29
+ );
30
+
31
+ try {
32
+ const response = await groupsApi.getGroup(organizationName, groupName, {
33
+ baseURL: controlPlane,
34
+ headers: {
35
+ Authorization: clientConfig.getAuthenticationHeader(),
36
+ },
37
+ });
38
+
39
+ const jsonString = JSON.stringify(response.data, null, 2);
40
+ console.log(jsonString);
41
+ logger.info("Group retrieved successfully");
42
+ } catch (error) {
43
+ logger.error("Failed to get group");
44
+ printOutError(error, debug);
45
+ process.exit(1);
46
+ }
47
+ }
@@ -0,0 +1,45 @@
1
+ import { GroupsApi } from "../../client/api";
2
+ import { Configuration } from "../../client/configuration";
3
+ import { ClientConfiguration } from "../../clientconfiguration";
4
+ import { logger } from "../../logger";
5
+ import { printOutError } from "../../utils";
6
+
7
+ const groupsApi = new GroupsApi(new Configuration());
8
+
9
+ export async function listGroups(controlPlane: string, debug: boolean) {
10
+ const clientConfig = new ClientConfiguration();
11
+
12
+ if (!clientConfig.doesConfigExists()) {
13
+ logger.error("Please, make sure that you are logged in");
14
+ return;
15
+ }
16
+
17
+ const organizationName = clientConfig.getOrganization();
18
+ if (!organizationName) {
19
+ logger.error("No organization set. Please login or set an organization.");
20
+ return;
21
+ }
22
+
23
+ logger.info(`Listing groups in organization ${organizationName}`);
24
+
25
+ try {
26
+ const response = await groupsApi.listGroups(organizationName, {
27
+ baseURL: controlPlane,
28
+ headers: {
29
+ Authorization: clientConfig.getAuthenticationHeader(),
30
+ },
31
+ });
32
+
33
+ const jsonString = JSON.stringify(response.data, null, 2);
34
+ console.log(jsonString);
35
+ logger.info("Groups listed successfully");
36
+ } catch (error) {
37
+ logger.error("Failed to list groups");
38
+ printOutError(error, debug);
39
+ process.exit(1);
40
+ }
41
+ }
42
+
43
+ export async function printOutGroups(controlPlane: string, debug: boolean) {
44
+ return listGroups(controlPlane, debug);
45
+ }
@@ -0,0 +1,74 @@
1
+ import {
2
+ GroupMember,
3
+ GroupsApi,
4
+ RemoveGroupMembersRequest,
5
+ } from "../../client/api";
6
+ import { Configuration } from "../../client/configuration";
7
+ import { ClientConfiguration } from "../../clientconfiguration";
8
+ import { UserGroupId } from "../../common/UserGroupId";
9
+ import { logger } from "../../logger";
10
+ import { printOutError } from "../../utils";
11
+
12
+ const groupsApi = new GroupsApi(new Configuration());
13
+
14
+ export async function removeMember(
15
+ controlPlane: string,
16
+ groupName: string,
17
+ memberType: string,
18
+ memberName: string,
19
+ debug: boolean,
20
+ ) {
21
+ const clientConfig = new ClientConfiguration();
22
+
23
+ if (!clientConfig.doesConfigExists()) {
24
+ logger.error("Please, make sure that you are logged in");
25
+ return;
26
+ }
27
+
28
+ const organizationName = clientConfig.getOrganization();
29
+ if (!organizationName) {
30
+ logger.error("No organization set. Please login or set an organization.");
31
+ return;
32
+ }
33
+
34
+ logger.info(
35
+ `Removing ${memberType} ${memberName} from group ${groupName} in organization ${organizationName}`,
36
+ );
37
+
38
+ // Create UserGroupId based on member type
39
+ let userGroupId: UserGroupId;
40
+ if (memberType.toLowerCase() === "user") {
41
+ userGroupId = UserGroupId.forUser(memberName);
42
+ } else if (memberType.toLowerCase() === "group") {
43
+ userGroupId = UserGroupId.forGroup(memberName);
44
+ } else {
45
+ logger.error("Error: memberType must be either 'user' or 'group'");
46
+ process.exit(1);
47
+ }
48
+
49
+ const groupMember: GroupMember = {
50
+ userGroupId: userGroupId.getValue(),
51
+ // Note: status is not required for removal, only the member identifier
52
+ };
53
+
54
+ const request: RemoveGroupMembersRequest = {
55
+ members: [groupMember],
56
+ };
57
+
58
+ try {
59
+ await groupsApi.removeGroupMembers(organizationName, groupName, request, {
60
+ baseURL: controlPlane,
61
+ headers: {
62
+ Authorization: clientConfig.getAuthenticationHeader(),
63
+ },
64
+ });
65
+
66
+ logger.info(
67
+ `Successfully removed ${memberType} ${memberName} from group ${groupName}`,
68
+ );
69
+ } catch (error) {
70
+ logger.error("Failed to remove member from group");
71
+ printOutError(error, debug);
72
+ process.exit(1);
73
+ }
74
+ }
@@ -0,0 +1,68 @@
1
+ import kleur from "kleur";
2
+ import { logger } from "../../logger";
3
+ import { withGroupContext } from "./groupContainer";
4
+
5
+ export async function createGroupAccessToken(
6
+ controlPlane: string,
7
+ groupName: string,
8
+ tokenName: string,
9
+ expiresAt: string | undefined,
10
+ debug: boolean,
11
+ ) {
12
+ // Validate expiresAt format if provided
13
+ if (expiresAt) {
14
+ const expiresAtDate = new Date(expiresAt);
15
+ if (isNaN(expiresAtDate.getTime())) {
16
+ logger.error(
17
+ `Invalid expiration date format: ${expiresAt}. Please use ISO 8601 format (e.g., 2025-12-31T23:59:59Z)`,
18
+ );
19
+ process.exit(1);
20
+ }
21
+ if (expiresAtDate <= new Date()) {
22
+ logger.error(
23
+ "Expiration date must be in the future",
24
+ );
25
+ process.exit(1);
26
+ }
27
+ }
28
+
29
+ const result = await withGroupContext(controlPlane, async (context) => {
30
+ const { organizationName, groupsApi, options } = context;
31
+
32
+ logger.info(
33
+ `Creating access token ${tokenName} for group ${groupName} in organization ${organizationName}${expiresAt ? ` (expires: ${expiresAt})` : ""}`,
34
+ );
35
+
36
+ const response = await groupsApi.createGroupAccessToken(
37
+ organizationName,
38
+ groupName,
39
+ tokenName,
40
+ expiresAt,
41
+ options,
42
+ );
43
+
44
+ return response.data;
45
+ }, undefined, debug);
46
+
47
+ // Display security warning after the response is printed
48
+ console.log("\n" + kleur.yellow().bold("⚠️ IMPORTANT SECURITY NOTICE ⚠️"));
49
+ console.log(
50
+ kleur.yellow(
51
+ "The token secret shown above will NOT be displayed again.",
52
+ ),
53
+ );
54
+ console.log(
55
+ kleur.yellow(
56
+ "Please store it securely in a password manager or secure vault.",
57
+ ),
58
+ );
59
+ console.log(
60
+ kleur.yellow(
61
+ "If you lose this token, you will need to create a new one.",
62
+ ),
63
+ );
64
+ console.log("");
65
+ logger.info("Group access token created successfully");
66
+
67
+ return result;
68
+ }
@@ -0,0 +1,24 @@
1
+ import { logger } from "../../logger";
2
+ import { withGroupContext } from "./groupContainer";
3
+
4
+ export async function deleteGroupAccessToken(
5
+ controlPlane: string,
6
+ groupName: string,
7
+ tokenName: string,
8
+ debug: boolean,
9
+ ) {
10
+ return withGroupContext(controlPlane, async (context) => {
11
+ const { organizationName, groupsApi, options } = context;
12
+
13
+ logger.info(
14
+ `Deleting access token ${tokenName} from group ${groupName} in organization ${organizationName}`,
15
+ );
16
+
17
+ await groupsApi.deleteGroupAccessToken(
18
+ organizationName,
19
+ groupName,
20
+ tokenName,
21
+ options,
22
+ );
23
+ }, `Successfully deleted access token ${tokenName}. This token has been immediately revoked.`, debug);
24
+ }
@@ -0,0 +1,83 @@
1
+ import { GroupsApi } from "../../client/api";
2
+ import { Configuration } from "../../client/configuration";
3
+ import { ClientConfiguration } from "../../clientconfiguration";
4
+ import { logger } from "../../logger";
5
+ import { printOutError } from "../../utils";
6
+
7
+ export interface ApiOptions {
8
+ baseURL: string;
9
+ headers: {
10
+ Authorization: string;
11
+ };
12
+ params?: Record<string, unknown>;
13
+ }
14
+
15
+ export interface GroupContext {
16
+ organizationName: string;
17
+ groupsApi: GroupsApi;
18
+ options: ApiOptions;
19
+ }
20
+
21
+ /**
22
+ * Container function that handles common ClientConfiguration checks, setup,
23
+ * error handling, and response printing for group access token operations.
24
+ *
25
+ * @param controlPlane The control plane URL
26
+ * @param handler The handler function to execute with the configured context
27
+ * @param successMessage Optional success message to display after the operation
28
+ * @param debug Whether to enable debug mode for error output
29
+ * @returns The result of the handler function, or void if validation fails
30
+ */
31
+ export async function withGroupContext<T>(
32
+ controlPlane: string,
33
+ handler: (context: GroupContext) => Promise<T>,
34
+ successMessage?: string,
35
+ debug: boolean = false,
36
+ ): Promise<T | void> {
37
+ const clientConfig = new ClientConfiguration();
38
+
39
+ if (!clientConfig.doesConfigExists()) {
40
+ logger.error("Please, make sure that you are logged in");
41
+ return;
42
+ }
43
+
44
+ const organizationName = clientConfig.getOrganization();
45
+ if (!organizationName) {
46
+ logger.error("No organization set. Please login or set an organization.");
47
+ return;
48
+ }
49
+
50
+ const options: ApiOptions = {
51
+ baseURL: controlPlane,
52
+ headers: {
53
+ Authorization: clientConfig.getAuthenticationHeader(),
54
+ },
55
+ };
56
+
57
+ const context: GroupContext = {
58
+ organizationName,
59
+ groupsApi: new GroupsApi(new Configuration()),
60
+ options,
61
+ };
62
+
63
+ try {
64
+ const result = await handler(context);
65
+
66
+ // Print response if it exists
67
+ if (result !== undefined && result !== null) {
68
+ const jsonString = JSON.stringify(result, null, 2);
69
+ console.log(jsonString);
70
+ }
71
+
72
+ // Print success message if provided
73
+ if (successMessage) {
74
+ logger.info(successMessage);
75
+ }
76
+
77
+ return result;
78
+ } catch (error) {
79
+ logger.error("Operation failed");
80
+ printOutError(error, debug);
81
+ process.exit(1);
82
+ }
83
+ }
@@ -0,0 +1,24 @@
1
+ import { logger } from "../../logger";
2
+ import { withGroupContext } from "./groupContainer";
3
+
4
+ export async function listGroupAccessTokens(
5
+ controlPlane: string,
6
+ groupName: string,
7
+ debug: boolean,
8
+ ) {
9
+ return withGroupContext(controlPlane, async (context) => {
10
+ const { organizationName, groupsApi, options } = context;
11
+
12
+ logger.info(
13
+ `Listing access tokens for group ${groupName} in organization ${organizationName}`,
14
+ );
15
+
16
+ const response = await groupsApi.listGroupAccessTokens(
17
+ organizationName,
18
+ groupName,
19
+ options,
20
+ );
21
+
22
+ return response.data;
23
+ }, "Group access tokens listed successfully", debug);
24
+ }
@@ -0,0 +1,90 @@
1
+ import { Command } from "commander";
2
+ import { controlPlanesByEnv, Environment } from "../constants";
3
+ import { listConnections } from "./connection/listconnections";
4
+ import { printOutGroups } from "./group/listgroups";
5
+ import { listGroupAccessTokens } from "./groupaccesstoken/listTokens";
6
+ import { printOutPackages } from "./package/listpackages";
7
+ import { printOutProjects } from "./project/listprojects";
8
+ import { listVersions } from "./version/listversions";
9
+
10
+ export const registerListCommands = (
11
+ cli: Command,
12
+ environment: Environment,
13
+ ) => {
14
+ const list = cli
15
+ .command("ls")
16
+ .description("List resources of a given type")
17
+ .argument("<resource>", "The resource to list");
18
+
19
+ list
20
+ .command("project")
21
+ .alias("projects")
22
+ .description("List all projects.")
23
+ .option("--debug")
24
+ .action((options) => {
25
+ printOutProjects(controlPlanesByEnv[environment], options.debug);
26
+ });
27
+
28
+ list
29
+ .command("connection")
30
+ .alias("connections")
31
+ .description("List all connections.")
32
+ .option("--debug")
33
+ .action((options) => {
34
+ listConnections(controlPlanesByEnv[environment], options.debug);
35
+ });
36
+
37
+ list
38
+ .command("package")
39
+ .alias("packages")
40
+ .description("List the packages in the active project.")
41
+ .option("--debug")
42
+ .action((options) => {
43
+ printOutPackages(controlPlanesByEnv[environment], options.debug);
44
+ });
45
+
46
+ list
47
+ .command("version")
48
+ .alias("versions")
49
+ .description("List the versions of a package.")
50
+ .argument(
51
+ "<packageName>",
52
+ "The name of the package to list the versions of.",
53
+ )
54
+ .option("--debug")
55
+ .action((packageName, options) => {
56
+ listVersions(
57
+ controlPlanesByEnv[environment],
58
+ packageName,
59
+ options.debug,
60
+ );
61
+ });
62
+
63
+ list
64
+ .command("group")
65
+ .alias("groups")
66
+ .description("List all groups in the organization.")
67
+ .option("--debug")
68
+ .action((options) => {
69
+ printOutGroups(controlPlanesByEnv[environment], options.debug);
70
+ });
71
+
72
+
73
+ list
74
+ .command("group-access-token")
75
+ .alias("group-access-tokens")
76
+ .description("List all access tokens for a group.")
77
+ .argument(
78
+ "<groupName>",
79
+ "The name of the group",
80
+ )
81
+ .option("--debug")
82
+ .action((groupName, options) => {
83
+ listGroupAccessTokens(
84
+ controlPlanesByEnv[environment],
85
+ groupName,
86
+ options.debug,
87
+ );
88
+ });
89
+
90
+ };
@@ -0,0 +1,128 @@
1
+ import axios from "axios";
2
+ import { expect } from "chai";
3
+ import fs from "fs";
4
+ import os from "os";
5
+ import { join } from "path";
6
+ import sinon from "sinon";
7
+ import { ClientConfiguration } from "../../clientconfiguration";
8
+ import { login } from "./login";
9
+
10
+ function createFakeIdToken(payload: Record<string, unknown>): string {
11
+ const header = Buffer.from(
12
+ JSON.stringify({ alg: "none", typ: "JWT" }),
13
+ ).toString("base64url");
14
+ const body = Buffer.from(JSON.stringify(payload)).toString("base64url");
15
+ return `${header}.${body}.`;
16
+ }
17
+
18
+ describe("login", () => {
19
+ let tmpConfigFilePath: string;
20
+ let axiosStub: sinon.SinonStub;
21
+ const mockDeviceCodeResponse = {
22
+ data: {
23
+ verification_uri_complete: "https://example.com",
24
+ user_code: "123456",
25
+ device_code: "device-code-123",
26
+ interval: 1,
27
+ },
28
+ };
29
+
30
+ const fakeJwtPayload = {
31
+ name: "Test User",
32
+ email: "test@example.com",
33
+ };
34
+
35
+ const mockedAccessTokenResponse = {
36
+ data: {
37
+ id_token: createFakeIdToken(fakeJwtPayload),
38
+ access_token: "mock-access-token",
39
+ refresh_token: "mock-refresh-token",
40
+ },
41
+ };
42
+
43
+ beforeEach(() => {
44
+ tmpConfigFilePath = join(os.tmpdir(), "cred.json");
45
+ fs.writeFileSync(tmpConfigFilePath, "{}");
46
+ axiosStub = sinon.stub(axios, "request");
47
+ });
48
+
49
+ afterEach(() => {
50
+ fs.rmSync(tmpConfigFilePath);
51
+ axiosStub.restore();
52
+ });
53
+
54
+ it("should not delete config properties on login", async function () {
55
+ this.timeout(2000);
56
+ const config = new ClientConfiguration(tmpConfigFilePath);
57
+ config.setEnvironment("localhost");
58
+ config.setOrganization("foo");
59
+ config.setProject("bar");
60
+ axiosStub
61
+ .onFirstCall()
62
+ .resolves(mockDeviceCodeResponse)
63
+ .onSecondCall()
64
+ .resolves(mockedAccessTokenResponse);
65
+ await login("foo", tmpConfigFilePath);
66
+ expect(config.getOrganization()).to.equal("foo");
67
+ expect(config.getProject()).to.equal("bar");
68
+ });
69
+
70
+ it("should change the active organization when login succeeds", async function () {
71
+ this.timeout(2000);
72
+ axiosStub
73
+ .onFirstCall()
74
+ .resolves(mockDeviceCodeResponse)
75
+ .onSecondCall()
76
+ .resolves(mockedAccessTokenResponse);
77
+ const config = new ClientConfiguration(tmpConfigFilePath);
78
+ config.setEnvironment("localhost");
79
+ config.setOrganization("foo");
80
+ config.setProject("bar");
81
+ await login("test", tmpConfigFilePath);
82
+ expect(config.getOrganization()).to.equal("test");
83
+ });
84
+
85
+ it("should set an access token when login succeeds", async function () {
86
+ this.timeout(2000);
87
+ axiosStub
88
+ .onFirstCall()
89
+ .resolves(mockDeviceCodeResponse)
90
+ .onSecondCall()
91
+ .resolves(mockedAccessTokenResponse);
92
+ const config = new ClientConfiguration(tmpConfigFilePath);
93
+ await login("test", tmpConfigFilePath);
94
+ expect(config.getAccessToken()).to.equal(
95
+ mockedAccessTokenResponse.data.access_token,
96
+ );
97
+ });
98
+
99
+ it("should unset the project field when switching organizations", async function () {
100
+ this.timeout(2000);
101
+ axiosStub
102
+ .onFirstCall()
103
+ .resolves(mockDeviceCodeResponse)
104
+ .onSecondCall()
105
+ .resolves(mockedAccessTokenResponse);
106
+ const config = new ClientConfiguration(tmpConfigFilePath);
107
+ config.setEnvironment("localhost");
108
+ config.setOrganization("foo");
109
+ config.setProject("bar");
110
+ await login("not-foo", tmpConfigFilePath);
111
+ expect(config.getProject()).to.equal(undefined);
112
+ });
113
+
114
+ it("should not unset the project field when logging in with the same organization", async function () {
115
+ this.timeout(2000);
116
+ axiosStub
117
+ .onFirstCall()
118
+ .resolves(mockDeviceCodeResponse)
119
+ .onSecondCall()
120
+ .resolves(mockedAccessTokenResponse);
121
+ const config = new ClientConfiguration(tmpConfigFilePath);
122
+ config.setEnvironment("localhost");
123
+ config.setOrganization("foo");
124
+ config.setProject("bar");
125
+ await login("foo", tmpConfigFilePath);
126
+ expect(config.getProject()).to.equal("bar");
127
+ });
128
+ });