@redocly/cli 1.22.0 → 1.23.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 +18 -0
- package/lib/__tests__/commands/bundle.test.js +110 -1
- package/lib/__tests__/fetch-with-timeout.test.js +29 -5
- package/lib/__tests__/utils.test.js +54 -32
- package/lib/cms/api/__tests__/api.client.test.js +17 -9
- package/lib/cms/api/api-client.d.ts +26 -7
- package/lib/cms/api/api-client.js +103 -72
- package/lib/cms/commands/__tests__/push-status.test.js +1 -1
- package/lib/cms/commands/__tests__/push.test.js +41 -1
- package/lib/cms/commands/__tests__/utils.test.js +1 -1
- package/lib/cms/commands/push-status.d.ts +1 -1
- package/lib/cms/commands/push-status.js +3 -7
- package/lib/cms/commands/push.js +4 -4
- package/lib/cms/commands/utils.d.ts +3 -0
- package/lib/cms/commands/utils.js +8 -1
- package/lib/commands/bundle.d.ts +1 -1
- package/lib/commands/bundle.js +9 -9
- package/lib/commands/eject.d.ts +1 -1
- package/lib/commands/eject.js +1 -1
- package/lib/commands/preview-project/index.js +1 -1
- package/lib/index.js +1 -2
- package/lib/types.d.ts +1 -0
- package/lib/utils/__mocks__/miscellaneous.d.ts +1 -0
- package/lib/utils/__mocks__/miscellaneous.js +2 -1
- package/lib/utils/fetch-with-timeout.d.ts +6 -1
- package/lib/utils/fetch-with-timeout.js +16 -14
- package/lib/utils/miscellaneous.d.ts +4 -1
- package/lib/utils/miscellaneous.js +24 -29
- package/lib/utils/update-version-notifier.js +8 -4
- package/package.json +2 -2
- package/src/__tests__/commands/bundle.test.ts +131 -4
- package/src/__tests__/fetch-with-timeout.test.ts +36 -6
- package/src/__tests__/utils.test.ts +58 -33
- package/src/cms/api/__tests__/api.client.test.ts +20 -11
- package/src/cms/api/api-client.ts +158 -91
- package/src/cms/commands/__tests__/push-status.test.ts +1 -1
- package/src/cms/commands/__tests__/push.test.ts +49 -2
- package/src/cms/commands/__tests__/utils.test.ts +1 -1
- package/src/cms/commands/push-status.ts +5 -9
- package/src/cms/commands/push.ts +5 -6
- package/src/cms/commands/utils.ts +15 -1
- package/src/commands/bundle.ts +14 -12
- package/src/commands/eject.ts +2 -2
- package/src/commands/preview-project/index.ts +1 -1
- package/src/index.ts +1 -2
- package/src/types.ts +1 -0
- package/src/utils/__mocks__/miscellaneous.ts +1 -0
- package/src/utils/fetch-with-timeout.ts +23 -14
- package/src/utils/miscellaneous.ts +32 -37
- package/src/utils/update-version-notifier.ts +11 -5
- package/tsconfig.tsbuildinfo +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,23 @@
|
|
|
1
1
|
# @redocly/cli
|
|
2
2
|
|
|
3
|
+
## 1.23.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- Added support for the `output` option in the per-API configuration so that the destination file can be specified in configuration.
|
|
8
|
+
|
|
9
|
+
### Patch Changes
|
|
10
|
+
|
|
11
|
+
- Fixed the absolute path for importing plugins in Windows.
|
|
12
|
+
- Added the ability to run the `eject` command without specifying components, which displays a selectable list of all available components.
|
|
13
|
+
- Updated @redocly/openapi-core to v1.23.0.
|
|
14
|
+
|
|
15
|
+
## 1.22.1
|
|
16
|
+
|
|
17
|
+
### Patch Changes
|
|
18
|
+
|
|
19
|
+
- Updated @redocly/openapi-core to v1.22.1.
|
|
20
|
+
|
|
3
21
|
## 1.22.0
|
|
4
22
|
|
|
5
23
|
### Minor Changes
|
|
@@ -6,21 +6,28 @@ const miscellaneous_1 = require("../../utils/miscellaneous");
|
|
|
6
6
|
const wrapper_1 = require("../../wrapper");
|
|
7
7
|
jest.mock('@redocly/openapi-core');
|
|
8
8
|
jest.mock('../../utils/miscellaneous');
|
|
9
|
+
// @ts-ignore
|
|
10
|
+
miscellaneous_1.getOutputFileName = jest.requireActual('../../utils/miscellaneous').getOutputFileName;
|
|
9
11
|
openapi_core_1.getMergedConfig.mockImplementation((config) => config);
|
|
10
12
|
describe('bundle', () => {
|
|
11
13
|
let processExitMock;
|
|
12
14
|
let exitCb;
|
|
15
|
+
let stderrWriteMock;
|
|
16
|
+
let stdoutWriteMock;
|
|
13
17
|
beforeEach(() => {
|
|
14
18
|
processExitMock = jest.spyOn(process, 'exit').mockImplementation();
|
|
15
19
|
jest.spyOn(process, 'once').mockImplementation((_e, cb) => {
|
|
16
20
|
exitCb = cb;
|
|
17
21
|
return process.on(_e, cb);
|
|
18
22
|
});
|
|
19
|
-
jest.spyOn(process.stderr, 'write').mockImplementation(()
|
|
23
|
+
stderrWriteMock = jest.spyOn(process.stderr, 'write').mockImplementation(jest.fn());
|
|
24
|
+
stdoutWriteMock = jest.spyOn(process.stdout, 'write').mockImplementation(jest.fn());
|
|
20
25
|
});
|
|
21
26
|
afterEach(() => {
|
|
22
27
|
openapi_core_1.bundle.mockClear();
|
|
23
28
|
openapi_core_1.getTotals.mockReset();
|
|
29
|
+
stderrWriteMock.mockRestore();
|
|
30
|
+
stdoutWriteMock.mockRestore();
|
|
24
31
|
});
|
|
25
32
|
it('bundles definitions', async () => {
|
|
26
33
|
const apis = ['foo.yaml', 'bar.yaml'];
|
|
@@ -87,4 +94,106 @@ describe('bundle', () => {
|
|
|
87
94
|
});
|
|
88
95
|
expect(miscellaneous_1.handleError).toHaveBeenCalledTimes(0);
|
|
89
96
|
});
|
|
97
|
+
it('should store bundled API descriptions in the output files described in the apis section of config IF no positional apis provided AND output is specified for both apis', async () => {
|
|
98
|
+
const apis = {
|
|
99
|
+
foo: {
|
|
100
|
+
root: 'foo.yaml',
|
|
101
|
+
output: 'output/foo.yaml',
|
|
102
|
+
},
|
|
103
|
+
bar: {
|
|
104
|
+
root: 'bar.yaml',
|
|
105
|
+
output: 'output/bar.json',
|
|
106
|
+
},
|
|
107
|
+
};
|
|
108
|
+
const config = {
|
|
109
|
+
apis,
|
|
110
|
+
styleguide: {
|
|
111
|
+
skipPreprocessors: jest.fn(),
|
|
112
|
+
skipDecorators: jest.fn(),
|
|
113
|
+
},
|
|
114
|
+
};
|
|
115
|
+
// @ts-ignore
|
|
116
|
+
miscellaneous_1.getFallbackApisOrExit = jest
|
|
117
|
+
.fn()
|
|
118
|
+
.mockResolvedValueOnce(Object.entries(apis).map(([alias, { root, ...api }]) => ({ ...api, path: root, alias })));
|
|
119
|
+
openapi_core_1.getTotals.mockReturnValue({
|
|
120
|
+
errors: 0,
|
|
121
|
+
warnings: 0,
|
|
122
|
+
ignored: 0,
|
|
123
|
+
});
|
|
124
|
+
await (0, bundle_1.handleBundle)({
|
|
125
|
+
argv: { apis: [] }, // positional
|
|
126
|
+
version: 'test',
|
|
127
|
+
config,
|
|
128
|
+
});
|
|
129
|
+
expect(miscellaneous_1.saveBundle).toBeCalledTimes(2);
|
|
130
|
+
expect(miscellaneous_1.saveBundle).toHaveBeenNthCalledWith(1, 'output/foo.yaml', expect.any(String));
|
|
131
|
+
expect(miscellaneous_1.saveBundle).toHaveBeenNthCalledWith(2, 'output/bar.json', expect.any(String));
|
|
132
|
+
});
|
|
133
|
+
it('should store bundled API descriptions in the output files described in the apis section of config AND print the bundled api without the output specified to the terminal IF no positional apis provided AND output is specified for one api', async () => {
|
|
134
|
+
const apis = {
|
|
135
|
+
foo: {
|
|
136
|
+
root: 'foo.yaml',
|
|
137
|
+
output: 'output/foo.yaml',
|
|
138
|
+
},
|
|
139
|
+
bar: {
|
|
140
|
+
root: 'bar.yaml',
|
|
141
|
+
},
|
|
142
|
+
};
|
|
143
|
+
const config = {
|
|
144
|
+
apis,
|
|
145
|
+
styleguide: {
|
|
146
|
+
skipPreprocessors: jest.fn(),
|
|
147
|
+
skipDecorators: jest.fn(),
|
|
148
|
+
},
|
|
149
|
+
};
|
|
150
|
+
// @ts-ignore
|
|
151
|
+
miscellaneous_1.getFallbackApisOrExit = jest
|
|
152
|
+
.fn()
|
|
153
|
+
.mockResolvedValueOnce(Object.entries(apis).map(([alias, { root, ...api }]) => ({ ...api, path: root, alias })));
|
|
154
|
+
openapi_core_1.getTotals.mockReturnValue({
|
|
155
|
+
errors: 0,
|
|
156
|
+
warnings: 0,
|
|
157
|
+
ignored: 0,
|
|
158
|
+
});
|
|
159
|
+
await (0, bundle_1.handleBundle)({
|
|
160
|
+
argv: { apis: [] }, // positional
|
|
161
|
+
version: 'test',
|
|
162
|
+
config,
|
|
163
|
+
});
|
|
164
|
+
expect(miscellaneous_1.saveBundle).toBeCalledTimes(1);
|
|
165
|
+
expect(miscellaneous_1.saveBundle).toHaveBeenCalledWith('output/foo.yaml', expect.any(String));
|
|
166
|
+
expect(process.stdout.write).toHaveBeenCalledTimes(1);
|
|
167
|
+
});
|
|
168
|
+
describe('per api output', () => {
|
|
169
|
+
it('should NOT store bundled API descriptions in the output files described in the apis section of config IF no there is a positional api provided', async () => {
|
|
170
|
+
const apis = {
|
|
171
|
+
foo: {
|
|
172
|
+
root: 'foo.yaml',
|
|
173
|
+
output: 'output/foo.yaml',
|
|
174
|
+
},
|
|
175
|
+
};
|
|
176
|
+
const config = {
|
|
177
|
+
apis,
|
|
178
|
+
styleguide: {
|
|
179
|
+
skipPreprocessors: jest.fn(),
|
|
180
|
+
skipDecorators: jest.fn(),
|
|
181
|
+
},
|
|
182
|
+
};
|
|
183
|
+
// @ts-ignore
|
|
184
|
+
miscellaneous_1.getFallbackApisOrExit = jest.fn().mockResolvedValueOnce([{ path: 'openapi.yaml' }]);
|
|
185
|
+
openapi_core_1.getTotals.mockReturnValue({
|
|
186
|
+
errors: 0,
|
|
187
|
+
warnings: 0,
|
|
188
|
+
ignored: 0,
|
|
189
|
+
});
|
|
190
|
+
await (0, bundle_1.handleBundle)({
|
|
191
|
+
argv: { apis: ['openapi.yaml'] }, // positional
|
|
192
|
+
version: 'test',
|
|
193
|
+
config,
|
|
194
|
+
});
|
|
195
|
+
expect(miscellaneous_1.saveBundle).toBeCalledTimes(0);
|
|
196
|
+
expect(process.stdout.write).toHaveBeenCalledTimes(1);
|
|
197
|
+
});
|
|
198
|
+
});
|
|
90
199
|
});
|
|
@@ -3,18 +3,42 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
const abort_controller_1 = require("abort-controller");
|
|
4
4
|
const fetch_with_timeout_1 = require("../utils/fetch-with-timeout");
|
|
5
5
|
const node_fetch_1 = require("node-fetch");
|
|
6
|
+
const openapi_core_1 = require("@redocly/openapi-core");
|
|
7
|
+
const https_proxy_agent_1 = require("https-proxy-agent");
|
|
6
8
|
jest.mock('node-fetch');
|
|
9
|
+
jest.mock('@redocly/openapi-core');
|
|
7
10
|
describe('fetchWithTimeout', () => {
|
|
11
|
+
beforeAll(() => {
|
|
12
|
+
// @ts-ignore
|
|
13
|
+
global.setTimeout = jest.fn();
|
|
14
|
+
global.clearTimeout = jest.fn();
|
|
15
|
+
});
|
|
16
|
+
beforeEach(() => {
|
|
17
|
+
openapi_core_1.getProxyAgent.mockReturnValueOnce(undefined);
|
|
18
|
+
});
|
|
8
19
|
afterEach(() => {
|
|
9
20
|
jest.clearAllMocks();
|
|
10
21
|
});
|
|
11
22
|
it('should call node-fetch with signal', async () => {
|
|
12
|
-
|
|
13
|
-
global.setTimeout = jest.fn();
|
|
14
|
-
global.clearTimeout = jest.fn();
|
|
15
|
-
await (0, fetch_with_timeout_1.default)('url');
|
|
23
|
+
await (0, fetch_with_timeout_1.default)('url', { timeout: 1000 });
|
|
16
24
|
expect(global.setTimeout).toHaveBeenCalledTimes(1);
|
|
17
|
-
expect(node_fetch_1.default).toHaveBeenCalledWith('url', {
|
|
25
|
+
expect(node_fetch_1.default).toHaveBeenCalledWith('url', {
|
|
26
|
+
signal: new abort_controller_1.default().signal,
|
|
27
|
+
agent: undefined,
|
|
28
|
+
});
|
|
18
29
|
expect(global.clearTimeout).toHaveBeenCalledTimes(1);
|
|
19
30
|
});
|
|
31
|
+
it('should call node-fetch with proxy agent', async () => {
|
|
32
|
+
openapi_core_1.getProxyAgent.mockRestore();
|
|
33
|
+
const proxyAgent = new https_proxy_agent_1.HttpsProxyAgent('http://localhost');
|
|
34
|
+
openapi_core_1.getProxyAgent.mockReturnValueOnce(proxyAgent);
|
|
35
|
+
await (0, fetch_with_timeout_1.default)('url');
|
|
36
|
+
expect(node_fetch_1.default).toHaveBeenCalledWith('url', { agent: proxyAgent });
|
|
37
|
+
});
|
|
38
|
+
it('should call node-fetch without signal when timeout is not passed', async () => {
|
|
39
|
+
await (0, fetch_with_timeout_1.default)('url');
|
|
40
|
+
expect(global.setTimeout).not.toHaveBeenCalled();
|
|
41
|
+
expect(node_fetch_1.default).toHaveBeenCalledWith('url', { agent: undefined });
|
|
42
|
+
expect(global.clearTimeout).not.toHaveBeenCalled();
|
|
43
|
+
});
|
|
20
44
|
});
|
|
@@ -46,19 +46,6 @@ describe('pathToFilename', () => {
|
|
|
46
46
|
expect(processedPath).toEqual('user_createWithList');
|
|
47
47
|
});
|
|
48
48
|
});
|
|
49
|
-
describe('getFallbackApisOrExit', () => {
|
|
50
|
-
it('should find alias by filename', async () => {
|
|
51
|
-
fs_1.existsSync.mockImplementationOnce(() => true);
|
|
52
|
-
const entry = await (0, miscellaneous_1.getFallbackApisOrExit)(['./test.yaml'], {
|
|
53
|
-
apis: {
|
|
54
|
-
main: {
|
|
55
|
-
root: 'test.yaml',
|
|
56
|
-
},
|
|
57
|
-
},
|
|
58
|
-
});
|
|
59
|
-
expect(entry).toEqual([{ path: './test.yaml', alias: 'main' }]);
|
|
60
|
-
});
|
|
61
|
-
});
|
|
62
49
|
describe('printConfigLintTotals', () => {
|
|
63
50
|
const totalProblemsMock = {
|
|
64
51
|
errors: 1,
|
|
@@ -143,6 +130,7 @@ describe('getFallbackApisOrExit', () => {
|
|
|
143
130
|
{
|
|
144
131
|
alias: 'main',
|
|
145
132
|
path: 'someFile.yaml',
|
|
133
|
+
output: undefined,
|
|
146
134
|
},
|
|
147
135
|
]);
|
|
148
136
|
});
|
|
@@ -215,6 +203,40 @@ describe('getFallbackApisOrExit', () => {
|
|
|
215
203
|
{
|
|
216
204
|
alias: 'main',
|
|
217
205
|
path: 'https://someLinkt/petstore.yaml?main',
|
|
206
|
+
output: undefined,
|
|
207
|
+
},
|
|
208
|
+
]);
|
|
209
|
+
openapi_core_1.isAbsoluteUrl.mockReset();
|
|
210
|
+
});
|
|
211
|
+
it('should find alias by filename', async () => {
|
|
212
|
+
fs_1.existsSync.mockImplementationOnce(() => true);
|
|
213
|
+
const entry = await (0, miscellaneous_1.getFallbackApisOrExit)(['./test.yaml'], {
|
|
214
|
+
apis: {
|
|
215
|
+
main: {
|
|
216
|
+
root: 'test.yaml',
|
|
217
|
+
styleguide: {},
|
|
218
|
+
},
|
|
219
|
+
},
|
|
220
|
+
});
|
|
221
|
+
expect(entry).toEqual([{ path: './test.yaml', alias: 'main' }]);
|
|
222
|
+
});
|
|
223
|
+
it('should return apis from config with paths and outputs resolved relatively to the config location', async () => {
|
|
224
|
+
fs_1.existsSync.mockImplementationOnce(() => true);
|
|
225
|
+
const entry = await (0, miscellaneous_1.getFallbackApisOrExit)(undefined, {
|
|
226
|
+
apis: {
|
|
227
|
+
main: {
|
|
228
|
+
root: 'test.yaml',
|
|
229
|
+
output: 'output/test.yaml',
|
|
230
|
+
styleguide: {},
|
|
231
|
+
},
|
|
232
|
+
},
|
|
233
|
+
configFile: 'project-folder/redocly.yaml',
|
|
234
|
+
});
|
|
235
|
+
expect(entry).toEqual([
|
|
236
|
+
{
|
|
237
|
+
path: expect.stringMatching(/project\-folder\/test\.yaml$/),
|
|
238
|
+
output: expect.stringMatching(/project\-folder\/output\/test\.yaml$/),
|
|
239
|
+
alias: 'main',
|
|
218
240
|
},
|
|
219
241
|
]);
|
|
220
242
|
});
|
|
@@ -487,24 +509,24 @@ describe('cleanRawInput', () => {
|
|
|
487
509
|
expect(stderrMock).toHaveBeenCalledWith(`Unsupported file extension: xml. Using yaml.\n`);
|
|
488
510
|
});
|
|
489
511
|
});
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
512
|
+
});
|
|
513
|
+
describe('writeToFileByExtension', () => {
|
|
514
|
+
beforeEach(() => {
|
|
515
|
+
jest.spyOn(process.stderr, 'write').mockImplementation(jest.fn());
|
|
516
|
+
colorette_1.yellow.mockImplementation((text) => text);
|
|
517
|
+
});
|
|
518
|
+
afterEach(() => {
|
|
519
|
+
jest.restoreAllMocks();
|
|
520
|
+
});
|
|
521
|
+
it('should call stringifyYaml function', () => {
|
|
522
|
+
(0, miscellaneous_1.writeToFileByExtension)('test data', 'test.yaml');
|
|
523
|
+
expect(openapi_core_1.stringifyYaml).toHaveBeenCalledWith('test data', { noRefs: false });
|
|
524
|
+
expect(process.stderr.write).toHaveBeenCalledWith(`test data`);
|
|
525
|
+
});
|
|
526
|
+
it('should call JSON.stringify function', () => {
|
|
527
|
+
const stringifySpy = jest.spyOn(JSON, 'stringify').mockImplementation((data) => data);
|
|
528
|
+
(0, miscellaneous_1.writeToFileByExtension)('test data', 'test.json');
|
|
529
|
+
expect(stringifySpy).toHaveBeenCalledWith('test data', null, 2);
|
|
530
|
+
expect(process.stderr.write).toHaveBeenCalledWith(`test data`);
|
|
509
531
|
});
|
|
510
532
|
});
|
|
@@ -14,10 +14,13 @@ describe('ApiClient', () => {
|
|
|
14
14
|
const testDomain = 'test-domain.com';
|
|
15
15
|
const testOrg = 'test-org';
|
|
16
16
|
const testProject = 'test-project';
|
|
17
|
+
const version = '1.0.0';
|
|
18
|
+
const command = 'push';
|
|
19
|
+
const expectedUserAgent = `redocly-cli/${version} ${command}`;
|
|
17
20
|
describe('getDefaultBranch()', () => {
|
|
18
21
|
let apiClient;
|
|
19
22
|
beforeEach(() => {
|
|
20
|
-
apiClient = new api_client_1.ReuniteApiClient(testDomain, testToken);
|
|
23
|
+
apiClient = new api_client_1.ReuniteApiClient({ domain: testDomain, apiKey: testToken, version, command });
|
|
21
24
|
});
|
|
22
25
|
it('should get default project branch', async () => {
|
|
23
26
|
mockFetchResponse({
|
|
@@ -32,6 +35,7 @@ describe('ApiClient', () => {
|
|
|
32
35
|
headers: {
|
|
33
36
|
'Content-Type': 'application/json',
|
|
34
37
|
Authorization: `Bearer ${testToken}`,
|
|
38
|
+
'user-agent': expectedUserAgent,
|
|
35
39
|
},
|
|
36
40
|
signal: expect.any(Object),
|
|
37
41
|
});
|
|
@@ -48,7 +52,7 @@ describe('ApiClient', () => {
|
|
|
48
52
|
object: 'problem',
|
|
49
53
|
}),
|
|
50
54
|
});
|
|
51
|
-
await expect(apiClient.remotes.getDefaultBranch(testOrg, testProject)).rejects.toThrow(new
|
|
55
|
+
await expect(apiClient.remotes.getDefaultBranch(testOrg, testProject)).rejects.toThrow(new api_client_1.ReuniteApiError('Failed to fetch default branch. Project source not found.', 404));
|
|
52
56
|
});
|
|
53
57
|
it('should throw statusText error if response is not ok', async () => {
|
|
54
58
|
mockFetchResponse({
|
|
@@ -58,7 +62,7 @@ describe('ApiClient', () => {
|
|
|
58
62
|
unknownField: 'unknown-error',
|
|
59
63
|
}),
|
|
60
64
|
});
|
|
61
|
-
await expect(apiClient.remotes.getDefaultBranch(testOrg, testProject)).rejects.toThrow(new
|
|
65
|
+
await expect(apiClient.remotes.getDefaultBranch(testOrg, testProject)).rejects.toThrow(new api_client_1.ReuniteApiError('Failed to fetch default branch. Not found.', 404));
|
|
62
66
|
});
|
|
63
67
|
});
|
|
64
68
|
describe('upsert()', () => {
|
|
@@ -68,7 +72,7 @@ describe('ApiClient', () => {
|
|
|
68
72
|
};
|
|
69
73
|
let apiClient;
|
|
70
74
|
beforeEach(() => {
|
|
71
|
-
apiClient = new api_client_1.ReuniteApiClient(testDomain, testToken);
|
|
75
|
+
apiClient = new api_client_1.ReuniteApiClient({ domain: testDomain, apiKey: testToken, version, command });
|
|
72
76
|
});
|
|
73
77
|
it('should upsert remote', async () => {
|
|
74
78
|
const responseMock = {
|
|
@@ -89,6 +93,7 @@ describe('ApiClient', () => {
|
|
|
89
93
|
headers: {
|
|
90
94
|
'Content-Type': 'application/json',
|
|
91
95
|
Authorization: `Bearer ${testToken}`,
|
|
96
|
+
'user-agent': expectedUserAgent,
|
|
92
97
|
},
|
|
93
98
|
body: JSON.stringify({
|
|
94
99
|
mountPath: remotePayload.mountPath,
|
|
@@ -112,17 +117,18 @@ describe('ApiClient', () => {
|
|
|
112
117
|
object: 'problem',
|
|
113
118
|
}),
|
|
114
119
|
});
|
|
115
|
-
await expect(apiClient.remotes.upsert(testOrg, testProject, remotePayload)).rejects.toThrow(new
|
|
120
|
+
await expect(apiClient.remotes.upsert(testOrg, testProject, remotePayload)).rejects.toThrow(new api_client_1.ReuniteApiError('Failed to upsert remote. Not allowed to mount remote outside of project content path: /docs.', 403));
|
|
116
121
|
});
|
|
117
122
|
it('should throw statusText error if response is not ok', async () => {
|
|
118
123
|
mockFetchResponse({
|
|
119
124
|
ok: false,
|
|
125
|
+
status: 404,
|
|
120
126
|
statusText: 'Not found',
|
|
121
127
|
json: jest.fn().mockResolvedValue({
|
|
122
128
|
unknownField: 'unknown-error',
|
|
123
129
|
}),
|
|
124
130
|
});
|
|
125
|
-
await expect(apiClient.remotes.upsert(testOrg, testProject, remotePayload)).rejects.toThrow(new
|
|
131
|
+
await expect(apiClient.remotes.upsert(testOrg, testProject, remotePayload)).rejects.toThrow(new api_client_1.ReuniteApiError('Failed to upsert remote. Not found.', 404));
|
|
126
132
|
});
|
|
127
133
|
});
|
|
128
134
|
describe('push()', () => {
|
|
@@ -156,7 +162,7 @@ describe('ApiClient', () => {
|
|
|
156
162
|
};
|
|
157
163
|
let apiClient;
|
|
158
164
|
beforeEach(() => {
|
|
159
|
-
apiClient = new api_client_1.ReuniteApiClient(testDomain, testToken);
|
|
165
|
+
apiClient = new api_client_1.ReuniteApiClient({ domain: testDomain, apiKey: testToken, version, command });
|
|
160
166
|
});
|
|
161
167
|
it('should push to remote', async () => {
|
|
162
168
|
let passedFormData = new FormData();
|
|
@@ -179,6 +185,7 @@ describe('ApiClient', () => {
|
|
|
179
185
|
method: 'POST',
|
|
180
186
|
headers: {
|
|
181
187
|
Authorization: `Bearer ${testToken}`,
|
|
188
|
+
'user-agent': expectedUserAgent,
|
|
182
189
|
},
|
|
183
190
|
}));
|
|
184
191
|
expect(JSON.stringify(passedFormData).replace(new RegExp(passedFormData.getBoundary(), 'g'), '')).toEqual(JSON.stringify(formData).replace(new RegExp(formData.getBoundary(), 'g'), ''));
|
|
@@ -195,17 +202,18 @@ describe('ApiClient', () => {
|
|
|
195
202
|
object: 'problem',
|
|
196
203
|
}),
|
|
197
204
|
});
|
|
198
|
-
await expect(apiClient.remotes.push(testOrg, testProject, pushPayload, filesMock)).rejects.toThrow(new
|
|
205
|
+
await expect(apiClient.remotes.push(testOrg, testProject, pushPayload, filesMock)).rejects.toThrow(new api_client_1.ReuniteApiError('Failed to push. Cannot push to remote.', 403));
|
|
199
206
|
});
|
|
200
207
|
it('should throw statusText error if response is not ok', async () => {
|
|
201
208
|
mockFetchResponse({
|
|
202
209
|
ok: false,
|
|
210
|
+
status: 404,
|
|
203
211
|
statusText: 'Not found',
|
|
204
212
|
json: jest.fn().mockResolvedValue({
|
|
205
213
|
unknownField: 'unknown-error',
|
|
206
214
|
}),
|
|
207
215
|
});
|
|
208
|
-
await expect(apiClient.remotes.push(testOrg, testProject, pushPayload, filesMock)).rejects.toThrow(new
|
|
216
|
+
await expect(apiClient.remotes.push(testOrg, testProject, pushPayload, filesMock)).rejects.toThrow(new api_client_1.ReuniteApiError('Failed to push. Not found.', 404));
|
|
209
217
|
});
|
|
210
218
|
});
|
|
211
219
|
});
|
|
@@ -1,10 +1,22 @@
|
|
|
1
|
+
import { type FetchWithTimeoutOptions } from '../../utils/fetch-with-timeout';
|
|
2
|
+
import type { Response } from 'node-fetch';
|
|
1
3
|
import type { ReadStream } from 'fs';
|
|
2
4
|
import type { ListRemotesResponse, PushResponse, UpsertRemoteResponse } from './types';
|
|
3
|
-
declare class
|
|
5
|
+
export declare class ReuniteApiError extends Error {
|
|
6
|
+
status: number;
|
|
7
|
+
constructor(message: string, status: number);
|
|
8
|
+
}
|
|
9
|
+
declare class ReuniteBaseApiClient {
|
|
10
|
+
protected version: string;
|
|
11
|
+
protected command: string;
|
|
12
|
+
constructor(version: string, command: string);
|
|
13
|
+
protected getParsedResponse<T>(response: Response): Promise<T>;
|
|
14
|
+
protected request(url: string, options: FetchWithTimeoutOptions): Promise<Response>;
|
|
15
|
+
}
|
|
16
|
+
declare class RemotesApiClient extends ReuniteBaseApiClient {
|
|
4
17
|
private readonly domain;
|
|
5
18
|
private readonly apiKey;
|
|
6
|
-
constructor(domain: string, apiKey: string);
|
|
7
|
-
private getParsedResponse;
|
|
19
|
+
constructor(domain: string, apiKey: string, version: string, command: string);
|
|
8
20
|
getDefaultBranch(organizationId: string, projectId: string): Promise<string>;
|
|
9
21
|
upsert(organizationId: string, projectId: string, remote: {
|
|
10
22
|
mountPath: string;
|
|
@@ -14,7 +26,11 @@ declare class RemotesApiClient {
|
|
|
14
26
|
path: string;
|
|
15
27
|
stream: ReadStream | Buffer;
|
|
16
28
|
}[]): Promise<PushResponse>;
|
|
17
|
-
getRemotesList(organizationId
|
|
29
|
+
getRemotesList({ organizationId, projectId, mountPath, }: {
|
|
30
|
+
organizationId: string;
|
|
31
|
+
projectId: string;
|
|
32
|
+
mountPath: string;
|
|
33
|
+
}): Promise<ListRemotesResponse>;
|
|
18
34
|
getPush({ organizationId, projectId, pushId, }: {
|
|
19
35
|
organizationId: string;
|
|
20
36
|
projectId: string;
|
|
@@ -22,10 +38,13 @@ declare class RemotesApiClient {
|
|
|
22
38
|
}): Promise<PushResponse>;
|
|
23
39
|
}
|
|
24
40
|
export declare class ReuniteApiClient {
|
|
25
|
-
domain: string;
|
|
26
|
-
private readonly apiKey;
|
|
27
41
|
remotes: RemotesApiClient;
|
|
28
|
-
constructor(domain
|
|
42
|
+
constructor({ domain, apiKey, version, command, }: {
|
|
43
|
+
domain: string;
|
|
44
|
+
apiKey: string;
|
|
45
|
+
version: string;
|
|
46
|
+
command: 'push' | 'push-status';
|
|
47
|
+
});
|
|
29
48
|
}
|
|
30
49
|
export type PushPayload = {
|
|
31
50
|
remoteId: string;
|