@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.
- package/.eslintrc +33 -0
- package/.github/workflows/build.yml +23 -0
- package/.mocharc.json +10 -0
- package/.prettierignore +1 -0
- package/.prettierrc +4 -0
- package/README.md +40 -0
- package/bun.lock +1259 -0
- package/dist/client/api.js +7022 -0
- package/dist/client/api.js.map +1 -0
- package/dist/client/base.js +69 -0
- package/dist/client/base.js.map +1 -0
- package/dist/client/common.js +162 -0
- package/dist/client/common.js.map +1 -0
- package/dist/client/configuration.js +44 -0
- package/dist/client/configuration.js.map +1 -0
- package/dist/client/index.js +32 -0
- package/dist/client/index.js.map +1 -0
- package/dist/clientconfiguration.js +163 -0
- package/dist/clientconfiguration.js.map +1 -0
- package/dist/clientconfiguration.spec.js +92 -0
- package/dist/clientconfiguration.spec.js.map +1 -0
- package/dist/commands/add.js +61 -0
- package/dist/commands/add.js.map +1 -0
- package/dist/commands/connection/createconnections.js +62 -0
- package/dist/commands/connection/createconnections.js.map +1 -0
- package/dist/commands/connection/deleteconnection.js +52 -0
- package/dist/commands/connection/deleteconnection.js.map +1 -0
- package/dist/commands/connection/listconnections.js +44 -0
- package/dist/commands/connection/listconnections.js.map +1 -0
- package/dist/commands/get.js +28 -0
- package/dist/commands/get.js.map +1 -0
- package/dist/commands/group/addmember.js +73 -0
- package/dist/commands/group/addmember.js.map +1 -0
- package/dist/commands/group/creategroup.js +55 -0
- package/dist/commands/group/creategroup.js.map +1 -0
- package/dist/commands/group/deletegroup.js +48 -0
- package/dist/commands/group/deletegroup.js.map +1 -0
- package/dist/commands/group/getgroup.js +50 -0
- package/dist/commands/group/getgroup.js.map +1 -0
- package/dist/commands/group/listgroups.js +56 -0
- package/dist/commands/group/listgroups.js.map +1 -0
- package/dist/commands/group/removemember.js +68 -0
- package/dist/commands/group/removemember.js.map +1 -0
- package/dist/commands/groupaccesstoken/createToken.js +49 -0
- package/dist/commands/groupaccesstoken/createToken.js.map +1 -0
- package/dist/commands/groupaccesstoken/deleteToken.js +24 -0
- package/dist/commands/groupaccesstoken/deleteToken.js.map +1 -0
- package/dist/commands/groupaccesstoken/groupContainer.js +71 -0
- package/dist/commands/groupaccesstoken/groupContainer.js.map +1 -0
- package/dist/commands/groupaccesstoken/listTokens.js +25 -0
- package/dist/commands/groupaccesstoken/listTokens.js.map +1 -0
- package/dist/commands/list.js +68 -0
- package/dist/commands/list.js.map +1 -0
- package/dist/commands/misc/login.js +166 -0
- package/dist/commands/misc/login.js.map +1 -0
- package/dist/commands/misc/login.spec.js +138 -0
- package/dist/commands/misc/login.spec.js.map +1 -0
- package/dist/commands/misc/logout.js +11 -0
- package/dist/commands/misc/logout.js.map +1 -0
- package/dist/commands/misc/set-access-token.js +27 -0
- package/dist/commands/misc/set-access-token.js.map +1 -0
- package/dist/commands/package/deletepackage.js +54 -0
- package/dist/commands/package/deletepackage.js.map +1 -0
- package/dist/commands/package/listpackages.js +52 -0
- package/dist/commands/package/listpackages.js.map +1 -0
- package/dist/commands/package/publish.js +134 -0
- package/dist/commands/package/publish.js.map +1 -0
- package/dist/commands/package/updatepackage.js +75 -0
- package/dist/commands/package/updatepackage.js.map +1 -0
- package/dist/commands/package/updatepackage.spec.js +139 -0
- package/dist/commands/package/updatepackage.spec.js.map +1 -0
- package/dist/commands/project/createproject.js +65 -0
- package/dist/commands/project/createproject.js.map +1 -0
- package/dist/commands/project/deleteproject.js +49 -0
- package/dist/commands/project/deleteproject.js.map +1 -0
- package/dist/commands/project/getproject.js +44 -0
- package/dist/commands/project/getproject.js.map +1 -0
- package/dist/commands/project/listprojects.js +46 -0
- package/dist/commands/project/listprojects.js.map +1 -0
- package/dist/commands/project/setproject.js +26 -0
- package/dist/commands/project/setproject.js.map +1 -0
- package/dist/commands/rm.js +68 -0
- package/dist/commands/rm.js.map +1 -0
- package/dist/commands/set.js +17 -0
- package/dist/commands/set.js.map +1 -0
- package/dist/commands/update.js +29 -0
- package/dist/commands/update.js.map +1 -0
- package/dist/commands/version/archive-unarchive-version.js +79 -0
- package/dist/commands/version/archive-unarchive-version.js.map +1 -0
- package/dist/commands/version/archive-unarchive-version.spec.js +244 -0
- package/dist/commands/version/archive-unarchive-version.spec.js.map +1 -0
- package/dist/commands/version/listversions.js +46 -0
- package/dist/commands/version/listversions.js.map +1 -0
- package/dist/common/UserGroupId.js +112 -0
- package/dist/common/UserGroupId.js.map +1 -0
- package/dist/constants.js +54 -0
- package/dist/constants.js.map +1 -0
- package/dist/index.js +137 -0
- package/dist/index.js.map +1 -0
- package/dist/logger.js +77 -0
- package/dist/logger.js.map +1 -0
- package/dist/polyfills/file.polyfill.js +6 -0
- package/dist/polyfills/file.polyfill.js.map +1 -0
- package/dist/utils.js +51 -0
- package/dist/utils.js.map +1 -0
- package/dist/validator.js +53 -0
- package/dist/validator.js.map +1 -0
- package/dist/validator.spec.js +41 -0
- package/dist/validator.spec.js.map +1 -0
- package/docker/Dockerfile.ci +10 -0
- package/docker-compose.yml +18 -0
- package/mocha.setup.ts +4 -0
- package/openapitools.json +7 -0
- package/package.json +67 -0
- package/src/client/.openapi-generator/FILES +8 -0
- package/src/client/.openapi-generator/VERSION +1 -0
- package/src/client/.openapi-generator-ignore +23 -0
- package/src/client/api.ts +8408 -0
- package/src/client/base.ts +86 -0
- package/src/client/common.ts +150 -0
- package/src/client/configuration.ts +110 -0
- package/src/client/git_push.sh +57 -0
- package/src/client/index.ts +18 -0
- package/src/clientconfiguration.spec.ts +116 -0
- package/src/clientconfiguration.ts +204 -0
- package/src/commands/add.ts +106 -0
- package/src/commands/connection/createconnections.ts +67 -0
- package/src/commands/connection/deleteconnection.ts +57 -0
- package/src/commands/connection/listconnections.ts +39 -0
- package/src/commands/get.ts +31 -0
- package/src/commands/group/addmember.ts +83 -0
- package/src/commands/group/creategroup.ts +54 -0
- package/src/commands/group/deletegroup.ts +45 -0
- package/src/commands/group/getgroup.ts +47 -0
- package/src/commands/group/listgroups.ts +45 -0
- package/src/commands/group/removemember.ts +74 -0
- package/src/commands/groupaccesstoken/createToken.ts +68 -0
- package/src/commands/groupaccesstoken/deleteToken.ts +24 -0
- package/src/commands/groupaccesstoken/groupContainer.ts +83 -0
- package/src/commands/groupaccesstoken/listTokens.ts +24 -0
- package/src/commands/list.ts +90 -0
- package/src/commands/misc/login.spec.ts +128 -0
- package/src/commands/misc/login.ts +142 -0
- package/src/commands/misc/logout.ts +9 -0
- package/src/commands/misc/set-access-token.ts +32 -0
- package/src/commands/package/deletepackage.ts +58 -0
- package/src/commands/package/listpackages.ts +44 -0
- package/src/commands/package/publish.ts +179 -0
- package/src/commands/package/updatepackage.spec.ts +221 -0
- package/src/commands/package/updatepackage.ts +95 -0
- package/src/commands/project/createproject.ts +59 -0
- package/src/commands/project/deleteproject.ts +48 -0
- package/src/commands/project/getproject.ts +43 -0
- package/src/commands/project/listprojects.ts +36 -0
- package/src/commands/project/setproject.ts +14 -0
- package/src/commands/rm.ts +106 -0
- package/src/commands/set.ts +16 -0
- package/src/commands/update.ts +41 -0
- package/src/commands/version/archive-unarchive-version.spec.ts +456 -0
- package/src/commands/version/archive-unarchive-version.ts +111 -0
- package/src/commands/version/listversions.ts +45 -0
- package/src/common/UserGroupId.ts +137 -0
- package/src/constants.ts +70 -0
- package/src/index.ts +183 -0
- package/src/logger.ts +86 -0
- package/src/polyfills/file.polyfill.ts +5 -0
- package/src/utils.ts +34 -0
- package/src/validator.spec.ts +51 -0
- package/src/validator.ts +44 -0
- package/tsconfig.json +20 -0
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
import axios, { isAxiosError } from "axios";
|
|
2
|
+
import { jwtDecode } from "jwt-decode";
|
|
3
|
+
import { ClientConfiguration } from "../../clientconfiguration";
|
|
4
|
+
import {
|
|
5
|
+
auth0ConfigsByEnv,
|
|
6
|
+
Environment,
|
|
7
|
+
LOGIN_TIMEOUT_MS,
|
|
8
|
+
} from "../../constants";
|
|
9
|
+
import { logger } from "../../logger";
|
|
10
|
+
import { prettifyJson, printOutError, sleep } from "../../utils";
|
|
11
|
+
|
|
12
|
+
export async function login(organizationName: string, configPath?: string) {
|
|
13
|
+
const config = new ClientConfiguration(configPath);
|
|
14
|
+
const environment = config.getEnvironment();
|
|
15
|
+
|
|
16
|
+
if (environment === "e2e-test") {
|
|
17
|
+
return loginM2M(organizationName, config);
|
|
18
|
+
} else {
|
|
19
|
+
return loginWithDeviceCode(environment, organizationName, config);
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
async function loginWithDeviceCode(
|
|
24
|
+
environment: Environment,
|
|
25
|
+
organizationName: string,
|
|
26
|
+
config: ClientConfiguration,
|
|
27
|
+
) {
|
|
28
|
+
const auth0Domain = auth0ConfigsByEnv[environment].domain;
|
|
29
|
+
const auth0ClientId = auth0ConfigsByEnv[environment].clientId;
|
|
30
|
+
let deviceCodeData = null;
|
|
31
|
+
|
|
32
|
+
try {
|
|
33
|
+
const deviceCode = await axios.request({
|
|
34
|
+
method: "POST",
|
|
35
|
+
url: `${auth0Domain}/oauth/device/code`,
|
|
36
|
+
headers: { "content-type": "application/x-www-form-urlencoded" },
|
|
37
|
+
data: {
|
|
38
|
+
client_id: auth0ClientId,
|
|
39
|
+
audience: auth0ConfigsByEnv[environment].audience,
|
|
40
|
+
scope: "openid profile email offline_access",
|
|
41
|
+
},
|
|
42
|
+
});
|
|
43
|
+
deviceCodeData = deviceCode.data;
|
|
44
|
+
|
|
45
|
+
logger.info(
|
|
46
|
+
`Please visit the following URL to login ${deviceCode.data.verification_uri_complete} and verify user code ${deviceCode.data.user_code}.`,
|
|
47
|
+
);
|
|
48
|
+
logger.info("Waiting for login...");
|
|
49
|
+
} catch (e) {
|
|
50
|
+
printOutError(e, false);
|
|
51
|
+
process.exit(1);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
const startTime = Date.now();
|
|
55
|
+
|
|
56
|
+
while (true) {
|
|
57
|
+
try {
|
|
58
|
+
const accessToken = await axios.request({
|
|
59
|
+
method: "POST",
|
|
60
|
+
url: `${auth0Domain}/oauth/token`,
|
|
61
|
+
headers: { "content-type": "application/x-www-form-urlencoded" },
|
|
62
|
+
data: new URLSearchParams({
|
|
63
|
+
grant_type: "urn:ietf:params:oauth:grant-type:device_code",
|
|
64
|
+
device_code: deviceCodeData.device_code,
|
|
65
|
+
client_id: auth0ClientId,
|
|
66
|
+
}),
|
|
67
|
+
});
|
|
68
|
+
const decoded = jwtDecode(accessToken.data.id_token);
|
|
69
|
+
if (
|
|
70
|
+
typeof decoded === "object" &&
|
|
71
|
+
decoded !== null &&
|
|
72
|
+
"email" in decoded &&
|
|
73
|
+
"name" in decoded
|
|
74
|
+
) {
|
|
75
|
+
const user = decoded as { email?: string; name?: string };
|
|
76
|
+
config.setVSCodeUser(user.name, user.email);
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
config.setAccessToken(true, accessToken.data.access_token);
|
|
80
|
+
config.setJwtAccessToken(accessToken.data.access_token);
|
|
81
|
+
config.setRefreshToken(accessToken.data.refresh_token);
|
|
82
|
+
if (organizationName !== config.getOrganization()) {
|
|
83
|
+
config.setOrganization(organizationName);
|
|
84
|
+
config.setProject(undefined);
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
logger.info("Login successful");
|
|
88
|
+
break;
|
|
89
|
+
} catch (error) {
|
|
90
|
+
if (
|
|
91
|
+
isAxiosError(error) &&
|
|
92
|
+
error.response?.data?.error !== "authorization_pending"
|
|
93
|
+
) {
|
|
94
|
+
logger.error(prettifyJson(error.response?.data));
|
|
95
|
+
}
|
|
96
|
+
if (Date.now() - startTime > LOGIN_TIMEOUT_MS) {
|
|
97
|
+
logger.info("Login timed out after 5 minutes");
|
|
98
|
+
process.exit(1);
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
await sleep(deviceCodeData.interval * 1000);
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
async function loginM2M(
|
|
107
|
+
organizationName: string,
|
|
108
|
+
config: ClientConfiguration,
|
|
109
|
+
): Promise<void> {
|
|
110
|
+
const auth0Config = auth0ConfigsByEnv["e2e-test"];
|
|
111
|
+
|
|
112
|
+
if (!auth0Config) {
|
|
113
|
+
throw new Error("No environment variables present for e2e");
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
try {
|
|
117
|
+
const response = await axios.request({
|
|
118
|
+
method: "POST",
|
|
119
|
+
url: `${auth0Config.domain}/oauth/token`,
|
|
120
|
+
headers: { "content-type": "application/x-www-form-urlencoded" },
|
|
121
|
+
data: {
|
|
122
|
+
grant_type: "client_credentials",
|
|
123
|
+
client_id: auth0Config.clientId,
|
|
124
|
+
client_secret: auth0Config.clientSecret,
|
|
125
|
+
audience: auth0Config.audience,
|
|
126
|
+
},
|
|
127
|
+
});
|
|
128
|
+
|
|
129
|
+
const token = response.data.access_token;
|
|
130
|
+
|
|
131
|
+
if (!token) {
|
|
132
|
+
throw new Error("No access token received");
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
config.destroyConfig();
|
|
136
|
+
config.setAccessToken(true, token);
|
|
137
|
+
config.setOrganization(organizationName);
|
|
138
|
+
} catch (e) {
|
|
139
|
+
printOutError(e, false);
|
|
140
|
+
process.exit(1);
|
|
141
|
+
}
|
|
142
|
+
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { ClientConfiguration } from "../../clientconfiguration";
|
|
2
|
+
import { logger } from "../../logger";
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Sets a service account JWT token for CLI authentication.
|
|
6
|
+
*
|
|
7
|
+
* @param jwtToken The JWT token to use for authentication
|
|
8
|
+
* @param organizationName Optional organization name to set
|
|
9
|
+
* @param configPath Optional custom config path
|
|
10
|
+
*/
|
|
11
|
+
export function setAccessToken(
|
|
12
|
+
jwtToken: string,
|
|
13
|
+
organizationName?: string,
|
|
14
|
+
configPath?: string
|
|
15
|
+
) {
|
|
16
|
+
const config = new ClientConfiguration(configPath);
|
|
17
|
+
|
|
18
|
+
// Set the JWT token as both access token and JWT access token
|
|
19
|
+
config.setAccessToken(false, jwtToken);
|
|
20
|
+
config.setJwtAccessToken(jwtToken);
|
|
21
|
+
|
|
22
|
+
// Set organization if provided
|
|
23
|
+
if (organizationName) {
|
|
24
|
+
config.setOrganization(organizationName);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
logger.info("Access token set successfully");
|
|
28
|
+
|
|
29
|
+
if (organizationName) {
|
|
30
|
+
logger.info(`Organization set to: ${organizationName}`);
|
|
31
|
+
}
|
|
32
|
+
}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import { PackagesApi } from "../../client/api";
|
|
2
|
+
import { Configuration } from "../../client/configuration";
|
|
3
|
+
import { ClientConfiguration } from "../../clientconfiguration";
|
|
4
|
+
import { logger } from "../../logger";
|
|
5
|
+
import { getConfirmation, printOutError } from "../../utils";
|
|
6
|
+
|
|
7
|
+
const packagesApi = new PackagesApi(new Configuration());
|
|
8
|
+
|
|
9
|
+
export async function deletePackage(
|
|
10
|
+
packageServer: string,
|
|
11
|
+
packagePath: string,
|
|
12
|
+
debug: boolean,
|
|
13
|
+
autoConfirm: boolean,
|
|
14
|
+
) {
|
|
15
|
+
const clientConfig = new ClientConfiguration();
|
|
16
|
+
const organizationName = clientConfig.getOrganization();
|
|
17
|
+
const projectName = clientConfig.getProject();
|
|
18
|
+
const packageName = packagePath.replace("/", "");
|
|
19
|
+
|
|
20
|
+
if (!clientConfig.doesConfigExists()) {
|
|
21
|
+
logger.error("Please, make sure that you are logged in");
|
|
22
|
+
return;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
if (!projectName) {
|
|
26
|
+
logger.error("Please, make sure to set a project");
|
|
27
|
+
return;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
if (!autoConfirm) {
|
|
31
|
+
const confirmation = await getConfirmation(
|
|
32
|
+
`Are you sure you that want to delete the ${packagePath} package? This action cannot be undone.`,
|
|
33
|
+
);
|
|
34
|
+
if (!confirmation) {
|
|
35
|
+
logger.info("Operation cancelled.");
|
|
36
|
+
return;
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
logger.info("Deleting package " + packagePath);
|
|
41
|
+
|
|
42
|
+
try {
|
|
43
|
+
await packagesApi.deletePackage(
|
|
44
|
+
organizationName,
|
|
45
|
+
projectName,
|
|
46
|
+
packageName,
|
|
47
|
+
{
|
|
48
|
+
baseURL: packageServer,
|
|
49
|
+
headers: clientConfig.getHeaders(),
|
|
50
|
+
},
|
|
51
|
+
);
|
|
52
|
+
|
|
53
|
+
logger.info("Package deleted successfully");
|
|
54
|
+
} catch (e) {
|
|
55
|
+
logger.error("Failed to delete package");
|
|
56
|
+
printOutError(e, debug);
|
|
57
|
+
}
|
|
58
|
+
}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { PackagesApi } from "../../client/api";
|
|
2
|
+
import { Configuration } from "../../client/configuration";
|
|
3
|
+
import { ClientConfiguration } from "../../clientconfiguration";
|
|
4
|
+
import { logger } from "../../logger";
|
|
5
|
+
import { prettifyJson, printOutError } from "../../utils";
|
|
6
|
+
|
|
7
|
+
const packagesApi = new PackagesApi(new Configuration());
|
|
8
|
+
|
|
9
|
+
export async function listPackages(controlPlane: string, debug: boolean) {
|
|
10
|
+
const clientConfig = new ClientConfiguration();
|
|
11
|
+
const organizationName = clientConfig.getOrganization();
|
|
12
|
+
const projectName = clientConfig.getProject();
|
|
13
|
+
|
|
14
|
+
if (!clientConfig.doesConfigExists()) {
|
|
15
|
+
logger.error("Please, make sure that you are logged in");
|
|
16
|
+
return;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
if (!projectName) {
|
|
20
|
+
logger.error("Please, make sure to set a project");
|
|
21
|
+
return;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
try {
|
|
25
|
+
const response = await packagesApi.listPackages(
|
|
26
|
+
organizationName,
|
|
27
|
+
projectName,
|
|
28
|
+
{
|
|
29
|
+
baseURL: controlPlane,
|
|
30
|
+
headers: clientConfig.getHeaders(),
|
|
31
|
+
},
|
|
32
|
+
);
|
|
33
|
+
|
|
34
|
+
return response.data;
|
|
35
|
+
} catch (e) {
|
|
36
|
+
logger.error("Failed to list packages");
|
|
37
|
+
printOutError(e, debug);
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
export async function printOutPackages(controlPlane: string, debug: boolean) {
|
|
42
|
+
const packages = await listPackages(controlPlane, debug);
|
|
43
|
+
console.log(prettifyJson(packages));
|
|
44
|
+
}
|
|
@@ -0,0 +1,179 @@
|
|
|
1
|
+
import "../../polyfills/file.polyfill"; // Native support for File is only available on Node 19.2 or above
|
|
2
|
+
|
|
3
|
+
import archiver from "archiver";
|
|
4
|
+
import crypto from "crypto";
|
|
5
|
+
import fs from "fs";
|
|
6
|
+
import path from "path";
|
|
7
|
+
import { Package, PackagesApi } from "../../client/api";
|
|
8
|
+
import { Configuration } from "../../client/configuration";
|
|
9
|
+
import { ClientConfiguration } from "../../clientconfiguration";
|
|
10
|
+
import { logger } from "../../logger";
|
|
11
|
+
import { getConfirmation, prettifyJson, printOutError } from "../../utils";
|
|
12
|
+
import { validateManifest } from "../../validator";
|
|
13
|
+
const packagesApi = new PackagesApi(new Configuration());
|
|
14
|
+
const PACKAGE_MANIFEST_NAME = "publisher.json";
|
|
15
|
+
|
|
16
|
+
export async function publish(
|
|
17
|
+
controlPlane: string,
|
|
18
|
+
replicationCount: number,
|
|
19
|
+
debug: boolean,
|
|
20
|
+
autoConfirm: boolean,
|
|
21
|
+
setLatest: boolean,
|
|
22
|
+
) {
|
|
23
|
+
const clientConfig = new ClientConfiguration();
|
|
24
|
+
|
|
25
|
+
if (!clientConfig.doesConfigExists()) {
|
|
26
|
+
logger.error("Please, make sure that you are logged in");
|
|
27
|
+
return;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
if (!validateManifest()) {
|
|
31
|
+
return;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
const projectName = clientConfig.getProject();
|
|
35
|
+
const organizationName = clientConfig.getOrganization();
|
|
36
|
+
|
|
37
|
+
if (!projectName) {
|
|
38
|
+
logger.error("Please, make sure to set a project");
|
|
39
|
+
return;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
const packageManifestJson = fs.readFileSync(PACKAGE_MANIFEST_NAME);
|
|
43
|
+
const packageManifest = JSON.parse(packageManifestJson.toString());
|
|
44
|
+
|
|
45
|
+
const workingDirectory = process.cwd();
|
|
46
|
+
const packageName = workingDirectory.substring(
|
|
47
|
+
workingDirectory.lastIndexOf("/") + 1,
|
|
48
|
+
);
|
|
49
|
+
const tmpPackagePath = path.join("/tmp", packageName + ".zip");
|
|
50
|
+
if (!autoConfirm) {
|
|
51
|
+
const confirmation = await getConfirmation(
|
|
52
|
+
`Are you sure you that want to publish a package named ${packageName}?`,
|
|
53
|
+
);
|
|
54
|
+
if (!confirmation) {
|
|
55
|
+
logger.info("Operation cancelled.");
|
|
56
|
+
return;
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
await createTmpPackageArchive(tmpPackagePath);
|
|
60
|
+
await pushTmpPackageToRemoteServer(
|
|
61
|
+
clientConfig,
|
|
62
|
+
controlPlane,
|
|
63
|
+
replicationCount,
|
|
64
|
+
organizationName,
|
|
65
|
+
projectName,
|
|
66
|
+
tmpPackagePath,
|
|
67
|
+
packageManifest.name,
|
|
68
|
+
packageManifest.description,
|
|
69
|
+
packageManifest.version,
|
|
70
|
+
debug,
|
|
71
|
+
setLatest,
|
|
72
|
+
);
|
|
73
|
+
await removeTmpPackage(tmpPackagePath);
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
async function createTmpPackageArchive(
|
|
77
|
+
packagePath: string,
|
|
78
|
+
): Promise<undefined> {
|
|
79
|
+
logger.info("Creating new package version in " + packagePath);
|
|
80
|
+
const output = fs.createWriteStream(packagePath);
|
|
81
|
+
const archive = archiver("zip", {
|
|
82
|
+
zlib: { level: 9 }, // Sets the compression level.
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
return new Promise((resolve, reject) => {
|
|
86
|
+
archive
|
|
87
|
+
.on("warning", function (err) {
|
|
88
|
+
reject(err);
|
|
89
|
+
})
|
|
90
|
+
.on("error", function (err) {
|
|
91
|
+
reject(err);
|
|
92
|
+
})
|
|
93
|
+
.directory(".", false)
|
|
94
|
+
.pipe(output);
|
|
95
|
+
|
|
96
|
+
output.on("close", function () {
|
|
97
|
+
logger.info(archive.pointer() + " total bytes");
|
|
98
|
+
resolve(undefined);
|
|
99
|
+
});
|
|
100
|
+
archive.finalize();
|
|
101
|
+
});
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
async function pushTmpPackageToRemoteServer(
|
|
105
|
+
clientConfig: ClientConfiguration,
|
|
106
|
+
controlPlane: string,
|
|
107
|
+
replicationCount: number,
|
|
108
|
+
organizationName: string,
|
|
109
|
+
projectName: string,
|
|
110
|
+
tmpPackagePath: string,
|
|
111
|
+
name: string,
|
|
112
|
+
description: string,
|
|
113
|
+
versionId: string,
|
|
114
|
+
debug: boolean,
|
|
115
|
+
setLatest: boolean,
|
|
116
|
+
): Promise<undefined> {
|
|
117
|
+
logger.info("Pushing package " + name + " version " + versionId);
|
|
118
|
+
|
|
119
|
+
const packageContents = new File(
|
|
120
|
+
[fs.readFileSync(tmpPackagePath)],
|
|
121
|
+
tmpPackagePath,
|
|
122
|
+
);
|
|
123
|
+
|
|
124
|
+
// Build the post string from an object
|
|
125
|
+
const _package: Package = {
|
|
126
|
+
name: name,
|
|
127
|
+
description: description,
|
|
128
|
+
replicationCount: replicationCount,
|
|
129
|
+
};
|
|
130
|
+
|
|
131
|
+
// Set latest version based on the setLatest flag
|
|
132
|
+
if (setLatest) {
|
|
133
|
+
_package.latestVersion = versionId;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
const version = {
|
|
137
|
+
id: versionId,
|
|
138
|
+
};
|
|
139
|
+
try {
|
|
140
|
+
const md5Hash = crypto
|
|
141
|
+
.createHash("md5")
|
|
142
|
+
.update(fs.readFileSync(tmpPackagePath))
|
|
143
|
+
.digest("hex");
|
|
144
|
+
const response = await packagesApi.createOrUpdatePackage(
|
|
145
|
+
organizationName,
|
|
146
|
+
projectName,
|
|
147
|
+
name,
|
|
148
|
+
_package,
|
|
149
|
+
version,
|
|
150
|
+
packageContents,
|
|
151
|
+
md5Hash,
|
|
152
|
+
{
|
|
153
|
+
baseURL: controlPlane,
|
|
154
|
+
headers: clientConfig.getHeaders(),
|
|
155
|
+
},
|
|
156
|
+
);
|
|
157
|
+
|
|
158
|
+
logger.info(prettifyJson(response.data));
|
|
159
|
+
const packageUrl = path.join(
|
|
160
|
+
organizationName,
|
|
161
|
+
projectName,
|
|
162
|
+
name,
|
|
163
|
+
versionId,
|
|
164
|
+
);
|
|
165
|
+
logger.info(`Package available at: ${packageUrl}`);
|
|
166
|
+
} catch (e) {
|
|
167
|
+
logger.error("Failed to publish package");
|
|
168
|
+
printOutError(e, debug);
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
async function removeTmpPackage(tmpPackagePath: string): Promise<undefined> {
|
|
173
|
+
return new Promise((resolve) => {
|
|
174
|
+
fs.unlink(tmpPackagePath, (err) => {
|
|
175
|
+
if (err) throw err;
|
|
176
|
+
resolve(undefined);
|
|
177
|
+
});
|
|
178
|
+
});
|
|
179
|
+
}
|
|
@@ -0,0 +1,221 @@
|
|
|
1
|
+
import { expect } from "chai";
|
|
2
|
+
import sinon from "sinon";
|
|
3
|
+
import { PackagesApi } from "../../client/api";
|
|
4
|
+
import { ClientConfiguration } from "../../clientconfiguration";
|
|
5
|
+
import { logger } from "../../logger";
|
|
6
|
+
import { updatePackage } from "./updatepackage";
|
|
7
|
+
|
|
8
|
+
describe("set-replication-count", () => {
|
|
9
|
+
let sandbox: sinon.SinonSandbox;
|
|
10
|
+
let updatePackageStub: sinon.SinonStub;
|
|
11
|
+
let loggerStub: sinon.SinonStubbedInstance<typeof logger>;
|
|
12
|
+
|
|
13
|
+
beforeEach(() => {
|
|
14
|
+
sandbox = sinon.createSandbox();
|
|
15
|
+
updatePackageStub = sandbox.stub();
|
|
16
|
+
loggerStub = sandbox.stub(logger);
|
|
17
|
+
|
|
18
|
+
// Mock ClientConfiguration constructor
|
|
19
|
+
sandbox
|
|
20
|
+
.stub(ClientConfiguration.prototype, "getOrganization")
|
|
21
|
+
.returns("test-org");
|
|
22
|
+
sandbox
|
|
23
|
+
.stub(ClientConfiguration.prototype, "getProject")
|
|
24
|
+
.returns("test-project");
|
|
25
|
+
sandbox
|
|
26
|
+
.stub(ClientConfiguration.prototype, "doesConfigExists")
|
|
27
|
+
.returns(true);
|
|
28
|
+
sandbox
|
|
29
|
+
.stub(ClientConfiguration.prototype, "getHeaders")
|
|
30
|
+
.returns({ Authorization: "Bearer test-token" });
|
|
31
|
+
|
|
32
|
+
// Mock PackagesApi constructor and method
|
|
33
|
+
sandbox
|
|
34
|
+
.stub(PackagesApi.prototype, "updatePackage")
|
|
35
|
+
.callsFake(updatePackageStub);
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
afterEach(() => {
|
|
39
|
+
sandbox.restore();
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
describe("setReplicationCount", () => {
|
|
43
|
+
it("should set replication count successfully", async () => {
|
|
44
|
+
const mockResponse = { status: 200 };
|
|
45
|
+
updatePackageStub.resolves(mockResponse);
|
|
46
|
+
|
|
47
|
+
await updatePackage(
|
|
48
|
+
"http://test-server",
|
|
49
|
+
"test-package",
|
|
50
|
+
{ replicationCount: 3 },
|
|
51
|
+
false,
|
|
52
|
+
true,
|
|
53
|
+
);
|
|
54
|
+
|
|
55
|
+
expect(updatePackageStub.calledOnce).to.be.true;
|
|
56
|
+
expect(updatePackageStub.firstCall.args[0]).to.equal("test-org");
|
|
57
|
+
expect(updatePackageStub.firstCall.args[1]).to.equal("test-project");
|
|
58
|
+
expect(updatePackageStub.firstCall.args[2]).to.equal("test-package");
|
|
59
|
+
expect(updatePackageStub.firstCall.args[3]).to.include({
|
|
60
|
+
replicationCount: 3,
|
|
61
|
+
});
|
|
62
|
+
expect(updatePackageStub.firstCall.args[4]).to.deep.include({
|
|
63
|
+
baseURL: "http://test-server",
|
|
64
|
+
headers: { Authorization: "Bearer test-token" },
|
|
65
|
+
});
|
|
66
|
+
expect(
|
|
67
|
+
loggerStub.info.calledWith(
|
|
68
|
+
sinon.match(
|
|
69
|
+
"Successfully set replication count for package test-package to 3",
|
|
70
|
+
),
|
|
71
|
+
),
|
|
72
|
+
).to.be.true;
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
it("should handle missing package name", async () => {
|
|
76
|
+
await updatePackage(
|
|
77
|
+
"http://test-server",
|
|
78
|
+
"",
|
|
79
|
+
{ replicationCount: 3 },
|
|
80
|
+
false,
|
|
81
|
+
true,
|
|
82
|
+
);
|
|
83
|
+
|
|
84
|
+
expect(updatePackageStub.called).to.be.false;
|
|
85
|
+
expect(
|
|
86
|
+
loggerStub.error.calledWith(
|
|
87
|
+
sinon.match("Please provide a package name"),
|
|
88
|
+
),
|
|
89
|
+
).to.be.true;
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
it("should handle API errors", async () => {
|
|
93
|
+
const mockError = new Error("API Error");
|
|
94
|
+
updatePackageStub.rejects(mockError);
|
|
95
|
+
|
|
96
|
+
await updatePackage(
|
|
97
|
+
"http://test-server",
|
|
98
|
+
"test-package",
|
|
99
|
+
{ replicationCount: 3 },
|
|
100
|
+
true,
|
|
101
|
+
true,
|
|
102
|
+
);
|
|
103
|
+
|
|
104
|
+
expect(updatePackageStub.calledOnce).to.be.true;
|
|
105
|
+
expect(
|
|
106
|
+
loggerStub.error.calledWith(
|
|
107
|
+
sinon.match(
|
|
108
|
+
"Failed to set replication count for package test-package",
|
|
109
|
+
),
|
|
110
|
+
),
|
|
111
|
+
).to.be.true;
|
|
112
|
+
});
|
|
113
|
+
|
|
114
|
+
it("should handle non-200 response status", async () => {
|
|
115
|
+
const mockResponse = { status: 400 };
|
|
116
|
+
updatePackageStub.resolves(mockResponse);
|
|
117
|
+
|
|
118
|
+
await updatePackage(
|
|
119
|
+
"http://test-server",
|
|
120
|
+
"test-package",
|
|
121
|
+
{ replicationCount: 3 },
|
|
122
|
+
false,
|
|
123
|
+
true,
|
|
124
|
+
);
|
|
125
|
+
|
|
126
|
+
expect(updatePackageStub.calledOnce).to.be.true;
|
|
127
|
+
expect(
|
|
128
|
+
loggerStub.error.calledWith(
|
|
129
|
+
sinon.match("Failed to set replication count. Status: 400"),
|
|
130
|
+
),
|
|
131
|
+
).to.be.true;
|
|
132
|
+
});
|
|
133
|
+
});
|
|
134
|
+
|
|
135
|
+
describe("validators", () => {
|
|
136
|
+
describe("authentication validation", () => {
|
|
137
|
+
it("should fail when user is not logged in", async () => {
|
|
138
|
+
sandbox.restore();
|
|
139
|
+
sandbox = sinon.createSandbox();
|
|
140
|
+
updatePackageStub = sandbox.stub();
|
|
141
|
+
loggerStub = sandbox.stub(logger);
|
|
142
|
+
|
|
143
|
+
sandbox
|
|
144
|
+
.stub(ClientConfiguration.prototype, "doesConfigExists")
|
|
145
|
+
.returns(false);
|
|
146
|
+
sandbox
|
|
147
|
+
.stub(PackagesApi.prototype, "updatePackage")
|
|
148
|
+
.callsFake(updatePackageStub);
|
|
149
|
+
|
|
150
|
+
await updatePackage(
|
|
151
|
+
"http://test-server",
|
|
152
|
+
"test-package",
|
|
153
|
+
{ replicationCount: 3 },
|
|
154
|
+
false,
|
|
155
|
+
true,
|
|
156
|
+
);
|
|
157
|
+
|
|
158
|
+
expect(
|
|
159
|
+
loggerStub.error.calledWith(
|
|
160
|
+
sinon.match("Please, make sure that you are logged in"),
|
|
161
|
+
),
|
|
162
|
+
).to.be.true;
|
|
163
|
+
});
|
|
164
|
+
|
|
165
|
+
it("should fail when no project is set", async () => {
|
|
166
|
+
sandbox.restore();
|
|
167
|
+
sandbox = sinon.createSandbox();
|
|
168
|
+
updatePackageStub = sandbox.stub();
|
|
169
|
+
loggerStub = sandbox.stub(logger);
|
|
170
|
+
|
|
171
|
+
sandbox
|
|
172
|
+
.stub(ClientConfiguration.prototype, "doesConfigExists")
|
|
173
|
+
.returns(true);
|
|
174
|
+
sandbox
|
|
175
|
+
.stub(ClientConfiguration.prototype, "getProject")
|
|
176
|
+
.returns("");
|
|
177
|
+
sandbox
|
|
178
|
+
.stub(PackagesApi.prototype, "updatePackage")
|
|
179
|
+
.callsFake(updatePackageStub);
|
|
180
|
+
|
|
181
|
+
await updatePackage(
|
|
182
|
+
"http://test-server",
|
|
183
|
+
"test-package",
|
|
184
|
+
{ replicationCount: 3 },
|
|
185
|
+
false,
|
|
186
|
+
true,
|
|
187
|
+
);
|
|
188
|
+
|
|
189
|
+
expect(
|
|
190
|
+
loggerStub.error.calledWith(
|
|
191
|
+
sinon.match("Please, make sure to set a project"),
|
|
192
|
+
),
|
|
193
|
+
).to.be.true;
|
|
194
|
+
});
|
|
195
|
+
});
|
|
196
|
+
|
|
197
|
+
describe("replication count validation", () => {
|
|
198
|
+
it("should accept valid replication counts", async () => {
|
|
199
|
+
const mockResponse = { status: 200 };
|
|
200
|
+
updatePackageStub.resolves(mockResponse);
|
|
201
|
+
|
|
202
|
+
const validCounts = [1, 2, 5, 10, 100];
|
|
203
|
+
|
|
204
|
+
for (const count of validCounts) {
|
|
205
|
+
await updatePackage(
|
|
206
|
+
"http://test-server",
|
|
207
|
+
"test-package",
|
|
208
|
+
{ replicationCount: count },
|
|
209
|
+
false,
|
|
210
|
+
true,
|
|
211
|
+
);
|
|
212
|
+
expect(updatePackageStub.called).to.be.true;
|
|
213
|
+
expect(updatePackageStub.firstCall.args[3]).to.include({
|
|
214
|
+
replicationCount: count,
|
|
215
|
+
});
|
|
216
|
+
updatePackageStub.reset();
|
|
217
|
+
}
|
|
218
|
+
});
|
|
219
|
+
});
|
|
220
|
+
});
|
|
221
|
+
});
|