@redocly/cli 1.6.0 → 1.8.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/CHANGELOG.md +23 -0
- package/README.md +5 -5
- package/lib/__tests__/commands/build-docs.test.js +3 -3
- package/lib/__tests__/commands/bundle.test.js +5 -5
- package/lib/__tests__/commands/join.test.js +11 -11
- package/lib/__tests__/commands/lint.test.js +14 -14
- package/lib/__tests__/commands/push-region.test.js +1 -1
- package/lib/__tests__/commands/push.test.js +11 -11
- package/lib/__tests__/fetch-with-timeout.test.js +4 -13
- package/lib/__tests__/spinner.test.js +43 -0
- package/lib/__tests__/utils.test.js +54 -36
- package/lib/__tests__/wrapper.test.js +8 -8
- package/lib/cms/api/__tests__/api-keys.test.d.ts +1 -0
- package/lib/cms/api/__tests__/api-keys.test.js +26 -0
- package/lib/cms/api/__tests__/api.client.test.d.ts +1 -0
- package/lib/cms/api/__tests__/api.client.test.js +217 -0
- package/lib/cms/api/__tests__/domains.test.d.ts +1 -0
- package/lib/cms/api/__tests__/domains.test.js +13 -0
- package/lib/cms/api/api-client.d.ts +50 -0
- package/lib/cms/api/api-client.js +148 -0
- package/lib/cms/api/api-keys.d.ts +1 -0
- package/lib/cms/api/api-keys.js +24 -0
- package/lib/cms/api/domains.d.ts +1 -0
- package/lib/cms/api/domains.js +12 -0
- package/lib/cms/api/index.d.ts +3 -0
- package/lib/cms/api/index.js +19 -0
- package/lib/cms/api/types.d.ts +91 -0
- package/lib/cms/api/types.js +2 -0
- package/lib/cms/commands/__tests__/push-status.test.d.ts +1 -0
- package/lib/cms/commands/__tests__/push-status.test.js +164 -0
- package/lib/cms/commands/__tests__/push.test.d.ts +1 -0
- package/lib/cms/commands/__tests__/push.test.js +226 -0
- package/lib/cms/commands/push-status.d.ts +12 -0
- package/lib/cms/commands/push-status.js +150 -0
- package/lib/cms/commands/push.d.ts +23 -0
- package/lib/cms/commands/push.js +142 -0
- package/lib/cms/utils.d.ts +2 -0
- package/lib/cms/utils.js +6 -0
- package/lib/commands/build-docs/index.js +4 -4
- package/lib/commands/build-docs/utils.js +2 -2
- package/lib/commands/bundle.js +13 -13
- package/lib/commands/join.js +25 -25
- package/lib/commands/lint.js +10 -10
- package/lib/commands/login.js +2 -2
- package/lib/commands/preview-docs/index.js +4 -4
- package/lib/commands/preview-docs/preview-server/preview-server.js +2 -2
- package/lib/commands/preview-project/constants.d.ts +14 -0
- package/lib/commands/preview-project/constants.js +22 -0
- package/lib/commands/preview-project/index.d.ts +2 -0
- package/lib/commands/preview-project/index.js +58 -0
- package/lib/commands/preview-project/types.d.ts +10 -0
- package/lib/commands/preview-project/types.js +2 -0
- package/lib/commands/push.d.ts +5 -0
- package/lib/commands/push.js +25 -17
- package/lib/commands/split/__tests__/index.test.js +2 -2
- package/lib/commands/split/index.js +20 -20
- package/lib/commands/stats.js +4 -4
- package/lib/index.d.ts +1 -1
- package/lib/index.js +169 -25
- package/lib/types.d.ts +9 -1
- package/lib/{__mocks__/utils.js → utils/__mocks__/miscellaneous.js} +1 -1
- package/lib/utils/assert-node-version.d.ts +1 -0
- package/lib/{fetch-with-timeout.js → utils/fetch-with-timeout.js} +2 -7
- package/lib/{utils.d.ts → utils/miscellaneous.d.ts} +1 -1
- package/lib/{utils.js → utils/miscellaneous.js} +20 -2
- package/lib/utils/spinner.d.ts +10 -0
- package/lib/utils/spinner.js +42 -0
- package/lib/{update-version-notifier.js → utils/update-version-notifier.js} +4 -4
- package/lib/wrapper.js +5 -5
- package/package.json +5 -3
- package/src/__tests__/commands/build-docs.test.ts +2 -2
- package/src/__tests__/commands/bundle.test.ts +2 -2
- package/src/__tests__/commands/join.test.ts +2 -2
- package/src/__tests__/commands/lint.test.ts +3 -3
- package/src/__tests__/commands/push-region.test.ts +1 -1
- package/src/__tests__/commands/push.test.ts +2 -2
- package/src/__tests__/fetch-with-timeout.test.ts +4 -16
- package/src/__tests__/spinner.test.ts +51 -0
- package/src/__tests__/utils.test.ts +20 -5
- package/src/__tests__/wrapper.test.ts +2 -2
- package/src/cms/api/__tests__/api-keys.test.ts +37 -0
- package/src/cms/api/__tests__/api.client.test.ts +275 -0
- package/src/cms/api/__tests__/domains.test.ts +15 -0
- package/src/cms/api/api-client.ts +199 -0
- package/src/cms/api/api-keys.ts +26 -0
- package/src/cms/api/domains.ts +11 -0
- package/src/cms/api/index.ts +3 -0
- package/src/cms/api/types.ts +101 -0
- package/src/cms/commands/__tests__/push-status.test.ts +212 -0
- package/src/cms/commands/__tests__/push.test.ts +293 -0
- package/src/cms/commands/push-status.ts +203 -0
- package/src/cms/commands/push.ts +215 -0
- package/src/cms/utils.ts +1 -0
- package/src/commands/build-docs/index.ts +1 -1
- package/src/commands/build-docs/utils.ts +1 -1
- package/src/commands/bundle.ts +2 -2
- package/src/commands/join.ts +2 -2
- package/src/commands/lint.ts +1 -1
- package/src/commands/login.ts +1 -1
- package/src/commands/preview-docs/index.ts +5 -1
- package/src/commands/preview-docs/preview-server/preview-server.ts +1 -1
- package/src/commands/preview-project/constants.ts +23 -0
- package/src/commands/preview-project/index.ts +58 -0
- package/src/commands/preview-project/types.ts +12 -0
- package/src/commands/push.ts +15 -1
- package/src/commands/split/__tests__/index.test.ts +3 -4
- package/src/commands/split/index.ts +5 -5
- package/src/commands/stats.ts +2 -2
- package/src/index.ts +184 -28
- package/src/types.ts +12 -1
- package/src/{__mocks__/utils.ts → utils/__mocks__/miscellaneous.ts} +1 -1
- package/src/{fetch-with-timeout.ts → utils/fetch-with-timeout.ts} +1 -6
- package/src/{utils.ts → utils/miscellaneous.ts} +20 -2
- package/src/utils/spinner.ts +50 -0
- package/src/{update-version-notifier.ts → utils/update-version-notifier.ts} +2 -2
- package/src/wrapper.ts +7 -2
- package/tsconfig.tsbuildinfo +1 -1
- /package/lib/{assert-node-version.d.ts → __tests__/spinner.test.d.ts} +0 -0
- /package/lib/{__mocks__/utils.d.ts → utils/__mocks__/miscellaneous.d.ts} +0 -0
- /package/lib/{assert-node-version.js → utils/assert-node-version.js} +0 -0
- /package/lib/{fetch-with-timeout.d.ts → utils/fetch-with-timeout.d.ts} +0 -0
- /package/lib/{js-utils.d.ts → utils/js-utils.d.ts} +0 -0
- /package/lib/{js-utils.js → utils/js-utils.js} +0 -0
- /package/lib/{update-version-notifier.d.ts → utils/update-version-notifier.d.ts} +0 -0
- /package/src/{assert-node-version.ts → utils/assert-node-version.ts} +0 -0
- /package/src/{js-utils.ts → utils/js-utils.ts} +0 -0
|
@@ -0,0 +1,199 @@
|
|
|
1
|
+
import fetchWithTimeout from '../../utils/fetch-with-timeout';
|
|
2
|
+
import fetch from 'node-fetch';
|
|
3
|
+
import * as FormData from 'form-data';
|
|
4
|
+
|
|
5
|
+
import type { Response } from 'node-fetch';
|
|
6
|
+
import type { ReadStream } from 'fs';
|
|
7
|
+
import type {
|
|
8
|
+
ListRemotesResponse,
|
|
9
|
+
ProjectSourceResponse,
|
|
10
|
+
PushResponse,
|
|
11
|
+
UpsertRemoteResponse,
|
|
12
|
+
} from './types';
|
|
13
|
+
|
|
14
|
+
class RemotesApiClient {
|
|
15
|
+
constructor(private readonly domain: string, private readonly apiKey: string) {}
|
|
16
|
+
|
|
17
|
+
private async getParsedResponse<T>(response: Response): Promise<T> {
|
|
18
|
+
const responseBody = await response.json();
|
|
19
|
+
|
|
20
|
+
if (response.ok) {
|
|
21
|
+
return responseBody as T;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
throw new Error(responseBody.title || response.statusText);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
async getDefaultBranch(organizationId: string, projectId: string) {
|
|
28
|
+
const response = await fetch(
|
|
29
|
+
`${this.domain}/api/orgs/${organizationId}/projects/${projectId}/source`,
|
|
30
|
+
{
|
|
31
|
+
method: 'GET',
|
|
32
|
+
headers: {
|
|
33
|
+
'Content-Type': 'application/json',
|
|
34
|
+
Authorization: `Bearer ${this.apiKey}`,
|
|
35
|
+
},
|
|
36
|
+
}
|
|
37
|
+
);
|
|
38
|
+
|
|
39
|
+
try {
|
|
40
|
+
const source = await this.getParsedResponse<ProjectSourceResponse>(response);
|
|
41
|
+
|
|
42
|
+
return source.branchName;
|
|
43
|
+
} catch (err) {
|
|
44
|
+
throw new Error(`Failed to fetch default branch: ${err.message || 'Unknown error'}`);
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
async upsert(
|
|
49
|
+
organizationId: string,
|
|
50
|
+
projectId: string,
|
|
51
|
+
remote: {
|
|
52
|
+
mountPath: string;
|
|
53
|
+
mountBranchName: string;
|
|
54
|
+
}
|
|
55
|
+
): Promise<UpsertRemoteResponse> {
|
|
56
|
+
const response = await fetch(
|
|
57
|
+
`${this.domain}/api/orgs/${organizationId}/projects/${projectId}/remotes`,
|
|
58
|
+
{
|
|
59
|
+
method: 'POST',
|
|
60
|
+
headers: {
|
|
61
|
+
'Content-Type': 'application/json',
|
|
62
|
+
Authorization: `Bearer ${this.apiKey}`,
|
|
63
|
+
},
|
|
64
|
+
body: JSON.stringify({
|
|
65
|
+
mountPath: remote.mountPath,
|
|
66
|
+
mountBranchName: remote.mountBranchName,
|
|
67
|
+
type: 'CICD',
|
|
68
|
+
autoMerge: true,
|
|
69
|
+
}),
|
|
70
|
+
}
|
|
71
|
+
);
|
|
72
|
+
|
|
73
|
+
try {
|
|
74
|
+
return await this.getParsedResponse<UpsertRemoteResponse>(response);
|
|
75
|
+
} catch (err) {
|
|
76
|
+
throw new Error(`Failed to upsert remote: ${err.message || 'Unknown error'}`);
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
async push(
|
|
81
|
+
organizationId: string,
|
|
82
|
+
projectId: string,
|
|
83
|
+
payload: PushPayload,
|
|
84
|
+
files: { path: string; stream: ReadStream | Buffer }[]
|
|
85
|
+
): Promise<PushResponse> {
|
|
86
|
+
const formData = new FormData();
|
|
87
|
+
|
|
88
|
+
formData.append('remoteId', payload.remoteId);
|
|
89
|
+
formData.append('commit[message]', payload.commit.message);
|
|
90
|
+
formData.append('commit[author][name]', payload.commit.author.name);
|
|
91
|
+
formData.append('commit[author][email]', payload.commit.author.email);
|
|
92
|
+
formData.append('commit[branchName]', payload.commit.branchName);
|
|
93
|
+
payload.commit.url && formData.append('commit[url]', payload.commit.url);
|
|
94
|
+
payload.commit.namespace && formData.append('commit[namespaceId]', payload.commit.namespace);
|
|
95
|
+
payload.commit.sha && formData.append('commit[sha]', payload.commit.sha);
|
|
96
|
+
payload.commit.repository && formData.append('commit[repositoryId]', payload.commit.repository);
|
|
97
|
+
payload.commit.createdAt && formData.append('commit[createdAt]', payload.commit.createdAt);
|
|
98
|
+
|
|
99
|
+
for (const file of files) {
|
|
100
|
+
formData.append(`files[${file.path}]`, file.stream);
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
payload.isMainBranch && formData.append('isMainBranch', 'true');
|
|
104
|
+
|
|
105
|
+
const response = await fetch(
|
|
106
|
+
`${this.domain}/api/orgs/${organizationId}/projects/${projectId}/pushes`,
|
|
107
|
+
{
|
|
108
|
+
method: 'POST',
|
|
109
|
+
headers: {
|
|
110
|
+
Authorization: `Bearer ${this.apiKey}`,
|
|
111
|
+
},
|
|
112
|
+
body: formData,
|
|
113
|
+
}
|
|
114
|
+
);
|
|
115
|
+
|
|
116
|
+
try {
|
|
117
|
+
return await this.getParsedResponse<PushResponse>(response);
|
|
118
|
+
} catch (err) {
|
|
119
|
+
throw new Error(`Failed to push: ${err.message || 'Unknown error'}`);
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
async getRemotesList(organizationId: string, projectId: string, mountPath: string) {
|
|
124
|
+
const response = await fetch(
|
|
125
|
+
`${this.domain}/api/orgs/${organizationId}/projects/${projectId}/remotes?filter=mountPath:/${mountPath}/`,
|
|
126
|
+
{
|
|
127
|
+
method: 'GET',
|
|
128
|
+
headers: {
|
|
129
|
+
'Content-Type': 'application/json',
|
|
130
|
+
Authorization: `Bearer ${this.apiKey}`,
|
|
131
|
+
},
|
|
132
|
+
}
|
|
133
|
+
);
|
|
134
|
+
|
|
135
|
+
try {
|
|
136
|
+
return await this.getParsedResponse<ListRemotesResponse>(response);
|
|
137
|
+
} catch (err) {
|
|
138
|
+
throw new Error(`Failed to get remote list: ${err.message || 'Unknown error'}`);
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
async getPush({
|
|
143
|
+
organizationId,
|
|
144
|
+
projectId,
|
|
145
|
+
pushId,
|
|
146
|
+
}: {
|
|
147
|
+
organizationId: string;
|
|
148
|
+
projectId: string;
|
|
149
|
+
pushId: string;
|
|
150
|
+
}) {
|
|
151
|
+
const response = await fetchWithTimeout(
|
|
152
|
+
`${this.domain}/api/orgs/${organizationId}/projects/${projectId}/pushes/${pushId}`,
|
|
153
|
+
{
|
|
154
|
+
method: 'GET',
|
|
155
|
+
headers: {
|
|
156
|
+
'Content-Type': 'application/json',
|
|
157
|
+
Authorization: `Bearer ${this.apiKey}`,
|
|
158
|
+
},
|
|
159
|
+
}
|
|
160
|
+
);
|
|
161
|
+
|
|
162
|
+
if (!response) {
|
|
163
|
+
throw new Error(`Failed to get push status: Time is up`);
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
try {
|
|
167
|
+
return await this.getParsedResponse<PushResponse>(response);
|
|
168
|
+
} catch (err) {
|
|
169
|
+
throw new Error(`Failed to get push status: ${err.message || 'Unknown error'}`);
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
export class ReuniteApiClient {
|
|
175
|
+
remotes: RemotesApiClient;
|
|
176
|
+
|
|
177
|
+
constructor(public domain: string, private readonly apiKey: string) {
|
|
178
|
+
this.remotes = new RemotesApiClient(this.domain, this.apiKey);
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
export type PushPayload = {
|
|
183
|
+
remoteId: string;
|
|
184
|
+
commit: {
|
|
185
|
+
message: string;
|
|
186
|
+
branchName: string;
|
|
187
|
+
sha?: string;
|
|
188
|
+
url?: string;
|
|
189
|
+
createdAt?: string;
|
|
190
|
+
namespace?: string;
|
|
191
|
+
repository?: string;
|
|
192
|
+
author: {
|
|
193
|
+
name: string;
|
|
194
|
+
email: string;
|
|
195
|
+
image?: string;
|
|
196
|
+
};
|
|
197
|
+
};
|
|
198
|
+
isMainBranch?: boolean;
|
|
199
|
+
};
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { resolve } from 'path';
|
|
2
|
+
import { homedir } from 'os';
|
|
3
|
+
import { existsSync, readFileSync } from 'fs';
|
|
4
|
+
import { isNotEmptyObject } from '@redocly/openapi-core/lib/utils';
|
|
5
|
+
import { TOKEN_FILENAME } from '@redocly/openapi-core/lib/redocly';
|
|
6
|
+
|
|
7
|
+
function readCredentialsFile(credentialsPath: string) {
|
|
8
|
+
return existsSync(credentialsPath) ? JSON.parse(readFileSync(credentialsPath, 'utf-8')) : {};
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export function getApiKeys(domain: string) {
|
|
12
|
+
const apiKey = process.env.REDOCLY_AUTHORIZATION;
|
|
13
|
+
|
|
14
|
+
if (apiKey) {
|
|
15
|
+
return apiKey;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
const credentialsPath = resolve(homedir(), TOKEN_FILENAME);
|
|
19
|
+
const credentials = readCredentialsFile(credentialsPath);
|
|
20
|
+
|
|
21
|
+
if (isNotEmptyObject(credentials) && credentials[domain]) {
|
|
22
|
+
return credentials[domain];
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
throw new Error('No api key provided, please use environment variable REDOCLY_AUTHORIZATION.');
|
|
26
|
+
}
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
export type ProjectSourceResponse = {
|
|
2
|
+
branchName: string;
|
|
3
|
+
contentPath: string;
|
|
4
|
+
isInternal: boolean;
|
|
5
|
+
};
|
|
6
|
+
|
|
7
|
+
export type UpsertRemoteResponse = {
|
|
8
|
+
id: string;
|
|
9
|
+
type: 'CICD';
|
|
10
|
+
mountPath: string;
|
|
11
|
+
mountBranchName: string;
|
|
12
|
+
organizationId: string;
|
|
13
|
+
projectId: string;
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
export type ListRemotesResponse = {
|
|
17
|
+
object: 'list';
|
|
18
|
+
page: {
|
|
19
|
+
endCursor: string;
|
|
20
|
+
startCursor: string;
|
|
21
|
+
haxNextPage: boolean;
|
|
22
|
+
hasPrevPage: boolean;
|
|
23
|
+
limit: number;
|
|
24
|
+
total: number;
|
|
25
|
+
};
|
|
26
|
+
items: Remote[];
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
export type Remote = {
|
|
30
|
+
mountPath: string;
|
|
31
|
+
type: string;
|
|
32
|
+
autoSync: boolean;
|
|
33
|
+
autoMerge: boolean;
|
|
34
|
+
createdAt: string;
|
|
35
|
+
updatedAt: string;
|
|
36
|
+
providerType: string;
|
|
37
|
+
namespaceId: string;
|
|
38
|
+
repositoryId: string;
|
|
39
|
+
projectId: string;
|
|
40
|
+
mountBranchName: string;
|
|
41
|
+
contentPath: string;
|
|
42
|
+
credentialId: string;
|
|
43
|
+
branchName: string;
|
|
44
|
+
contentType: string;
|
|
45
|
+
id: string;
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
export type PushResponse = {
|
|
49
|
+
id: string;
|
|
50
|
+
remoteId: string;
|
|
51
|
+
commit: {
|
|
52
|
+
message: string;
|
|
53
|
+
branchName: string;
|
|
54
|
+
sha: string | null;
|
|
55
|
+
url: string | null;
|
|
56
|
+
createdAt: string | null;
|
|
57
|
+
namespace: string | null;
|
|
58
|
+
repository: string | null;
|
|
59
|
+
author: {
|
|
60
|
+
name: string;
|
|
61
|
+
email: string;
|
|
62
|
+
image: string | null;
|
|
63
|
+
};
|
|
64
|
+
};
|
|
65
|
+
remote: {
|
|
66
|
+
commits: {
|
|
67
|
+
branchName: string;
|
|
68
|
+
sha: string;
|
|
69
|
+
}[];
|
|
70
|
+
};
|
|
71
|
+
hasChanges: boolean;
|
|
72
|
+
isOutdated: boolean;
|
|
73
|
+
isMainBranch: boolean;
|
|
74
|
+
status: PushStatusResponse;
|
|
75
|
+
};
|
|
76
|
+
|
|
77
|
+
type DeploymentStatusResponse = {
|
|
78
|
+
deploy: {
|
|
79
|
+
url: string | null;
|
|
80
|
+
status: DeploymentStatus;
|
|
81
|
+
};
|
|
82
|
+
scorecard: ScorecardItem[];
|
|
83
|
+
};
|
|
84
|
+
|
|
85
|
+
export type PushStatusResponse = {
|
|
86
|
+
preview: DeploymentStatusResponse;
|
|
87
|
+
production: DeploymentStatusResponse;
|
|
88
|
+
};
|
|
89
|
+
|
|
90
|
+
export type ScorecardItem = {
|
|
91
|
+
name: string;
|
|
92
|
+
status: PushStatusBase;
|
|
93
|
+
description: string;
|
|
94
|
+
url: string;
|
|
95
|
+
};
|
|
96
|
+
|
|
97
|
+
export type PushStatusBase = 'pending' | 'success' | 'running' | 'failed';
|
|
98
|
+
|
|
99
|
+
// export type BuildStatus = PushStatusBase | 'NOT_STARTED' | 'QUEUED';
|
|
100
|
+
|
|
101
|
+
export type DeploymentStatus = 'skipped' | PushStatusBase;
|
|
@@ -0,0 +1,212 @@
|
|
|
1
|
+
import { handlePushStatus } from '../push-status';
|
|
2
|
+
import { PushResponse } from '../../api/types';
|
|
3
|
+
import { exitWithError } from '../../../utils/miscellaneous';
|
|
4
|
+
|
|
5
|
+
const remotes = {
|
|
6
|
+
getPush: jest.fn(),
|
|
7
|
+
getRemotesList: jest.fn(),
|
|
8
|
+
};
|
|
9
|
+
|
|
10
|
+
jest.mock('../../../utils/miscellaneous');
|
|
11
|
+
|
|
12
|
+
jest.mock('colorette', () => ({
|
|
13
|
+
green: (str: string) => str,
|
|
14
|
+
yellow: (str: string) => str,
|
|
15
|
+
red: (str: string) => str,
|
|
16
|
+
gray: (str: string) => str,
|
|
17
|
+
magenta: (str: string) => str,
|
|
18
|
+
cyan: (str: string) => str,
|
|
19
|
+
}));
|
|
20
|
+
|
|
21
|
+
jest.mock('../../api', () => ({
|
|
22
|
+
...jest.requireActual('../../api'),
|
|
23
|
+
ReuniteApiClient: jest.fn().mockImplementation(function (this: any, ...args) {
|
|
24
|
+
this.remotes = remotes;
|
|
25
|
+
}),
|
|
26
|
+
}));
|
|
27
|
+
|
|
28
|
+
describe('handlePushStatus()', () => {
|
|
29
|
+
const mockConfig = { apis: {} } as any;
|
|
30
|
+
|
|
31
|
+
const pushResponseStub = {
|
|
32
|
+
hasChanges: true,
|
|
33
|
+
status: {
|
|
34
|
+
preview: {
|
|
35
|
+
scorecard: [],
|
|
36
|
+
deploy: {
|
|
37
|
+
url: 'https://test-url',
|
|
38
|
+
status: 'success',
|
|
39
|
+
},
|
|
40
|
+
},
|
|
41
|
+
production: {
|
|
42
|
+
scorecard: [],
|
|
43
|
+
deploy: {
|
|
44
|
+
url: 'https://test-url',
|
|
45
|
+
status: 'success',
|
|
46
|
+
},
|
|
47
|
+
},
|
|
48
|
+
},
|
|
49
|
+
} as unknown as PushResponse;
|
|
50
|
+
|
|
51
|
+
beforeEach(() => {
|
|
52
|
+
jest.spyOn(process.stderr, 'write').mockImplementation(() => true);
|
|
53
|
+
jest.spyOn(process.stdout, 'write').mockImplementation(() => true);
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
afterEach(() => {
|
|
57
|
+
jest.clearAllMocks();
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
it('should throw error if organization not provided', async () => {
|
|
61
|
+
await handlePushStatus(
|
|
62
|
+
{
|
|
63
|
+
domain: 'test-domain',
|
|
64
|
+
organization: '',
|
|
65
|
+
project: 'test-project',
|
|
66
|
+
pushId: 'test-push-id',
|
|
67
|
+
'max-execution-time': 1000,
|
|
68
|
+
},
|
|
69
|
+
mockConfig
|
|
70
|
+
);
|
|
71
|
+
|
|
72
|
+
expect(exitWithError).toHaveBeenCalledWith(
|
|
73
|
+
"No organization provided, please use --organization option or specify the 'organization' field in the config file."
|
|
74
|
+
);
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
it('should return success push status for preview-build', async () => {
|
|
78
|
+
process.env.REDOCLY_AUTHORIZATION = 'test-api-key';
|
|
79
|
+
remotes.getPush.mockResolvedValueOnce(pushResponseStub);
|
|
80
|
+
|
|
81
|
+
await handlePushStatus(
|
|
82
|
+
{
|
|
83
|
+
domain: 'test-domain',
|
|
84
|
+
organization: 'test-org',
|
|
85
|
+
project: 'test-project',
|
|
86
|
+
pushId: 'test-push-id',
|
|
87
|
+
'max-execution-time': 1000,
|
|
88
|
+
},
|
|
89
|
+
mockConfig
|
|
90
|
+
);
|
|
91
|
+
expect(process.stdout.write).toHaveBeenCalledTimes(1);
|
|
92
|
+
expect(process.stdout.write).toHaveBeenCalledWith(
|
|
93
|
+
'🚀 PREVIEW deployment succeeded.\nPreview URL: https://test-url\n'
|
|
94
|
+
);
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
it('should return success push status for preview and production builds', async () => {
|
|
98
|
+
process.env.REDOCLY_AUTHORIZATION = 'test-api-key';
|
|
99
|
+
remotes.getPush.mockResolvedValue({ ...pushResponseStub, isMainBranch: true });
|
|
100
|
+
|
|
101
|
+
await handlePushStatus(
|
|
102
|
+
{
|
|
103
|
+
domain: 'test-domain',
|
|
104
|
+
organization: 'test-org',
|
|
105
|
+
project: 'test-project',
|
|
106
|
+
pushId: 'test-push-id',
|
|
107
|
+
'max-execution-time': 1000,
|
|
108
|
+
},
|
|
109
|
+
mockConfig
|
|
110
|
+
);
|
|
111
|
+
expect(process.stdout.write).toHaveBeenCalledTimes(2);
|
|
112
|
+
expect(process.stdout.write).toHaveBeenCalledWith(
|
|
113
|
+
'🚀 PREVIEW deployment succeeded.\nPreview URL: https://test-url\n'
|
|
114
|
+
);
|
|
115
|
+
expect(process.stdout.write).toHaveBeenCalledWith(
|
|
116
|
+
'🚀 PRODUCTION deployment succeeded.\nPreview URL: https://test-url\n'
|
|
117
|
+
);
|
|
118
|
+
});
|
|
119
|
+
|
|
120
|
+
it('should return failed push status for preview build', async () => {
|
|
121
|
+
process.env.REDOCLY_AUTHORIZATION = 'test-api-key';
|
|
122
|
+
|
|
123
|
+
remotes.getPush.mockResolvedValue({
|
|
124
|
+
isOutdated: false,
|
|
125
|
+
hasChanges: true,
|
|
126
|
+
status: {
|
|
127
|
+
preview: { deploy: { status: 'failed', url: 'https://test-url' }, scorecard: [] },
|
|
128
|
+
},
|
|
129
|
+
});
|
|
130
|
+
|
|
131
|
+
await handlePushStatus(
|
|
132
|
+
{
|
|
133
|
+
domain: 'test-domain',
|
|
134
|
+
organization: 'test-org',
|
|
135
|
+
project: 'test-project',
|
|
136
|
+
pushId: 'test-push-id',
|
|
137
|
+
'max-execution-time': 1000,
|
|
138
|
+
},
|
|
139
|
+
mockConfig
|
|
140
|
+
);
|
|
141
|
+
expect(exitWithError).toHaveBeenCalledWith(
|
|
142
|
+
'❌ PREVIEW deployment failed.\nPreview URL: https://test-url'
|
|
143
|
+
);
|
|
144
|
+
});
|
|
145
|
+
|
|
146
|
+
it('should return success push status for preview build and print scorecards', async () => {
|
|
147
|
+
process.env.REDOCLY_AUTHORIZATION = 'test-api-key';
|
|
148
|
+
|
|
149
|
+
remotes.getPush.mockResolvedValue({
|
|
150
|
+
isOutdated: false,
|
|
151
|
+
hasChanges: true,
|
|
152
|
+
status: {
|
|
153
|
+
preview: {
|
|
154
|
+
deploy: { status: 'success', url: 'https://test-url' },
|
|
155
|
+
scorecard: [
|
|
156
|
+
{
|
|
157
|
+
name: 'test-name',
|
|
158
|
+
status: 'success',
|
|
159
|
+
description: 'test-description',
|
|
160
|
+
url: 'test-url',
|
|
161
|
+
},
|
|
162
|
+
],
|
|
163
|
+
},
|
|
164
|
+
},
|
|
165
|
+
});
|
|
166
|
+
|
|
167
|
+
await handlePushStatus(
|
|
168
|
+
{
|
|
169
|
+
domain: 'test-domain',
|
|
170
|
+
organization: 'test-org',
|
|
171
|
+
project: 'test-project',
|
|
172
|
+
pushId: 'test-push-id',
|
|
173
|
+
'max-execution-time': 1000,
|
|
174
|
+
},
|
|
175
|
+
mockConfig
|
|
176
|
+
);
|
|
177
|
+
expect(process.stdout.write).toHaveBeenCalledTimes(4);
|
|
178
|
+
expect(process.stdout.write).toHaveBeenCalledWith(
|
|
179
|
+
'🚀 PREVIEW deployment succeeded.\nPreview URL: https://test-url\n'
|
|
180
|
+
);
|
|
181
|
+
expect(process.stdout.write).toHaveBeenCalledWith('\nScorecard:');
|
|
182
|
+
expect(process.stdout.write).toHaveBeenCalledWith(
|
|
183
|
+
'\n Name: test-name\n Status: success\n URL: test-url\n Description: test-description\n'
|
|
184
|
+
);
|
|
185
|
+
expect(process.stdout.write).toHaveBeenCalledWith('\n');
|
|
186
|
+
});
|
|
187
|
+
|
|
188
|
+
it('should display message if there is no changes', async () => {
|
|
189
|
+
process.env.REDOCLY_AUTHORIZATION = 'test-api-key';
|
|
190
|
+
|
|
191
|
+
remotes.getPush.mockResolvedValueOnce({
|
|
192
|
+
isOutdated: false,
|
|
193
|
+
hasChanges: false,
|
|
194
|
+
status: {
|
|
195
|
+
preview: { deploy: { status: 'skipped', url: 'https://test-url' }, scorecard: [] },
|
|
196
|
+
},
|
|
197
|
+
});
|
|
198
|
+
|
|
199
|
+
await handlePushStatus(
|
|
200
|
+
{
|
|
201
|
+
domain: 'test-domain',
|
|
202
|
+
organization: 'test-org',
|
|
203
|
+
project: 'test-project',
|
|
204
|
+
pushId: 'test-push-id',
|
|
205
|
+
wait: true,
|
|
206
|
+
'max-execution-time': 1000,
|
|
207
|
+
},
|
|
208
|
+
mockConfig
|
|
209
|
+
);
|
|
210
|
+
expect(process.stderr.write).toHaveBeenCalledWith('Files not uploaded. Reason: no changes.\n');
|
|
211
|
+
});
|
|
212
|
+
});
|