@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.
Files changed (126) hide show
  1. package/CHANGELOG.md +23 -0
  2. package/README.md +5 -5
  3. package/lib/__tests__/commands/build-docs.test.js +3 -3
  4. package/lib/__tests__/commands/bundle.test.js +5 -5
  5. package/lib/__tests__/commands/join.test.js +11 -11
  6. package/lib/__tests__/commands/lint.test.js +14 -14
  7. package/lib/__tests__/commands/push-region.test.js +1 -1
  8. package/lib/__tests__/commands/push.test.js +11 -11
  9. package/lib/__tests__/fetch-with-timeout.test.js +4 -13
  10. package/lib/__tests__/spinner.test.js +43 -0
  11. package/lib/__tests__/utils.test.js +54 -36
  12. package/lib/__tests__/wrapper.test.js +8 -8
  13. package/lib/cms/api/__tests__/api-keys.test.d.ts +1 -0
  14. package/lib/cms/api/__tests__/api-keys.test.js +26 -0
  15. package/lib/cms/api/__tests__/api.client.test.d.ts +1 -0
  16. package/lib/cms/api/__tests__/api.client.test.js +217 -0
  17. package/lib/cms/api/__tests__/domains.test.d.ts +1 -0
  18. package/lib/cms/api/__tests__/domains.test.js +13 -0
  19. package/lib/cms/api/api-client.d.ts +50 -0
  20. package/lib/cms/api/api-client.js +148 -0
  21. package/lib/cms/api/api-keys.d.ts +1 -0
  22. package/lib/cms/api/api-keys.js +24 -0
  23. package/lib/cms/api/domains.d.ts +1 -0
  24. package/lib/cms/api/domains.js +12 -0
  25. package/lib/cms/api/index.d.ts +3 -0
  26. package/lib/cms/api/index.js +19 -0
  27. package/lib/cms/api/types.d.ts +91 -0
  28. package/lib/cms/api/types.js +2 -0
  29. package/lib/cms/commands/__tests__/push-status.test.d.ts +1 -0
  30. package/lib/cms/commands/__tests__/push-status.test.js +164 -0
  31. package/lib/cms/commands/__tests__/push.test.d.ts +1 -0
  32. package/lib/cms/commands/__tests__/push.test.js +226 -0
  33. package/lib/cms/commands/push-status.d.ts +12 -0
  34. package/lib/cms/commands/push-status.js +150 -0
  35. package/lib/cms/commands/push.d.ts +23 -0
  36. package/lib/cms/commands/push.js +142 -0
  37. package/lib/cms/utils.d.ts +2 -0
  38. package/lib/cms/utils.js +6 -0
  39. package/lib/commands/build-docs/index.js +4 -4
  40. package/lib/commands/build-docs/utils.js +2 -2
  41. package/lib/commands/bundle.js +13 -13
  42. package/lib/commands/join.js +25 -25
  43. package/lib/commands/lint.js +10 -10
  44. package/lib/commands/login.js +2 -2
  45. package/lib/commands/preview-docs/index.js +4 -4
  46. package/lib/commands/preview-docs/preview-server/preview-server.js +2 -2
  47. package/lib/commands/preview-project/constants.d.ts +14 -0
  48. package/lib/commands/preview-project/constants.js +22 -0
  49. package/lib/commands/preview-project/index.d.ts +2 -0
  50. package/lib/commands/preview-project/index.js +58 -0
  51. package/lib/commands/preview-project/types.d.ts +10 -0
  52. package/lib/commands/preview-project/types.js +2 -0
  53. package/lib/commands/push.d.ts +5 -0
  54. package/lib/commands/push.js +25 -17
  55. package/lib/commands/split/__tests__/index.test.js +2 -2
  56. package/lib/commands/split/index.js +20 -20
  57. package/lib/commands/stats.js +4 -4
  58. package/lib/index.d.ts +1 -1
  59. package/lib/index.js +169 -25
  60. package/lib/types.d.ts +9 -1
  61. package/lib/{__mocks__/utils.js → utils/__mocks__/miscellaneous.js} +1 -1
  62. package/lib/utils/assert-node-version.d.ts +1 -0
  63. package/lib/{fetch-with-timeout.js → utils/fetch-with-timeout.js} +2 -7
  64. package/lib/{utils.d.ts → utils/miscellaneous.d.ts} +1 -1
  65. package/lib/{utils.js → utils/miscellaneous.js} +20 -2
  66. package/lib/utils/spinner.d.ts +10 -0
  67. package/lib/utils/spinner.js +42 -0
  68. package/lib/{update-version-notifier.js → utils/update-version-notifier.js} +4 -4
  69. package/lib/wrapper.js +5 -5
  70. package/package.json +5 -3
  71. package/src/__tests__/commands/build-docs.test.ts +2 -2
  72. package/src/__tests__/commands/bundle.test.ts +2 -2
  73. package/src/__tests__/commands/join.test.ts +2 -2
  74. package/src/__tests__/commands/lint.test.ts +3 -3
  75. package/src/__tests__/commands/push-region.test.ts +1 -1
  76. package/src/__tests__/commands/push.test.ts +2 -2
  77. package/src/__tests__/fetch-with-timeout.test.ts +4 -16
  78. package/src/__tests__/spinner.test.ts +51 -0
  79. package/src/__tests__/utils.test.ts +20 -5
  80. package/src/__tests__/wrapper.test.ts +2 -2
  81. package/src/cms/api/__tests__/api-keys.test.ts +37 -0
  82. package/src/cms/api/__tests__/api.client.test.ts +275 -0
  83. package/src/cms/api/__tests__/domains.test.ts +15 -0
  84. package/src/cms/api/api-client.ts +199 -0
  85. package/src/cms/api/api-keys.ts +26 -0
  86. package/src/cms/api/domains.ts +11 -0
  87. package/src/cms/api/index.ts +3 -0
  88. package/src/cms/api/types.ts +101 -0
  89. package/src/cms/commands/__tests__/push-status.test.ts +212 -0
  90. package/src/cms/commands/__tests__/push.test.ts +293 -0
  91. package/src/cms/commands/push-status.ts +203 -0
  92. package/src/cms/commands/push.ts +215 -0
  93. package/src/cms/utils.ts +1 -0
  94. package/src/commands/build-docs/index.ts +1 -1
  95. package/src/commands/build-docs/utils.ts +1 -1
  96. package/src/commands/bundle.ts +2 -2
  97. package/src/commands/join.ts +2 -2
  98. package/src/commands/lint.ts +1 -1
  99. package/src/commands/login.ts +1 -1
  100. package/src/commands/preview-docs/index.ts +5 -1
  101. package/src/commands/preview-docs/preview-server/preview-server.ts +1 -1
  102. package/src/commands/preview-project/constants.ts +23 -0
  103. package/src/commands/preview-project/index.ts +58 -0
  104. package/src/commands/preview-project/types.ts +12 -0
  105. package/src/commands/push.ts +15 -1
  106. package/src/commands/split/__tests__/index.test.ts +3 -4
  107. package/src/commands/split/index.ts +5 -5
  108. package/src/commands/stats.ts +2 -2
  109. package/src/index.ts +184 -28
  110. package/src/types.ts +12 -1
  111. package/src/{__mocks__/utils.ts → utils/__mocks__/miscellaneous.ts} +1 -1
  112. package/src/{fetch-with-timeout.ts → utils/fetch-with-timeout.ts} +1 -6
  113. package/src/{utils.ts → utils/miscellaneous.ts} +20 -2
  114. package/src/utils/spinner.ts +50 -0
  115. package/src/{update-version-notifier.ts → utils/update-version-notifier.ts} +2 -2
  116. package/src/wrapper.ts +7 -2
  117. package/tsconfig.tsbuildinfo +1 -1
  118. /package/lib/{assert-node-version.d.ts → __tests__/spinner.test.d.ts} +0 -0
  119. /package/lib/{__mocks__/utils.d.ts → utils/__mocks__/miscellaneous.d.ts} +0 -0
  120. /package/lib/{assert-node-version.js → utils/assert-node-version.js} +0 -0
  121. /package/lib/{fetch-with-timeout.d.ts → utils/fetch-with-timeout.d.ts} +0 -0
  122. /package/lib/{js-utils.d.ts → utils/js-utils.d.ts} +0 -0
  123. /package/lib/{js-utils.js → utils/js-utils.js} +0 -0
  124. /package/lib/{update-version-notifier.d.ts → utils/update-version-notifier.d.ts} +0 -0
  125. /package/src/{assert-node-version.ts → utils/assert-node-version.ts} +0 -0
  126. /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 utils_1 = require("../utils");
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, utils_1.isSubdir)(parent, child)).toBe(expectRes);
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, utils_1.isSubdir)(parent, child)).toBe(expectRes);
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, utils_1.pathToFilename)('/user/createWithList', '_');
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, utils_1.getFallbackApisOrExit)(['./test.yaml'], {
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, utils_1.printConfigLintTotals)(totalProblemsMock);
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, utils_1.printConfigLintTotals)(Object.assign(Object.assign({}, totalProblemsMock), { errors: 0, warnings: 2 }));
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, utils_1.printConfigLintTotals)(Object.assign(Object.assign({}, totalProblemsMock), { errors: 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, utils_1.getFallbackApisOrExit)([''], apisConfig);
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, utils_1.getFallbackApisOrExit)(undefined, config);
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, utils_1.getFallbackApisOrExit)(undefined, config);
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, utils_1.getFallbackApisOrExit)(['someFile.yaml'], apisConfig);
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, utils_1.getFallbackApisOrExit)(['someFile.yaml', 'someFile2.yaml'], apisConfig);
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, utils_1.getFallbackApisOrExit)(undefined, configStub);
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, utils_1.getFallbackApisOrExit)(undefined, apisConfig);
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, utils_1.langToExt)(lang)).toBe(expected);
257
+ expect((0, miscellaneous_1.langToExt)(lang)).toBe(expected);
240
258
  });
241
259
  it('should ignore case when inferring file extension', () => {
242
- expect((0, utils_1.langToExt)('JavaScript')).toBe('.js');
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, utils_1.sortTopLevelKeysForOas)(openApi);
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, utils_1.sortTopLevelKeysForOas)(openApi);
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, utils_1.handleError)(resolveError, ref)).toThrowError(utils_1.HandledError);
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, utils_1.handleError)(yamlParseError, ref)).toThrowError(utils_1.HandledError);
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 utils_1.CircularJSONNotSupportedError(new Error('Circular json'));
346
- expect(() => (0, utils_1.handleError)(circularError, ref)).toThrowError(utils_1.HandledError);
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, utils_1.handleError)(testError, ref)).toThrowError(utils_1.HandledError);
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, utils_1.handleError)(testError, ref)).toThrowError(utils_1.HandledError);
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, utils_1.checkIfRulesetExist)(rules)).toThrowError('⚠️ No rules were configured. Learn how to configure rules: https://redocly.com/docs/cli/rules/');
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, utils_1.checkIfRulesetExist)(rules);
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, utils_1.cleanColors)(stringWithColors);
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, utils_1.cleanArgs)(testArgs)).toEqual({
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, utils_1.cleanArgs)(testArgs)).toEqual({
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, utils_1.cleanRawInput)(rawInput)).toEqual('redocly bundle api-name@api-version file-yaml http://url --config=file-yaml --output folder');
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, utils_1.cleanRawInput)(rawInput)).toEqual('redocly lint file-json --format stylish --extends=minimal --skip-rule operation-4xx-response');
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, utils_1.getAndValidateFileExtension)('test.json')).toEqual('json');
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, utils_1.getAndValidateFileExtension)('test.xml')).toEqual('yaml');
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, utils_1.writeToFileByExtension)('test data', 'test.yaml');
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, utils_1.writeToFileByExtension)('test data', 'test.json');
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 utils_1 = require("../utils");
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
- utils_1.loadConfigAndHandleErrors.mockImplementation(() => {
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(utils_1.sendTelemetry).toHaveBeenCalledTimes(1);
36
- expect(utils_1.sendTelemetry).toHaveBeenCalledWith({}, 0, false);
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
- utils_1.loadConfigAndHandleErrors.mockImplementation(() => {
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(utils_1.sendTelemetry).toHaveBeenCalledTimes(0);
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 = utils_1.loadConfigAndHandleErrors;
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
+ });