@redocly/cli 1.25.3 → 1.25.4
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 +7 -0
- package/lib/cms/api/__tests__/api.client.test.js +125 -11
- package/lib/cms/api/api-client.d.ts +20 -12
- package/lib/cms/api/api-client.js +78 -22
- package/lib/cms/commands/__tests__/push-status.test.js +2 -1
- package/lib/cms/commands/__tests__/push.test.js +3 -2
- package/lib/cms/commands/push-status.js +2 -1
- package/lib/cms/commands/push.js +5 -3
- package/package.json +2 -2
- package/src/cms/api/__tests__/api.client.test.ts +179 -16
- package/src/cms/api/api-client.ts +122 -31
- package/src/cms/commands/__tests__/push-status.test.ts +2 -1
- package/src/cms/commands/__tests__/push.test.ts +4 -3
- package/src/cms/commands/push-status.ts +4 -2
- package/src/cms/commands/push.ts +7 -4
- package/tsconfig.tsbuildinfo +1 -1
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import fetch, { Response } from 'node-fetch';
|
|
2
2
|
import * as FormData from 'form-data';
|
|
3
|
+
import { red, yellow } from 'colorette';
|
|
3
4
|
|
|
4
|
-
import {
|
|
5
|
+
import { ReuniteApi, PushPayload, ReuniteApiError } from '../api-client';
|
|
5
6
|
|
|
6
7
|
jest.mock('node-fetch', () => ({
|
|
7
8
|
default: jest.fn(),
|
|
@@ -21,10 +22,10 @@ describe('ApiClient', () => {
|
|
|
21
22
|
const expectedUserAgent = `redocly-cli/${version} ${command}`;
|
|
22
23
|
|
|
23
24
|
describe('getDefaultBranch()', () => {
|
|
24
|
-
let apiClient:
|
|
25
|
+
let apiClient: ReuniteApi;
|
|
25
26
|
|
|
26
27
|
beforeEach(() => {
|
|
27
|
-
apiClient = new
|
|
28
|
+
apiClient = new ReuniteApi({ domain: testDomain, apiKey: testToken, version, command });
|
|
28
29
|
});
|
|
29
30
|
|
|
30
31
|
it('should get default project branch', async () => {
|
|
@@ -90,22 +91,23 @@ describe('ApiClient', () => {
|
|
|
90
91
|
mountBranchName: 'remote-mount-branch-name',
|
|
91
92
|
mountPath: 'remote-mount-path',
|
|
92
93
|
};
|
|
93
|
-
|
|
94
|
+
|
|
95
|
+
const responseMock = {
|
|
96
|
+
id: 'remote-id',
|
|
97
|
+
type: 'CICD',
|
|
98
|
+
mountPath: 'remote-mount-path',
|
|
99
|
+
mountBranchName: 'remote-mount-branch-name',
|
|
100
|
+
organizationId: testOrg,
|
|
101
|
+
projectId: testProject,
|
|
102
|
+
};
|
|
103
|
+
|
|
104
|
+
let apiClient: ReuniteApi;
|
|
94
105
|
|
|
95
106
|
beforeEach(() => {
|
|
96
|
-
apiClient = new
|
|
107
|
+
apiClient = new ReuniteApi({ domain: testDomain, apiKey: testToken, version, command });
|
|
97
108
|
});
|
|
98
109
|
|
|
99
110
|
it('should upsert remote', async () => {
|
|
100
|
-
const responseMock = {
|
|
101
|
-
id: 'remote-id',
|
|
102
|
-
type: 'CICD',
|
|
103
|
-
mountPath: 'remote-mount-path',
|
|
104
|
-
mountBranchName: 'remote-mount-branch-name',
|
|
105
|
-
organizationId: testOrg,
|
|
106
|
-
projectId: testProject,
|
|
107
|
-
};
|
|
108
|
-
|
|
109
111
|
mockFetchResponse({
|
|
110
112
|
ok: true,
|
|
111
113
|
json: jest.fn().mockResolvedValue(responseMock),
|
|
@@ -204,10 +206,10 @@ describe('ApiClient', () => {
|
|
|
204
206
|
outdated: false,
|
|
205
207
|
};
|
|
206
208
|
|
|
207
|
-
let apiClient:
|
|
209
|
+
let apiClient: ReuniteApi;
|
|
208
210
|
|
|
209
211
|
beforeEach(() => {
|
|
210
|
-
apiClient = new
|
|
212
|
+
apiClient = new ReuniteApi({ domain: testDomain, apiKey: testToken, version, command });
|
|
211
213
|
});
|
|
212
214
|
|
|
213
215
|
it('should push to remote', async () => {
|
|
@@ -284,4 +286,165 @@ describe('ApiClient', () => {
|
|
|
284
286
|
).rejects.toThrow(new ReuniteApiError('Failed to push. Not found.', 404));
|
|
285
287
|
});
|
|
286
288
|
});
|
|
289
|
+
|
|
290
|
+
describe('Sunset header', () => {
|
|
291
|
+
const upsertRemoteMock = {
|
|
292
|
+
requestFn: () =>
|
|
293
|
+
apiClient.remotes.upsert(testOrg, testProject, {
|
|
294
|
+
mountBranchName: 'remote-mount-branch-name',
|
|
295
|
+
mountPath: 'remote-mount-path',
|
|
296
|
+
}),
|
|
297
|
+
responseBody: {
|
|
298
|
+
id: 'remote-id',
|
|
299
|
+
type: 'CICD',
|
|
300
|
+
mountPath: 'remote-mount-path',
|
|
301
|
+
mountBranchName: 'remote-mount-branch-name',
|
|
302
|
+
organizationId: testOrg,
|
|
303
|
+
projectId: testProject,
|
|
304
|
+
},
|
|
305
|
+
};
|
|
306
|
+
|
|
307
|
+
const getDefaultBranchMock = {
|
|
308
|
+
requestFn: () => apiClient.remotes.getDefaultBranch(testOrg, testProject),
|
|
309
|
+
responseBody: {
|
|
310
|
+
branchName: 'test-branch',
|
|
311
|
+
},
|
|
312
|
+
};
|
|
313
|
+
|
|
314
|
+
const pushMock = {
|
|
315
|
+
requestFn: () =>
|
|
316
|
+
apiClient.remotes.push(
|
|
317
|
+
testOrg,
|
|
318
|
+
testProject,
|
|
319
|
+
{
|
|
320
|
+
remoteId: 'test-remote-id',
|
|
321
|
+
commit: {
|
|
322
|
+
message: 'test-message',
|
|
323
|
+
author: {
|
|
324
|
+
name: 'test-name',
|
|
325
|
+
email: 'test-email',
|
|
326
|
+
},
|
|
327
|
+
branchName: 'test-branch-name',
|
|
328
|
+
},
|
|
329
|
+
},
|
|
330
|
+
[{ path: 'some-file.yaml', stream: Buffer.from('text content') }]
|
|
331
|
+
),
|
|
332
|
+
responseBody: {
|
|
333
|
+
branchName: 'rem/cicd/rem_01he7sr6ys2agb7w0g9t7978fn-main',
|
|
334
|
+
hasChanges: true,
|
|
335
|
+
files: [
|
|
336
|
+
{
|
|
337
|
+
type: 'file',
|
|
338
|
+
name: 'some-file.yaml',
|
|
339
|
+
path: 'docs/remotes/some-file.yaml',
|
|
340
|
+
lastModified: 1698925132394.2993,
|
|
341
|
+
mimeType: 'text/yaml',
|
|
342
|
+
},
|
|
343
|
+
],
|
|
344
|
+
commitSha: 'bb23a2f8e012ac0b7b9961b57fb40d8686b21b43',
|
|
345
|
+
outdated: false,
|
|
346
|
+
},
|
|
347
|
+
};
|
|
348
|
+
|
|
349
|
+
const endpointMocks = [upsertRemoteMock, getDefaultBranchMock, pushMock];
|
|
350
|
+
|
|
351
|
+
let apiClient: ReuniteApi;
|
|
352
|
+
|
|
353
|
+
beforeEach(() => {
|
|
354
|
+
apiClient = new ReuniteApi({ domain: testDomain, apiKey: testToken, version, command });
|
|
355
|
+
});
|
|
356
|
+
|
|
357
|
+
it.each(endpointMocks)(
|
|
358
|
+
'should report endpoint sunset in the past',
|
|
359
|
+
async ({ responseBody, requestFn }) => {
|
|
360
|
+
jest.spyOn(process.stdout, 'write').mockImplementationOnce(() => true);
|
|
361
|
+
const sunsetDate = new Date('2024-09-06T12:30:32.456Z');
|
|
362
|
+
|
|
363
|
+
mockFetchResponse({
|
|
364
|
+
ok: true,
|
|
365
|
+
json: jest.fn().mockResolvedValue(responseBody),
|
|
366
|
+
headers: new Headers({
|
|
367
|
+
Sunset: sunsetDate.toISOString(),
|
|
368
|
+
}),
|
|
369
|
+
});
|
|
370
|
+
|
|
371
|
+
await requestFn();
|
|
372
|
+
apiClient.reportSunsetWarnings();
|
|
373
|
+
|
|
374
|
+
expect(process.stdout.write).toHaveBeenCalledWith(
|
|
375
|
+
red(
|
|
376
|
+
`The "push" command is not compatible with your version of Redocly CLI. Update to the latest version by running "npm install @redocly/cli@latest".\n\n`
|
|
377
|
+
)
|
|
378
|
+
);
|
|
379
|
+
}
|
|
380
|
+
);
|
|
381
|
+
|
|
382
|
+
it.each(endpointMocks)(
|
|
383
|
+
'should report endpoint sunset in the future',
|
|
384
|
+
async ({ responseBody, requestFn }) => {
|
|
385
|
+
jest.spyOn(process.stdout, 'write').mockImplementationOnce(() => true);
|
|
386
|
+
const sunsetDate = new Date(Date.now() + 1000 * 60 * 60 * 24);
|
|
387
|
+
|
|
388
|
+
mockFetchResponse({
|
|
389
|
+
ok: true,
|
|
390
|
+
json: jest.fn().mockResolvedValue(responseBody),
|
|
391
|
+
headers: new Headers({
|
|
392
|
+
Sunset: sunsetDate.toISOString(),
|
|
393
|
+
}),
|
|
394
|
+
});
|
|
395
|
+
|
|
396
|
+
await requestFn();
|
|
397
|
+
apiClient.reportSunsetWarnings();
|
|
398
|
+
|
|
399
|
+
expect(process.stdout.write).toHaveBeenCalledWith(
|
|
400
|
+
yellow(
|
|
401
|
+
`The "push" command will be incompatible with your version of Redocly CLI after ${sunsetDate.toLocaleString()}. Update to the latest version by running "npm install @redocly/cli@latest".\n\n`
|
|
402
|
+
)
|
|
403
|
+
);
|
|
404
|
+
}
|
|
405
|
+
);
|
|
406
|
+
|
|
407
|
+
it('should report only expired resource', async () => {
|
|
408
|
+
jest.spyOn(process.stdout, 'write').mockImplementationOnce(() => true);
|
|
409
|
+
|
|
410
|
+
mockFetchResponse({
|
|
411
|
+
ok: true,
|
|
412
|
+
json: jest.fn().mockResolvedValue(upsertRemoteMock.responseBody),
|
|
413
|
+
headers: new Headers({
|
|
414
|
+
Sunset: new Date('2024-08-06T12:30:32.456Z').toISOString(),
|
|
415
|
+
}),
|
|
416
|
+
});
|
|
417
|
+
|
|
418
|
+
await upsertRemoteMock.requestFn();
|
|
419
|
+
|
|
420
|
+
mockFetchResponse({
|
|
421
|
+
ok: true,
|
|
422
|
+
json: jest.fn().mockResolvedValue(getDefaultBranchMock.responseBody),
|
|
423
|
+
headers: new Headers({
|
|
424
|
+
Sunset: new Date(Date.now() + 1000 * 60 * 60 * 24).toISOString(),
|
|
425
|
+
}),
|
|
426
|
+
});
|
|
427
|
+
|
|
428
|
+
await getDefaultBranchMock.requestFn();
|
|
429
|
+
|
|
430
|
+
mockFetchResponse({
|
|
431
|
+
ok: true,
|
|
432
|
+
json: jest.fn().mockResolvedValue(pushMock.responseBody),
|
|
433
|
+
headers: new Headers({
|
|
434
|
+
Sunset: new Date('2024-08-06T12:30:32.456Z').toISOString(),
|
|
435
|
+
}),
|
|
436
|
+
});
|
|
437
|
+
|
|
438
|
+
await pushMock.requestFn();
|
|
439
|
+
|
|
440
|
+
apiClient.reportSunsetWarnings();
|
|
441
|
+
|
|
442
|
+
expect(process.stdout.write).toHaveBeenCalledTimes(1);
|
|
443
|
+
expect(process.stdout.write).toHaveBeenCalledWith(
|
|
444
|
+
red(
|
|
445
|
+
`The "push" command is not compatible with your version of Redocly CLI. Update to the latest version by running "npm install @redocly/cli@latest".\n\n`
|
|
446
|
+
)
|
|
447
|
+
);
|
|
448
|
+
});
|
|
449
|
+
});
|
|
287
450
|
});
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { yellow, red } from 'colorette';
|
|
1
2
|
import * as FormData from 'form-data';
|
|
2
3
|
import fetchWithTimeout, {
|
|
3
4
|
type FetchWithTimeoutOptions,
|
|
@@ -13,54 +14,100 @@ import type {
|
|
|
13
14
|
UpsertRemoteResponse,
|
|
14
15
|
} from './types';
|
|
15
16
|
|
|
17
|
+
interface BaseApiClient {
|
|
18
|
+
request(url: string, options: FetchWithTimeoutOptions): Promise<Response>;
|
|
19
|
+
}
|
|
20
|
+
type CommandOption = 'push' | 'push-status';
|
|
21
|
+
export type SunsetWarning = { sunsetDate: Date; isSunsetExpired: boolean };
|
|
22
|
+
export type SunsetWarningsBuffer = SunsetWarning[];
|
|
23
|
+
|
|
16
24
|
export class ReuniteApiError extends Error {
|
|
17
25
|
constructor(message: string, public status: number) {
|
|
18
26
|
super(message);
|
|
19
27
|
}
|
|
20
28
|
}
|
|
21
29
|
|
|
22
|
-
class
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
protected async getParsedResponse<T>(response: Response): Promise<T> {
|
|
26
|
-
const responseBody = await response.json();
|
|
27
|
-
|
|
28
|
-
if (response.ok) {
|
|
29
|
-
return responseBody as T;
|
|
30
|
-
}
|
|
30
|
+
class ReuniteApiClient implements BaseApiClient {
|
|
31
|
+
public sunsetWarnings: SunsetWarningsBuffer = [];
|
|
31
32
|
|
|
32
|
-
|
|
33
|
-
`${responseBody.title || response.statusText || 'Unknown error'}.`,
|
|
34
|
-
response.status
|
|
35
|
-
);
|
|
36
|
-
}
|
|
33
|
+
constructor(protected version: string, protected command: string) {}
|
|
37
34
|
|
|
38
|
-
|
|
35
|
+
public async request(url: string, options: FetchWithTimeoutOptions) {
|
|
39
36
|
const headers = {
|
|
40
37
|
...options.headers,
|
|
41
38
|
'user-agent': `redocly-cli/${this.version.trim()} ${this.command}`,
|
|
42
39
|
};
|
|
43
40
|
|
|
44
|
-
|
|
41
|
+
const response = await fetchWithTimeout(url, {
|
|
45
42
|
...options,
|
|
46
43
|
headers,
|
|
47
44
|
});
|
|
45
|
+
|
|
46
|
+
this.collectSunsetWarning(response);
|
|
47
|
+
|
|
48
|
+
return response;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
private collectSunsetWarning(response: Response) {
|
|
52
|
+
const sunsetTime = this.getSunsetDate(response);
|
|
53
|
+
|
|
54
|
+
if (!sunsetTime) return;
|
|
55
|
+
|
|
56
|
+
const sunsetDate = new Date(sunsetTime);
|
|
57
|
+
|
|
58
|
+
if (sunsetTime > Date.now()) {
|
|
59
|
+
this.sunsetWarnings.push({
|
|
60
|
+
sunsetDate,
|
|
61
|
+
isSunsetExpired: false,
|
|
62
|
+
});
|
|
63
|
+
} else {
|
|
64
|
+
this.sunsetWarnings.push({
|
|
65
|
+
sunsetDate,
|
|
66
|
+
isSunsetExpired: true,
|
|
67
|
+
});
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
private getSunsetDate(response: Response): number | undefined {
|
|
72
|
+
const { headers } = response;
|
|
73
|
+
|
|
74
|
+
if (!headers) {
|
|
75
|
+
return;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
const sunsetDate = headers.get('sunset') || headers.get('Sunset');
|
|
79
|
+
|
|
80
|
+
if (!sunsetDate) {
|
|
81
|
+
return;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
return Date.parse(sunsetDate);
|
|
48
85
|
}
|
|
49
86
|
}
|
|
50
87
|
|
|
51
|
-
class
|
|
88
|
+
class RemotesApi {
|
|
52
89
|
constructor(
|
|
90
|
+
private client: BaseApiClient,
|
|
53
91
|
private readonly domain: string,
|
|
54
|
-
private readonly apiKey: string
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
) {
|
|
58
|
-
|
|
92
|
+
private readonly apiKey: string
|
|
93
|
+
) {}
|
|
94
|
+
|
|
95
|
+
protected async getParsedResponse<T>(response: Response): Promise<T> {
|
|
96
|
+
const responseBody = await response.json();
|
|
97
|
+
|
|
98
|
+
if (response.ok) {
|
|
99
|
+
return responseBody as T;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
throw new ReuniteApiError(
|
|
103
|
+
`${responseBody.title || response.statusText || 'Unknown error'}.`,
|
|
104
|
+
response.status
|
|
105
|
+
);
|
|
59
106
|
}
|
|
60
107
|
|
|
61
108
|
async getDefaultBranch(organizationId: string, projectId: string) {
|
|
62
109
|
try {
|
|
63
|
-
const response = await this.request(
|
|
110
|
+
const response = await this.client.request(
|
|
64
111
|
`${this.domain}/api/orgs/${organizationId}/projects/${projectId}/source`,
|
|
65
112
|
{
|
|
66
113
|
timeout: DEFAULT_FETCH_TIMEOUT,
|
|
@@ -95,7 +142,7 @@ class RemotesApiClient extends ReuniteBaseApiClient {
|
|
|
95
142
|
}
|
|
96
143
|
): Promise<UpsertRemoteResponse> {
|
|
97
144
|
try {
|
|
98
|
-
const response = await this.request(
|
|
145
|
+
const response = await this.client.request(
|
|
99
146
|
`${this.domain}/api/orgs/${organizationId}/projects/${projectId}/remotes`,
|
|
100
147
|
{
|
|
101
148
|
timeout: DEFAULT_FETCH_TIMEOUT,
|
|
@@ -150,7 +197,7 @@ class RemotesApiClient extends ReuniteBaseApiClient {
|
|
|
150
197
|
|
|
151
198
|
payload.isMainBranch && formData.append('isMainBranch', 'true');
|
|
152
199
|
try {
|
|
153
|
-
const response = await this.request(
|
|
200
|
+
const response = await this.client.request(
|
|
154
201
|
`${this.domain}/api/orgs/${organizationId}/projects/${projectId}/pushes`,
|
|
155
202
|
{
|
|
156
203
|
method: 'POST',
|
|
@@ -183,7 +230,7 @@ class RemotesApiClient extends ReuniteBaseApiClient {
|
|
|
183
230
|
mountPath: string;
|
|
184
231
|
}) {
|
|
185
232
|
try {
|
|
186
|
-
const response = await this.request(
|
|
233
|
+
const response = await this.client.request(
|
|
187
234
|
`${this.domain}/api/orgs/${organizationId}/projects/${projectId}/remotes?filter=mountPath:/${mountPath}/`,
|
|
188
235
|
{
|
|
189
236
|
timeout: DEFAULT_FETCH_TIMEOUT,
|
|
@@ -217,7 +264,7 @@ class RemotesApiClient extends ReuniteBaseApiClient {
|
|
|
217
264
|
pushId: string;
|
|
218
265
|
}) {
|
|
219
266
|
try {
|
|
220
|
-
const response = await this.request(
|
|
267
|
+
const response = await this.client.request(
|
|
221
268
|
`${this.domain}/api/orgs/${organizationId}/projects/${projectId}/pushes/${pushId}`,
|
|
222
269
|
{
|
|
223
270
|
timeout: DEFAULT_FETCH_TIMEOUT,
|
|
@@ -242,8 +289,12 @@ class RemotesApiClient extends ReuniteBaseApiClient {
|
|
|
242
289
|
}
|
|
243
290
|
}
|
|
244
291
|
|
|
245
|
-
export class
|
|
246
|
-
|
|
292
|
+
export class ReuniteApi {
|
|
293
|
+
private apiClient: ReuniteApiClient;
|
|
294
|
+
private version: string;
|
|
295
|
+
private command: CommandOption;
|
|
296
|
+
|
|
297
|
+
public remotes: RemotesApi;
|
|
247
298
|
|
|
248
299
|
constructor({
|
|
249
300
|
domain,
|
|
@@ -254,9 +305,49 @@ export class ReuniteApiClient {
|
|
|
254
305
|
domain: string;
|
|
255
306
|
apiKey: string;
|
|
256
307
|
version: string;
|
|
257
|
-
command:
|
|
308
|
+
command: CommandOption;
|
|
258
309
|
}) {
|
|
259
|
-
this.
|
|
310
|
+
this.command = command;
|
|
311
|
+
this.version = version;
|
|
312
|
+
this.apiClient = new ReuniteApiClient(this.version, this.command);
|
|
313
|
+
|
|
314
|
+
this.remotes = new RemotesApi(this.apiClient, domain, apiKey);
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
public reportSunsetWarnings(): void {
|
|
318
|
+
const sunsetWarnings = this.apiClient.sunsetWarnings;
|
|
319
|
+
|
|
320
|
+
if (sunsetWarnings.length) {
|
|
321
|
+
const [{ isSunsetExpired, sunsetDate }] = sunsetWarnings.sort(
|
|
322
|
+
(a: SunsetWarning, b: SunsetWarning) => {
|
|
323
|
+
// First, prioritize by expiration status
|
|
324
|
+
if (a.isSunsetExpired !== b.isSunsetExpired) {
|
|
325
|
+
return a.isSunsetExpired ? -1 : 1;
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
// If both are either expired or not, sort by sunset date
|
|
329
|
+
return a.sunsetDate > b.sunsetDate ? 1 : -1;
|
|
330
|
+
}
|
|
331
|
+
);
|
|
332
|
+
|
|
333
|
+
const updateVersionMessage = `Update to the latest version by running "npm install @redocly/cli@latest".`;
|
|
334
|
+
|
|
335
|
+
if (isSunsetExpired) {
|
|
336
|
+
process.stdout.write(
|
|
337
|
+
red(
|
|
338
|
+
`The "${this.command}" command is not compatible with your version of Redocly CLI. ${updateVersionMessage}\n\n`
|
|
339
|
+
)
|
|
340
|
+
);
|
|
341
|
+
} else {
|
|
342
|
+
process.stdout.write(
|
|
343
|
+
yellow(
|
|
344
|
+
`The "${
|
|
345
|
+
this.command
|
|
346
|
+
}" command will be incompatible with your version of Redocly CLI after ${sunsetDate.toLocaleString()}. ${updateVersionMessage}\n\n`
|
|
347
|
+
)
|
|
348
|
+
);
|
|
349
|
+
}
|
|
350
|
+
}
|
|
260
351
|
}
|
|
261
352
|
}
|
|
262
353
|
|
|
@@ -17,8 +17,9 @@ jest.mock('colorette', () => ({
|
|
|
17
17
|
|
|
18
18
|
jest.mock('../../api', () => ({
|
|
19
19
|
...jest.requireActual('../../api'),
|
|
20
|
-
|
|
20
|
+
ReuniteApi: jest.fn().mockImplementation(function (this: any, ...args) {
|
|
21
21
|
this.remotes = remotes;
|
|
22
|
+
this.reportSunsetWarnings = jest.fn();
|
|
22
23
|
}),
|
|
23
24
|
}));
|
|
24
25
|
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import * as fs from 'fs';
|
|
2
2
|
import * as path from 'path';
|
|
3
3
|
import { handlePush } from '../push';
|
|
4
|
-
import {
|
|
4
|
+
import { ReuniteApi, ReuniteApiError } from '../../api';
|
|
5
5
|
|
|
6
6
|
const remotes = {
|
|
7
7
|
push: jest.fn(),
|
|
@@ -15,8 +15,9 @@ jest.mock('@redocly/openapi-core', () => ({
|
|
|
15
15
|
|
|
16
16
|
jest.mock('../../api', () => ({
|
|
17
17
|
...jest.requireActual('../../api'),
|
|
18
|
-
|
|
18
|
+
ReuniteApi: jest.fn().mockImplementation(function (this: any, ...args) {
|
|
19
19
|
this.remotes = remotes;
|
|
20
|
+
this.reportSunsetWarnings = jest.fn();
|
|
20
21
|
}),
|
|
21
22
|
}));
|
|
22
23
|
|
|
@@ -332,7 +333,7 @@ describe('handlePush()', () => {
|
|
|
332
333
|
version: 'cli-version',
|
|
333
334
|
});
|
|
334
335
|
|
|
335
|
-
expect(
|
|
336
|
+
expect(ReuniteApi).toBeCalledWith({
|
|
336
337
|
domain: 'test-domain-from-env',
|
|
337
338
|
apiKey: 'test-api-key',
|
|
338
339
|
version: 'cli-version',
|
|
@@ -2,7 +2,7 @@ import * as colors from 'colorette';
|
|
|
2
2
|
import { exitWithError, printExecutionTime } from '../../utils/miscellaneous';
|
|
3
3
|
import { Spinner } from '../../utils/spinner';
|
|
4
4
|
import { DeploymentError } from '../utils';
|
|
5
|
-
import {
|
|
5
|
+
import { ReuniteApi, getApiKeys, getDomain } from '../api';
|
|
6
6
|
import { capitalize } from '../../utils/js-utils';
|
|
7
7
|
import { handleReuniteError, retryUntilConditionMet } from './utils';
|
|
8
8
|
|
|
@@ -68,7 +68,7 @@ export async function handlePushStatus({
|
|
|
68
68
|
|
|
69
69
|
try {
|
|
70
70
|
const apiKey = getApiKeys(domain);
|
|
71
|
-
const client = new
|
|
71
|
+
const client = new ReuniteApi({ domain, apiKey, version, command: 'push-status' });
|
|
72
72
|
|
|
73
73
|
let pushResponse: PushResponse;
|
|
74
74
|
|
|
@@ -169,6 +169,8 @@ export async function handlePushStatus({
|
|
|
169
169
|
}
|
|
170
170
|
printPushStatusInfo({ orgId, projectId, pushId, startedAt });
|
|
171
171
|
|
|
172
|
+
client.reportSunsetWarnings();
|
|
173
|
+
|
|
172
174
|
const summary: PushStatusSummary = {
|
|
173
175
|
preview: pushResponse.status.preview,
|
|
174
176
|
production: pushResponse.isMainBranch ? pushResponse.status.production : null,
|
package/src/cms/commands/push.ts
CHANGED
|
@@ -5,7 +5,7 @@ import { pluralize } from '@redocly/openapi-core/lib/utils';
|
|
|
5
5
|
import { green, yellow } from 'colorette';
|
|
6
6
|
import { exitWithError, printExecutionTime } from '../../utils/miscellaneous';
|
|
7
7
|
import { handlePushStatus } from './push-status';
|
|
8
|
-
import {
|
|
8
|
+
import { ReuniteApi, getDomain, getApiKeys } from '../api';
|
|
9
9
|
import { handleReuniteError } from './utils';
|
|
10
10
|
|
|
11
11
|
import type { OutputFormat } from '@redocly/openapi-core';
|
|
@@ -81,12 +81,13 @@ export async function handlePush({
|
|
|
81
81
|
const author = parseCommitAuthor(argv.author);
|
|
82
82
|
const apiKey = getApiKeys(domain);
|
|
83
83
|
const filesToUpload = collectFilesToPush(argv.files || argv.apis);
|
|
84
|
+
const commandName = 'push' as const;
|
|
84
85
|
|
|
85
86
|
if (!filesToUpload.length) {
|
|
86
|
-
return printExecutionTime(
|
|
87
|
+
return printExecutionTime(commandName, startedAt, `No files to upload`);
|
|
87
88
|
}
|
|
88
89
|
|
|
89
|
-
const client = new
|
|
90
|
+
const client = new ReuniteApi({ domain, apiKey, version, command: commandName });
|
|
90
91
|
const projectDefaultBranch = await client.remotes.getDefaultBranch(orgId, projectId);
|
|
91
92
|
const remote = await client.remotes.upsert(orgId, projectId, {
|
|
92
93
|
mountBranchName: projectDefaultBranch,
|
|
@@ -147,7 +148,7 @@ export async function handlePush({
|
|
|
147
148
|
}
|
|
148
149
|
verbose &&
|
|
149
150
|
printExecutionTime(
|
|
150
|
-
|
|
151
|
+
commandName,
|
|
151
152
|
startedAt,
|
|
152
153
|
`${pluralize(
|
|
153
154
|
'file',
|
|
@@ -155,6 +156,8 @@ export async function handlePush({
|
|
|
155
156
|
)} uploaded to organization ${orgId}, project ${projectId}. Push ID: ${id}.`
|
|
156
157
|
);
|
|
157
158
|
|
|
159
|
+
client.reportSunsetWarnings();
|
|
160
|
+
|
|
158
161
|
return {
|
|
159
162
|
pushId: id,
|
|
160
163
|
};
|