@redocly/cli 1.8.2 → 1.9.1
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/lib/__mocks__/@redocly/openapi-core.js +2 -1
- package/lib/__mocks__/documents.d.ts +58 -0
- package/lib/__mocks__/documents.js +63 -3
- package/lib/__tests__/commands/join.test.js +126 -3
- package/lib/cms/commands/__tests__/push-status.test.js +5 -5
- package/lib/cms/commands/push-status.js +3 -2
- package/lib/commands/join.d.ts +2 -1
- package/lib/commands/join.js +36 -50
- package/lib/commands/split/index.d.ts +3 -1
- package/lib/commands/split/index.js +10 -9
- package/lib/commands/split/types.d.ts +1 -2
- package/lib/commands/split/types.js +1 -2
- package/lib/utils/js-utils.d.ts +2 -1
- package/lib/utils/js-utils.js +8 -1
- package/package.json +2 -2
- package/src/__mocks__/@redocly/openapi-core.ts +4 -1
- package/src/__mocks__/documents.ts +63 -2
- package/src/__tests__/commands/join.test.ts +137 -3
- package/src/cms/commands/__tests__/push-status.test.ts +5 -5
- package/src/cms/commands/push-status.ts +6 -5
- package/src/commands/join.ts +56 -63
- package/src/commands/split/index.ts +24 -25
- package/src/commands/split/types.ts +1 -3
- package/src/utils/js-utils.ts +8 -1
- package/tsconfig.tsbuildinfo +1 -1
package/src/commands/join.ts
CHANGED
|
@@ -4,22 +4,16 @@ import { performance } from 'perf_hooks';
|
|
|
4
4
|
const isEqual = require('lodash.isequal');
|
|
5
5
|
import {
|
|
6
6
|
Config,
|
|
7
|
-
Oas3Definition,
|
|
8
7
|
SpecVersion,
|
|
9
8
|
BaseResolver,
|
|
10
|
-
Document,
|
|
11
9
|
StyleguideConfig,
|
|
12
|
-
Oas3Tag,
|
|
13
10
|
formatProblems,
|
|
14
11
|
getTotals,
|
|
15
12
|
lintDocument,
|
|
16
13
|
detectSpec,
|
|
17
14
|
bundleDocument,
|
|
18
|
-
Referenced,
|
|
19
15
|
isRef,
|
|
20
|
-
RuleSeverity,
|
|
21
16
|
} from '@redocly/openapi-core';
|
|
22
|
-
|
|
23
17
|
import {
|
|
24
18
|
getFallbackApisOrExit,
|
|
25
19
|
printExecutionTime,
|
|
@@ -32,16 +26,24 @@ import {
|
|
|
32
26
|
checkForDeprecatedOptions,
|
|
33
27
|
} from '../utils/miscellaneous';
|
|
34
28
|
import { isObject, isString, keysOf } from '../utils/js-utils';
|
|
35
|
-
import {
|
|
29
|
+
import { COMPONENTS, OPENAPI3_METHOD } from './split/types';
|
|
30
|
+
import { crawl, startsWithComponents } from './split';
|
|
31
|
+
|
|
32
|
+
import type {
|
|
33
|
+
Oas3Definition,
|
|
34
|
+
Document,
|
|
35
|
+
Oas3Tag,
|
|
36
|
+
Referenced,
|
|
37
|
+
RuleSeverity,
|
|
38
|
+
} from '@redocly/openapi-core';
|
|
39
|
+
import type { BundleResult } from '@redocly/openapi-core/lib/bundle';
|
|
40
|
+
import type {
|
|
36
41
|
Oas3Parameter,
|
|
37
42
|
Oas3PathItem,
|
|
38
43
|
Oas3Server,
|
|
39
44
|
Oas3_1Definition,
|
|
40
45
|
} from '@redocly/openapi-core/lib/typings/openapi';
|
|
41
|
-
import { OPENAPI3_METHOD } from './split/types';
|
|
42
|
-
import { BundleResult } from '@redocly/openapi-core/lib/bundle';
|
|
43
46
|
|
|
44
|
-
const COMPONENTS = 'components';
|
|
45
47
|
const Tags = 'tags';
|
|
46
48
|
const xTagGroups = 'x-tagGroups';
|
|
47
49
|
let potentialConflictsTotal = 0;
|
|
@@ -49,6 +51,7 @@ let potentialConflictsTotal = 0;
|
|
|
49
51
|
type JoinDocumentContext = {
|
|
50
52
|
api: string;
|
|
51
53
|
apiFilename: string;
|
|
54
|
+
apiTitle?: string;
|
|
52
55
|
tags: Oas3Tag[];
|
|
53
56
|
potentialConflicts: any;
|
|
54
57
|
tagsPrefix: string;
|
|
@@ -209,6 +212,7 @@ export async function handleJoin(argv: JoinOptions, config: Config, packageVersi
|
|
|
209
212
|
const context = {
|
|
210
213
|
api,
|
|
211
214
|
apiFilename,
|
|
215
|
+
apiTitle: info?.title,
|
|
212
216
|
tags,
|
|
213
217
|
potentialConflicts,
|
|
214
218
|
tagsPrefix,
|
|
@@ -218,7 +222,6 @@ export async function handleJoin(argv: JoinOptions, config: Config, packageVersi
|
|
|
218
222
|
populateTags(context);
|
|
219
223
|
}
|
|
220
224
|
collectServers(openapi);
|
|
221
|
-
collectInfoDescriptions(openapi, context);
|
|
222
225
|
collectExternalDocs(openapi, context);
|
|
223
226
|
collectPaths(openapi, context);
|
|
224
227
|
collectComponents(openapi, context);
|
|
@@ -242,6 +245,7 @@ export async function handleJoin(argv: JoinOptions, config: Config, packageVersi
|
|
|
242
245
|
function populateTags({
|
|
243
246
|
api,
|
|
244
247
|
apiFilename,
|
|
248
|
+
apiTitle,
|
|
245
249
|
tags,
|
|
246
250
|
potentialConflicts,
|
|
247
251
|
tagsPrefix,
|
|
@@ -285,9 +289,10 @@ export async function handleJoin(argv: JoinOptions, config: Config, packageVersi
|
|
|
285
289
|
}
|
|
286
290
|
|
|
287
291
|
if (!withoutXTagGroups) {
|
|
288
|
-
|
|
292
|
+
const groupName = apiTitle || apiFilename;
|
|
293
|
+
createXTagGroups(groupName);
|
|
289
294
|
if (!tagDuplicate) {
|
|
290
|
-
populateXTagGroups(entrypointTagName, getIndexGroup(
|
|
295
|
+
populateXTagGroups(entrypointTagName, getIndexGroup(groupName));
|
|
291
296
|
}
|
|
292
297
|
}
|
|
293
298
|
|
|
@@ -302,20 +307,20 @@ export async function handleJoin(argv: JoinOptions, config: Config, packageVersi
|
|
|
302
307
|
}
|
|
303
308
|
}
|
|
304
309
|
|
|
305
|
-
function getIndexGroup(
|
|
306
|
-
return joinedDef[xTagGroups].findIndex((item: any) => item.name ===
|
|
310
|
+
function getIndexGroup(name: string): number {
|
|
311
|
+
return joinedDef[xTagGroups].findIndex((item: any) => item.name === name);
|
|
307
312
|
}
|
|
308
313
|
|
|
309
|
-
function createXTagGroups(
|
|
314
|
+
function createXTagGroups(name: string) {
|
|
310
315
|
if (!joinedDef.hasOwnProperty(xTagGroups)) {
|
|
311
316
|
joinedDef[xTagGroups] = [];
|
|
312
317
|
}
|
|
313
318
|
|
|
314
|
-
if (!joinedDef[xTagGroups].some((g: any) => g.name ===
|
|
315
|
-
joinedDef[xTagGroups].push({ name
|
|
319
|
+
if (!joinedDef[xTagGroups].some((g: any) => g.name === name)) {
|
|
320
|
+
joinedDef[xTagGroups].push({ name, tags: [] });
|
|
316
321
|
}
|
|
317
322
|
|
|
318
|
-
const indexGroup = getIndexGroup(
|
|
323
|
+
const indexGroup = getIndexGroup(name);
|
|
319
324
|
|
|
320
325
|
if (!joinedDef[xTagGroups][indexGroup].hasOwnProperty(Tags)) {
|
|
321
326
|
joinedDef[xTagGroups][indexGroup][Tags] = [];
|
|
@@ -344,27 +349,6 @@ export async function handleJoin(argv: JoinOptions, config: Config, packageVersi
|
|
|
344
349
|
}
|
|
345
350
|
}
|
|
346
351
|
|
|
347
|
-
function collectInfoDescriptions(
|
|
348
|
-
openapi: Oas3Definition,
|
|
349
|
-
{ apiFilename, componentsPrefix }: JoinDocumentContext
|
|
350
|
-
) {
|
|
351
|
-
const { info } = openapi;
|
|
352
|
-
if (info?.description) {
|
|
353
|
-
const groupIndex = joinedDef[xTagGroups] ? getIndexGroup(apiFilename) : -1;
|
|
354
|
-
if (
|
|
355
|
-
joinedDef.hasOwnProperty(xTagGroups) &&
|
|
356
|
-
groupIndex !== -1 &&
|
|
357
|
-
joinedDef[xTagGroups][groupIndex]['tags'] &&
|
|
358
|
-
joinedDef[xTagGroups][groupIndex]['tags'].length
|
|
359
|
-
) {
|
|
360
|
-
joinedDef[xTagGroups][groupIndex]['description'] = addComponentsPrefix(
|
|
361
|
-
info.description,
|
|
362
|
-
componentsPrefix!
|
|
363
|
-
);
|
|
364
|
-
}
|
|
365
|
-
}
|
|
366
|
-
}
|
|
367
|
-
|
|
368
352
|
function collectExternalDocs(openapi: Oas3Definition, { api }: JoinDocumentContext) {
|
|
369
353
|
const { externalDocs } = openapi;
|
|
370
354
|
if (externalDocs) {
|
|
@@ -380,7 +364,14 @@ export async function handleJoin(argv: JoinOptions, config: Config, packageVersi
|
|
|
380
364
|
|
|
381
365
|
function collectPaths(
|
|
382
366
|
openapi: Oas3Definition,
|
|
383
|
-
{
|
|
367
|
+
{
|
|
368
|
+
apiFilename,
|
|
369
|
+
apiTitle,
|
|
370
|
+
api,
|
|
371
|
+
potentialConflicts,
|
|
372
|
+
tagsPrefix,
|
|
373
|
+
componentsPrefix,
|
|
374
|
+
}: JoinDocumentContext
|
|
384
375
|
) {
|
|
385
376
|
const { paths } = openapi;
|
|
386
377
|
const operationsSet = new Set(keysOf<typeof OPENAPI3_METHOD>(OPENAPI3_METHOD));
|
|
@@ -532,6 +523,7 @@ export async function handleJoin(argv: JoinOptions, config: Config, packageVersi
|
|
|
532
523
|
populateTags({
|
|
533
524
|
api,
|
|
534
525
|
apiFilename,
|
|
526
|
+
apiTitle,
|
|
535
527
|
tags: formatTags(tags),
|
|
536
528
|
potentialConflicts,
|
|
537
529
|
tagsPrefix,
|
|
@@ -542,6 +534,7 @@ export async function handleJoin(argv: JoinOptions, config: Config, packageVersi
|
|
|
542
534
|
populateTags({
|
|
543
535
|
api,
|
|
544
536
|
apiFilename,
|
|
537
|
+
apiTitle,
|
|
545
538
|
tags: formatTags(['other']),
|
|
546
539
|
potentialConflicts,
|
|
547
540
|
tagsPrefix: tagsPrefix || apiFilename,
|
|
@@ -599,7 +592,14 @@ export async function handleJoin(argv: JoinOptions, config: Config, packageVersi
|
|
|
599
592
|
function collectWebhooks(
|
|
600
593
|
oasVersion: SpecVersion,
|
|
601
594
|
openapi: Oas3_1Definition,
|
|
602
|
-
{
|
|
595
|
+
{
|
|
596
|
+
apiFilename,
|
|
597
|
+
apiTitle,
|
|
598
|
+
api,
|
|
599
|
+
potentialConflicts,
|
|
600
|
+
tagsPrefix,
|
|
601
|
+
componentsPrefix,
|
|
602
|
+
}: JoinDocumentContext
|
|
603
603
|
) {
|
|
604
604
|
const webhooks = oasVersion === SpecVersion.OAS3_1 ? 'webhooks' : 'x-webhooks';
|
|
605
605
|
const openapiWebhooks = openapi[webhooks];
|
|
@@ -628,6 +628,7 @@ export async function handleJoin(argv: JoinOptions, config: Config, packageVersi
|
|
|
628
628
|
populateTags({
|
|
629
629
|
api,
|
|
630
630
|
apiFilename,
|
|
631
|
+
apiTitle,
|
|
631
632
|
tags: formatTags(tags),
|
|
632
633
|
potentialConflicts,
|
|
633
634
|
tagsPrefix,
|
|
@@ -757,8 +758,11 @@ function addComponentsPrefix(description: string, componentsPrefix: string) {
|
|
|
757
758
|
function addSecurityPrefix(security: any, componentsPrefix: string) {
|
|
758
759
|
return componentsPrefix
|
|
759
760
|
? security?.map((s: any) => {
|
|
760
|
-
const
|
|
761
|
-
|
|
761
|
+
const joinedSecuritySchema = {};
|
|
762
|
+
for (const [key, value] of Object.entries(s)) {
|
|
763
|
+
Object.assign(joinedSecuritySchema, { [componentsPrefix + '_' + key]: value });
|
|
764
|
+
}
|
|
765
|
+
return joinedSecuritySchema;
|
|
762
766
|
})
|
|
763
767
|
: security;
|
|
764
768
|
}
|
|
@@ -799,30 +803,19 @@ async function validateApi(
|
|
|
799
803
|
}
|
|
800
804
|
}
|
|
801
805
|
|
|
802
|
-
function
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
visitor(object, key);
|
|
806
|
-
crawl(object[key], visitor);
|
|
807
|
-
}
|
|
808
|
-
}
|
|
809
|
-
|
|
810
|
-
function replace$Refs(obj: any, componentsPrefix: string) {
|
|
811
|
-
crawl(obj, (node: any) => {
|
|
812
|
-
if (node.$ref && isString(node.$ref) && node.$ref.startsWith(`#/${COMPONENTS}/`)) {
|
|
806
|
+
function replace$Refs(obj: unknown, componentsPrefix: string) {
|
|
807
|
+
crawl(obj, (node: Record<string, unknown>) => {
|
|
808
|
+
if (node.$ref && typeof node.$ref === 'string' && startsWithComponents(node.$ref)) {
|
|
813
809
|
const name = path.basename(node.$ref);
|
|
814
810
|
node.$ref = node.$ref.replace(name, componentsPrefix + '_' + name);
|
|
815
|
-
} else if (
|
|
816
|
-
node.discriminator &&
|
|
817
|
-
node.discriminator.mapping &&
|
|
818
|
-
isObject(node.discriminator.mapping)
|
|
819
|
-
) {
|
|
811
|
+
} else if (isObject(node.discriminator) && isObject(node.discriminator.mapping)) {
|
|
820
812
|
const { mapping } = node.discriminator;
|
|
821
813
|
for (const name of Object.keys(mapping)) {
|
|
822
|
-
|
|
823
|
-
|
|
814
|
+
const mappingPointer = mapping[name];
|
|
815
|
+
if (typeof mappingPointer === 'string' && startsWithComponents(mappingPointer)) {
|
|
816
|
+
mapping[name] = mappingPointer
|
|
824
817
|
.split('/')
|
|
825
|
-
.map((name
|
|
818
|
+
.map((name, i, arr) => {
|
|
826
819
|
return arr.length - 1 === i && !name.includes(componentsPrefix)
|
|
827
820
|
? componentsPrefix + '_' + name
|
|
828
821
|
: name;
|
|
@@ -1,11 +1,9 @@
|
|
|
1
1
|
import { red, blue, yellow, green } from 'colorette';
|
|
2
2
|
import * as fs from 'fs';
|
|
3
3
|
import { parseYaml, slash, isRef, isTruthy } from '@redocly/openapi-core';
|
|
4
|
-
import type { OasRef } from '@redocly/openapi-core';
|
|
5
4
|
import * as path from 'path';
|
|
6
5
|
import { performance } from 'perf_hooks';
|
|
7
6
|
const isEqual = require('lodash.isequal');
|
|
8
|
-
|
|
9
7
|
import {
|
|
10
8
|
printExecutionTime,
|
|
11
9
|
pathToFilename,
|
|
@@ -16,8 +14,16 @@ import {
|
|
|
16
14
|
writeToFileByExtension,
|
|
17
15
|
getAndValidateFileExtension,
|
|
18
16
|
} from '../../utils/miscellaneous';
|
|
19
|
-
import {
|
|
17
|
+
import { isObject, isEmptyObject } from '../../utils/js-utils';
|
|
20
18
|
import {
|
|
19
|
+
OPENAPI3_COMPONENT,
|
|
20
|
+
COMPONENTS,
|
|
21
|
+
OPENAPI3_METHOD_NAMES,
|
|
22
|
+
OPENAPI3_COMPONENT_NAMES,
|
|
23
|
+
} from './types';
|
|
24
|
+
|
|
25
|
+
import type { OasRef } from '@redocly/openapi-core';
|
|
26
|
+
import type {
|
|
21
27
|
Definition,
|
|
22
28
|
Oas2Definition,
|
|
23
29
|
Oas3Schema,
|
|
@@ -26,13 +32,8 @@ import {
|
|
|
26
32
|
Oas3Components,
|
|
27
33
|
Oas3ComponentName,
|
|
28
34
|
ComponentsFiles,
|
|
29
|
-
|
|
35
|
+
RefObject,
|
|
30
36
|
Oas3PathItem,
|
|
31
|
-
OPENAPI3_COMPONENT,
|
|
32
|
-
COMPONENTS,
|
|
33
|
-
componentsPath,
|
|
34
|
-
OPENAPI3_METHOD_NAMES,
|
|
35
|
-
OPENAPI3_COMPONENT_NAMES,
|
|
36
37
|
Referenced,
|
|
37
38
|
} from './types';
|
|
38
39
|
|
|
@@ -93,8 +94,8 @@ function splitDefinition(
|
|
|
93
94
|
writeToFileByExtension(openapi, path.join(openapiDir, `openapi.${ext}`));
|
|
94
95
|
}
|
|
95
96
|
|
|
96
|
-
function
|
|
97
|
-
return node.startsWith(
|
|
97
|
+
export function startsWithComponents(node: string) {
|
|
98
|
+
return node.startsWith(`#/${COMPONENTS}/`);
|
|
98
99
|
}
|
|
99
100
|
|
|
100
101
|
function isSupportedExtension(filename: string) {
|
|
@@ -144,33 +145,31 @@ function traverseDirectoryDeepCallback(
|
|
|
144
145
|
writeToFileByExtension(pathData, filename);
|
|
145
146
|
}
|
|
146
147
|
|
|
147
|
-
function crawl(object:
|
|
148
|
+
export function crawl(object: unknown, visitor: (node: Record<string, unknown>) => void) {
|
|
148
149
|
if (!isObject(object)) return;
|
|
150
|
+
|
|
151
|
+
visitor(object);
|
|
149
152
|
for (const key of Object.keys(object)) {
|
|
150
|
-
visitor(object, key);
|
|
151
153
|
crawl(object[key], visitor);
|
|
152
154
|
}
|
|
153
155
|
}
|
|
154
156
|
|
|
155
|
-
function replace$Refs(obj:
|
|
156
|
-
crawl(obj, (node:
|
|
157
|
-
if (node.$ref &&
|
|
158
|
-
replace(node, '$ref');
|
|
159
|
-
} else if (
|
|
160
|
-
node.discriminator &&
|
|
161
|
-
node.discriminator.mapping &&
|
|
162
|
-
isObject(node.discriminator.mapping)
|
|
163
|
-
) {
|
|
157
|
+
function replace$Refs(obj: unknown, relativeFrom: string, componentFiles = {} as ComponentsFiles) {
|
|
158
|
+
crawl(obj, (node: Record<string, unknown>) => {
|
|
159
|
+
if (node.$ref && typeof node.$ref === 'string' && startsWithComponents(node.$ref)) {
|
|
160
|
+
replace(node as RefObject, '$ref');
|
|
161
|
+
} else if (isObject(node.discriminator) && isObject(node.discriminator.mapping)) {
|
|
164
162
|
const { mapping } = node.discriminator;
|
|
165
163
|
for (const name of Object.keys(mapping)) {
|
|
166
|
-
|
|
167
|
-
|
|
164
|
+
const mappingPointer = mapping[name];
|
|
165
|
+
if (typeof mappingPointer === 'string' && startsWithComponents(mappingPointer)) {
|
|
166
|
+
replace(node.discriminator.mapping as RefObject, name);
|
|
168
167
|
}
|
|
169
168
|
}
|
|
170
169
|
}
|
|
171
170
|
});
|
|
172
171
|
|
|
173
|
-
function replace(node:
|
|
172
|
+
function replace(node: RefObject, key: string) {
|
|
174
173
|
const splittedNode = node[key].split('/');
|
|
175
174
|
const name = splittedNode.pop();
|
|
176
175
|
const groupName = splittedNode[2];
|
|
@@ -28,7 +28,7 @@ export type Definition = Oas3_1Definition | Oas3Definition | Oas2Definition;
|
|
|
28
28
|
export interface ComponentsFiles {
|
|
29
29
|
[schemas: string]: any;
|
|
30
30
|
}
|
|
31
|
-
export interface
|
|
31
|
+
export interface RefObject {
|
|
32
32
|
[$ref: string]: string;
|
|
33
33
|
}
|
|
34
34
|
|
|
@@ -36,8 +36,6 @@ export const COMPONENTS = 'components';
|
|
|
36
36
|
export const PATHS = 'paths';
|
|
37
37
|
export const WEBHOOKS = 'webhooks';
|
|
38
38
|
export const xWEBHOOKS = 'x-webhooks';
|
|
39
|
-
export const componentsPath = `#/${COMPONENTS}/`;
|
|
40
|
-
|
|
41
39
|
export enum OPENAPI3_METHOD {
|
|
42
40
|
get = 'get',
|
|
43
41
|
put = 'put',
|
package/src/utils/js-utils.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export function isObject(obj:
|
|
1
|
+
export function isObject(obj: unknown): obj is Record<string, unknown> {
|
|
2
2
|
const type = typeof obj;
|
|
3
3
|
return type === 'function' || (type === 'object' && !!obj);
|
|
4
4
|
}
|
|
@@ -15,3 +15,10 @@ export function keysOf<T>(obj: T) {
|
|
|
15
15
|
if (!obj) return [];
|
|
16
16
|
return Object.keys(obj) as (keyof T)[];
|
|
17
17
|
}
|
|
18
|
+
|
|
19
|
+
export function capitalize(s: string) {
|
|
20
|
+
if (s?.length > 0) {
|
|
21
|
+
return s[0].toUpperCase() + s.slice(1);
|
|
22
|
+
}
|
|
23
|
+
return s;
|
|
24
|
+
}
|