@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
|
@@ -9,7 +9,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
|
|
|
9
9
|
});
|
|
10
10
|
};
|
|
11
11
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
|
-
const
|
|
12
|
+
const miscellaneous_1 = require("../utils/miscellaneous");
|
|
13
13
|
const openapi_core_1 = require("@redocly/openapi-core");
|
|
14
14
|
const colorette_1 = require("colorette");
|
|
15
15
|
const fs_1 = require("fs");
|
|
@@ -31,7 +31,7 @@ describe('isSubdir', () => {
|
|
|
31
31
|
['/foo', './bar', false],
|
|
32
32
|
['/foo', '/foo/..bar', true],
|
|
33
33
|
].forEach(([parent, child, expectRes]) => {
|
|
34
|
-
expect((0,
|
|
34
|
+
expect((0, miscellaneous_1.isSubdir)(parent, child)).toBe(expectRes);
|
|
35
35
|
});
|
|
36
36
|
});
|
|
37
37
|
it('can correctly determine if subdir for windows-based paths', () => {
|
|
@@ -42,7 +42,7 @@ describe('isSubdir', () => {
|
|
|
42
42
|
['C:\\Foo', 'C:\\Bar', false],
|
|
43
43
|
['C:\\Foo', 'D:\\Foo\\Bar', false],
|
|
44
44
|
].forEach(([parent, child, expectRes]) => {
|
|
45
|
-
expect((0,
|
|
45
|
+
expect((0, miscellaneous_1.isSubdir)(parent, child)).toBe(expectRes);
|
|
46
46
|
});
|
|
47
47
|
});
|
|
48
48
|
afterEach(() => {
|
|
@@ -51,14 +51,14 @@ describe('isSubdir', () => {
|
|
|
51
51
|
});
|
|
52
52
|
describe('pathToFilename', () => {
|
|
53
53
|
it('should use correct path separator', () => {
|
|
54
|
-
const processedPath = (0,
|
|
54
|
+
const processedPath = (0, miscellaneous_1.pathToFilename)('/user/createWithList', '_');
|
|
55
55
|
expect(processedPath).toEqual('user_createWithList');
|
|
56
56
|
});
|
|
57
57
|
});
|
|
58
58
|
describe('getFallbackApisOrExit', () => {
|
|
59
59
|
it('should find alias by filename', () => __awaiter(void 0, void 0, void 0, function* () {
|
|
60
60
|
fs_1.existsSync.mockImplementationOnce(() => true);
|
|
61
|
-
const entry = yield (0,
|
|
61
|
+
const entry = yield (0, miscellaneous_1.getFallbackApisOrExit)(['./test.yaml'], {
|
|
62
62
|
apis: {
|
|
63
63
|
main: {
|
|
64
64
|
root: 'test.yaml',
|
|
@@ -82,17 +82,17 @@ describe('printConfigLintTotals', () => {
|
|
|
82
82
|
jest.spyOn(process.stderr, 'write').mockImplementation(() => true);
|
|
83
83
|
});
|
|
84
84
|
it('should print errors if such exist', () => {
|
|
85
|
-
(0,
|
|
85
|
+
(0, miscellaneous_1.printConfigLintTotals)(totalProblemsMock);
|
|
86
86
|
expect(process.stderr.write).toHaveBeenCalledWith('❌ Your config has 1 error.');
|
|
87
87
|
expect(redColoretteMocks).toHaveBeenCalledWith('❌ Your config has 1 error.');
|
|
88
88
|
});
|
|
89
89
|
it('should print warnign if no error', () => {
|
|
90
|
-
(0,
|
|
90
|
+
(0, miscellaneous_1.printConfigLintTotals)(Object.assign(Object.assign({}, totalProblemsMock), { errors: 0, warnings: 2 }));
|
|
91
91
|
expect(process.stderr.write).toHaveBeenCalledWith('⚠️ Your config has 2 warnings.\n');
|
|
92
92
|
expect(yellowColoretteMocks).toHaveBeenCalledWith('⚠️ Your config has 2 warnings.\n');
|
|
93
93
|
});
|
|
94
94
|
it('should print nothing if no error and no warnings', () => {
|
|
95
|
-
const result = (0,
|
|
95
|
+
const result = (0, miscellaneous_1.printConfigLintTotals)(Object.assign(Object.assign({}, totalProblemsMock), { errors: 0 }));
|
|
96
96
|
expect(result).toBeUndefined();
|
|
97
97
|
expect(process.stderr.write).toHaveBeenCalledTimes(0);
|
|
98
98
|
expect(yellowColoretteMocks).toHaveBeenCalledTimes(0);
|
|
@@ -124,7 +124,7 @@ describe('getFallbackApisOrExit', () => {
|
|
|
124
124
|
};
|
|
125
125
|
expect.assertions(1);
|
|
126
126
|
try {
|
|
127
|
-
yield (0,
|
|
127
|
+
yield (0, miscellaneous_1.getFallbackApisOrExit)([''], apisConfig);
|
|
128
128
|
}
|
|
129
129
|
catch (e) {
|
|
130
130
|
expect(e.message).toEqual('Path cannot be empty.');
|
|
@@ -134,7 +134,7 @@ describe('getFallbackApisOrExit', () => {
|
|
|
134
134
|
fs_1.existsSync.mockImplementationOnce(() => false);
|
|
135
135
|
expect.assertions(3);
|
|
136
136
|
try {
|
|
137
|
-
yield (0,
|
|
137
|
+
yield (0, miscellaneous_1.getFallbackApisOrExit)(undefined, config);
|
|
138
138
|
}
|
|
139
139
|
catch (e) {
|
|
140
140
|
expect(process.stderr.write).toHaveBeenCalledWith('\nsomeFile.yaml does not exist or is invalid.\n\n');
|
|
@@ -145,7 +145,7 @@ describe('getFallbackApisOrExit', () => {
|
|
|
145
145
|
it('should return valid array with results if such file exist', () => __awaiter(void 0, void 0, void 0, function* () {
|
|
146
146
|
fs_1.existsSync.mockImplementationOnce(() => true);
|
|
147
147
|
jest.spyOn(path, 'resolve').mockImplementationOnce((_, path) => path);
|
|
148
|
-
const result = yield (0,
|
|
148
|
+
const result = yield (0, miscellaneous_1.getFallbackApisOrExit)(undefined, config);
|
|
149
149
|
expect(process.stderr.write).toHaveBeenCalledTimes(0);
|
|
150
150
|
expect(process.exit).toHaveBeenCalledTimes(0);
|
|
151
151
|
expect(result).toStrictEqual([
|
|
@@ -162,7 +162,7 @@ describe('getFallbackApisOrExit', () => {
|
|
|
162
162
|
fs_1.existsSync.mockImplementationOnce(() => false);
|
|
163
163
|
expect.assertions(3);
|
|
164
164
|
try {
|
|
165
|
-
yield (0,
|
|
165
|
+
yield (0, miscellaneous_1.getFallbackApisOrExit)(['someFile.yaml'], apisConfig);
|
|
166
166
|
}
|
|
167
167
|
catch (e) {
|
|
168
168
|
expect(process.stderr.write).toHaveBeenCalledWith('\nsomeFile.yaml does not exist or is invalid.\n\n');
|
|
@@ -177,7 +177,7 @@ describe('getFallbackApisOrExit', () => {
|
|
|
177
177
|
fs_1.existsSync.mockImplementationOnce(() => false);
|
|
178
178
|
expect.assertions(3);
|
|
179
179
|
try {
|
|
180
|
-
yield (0,
|
|
180
|
+
yield (0, miscellaneous_1.getFallbackApisOrExit)(['someFile.yaml', 'someFile2.yaml'], apisConfig);
|
|
181
181
|
}
|
|
182
182
|
catch (e) {
|
|
183
183
|
expect(process.stderr.write).toHaveBeenCalledWith('\nsomeFile.yaml does not exist or is invalid.\n\n');
|
|
@@ -194,7 +194,7 @@ describe('getFallbackApisOrExit', () => {
|
|
|
194
194
|
const existSyncMock = fs_1.existsSync.mockImplementation((path) => path.endsWith('someFile.yaml'));
|
|
195
195
|
expect.assertions(4);
|
|
196
196
|
try {
|
|
197
|
-
yield (0,
|
|
197
|
+
yield (0, miscellaneous_1.getFallbackApisOrExit)(undefined, configStub);
|
|
198
198
|
}
|
|
199
199
|
catch (e) {
|
|
200
200
|
expect(process.stderr.write).toHaveBeenCalledWith('\nnotExist.yaml does not exist or is invalid.\n\n');
|
|
@@ -215,7 +215,7 @@ describe('getFallbackApisOrExit', () => {
|
|
|
215
215
|
},
|
|
216
216
|
},
|
|
217
217
|
};
|
|
218
|
-
const result = yield (0,
|
|
218
|
+
const result = yield (0, miscellaneous_1.getFallbackApisOrExit)(undefined, apisConfig);
|
|
219
219
|
expect(process.stderr.write).toHaveBeenCalledTimes(0);
|
|
220
220
|
expect(result).toStrictEqual([
|
|
221
221
|
{
|
|
@@ -235,11 +235,29 @@ describe('langToExt', () => {
|
|
|
235
235
|
['javascript', '.js'],
|
|
236
236
|
['js', '.js'],
|
|
237
237
|
['python', '.py'],
|
|
238
|
+
['c', '.c'],
|
|
239
|
+
['c++', '.cpp'],
|
|
240
|
+
['coffeescript', '.litcoffee'],
|
|
241
|
+
['dart', '.dart'],
|
|
242
|
+
['elixir', '.ex'],
|
|
243
|
+
['go', '.go'],
|
|
244
|
+
['groovy', '.groovy'],
|
|
245
|
+
['java', '.java'],
|
|
246
|
+
['kotlin', '.kt'],
|
|
247
|
+
['objective-c', '.m'],
|
|
248
|
+
['perl', '.pl'],
|
|
249
|
+
['powershell', '.ps1'],
|
|
250
|
+
['ruby', '.rb'],
|
|
251
|
+
['rust', '.rs'],
|
|
252
|
+
['scala', '.sc'],
|
|
253
|
+
['swift', '.swift'],
|
|
254
|
+
['typescript', '.ts'],
|
|
255
|
+
['tsx', '.tsx'],
|
|
238
256
|
])('should infer file extension from lang - %s', (lang, expected) => {
|
|
239
|
-
expect((0,
|
|
257
|
+
expect((0, miscellaneous_1.langToExt)(lang)).toBe(expected);
|
|
240
258
|
});
|
|
241
259
|
it('should ignore case when inferring file extension', () => {
|
|
242
|
-
expect((0,
|
|
260
|
+
expect((0, miscellaneous_1.langToExt)('JavaScript')).toBe('.js');
|
|
243
261
|
});
|
|
244
262
|
});
|
|
245
263
|
describe('sorTopLevelKeysForOas', () => {
|
|
@@ -270,7 +288,7 @@ describe('sorTopLevelKeysForOas', () => {
|
|
|
270
288
|
'x-webhooks',
|
|
271
289
|
'components',
|
|
272
290
|
];
|
|
273
|
-
const result = (0,
|
|
291
|
+
const result = (0, miscellaneous_1.sortTopLevelKeysForOas)(openApi);
|
|
274
292
|
Object.keys(result).forEach((key, index) => {
|
|
275
293
|
expect(key).toEqual(orderedKeys[index]);
|
|
276
294
|
});
|
|
@@ -310,7 +328,7 @@ describe('sorTopLevelKeysForOas', () => {
|
|
|
310
328
|
'responses',
|
|
311
329
|
'securityDefinitions',
|
|
312
330
|
];
|
|
313
|
-
const result = (0,
|
|
331
|
+
const result = (0, miscellaneous_1.sortTopLevelKeysForOas)(openApi);
|
|
314
332
|
Object.keys(result).forEach((key, index) => {
|
|
315
333
|
expect(key).toEqual(orderedKeys[index]);
|
|
316
334
|
});
|
|
@@ -331,31 +349,31 @@ describe('handleErrors', () => {
|
|
|
331
349
|
});
|
|
332
350
|
it('should handle ResolveError', () => {
|
|
333
351
|
const resolveError = new openapi_core_1.ResolveError(new Error('File not found'));
|
|
334
|
-
expect(() => (0,
|
|
352
|
+
expect(() => (0, miscellaneous_1.handleError)(resolveError, ref)).toThrowError(miscellaneous_1.HandledError);
|
|
335
353
|
expect(redColoretteMocks).toHaveBeenCalledTimes(1);
|
|
336
354
|
expect(process.stderr.write).toHaveBeenCalledWith(`Failed to resolve API description at openapi/test.yaml:\n\n - File not found.\n\n`);
|
|
337
355
|
});
|
|
338
356
|
it('should handle YamlParseError', () => {
|
|
339
357
|
const yamlParseError = new openapi_core_1.YamlParseError(new Error('Invalid yaml'), {});
|
|
340
|
-
expect(() => (0,
|
|
358
|
+
expect(() => (0, miscellaneous_1.handleError)(yamlParseError, ref)).toThrowError(miscellaneous_1.HandledError);
|
|
341
359
|
expect(redColoretteMocks).toHaveBeenCalledTimes(1);
|
|
342
360
|
expect(process.stderr.write).toHaveBeenCalledWith(`Failed to parse API description at openapi/test.yaml:\n\n - Invalid yaml.\n\n`);
|
|
343
361
|
});
|
|
344
362
|
it('should handle CircularJSONNotSupportedError', () => {
|
|
345
|
-
const circularError = new
|
|
346
|
-
expect(() => (0,
|
|
363
|
+
const circularError = new miscellaneous_1.CircularJSONNotSupportedError(new Error('Circular json'));
|
|
364
|
+
expect(() => (0, miscellaneous_1.handleError)(circularError, ref)).toThrowError(miscellaneous_1.HandledError);
|
|
347
365
|
expect(process.stderr.write).toHaveBeenCalledWith(`Detected circular reference which can't be converted to JSON.\n` +
|
|
348
366
|
`Try to use ${(0, colorette_1.blue)('yaml')} output or remove ${(0, colorette_1.blue)('--dereferenced')}.\n\n`);
|
|
349
367
|
});
|
|
350
368
|
it('should handle SyntaxError', () => {
|
|
351
369
|
const testError = new SyntaxError('Unexpected identifier');
|
|
352
370
|
testError.stack = 'test stack';
|
|
353
|
-
expect(() => (0,
|
|
371
|
+
expect(() => (0, miscellaneous_1.handleError)(testError, ref)).toThrowError(miscellaneous_1.HandledError);
|
|
354
372
|
expect(process.stderr.write).toHaveBeenCalledWith('Syntax error: Unexpected identifier test stack\n\n');
|
|
355
373
|
});
|
|
356
374
|
it('should throw unknown error', () => {
|
|
357
375
|
const testError = new Error('Test error');
|
|
358
|
-
expect(() => (0,
|
|
376
|
+
expect(() => (0, miscellaneous_1.handleError)(testError, ref)).toThrowError(miscellaneous_1.HandledError);
|
|
359
377
|
expect(process.stderr.write).toHaveBeenCalledWith(`Something went wrong when processing openapi/test.yaml:\n\n - Test error.\n\n`);
|
|
360
378
|
});
|
|
361
379
|
});
|
|
@@ -373,7 +391,7 @@ describe('checkIfRulesetExist', () => {
|
|
|
373
391
|
oas3_1: {},
|
|
374
392
|
async2: {},
|
|
375
393
|
};
|
|
376
|
-
expect(() => (0,
|
|
394
|
+
expect(() => (0, miscellaneous_1.checkIfRulesetExist)(rules)).toThrowError('⚠️ No rules were configured. Learn how to configure rules: https://redocly.com/docs/cli/rules/');
|
|
377
395
|
});
|
|
378
396
|
it('should not throw an error if rules are provided', () => {
|
|
379
397
|
const rules = {
|
|
@@ -381,13 +399,13 @@ describe('checkIfRulesetExist', () => {
|
|
|
381
399
|
oas3_0: {},
|
|
382
400
|
oas3_1: {},
|
|
383
401
|
};
|
|
384
|
-
(0,
|
|
402
|
+
(0, miscellaneous_1.checkIfRulesetExist)(rules);
|
|
385
403
|
});
|
|
386
404
|
});
|
|
387
405
|
describe('cleanColors', () => {
|
|
388
406
|
it('should remove colors from string', () => {
|
|
389
407
|
const stringWithColors = `String for ${(0, colorette_1.red)('test')}`;
|
|
390
|
-
const result = (0,
|
|
408
|
+
const result = (0, miscellaneous_1.cleanColors)(stringWithColors);
|
|
391
409
|
expect(result).not.toMatch(/\x1b\[\d+m/g);
|
|
392
410
|
});
|
|
393
411
|
});
|
|
@@ -409,7 +427,7 @@ describe('cleanArgs', () => {
|
|
|
409
427
|
apis: ['main@v1', 'fixtures/openapi.yaml', 'http://some.url/openapi.yaml'],
|
|
410
428
|
format: 'codeframe',
|
|
411
429
|
};
|
|
412
|
-
expect((0,
|
|
430
|
+
expect((0, miscellaneous_1.cleanArgs)(testArgs)).toEqual({
|
|
413
431
|
config: 'file-yaml',
|
|
414
432
|
apis: ['api-name@api-version', 'file-yaml', 'http://url'],
|
|
415
433
|
format: 'codeframe',
|
|
@@ -419,7 +437,7 @@ describe('cleanArgs', () => {
|
|
|
419
437
|
const testArgs = {
|
|
420
438
|
destination: '@org/name@version',
|
|
421
439
|
};
|
|
422
|
-
expect((0,
|
|
440
|
+
expect((0, miscellaneous_1.cleanArgs)(testArgs)).toEqual({
|
|
423
441
|
destination: '@organization/api-name@api-version',
|
|
424
442
|
});
|
|
425
443
|
});
|
|
@@ -447,7 +465,7 @@ describe('cleanRawInput', () => {
|
|
|
447
465
|
'--output',
|
|
448
466
|
'fixtures',
|
|
449
467
|
];
|
|
450
|
-
expect((0,
|
|
468
|
+
expect((0, miscellaneous_1.cleanRawInput)(rawInput)).toEqual('redocly bundle api-name@api-version file-yaml http://url --config=file-yaml --output folder');
|
|
451
469
|
});
|
|
452
470
|
it('should preserve safe data from raw CLI input', () => {
|
|
453
471
|
const rawInput = [
|
|
@@ -460,16 +478,16 @@ describe('cleanRawInput', () => {
|
|
|
460
478
|
'--skip-rule',
|
|
461
479
|
'operation-4xx-response',
|
|
462
480
|
];
|
|
463
|
-
expect((0,
|
|
481
|
+
expect((0, miscellaneous_1.cleanRawInput)(rawInput)).toEqual('redocly lint file-json --format stylish --extends=minimal --skip-rule operation-4xx-response');
|
|
464
482
|
});
|
|
465
483
|
describe('validateFileExtension', () => {
|
|
466
484
|
it('should return current file extension', () => {
|
|
467
|
-
expect((0,
|
|
485
|
+
expect((0, miscellaneous_1.getAndValidateFileExtension)('test.json')).toEqual('json');
|
|
468
486
|
});
|
|
469
487
|
it('should return yaml and print warning if file extension does not supported', () => {
|
|
470
488
|
const stderrMock = jest.spyOn(process.stderr, 'write').mockImplementation(() => true);
|
|
471
489
|
colorette_1.yellow.mockImplementation((text) => text);
|
|
472
|
-
expect((0,
|
|
490
|
+
expect((0, miscellaneous_1.getAndValidateFileExtension)('test.xml')).toEqual('yaml');
|
|
473
491
|
expect(stderrMock).toHaveBeenCalledWith(`Unsupported file extension: xml. Using yaml.\n`);
|
|
474
492
|
});
|
|
475
493
|
});
|
|
@@ -482,13 +500,13 @@ describe('cleanRawInput', () => {
|
|
|
482
500
|
jest.restoreAllMocks();
|
|
483
501
|
});
|
|
484
502
|
it('should call stringifyYaml function', () => {
|
|
485
|
-
(0,
|
|
503
|
+
(0, miscellaneous_1.writeToFileByExtension)('test data', 'test.yaml');
|
|
486
504
|
expect(openapi_core_1.stringifyYaml).toHaveBeenCalledWith('test data', { noRefs: false });
|
|
487
505
|
expect(process.stderr.write).toHaveBeenCalledWith(`test data`);
|
|
488
506
|
});
|
|
489
507
|
it('should call JSON.stringify function', () => {
|
|
490
508
|
const stringifySpy = jest.spyOn(JSON, 'stringify').mockImplementation((data) => data);
|
|
491
|
-
(0,
|
|
509
|
+
(0, miscellaneous_1.writeToFileByExtension)('test data', 'test.json');
|
|
492
510
|
expect(stringifySpy).toHaveBeenCalledWith('test data', null, 2);
|
|
493
511
|
expect(process.stderr.write).toHaveBeenCalledWith(`test data`);
|
|
494
512
|
});
|
|
@@ -9,13 +9,13 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
|
|
|
9
9
|
});
|
|
10
10
|
};
|
|
11
11
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
|
-
const
|
|
12
|
+
const miscellaneous_1 = require("../utils/miscellaneous");
|
|
13
13
|
const process = require("process");
|
|
14
14
|
const wrapper_1 = require("../wrapper");
|
|
15
15
|
const lint_1 = require("../commands/lint");
|
|
16
16
|
const push_1 = require("../commands/push");
|
|
17
17
|
jest.mock('node-fetch');
|
|
18
|
-
jest.mock('../utils', () => ({
|
|
18
|
+
jest.mock('../utils/miscellaneous', () => ({
|
|
19
19
|
sendTelemetry: jest.fn(),
|
|
20
20
|
loadConfigAndHandleErrors: jest.fn(),
|
|
21
21
|
}));
|
|
@@ -25,29 +25,29 @@ jest.mock('../commands/lint', () => ({
|
|
|
25
25
|
}));
|
|
26
26
|
describe('commandWrapper', () => {
|
|
27
27
|
it('should send telemetry if there is "telemetry: on" in the config', () => __awaiter(void 0, void 0, void 0, function* () {
|
|
28
|
-
|
|
28
|
+
miscellaneous_1.loadConfigAndHandleErrors.mockImplementation(() => {
|
|
29
29
|
return { telemetry: 'on', styleguide: { recommendedFallback: true } };
|
|
30
30
|
});
|
|
31
31
|
process.env.REDOCLY_TELEMETRY = 'on';
|
|
32
32
|
const wrappedHandler = (0, wrapper_1.commandWrapper)(lint_1.handleLint);
|
|
33
33
|
yield wrappedHandler({});
|
|
34
34
|
expect(lint_1.handleLint).toHaveBeenCalledTimes(1);
|
|
35
|
-
expect(
|
|
36
|
-
expect(
|
|
35
|
+
expect(miscellaneous_1.sendTelemetry).toHaveBeenCalledTimes(1);
|
|
36
|
+
expect(miscellaneous_1.sendTelemetry).toHaveBeenCalledWith({}, 0, false);
|
|
37
37
|
}));
|
|
38
38
|
it('should NOT send telemetry if there is "telemetry: off" in the config', () => __awaiter(void 0, void 0, void 0, function* () {
|
|
39
|
-
|
|
39
|
+
miscellaneous_1.loadConfigAndHandleErrors.mockImplementation(() => {
|
|
40
40
|
return { telemetry: 'off', styleguide: { recommendedFallback: true } };
|
|
41
41
|
});
|
|
42
42
|
process.env.REDOCLY_TELEMETRY = 'on';
|
|
43
43
|
const wrappedHandler = (0, wrapper_1.commandWrapper)(lint_1.handleLint);
|
|
44
44
|
yield wrappedHandler({});
|
|
45
45
|
expect(lint_1.handleLint).toHaveBeenCalledTimes(1);
|
|
46
|
-
expect(
|
|
46
|
+
expect(miscellaneous_1.sendTelemetry).toHaveBeenCalledTimes(0);
|
|
47
47
|
}));
|
|
48
48
|
it('should pass files from arguments to config', () => __awaiter(void 0, void 0, void 0, function* () {
|
|
49
49
|
const filesToPush = ['test1.yaml', 'test2.yaml'];
|
|
50
|
-
const loadConfigMock =
|
|
50
|
+
const loadConfigMock = miscellaneous_1.loadConfigAndHandleErrors;
|
|
51
51
|
const argv = {
|
|
52
52
|
files: filesToPush,
|
|
53
53
|
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const api_keys_1 = require("../api-keys");
|
|
4
|
+
const fs = require("fs");
|
|
5
|
+
describe('getApiKeys()', () => {
|
|
6
|
+
afterEach(() => {
|
|
7
|
+
jest.resetAllMocks();
|
|
8
|
+
});
|
|
9
|
+
it('should return api key from environment variable', () => {
|
|
10
|
+
process.env.REDOCLY_AUTHORIZATION = 'test-api-key';
|
|
11
|
+
expect((0, api_keys_1.getApiKeys)('test-domain')).toEqual('test-api-key');
|
|
12
|
+
});
|
|
13
|
+
it('should return api key from credentials file', () => {
|
|
14
|
+
process.env.REDOCLY_AUTHORIZATION = '';
|
|
15
|
+
jest.spyOn(fs, 'existsSync').mockReturnValue(true);
|
|
16
|
+
jest.spyOn(fs, 'readFileSync').mockReturnValue(JSON.stringify({
|
|
17
|
+
['test-domain']: 'test-api-key-from-credentials-file',
|
|
18
|
+
}));
|
|
19
|
+
expect((0, api_keys_1.getApiKeys)('test-domain')).toEqual('test-api-key-from-credentials-file');
|
|
20
|
+
});
|
|
21
|
+
it('should throw an error if no api key provided', () => {
|
|
22
|
+
process.env.REDOCLY_AUTHORIZATION = '';
|
|
23
|
+
jest.spyOn(fs, 'existsSync').mockReturnValue(false);
|
|
24
|
+
expect(() => (0, api_keys_1.getApiKeys)('test-domain')).toThrowError('No api key provided, please use environment variable REDOCLY_AUTHORIZATION.');
|
|
25
|
+
});
|
|
26
|
+
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,217 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
3
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
4
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
5
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
6
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
7
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
8
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
9
|
+
});
|
|
10
|
+
};
|
|
11
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
|
+
const node_fetch_1 = require("node-fetch");
|
|
13
|
+
const FormData = require("form-data");
|
|
14
|
+
const api_client_1 = require("../api-client");
|
|
15
|
+
jest.mock('node-fetch', () => ({
|
|
16
|
+
default: jest.fn(),
|
|
17
|
+
}));
|
|
18
|
+
function mockFetchResponse(response) {
|
|
19
|
+
node_fetch_1.default.mockResolvedValue(response);
|
|
20
|
+
}
|
|
21
|
+
describe('ApiClient', () => {
|
|
22
|
+
const testToken = 'test-token';
|
|
23
|
+
const testDomain = 'test-domain.com';
|
|
24
|
+
const testOrg = 'test-org';
|
|
25
|
+
const testProject = 'test-project';
|
|
26
|
+
describe('getDefaultBranch()', () => {
|
|
27
|
+
let apiClient;
|
|
28
|
+
beforeEach(() => {
|
|
29
|
+
apiClient = new api_client_1.ReuniteApiClient(testDomain, testToken);
|
|
30
|
+
});
|
|
31
|
+
it('should get default project branch', () => __awaiter(void 0, void 0, void 0, function* () {
|
|
32
|
+
mockFetchResponse({
|
|
33
|
+
ok: true,
|
|
34
|
+
json: jest.fn().mockResolvedValue({
|
|
35
|
+
branchName: 'test-branch',
|
|
36
|
+
}),
|
|
37
|
+
});
|
|
38
|
+
const result = yield apiClient.remotes.getDefaultBranch(testOrg, testProject);
|
|
39
|
+
expect(node_fetch_1.default).toHaveBeenCalledWith(`${testDomain}/api/orgs/${testOrg}/projects/${testProject}/source`, {
|
|
40
|
+
method: 'GET',
|
|
41
|
+
headers: {
|
|
42
|
+
'Content-Type': 'application/json',
|
|
43
|
+
Authorization: `Bearer ${testToken}`,
|
|
44
|
+
},
|
|
45
|
+
});
|
|
46
|
+
expect(result).toEqual('test-branch');
|
|
47
|
+
}));
|
|
48
|
+
it('should throw parsed error if response is not ok', () => __awaiter(void 0, void 0, void 0, function* () {
|
|
49
|
+
mockFetchResponse({
|
|
50
|
+
ok: false,
|
|
51
|
+
json: jest.fn().mockResolvedValue({
|
|
52
|
+
type: 'about:blank',
|
|
53
|
+
title: 'Project source not found',
|
|
54
|
+
status: 404,
|
|
55
|
+
detail: 'Not Found',
|
|
56
|
+
object: 'problem',
|
|
57
|
+
}),
|
|
58
|
+
});
|
|
59
|
+
yield expect(apiClient.remotes.getDefaultBranch(testOrg, testProject)).rejects.toThrow(new Error('Failed to fetch default branch: Project source not found'));
|
|
60
|
+
}));
|
|
61
|
+
it('should throw statusText error if response is not ok', () => __awaiter(void 0, void 0, void 0, function* () {
|
|
62
|
+
mockFetchResponse({
|
|
63
|
+
ok: false,
|
|
64
|
+
statusText: 'Not found',
|
|
65
|
+
json: jest.fn().mockResolvedValue({
|
|
66
|
+
unknownField: 'unknown-error',
|
|
67
|
+
}),
|
|
68
|
+
});
|
|
69
|
+
yield expect(apiClient.remotes.getDefaultBranch(testOrg, testProject)).rejects.toThrow(new Error('Failed to fetch default branch: Not found'));
|
|
70
|
+
}));
|
|
71
|
+
});
|
|
72
|
+
describe('upsert()', () => {
|
|
73
|
+
const remotePayload = {
|
|
74
|
+
mountBranchName: 'remote-mount-branch-name',
|
|
75
|
+
mountPath: 'remote-mount-path',
|
|
76
|
+
};
|
|
77
|
+
let apiClient;
|
|
78
|
+
beforeEach(() => {
|
|
79
|
+
apiClient = new api_client_1.ReuniteApiClient(testDomain, testToken);
|
|
80
|
+
});
|
|
81
|
+
it('should upsert remote', () => __awaiter(void 0, void 0, void 0, function* () {
|
|
82
|
+
const responseMock = {
|
|
83
|
+
id: 'remote-id',
|
|
84
|
+
type: 'CICD',
|
|
85
|
+
mountPath: 'remote-mount-path',
|
|
86
|
+
mountBranchName: 'remote-mount-branch-name',
|
|
87
|
+
organizationId: testOrg,
|
|
88
|
+
projectId: testProject,
|
|
89
|
+
};
|
|
90
|
+
mockFetchResponse({
|
|
91
|
+
ok: true,
|
|
92
|
+
json: jest.fn().mockResolvedValue(responseMock),
|
|
93
|
+
});
|
|
94
|
+
const result = yield apiClient.remotes.upsert(testOrg, testProject, remotePayload);
|
|
95
|
+
expect(node_fetch_1.default).toHaveBeenCalledWith(`${testDomain}/api/orgs/${testOrg}/projects/${testProject}/remotes`, {
|
|
96
|
+
method: 'POST',
|
|
97
|
+
headers: {
|
|
98
|
+
'Content-Type': 'application/json',
|
|
99
|
+
Authorization: `Bearer ${testToken}`,
|
|
100
|
+
},
|
|
101
|
+
body: JSON.stringify({
|
|
102
|
+
mountPath: remotePayload.mountPath,
|
|
103
|
+
mountBranchName: remotePayload.mountBranchName,
|
|
104
|
+
type: 'CICD',
|
|
105
|
+
autoMerge: true,
|
|
106
|
+
}),
|
|
107
|
+
});
|
|
108
|
+
expect(result).toEqual(responseMock);
|
|
109
|
+
}));
|
|
110
|
+
it('should throw parsed error if response is not ok', () => __awaiter(void 0, void 0, void 0, function* () {
|
|
111
|
+
mockFetchResponse({
|
|
112
|
+
ok: false,
|
|
113
|
+
json: jest.fn().mockResolvedValue({
|
|
114
|
+
type: 'about:blank',
|
|
115
|
+
title: 'Not allowed to mount remote outside of project content path: /docs',
|
|
116
|
+
status: 403,
|
|
117
|
+
detail: 'Forbidden',
|
|
118
|
+
object: 'problem',
|
|
119
|
+
}),
|
|
120
|
+
});
|
|
121
|
+
yield expect(apiClient.remotes.upsert(testOrg, testProject, remotePayload)).rejects.toThrow(new Error('Failed to upsert remote: Not allowed to mount remote outside of project content path: /docs'));
|
|
122
|
+
}));
|
|
123
|
+
it('should throw statusText error if response is not ok', () => __awaiter(void 0, void 0, void 0, function* () {
|
|
124
|
+
mockFetchResponse({
|
|
125
|
+
ok: false,
|
|
126
|
+
statusText: 'Not found',
|
|
127
|
+
json: jest.fn().mockResolvedValue({
|
|
128
|
+
unknownField: 'unknown-error',
|
|
129
|
+
}),
|
|
130
|
+
});
|
|
131
|
+
yield expect(apiClient.remotes.upsert(testOrg, testProject, remotePayload)).rejects.toThrow(new Error('Failed to upsert remote: Not found'));
|
|
132
|
+
}));
|
|
133
|
+
});
|
|
134
|
+
describe('push()', () => {
|
|
135
|
+
const testRemoteId = 'test-remote-id';
|
|
136
|
+
const pushPayload = {
|
|
137
|
+
remoteId: testRemoteId,
|
|
138
|
+
commit: {
|
|
139
|
+
message: 'test-message',
|
|
140
|
+
author: {
|
|
141
|
+
name: 'test-name',
|
|
142
|
+
email: 'test-email',
|
|
143
|
+
},
|
|
144
|
+
branchName: 'test-branch-name',
|
|
145
|
+
},
|
|
146
|
+
};
|
|
147
|
+
const filesMock = [{ path: 'some-file.yaml', stream: Buffer.from('fefef') }];
|
|
148
|
+
const responseMock = {
|
|
149
|
+
branchName: 'rem/cicd/rem_01he7sr6ys2agb7w0g9t7978fn-main',
|
|
150
|
+
hasChanges: true,
|
|
151
|
+
files: [
|
|
152
|
+
{
|
|
153
|
+
type: 'file',
|
|
154
|
+
name: 'some-file.yaml',
|
|
155
|
+
path: 'docs/remotes/some-file.yaml',
|
|
156
|
+
lastModified: 1698925132394.2993,
|
|
157
|
+
mimeType: 'text/yaml',
|
|
158
|
+
},
|
|
159
|
+
],
|
|
160
|
+
commitSha: 'bb23a2f8e012ac0b7b9961b57fb40d8686b21b43',
|
|
161
|
+
outdated: false,
|
|
162
|
+
};
|
|
163
|
+
let apiClient;
|
|
164
|
+
beforeEach(() => {
|
|
165
|
+
apiClient = new api_client_1.ReuniteApiClient(testDomain, testToken);
|
|
166
|
+
});
|
|
167
|
+
it('should push to remote', () => __awaiter(void 0, void 0, void 0, function* () {
|
|
168
|
+
let passedFormData = new FormData();
|
|
169
|
+
node_fetch_1.default.mockImplementationOnce((_, options) => __awaiter(void 0, void 0, void 0, function* () {
|
|
170
|
+
passedFormData = options.body;
|
|
171
|
+
return {
|
|
172
|
+
ok: true,
|
|
173
|
+
json: jest.fn().mockResolvedValue(responseMock),
|
|
174
|
+
};
|
|
175
|
+
}));
|
|
176
|
+
const formData = new FormData();
|
|
177
|
+
formData.append('remoteId', testRemoteId);
|
|
178
|
+
formData.append('commit[message]', pushPayload.commit.message);
|
|
179
|
+
formData.append('commit[author][name]', pushPayload.commit.author.name);
|
|
180
|
+
formData.append('commit[author][email]', pushPayload.commit.author.email);
|
|
181
|
+
formData.append('commit[branchName]', pushPayload.commit.branchName);
|
|
182
|
+
formData.append('files[some-file.yaml]', filesMock[0].stream);
|
|
183
|
+
const result = yield apiClient.remotes.push(testOrg, testProject, pushPayload, filesMock);
|
|
184
|
+
expect(node_fetch_1.default).toHaveBeenCalledWith(`${testDomain}/api/orgs/${testOrg}/projects/${testProject}/pushes`, expect.objectContaining({
|
|
185
|
+
method: 'POST',
|
|
186
|
+
headers: {
|
|
187
|
+
Authorization: `Bearer ${testToken}`,
|
|
188
|
+
},
|
|
189
|
+
}));
|
|
190
|
+
expect(JSON.stringify(passedFormData).replace(new RegExp(passedFormData.getBoundary(), 'g'), '')).toEqual(JSON.stringify(formData).replace(new RegExp(formData.getBoundary(), 'g'), ''));
|
|
191
|
+
expect(result).toEqual(responseMock);
|
|
192
|
+
}));
|
|
193
|
+
it('should throw parsed error if response is not ok', () => __awaiter(void 0, void 0, void 0, function* () {
|
|
194
|
+
mockFetchResponse({
|
|
195
|
+
ok: false,
|
|
196
|
+
json: jest.fn().mockResolvedValue({
|
|
197
|
+
type: 'about:blank',
|
|
198
|
+
title: 'Cannot push to remote',
|
|
199
|
+
status: 403,
|
|
200
|
+
detail: 'Forbidden',
|
|
201
|
+
object: 'problem',
|
|
202
|
+
}),
|
|
203
|
+
});
|
|
204
|
+
yield expect(apiClient.remotes.push(testOrg, testProject, pushPayload, filesMock)).rejects.toThrow(new Error('Failed to push: Cannot push to remote'));
|
|
205
|
+
}));
|
|
206
|
+
it('should throw statusText error if response is not ok', () => __awaiter(void 0, void 0, void 0, function* () {
|
|
207
|
+
mockFetchResponse({
|
|
208
|
+
ok: false,
|
|
209
|
+
statusText: 'Not found',
|
|
210
|
+
json: jest.fn().mockResolvedValue({
|
|
211
|
+
unknownField: 'unknown-error',
|
|
212
|
+
}),
|
|
213
|
+
});
|
|
214
|
+
yield expect(apiClient.remotes.push(testOrg, testProject, pushPayload, filesMock)).rejects.toThrow(new Error('Failed to push: Not found'));
|
|
215
|
+
}));
|
|
216
|
+
});
|
|
217
|
+
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const domains_1 = require("../domains");
|
|
4
|
+
describe('getDomain()', () => {
|
|
5
|
+
it('should return the domain from environment variable', () => {
|
|
6
|
+
process.env.REDOCLY_DOMAIN = 'test-domain';
|
|
7
|
+
expect((0, domains_1.getDomain)()).toBe('test-domain');
|
|
8
|
+
});
|
|
9
|
+
it('should return the default domain if no domain provided', () => {
|
|
10
|
+
process.env.REDOCLY_DOMAIN = '';
|
|
11
|
+
expect((0, domains_1.getDomain)()).toBe('https://app.cloud.redocly.com');
|
|
12
|
+
});
|
|
13
|
+
});
|