@redocly/openapi-core 1.0.0-beta.64 → 1.0.0-beta.68
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/__tests__/__snapshots__/bundle.test.ts.snap +126 -0
- package/__tests__/bundle.test.ts +53 -1
- package/__tests__/fixtures/refs/definitions.yaml +3 -0
- package/__tests__/fixtures/refs/external-request-body.yaml +13 -0
- package/__tests__/fixtures/refs/externalref.yaml +35 -0
- package/__tests__/fixtures/refs/hosted.yaml +35 -0
- package/__tests__/fixtures/refs/rename.yaml +1 -0
- package/__tests__/fixtures/refs/requestBody.yaml +9 -0
- package/__tests__/fixtures/refs/simple.yaml +1 -0
- package/__tests__/fixtures/refs/vendor.schema.yaml +20 -0
- package/lib/bundle.js +16 -4
- package/lib/config/all.js +9 -1
- package/lib/config/config.js +1 -1
- package/lib/config/minimal.js +1 -0
- package/lib/config/recommended.js +1 -0
- package/lib/index.d.ts +1 -1
- package/lib/index.js +2 -1
- package/lib/ref-utils.js +1 -2
- package/lib/rules/builtin.d.ts +6 -0
- package/lib/rules/common/info-description-override.d.ts +2 -0
- package/lib/rules/common/info-description-override.js +24 -0
- package/lib/rules/common/info-license-url.js +1 -0
- package/lib/rules/common/no-http-verbs-in-paths.d.ts +2 -0
- package/lib/rules/common/no-http-verbs-in-paths.js +33 -0
- package/lib/rules/common/operation-4xx-response.d.ts +2 -0
- package/lib/rules/common/operation-4xx-response.js +17 -0
- package/lib/rules/common/operation-description-override.d.ts +2 -0
- package/lib/rules/common/operation-description-override.js +29 -0
- package/lib/rules/common/path-excludes-patterns.d.ts +2 -0
- package/lib/rules/common/path-excludes-patterns.js +22 -0
- package/lib/rules/common/path-segment-plural.d.ts +2 -0
- package/lib/rules/common/path-segment-plural.js +32 -0
- package/lib/rules/common/tag-description-override.d.ts +2 -0
- package/lib/rules/common/tag-description-override.js +25 -0
- package/lib/rules/oas2/index.d.ts +9 -0
- package/lib/rules/oas2/index.js +18 -0
- package/lib/rules/oas2/request-mime-type.d.ts +2 -0
- package/lib/rules/oas2/request-mime-type.js +17 -0
- package/lib/rules/oas2/response-mime-type.d.ts +2 -0
- package/lib/rules/oas2/response-mime-type.js +17 -0
- package/lib/rules/oas3/index.d.ts +3 -0
- package/lib/rules/oas3/index.js +19 -1
- package/lib/rules/oas3/no-server-trailing-slash.js +1 -1
- package/lib/rules/oas3/request-mime-type.d.ts +2 -0
- package/lib/rules/oas3/request-mime-type.js +31 -0
- package/lib/rules/oas3/response-mime-type.d.ts +2 -0
- package/lib/rules/oas3/response-mime-type.js +31 -0
- package/lib/types/oas3_1.js +6 -0
- package/lib/utils.d.ts +11 -0
- package/lib/utils.js +69 -1
- package/package.json +4 -2
- package/src/__tests__/utils.test.ts +19 -1
- package/src/bundle.ts +26 -6
- package/src/config/all.ts +9 -1
- package/src/config/config.ts +2 -2
- package/src/config/minimal.ts +1 -0
- package/src/config/recommended.ts +1 -0
- package/src/index.ts +1 -1
- package/src/ref-utils.ts +1 -3
- package/src/rules/common/__tests__/info-license.test.ts +1 -1
- package/src/rules/common/__tests__/operation-4xx-response.test.ts +108 -0
- package/src/rules/common/info-description-override.ts +24 -0
- package/src/rules/common/info-license-url.ts +1 -0
- package/src/rules/common/no-http-verbs-in-paths.ts +36 -0
- package/src/rules/common/operation-4xx-response.ts +17 -0
- package/src/rules/common/operation-description-override.ts +30 -0
- package/src/rules/common/path-excludes-patterns.ts +23 -0
- package/src/rules/common/path-segment-plural.ts +31 -0
- package/src/rules/common/tag-description-override.ts +25 -0
- package/src/rules/oas2/index.ts +18 -0
- package/src/rules/oas2/request-mime-type.ts +17 -0
- package/src/rules/oas2/response-mime-type.ts +17 -0
- package/src/rules/oas3/__tests__/no-server-trailing-slash.test.ts +19 -0
- package/src/rules/oas3/index.ts +20 -3
- package/src/rules/oas3/no-server-trailing-slash.ts +1 -1
- package/src/rules/oas3/request-mime-type.ts +31 -0
- package/src/rules/oas3/response-mime-type.ts +31 -0
- package/src/rules/utils.ts +1 -1
- package/src/types/oas3_1.ts +7 -0
- package/src/utils.ts +79 -0
- package/tsconfig.tsbuildinfo +1 -1
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.PathSegmentPlural = void 0;
|
|
4
|
+
const utils_1 = require("../../utils");
|
|
5
|
+
const PathSegmentPlural = (opts) => {
|
|
6
|
+
const { ignoreLastPathSegment, exceptions } = opts;
|
|
7
|
+
return {
|
|
8
|
+
PathItem: {
|
|
9
|
+
leave(_path, { report, key, location }) {
|
|
10
|
+
const pathKey = key.toString();
|
|
11
|
+
if (pathKey.startsWith('/')) {
|
|
12
|
+
const pathSegments = pathKey.split('/');
|
|
13
|
+
pathSegments.shift();
|
|
14
|
+
if (ignoreLastPathSegment && pathSegments.length > 1) {
|
|
15
|
+
pathSegments.pop();
|
|
16
|
+
}
|
|
17
|
+
for (const pathSegment of pathSegments) {
|
|
18
|
+
if (exceptions && exceptions.includes(pathSegment))
|
|
19
|
+
continue;
|
|
20
|
+
if (!utils_1.isPathParameter(pathSegment) && utils_1.isSingular(pathSegment)) {
|
|
21
|
+
report({
|
|
22
|
+
message: `path segment \`${pathSegment}\` should be plural.`,
|
|
23
|
+
location: location.key(),
|
|
24
|
+
});
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
},
|
|
29
|
+
},
|
|
30
|
+
};
|
|
31
|
+
};
|
|
32
|
+
exports.PathSegmentPlural = PathSegmentPlural;
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.TagDescriptionOverride = void 0;
|
|
4
|
+
const utils_1 = require("../../utils");
|
|
5
|
+
const TagDescriptionOverride = ({ tagNames }) => {
|
|
6
|
+
return {
|
|
7
|
+
Tag: {
|
|
8
|
+
leave(tag, { report }) {
|
|
9
|
+
if (!tagNames)
|
|
10
|
+
throw new Error(`Parameter "tagNames" is not provided for "tag-description-override" rule`);
|
|
11
|
+
if (tagNames[tag.name]) {
|
|
12
|
+
try {
|
|
13
|
+
tag.description = utils_1.readFileAsStringSync(tagNames[tag.name]);
|
|
14
|
+
}
|
|
15
|
+
catch (e) {
|
|
16
|
+
report({
|
|
17
|
+
message: `Failed to read markdown override file for tag "${tag.name}".\n${e.message}`,
|
|
18
|
+
});
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
},
|
|
22
|
+
},
|
|
23
|
+
};
|
|
24
|
+
};
|
|
25
|
+
exports.TagDescriptionOverride = TagDescriptionOverride;
|
|
@@ -12,6 +12,7 @@ export declare const rules: {
|
|
|
12
12
|
'boolean-parameter-prefixes': Oas2Rule;
|
|
13
13
|
'no-path-trailing-slash': Oas2Rule;
|
|
14
14
|
'operation-2xx-response': Oas2Rule;
|
|
15
|
+
'operation-4xx-response': Oas2Rule;
|
|
15
16
|
'operation-operationId-unique': Oas2Rule;
|
|
16
17
|
'operation-parameters-unique': Oas2Rule;
|
|
17
18
|
'path-parameters-defined': Oas2Rule;
|
|
@@ -30,8 +31,16 @@ export declare const rules: {
|
|
|
30
31
|
'no-identical-paths': Oas2Rule;
|
|
31
32
|
'no-ambiguous-paths': Oas2Rule;
|
|
32
33
|
'path-http-verbs-order': Oas2Rule;
|
|
34
|
+
'no-http-verbs-in-paths': Oas2Rule;
|
|
35
|
+
'path-excludes-patterns': Oas2Rule;
|
|
36
|
+
'request-mime-type': Oas2Rule;
|
|
37
|
+
'response-mime-type': Oas2Rule;
|
|
38
|
+
'path-segment-plural': Oas2Rule;
|
|
33
39
|
};
|
|
34
40
|
export declare const preprocessors: {};
|
|
35
41
|
export declare const decorators: {
|
|
36
42
|
'registry-dependencies': Oas2Decorator;
|
|
43
|
+
'operation-description-override': Oas2Decorator;
|
|
44
|
+
'tag-description-override': Oas2Decorator;
|
|
45
|
+
'info-description-override': Oas2Decorator;
|
|
37
46
|
};
|
package/lib/rules/oas2/index.js
CHANGED
|
@@ -13,6 +13,7 @@ const paths_kebab_case_1 = require("../common/paths-kebab-case");
|
|
|
13
13
|
const no_enum_type_mismatch_1 = require("../common/no-enum-type-mismatch");
|
|
14
14
|
const no_path_trailing_slash_1 = require("../common/no-path-trailing-slash");
|
|
15
15
|
const operation_2xx_response_1 = require("../common/operation-2xx-response");
|
|
16
|
+
const operation_4xx_response_1 = require("../common/operation-4xx-response");
|
|
16
17
|
const operation_operationId_unique_1 = require("../common/operation-operationId-unique");
|
|
17
18
|
const operation_parameters_unique_1 = require("../common/operation-parameters-unique");
|
|
18
19
|
const path_params_defined_1 = require("../common/path-params-defined");
|
|
@@ -31,6 +32,14 @@ const no_identical_paths_1 = require("../common/no-identical-paths");
|
|
|
31
32
|
const operation_operationId_1 = require("../common/operation-operationId");
|
|
32
33
|
const operation_summary_1 = require("../common/operation-summary");
|
|
33
34
|
const no_ambiguous_paths_1 = require("../common/no-ambiguous-paths");
|
|
35
|
+
const no_http_verbs_in_paths_1 = require("../common/no-http-verbs-in-paths");
|
|
36
|
+
const path_excludes_patterns_1 = require("../common/path-excludes-patterns");
|
|
37
|
+
const request_mime_type_1 = require("./request-mime-type");
|
|
38
|
+
const response_mime_type_1 = require("./response-mime-type");
|
|
39
|
+
const path_segment_plural_1 = require("../common/path-segment-plural");
|
|
40
|
+
const operation_description_override_1 = require("../common/operation-description-override");
|
|
41
|
+
const tag_description_override_1 = require("../common/tag-description-override");
|
|
42
|
+
const info_description_override_1 = require("../common/info-description-override");
|
|
34
43
|
exports.rules = {
|
|
35
44
|
spec: spec_1.OasSpec,
|
|
36
45
|
'info-description': info_description_1.InfoDescription,
|
|
@@ -44,6 +53,7 @@ exports.rules = {
|
|
|
44
53
|
'boolean-parameter-prefixes': boolean_parameter_prefixes_1.BooleanParameterPrefixes,
|
|
45
54
|
'no-path-trailing-slash': no_path_trailing_slash_1.NoPathTrailingSlash,
|
|
46
55
|
'operation-2xx-response': operation_2xx_response_1.Operation2xxResponse,
|
|
56
|
+
'operation-4xx-response': operation_4xx_response_1.Operation4xxResponse,
|
|
47
57
|
'operation-operationId-unique': operation_operationId_unique_1.OperationIdUnique,
|
|
48
58
|
'operation-parameters-unique': operation_parameters_unique_1.OperationParametersUnique,
|
|
49
59
|
'path-parameters-defined': path_params_defined_1.PathParamsDefined,
|
|
@@ -62,8 +72,16 @@ exports.rules = {
|
|
|
62
72
|
'no-identical-paths': no_identical_paths_1.NoIdenticalPaths,
|
|
63
73
|
'no-ambiguous-paths': no_ambiguous_paths_1.NoAmbiguousPaths,
|
|
64
74
|
'path-http-verbs-order': path_http_verbs_order_1.PathHttpVerbsOrder,
|
|
75
|
+
'no-http-verbs-in-paths': no_http_verbs_in_paths_1.NoHttpVerbsInPaths,
|
|
76
|
+
'path-excludes-patterns': path_excludes_patterns_1.PathExcludesPatterns,
|
|
77
|
+
'request-mime-type': request_mime_type_1.RequestMimeType,
|
|
78
|
+
'response-mime-type': response_mime_type_1.ResponseMimeType,
|
|
79
|
+
'path-segment-plural': path_segment_plural_1.PathSegmentPlural,
|
|
65
80
|
};
|
|
66
81
|
exports.preprocessors = {};
|
|
67
82
|
exports.decorators = {
|
|
68
83
|
'registry-dependencies': registry_dependencies_1.RegistryDependencies,
|
|
84
|
+
'operation-description-override': operation_description_override_1.OperationDescriptionOverride,
|
|
85
|
+
'tag-description-override': tag_description_override_1.TagDescriptionOverride,
|
|
86
|
+
'info-description-override': info_description_override_1.InfoDescriptionOverride,
|
|
69
87
|
};
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.RequestMimeType = void 0;
|
|
4
|
+
const utils_1 = require("../../utils");
|
|
5
|
+
const RequestMimeType = ({ allowedValues }) => {
|
|
6
|
+
return {
|
|
7
|
+
DefinitionRoot(root, ctx) {
|
|
8
|
+
utils_1.validateMimeType({ type: 'consumes', value: root }, ctx, allowedValues);
|
|
9
|
+
},
|
|
10
|
+
Operation: {
|
|
11
|
+
leave(operation, ctx) {
|
|
12
|
+
utils_1.validateMimeType({ type: 'consumes', value: operation }, ctx, allowedValues);
|
|
13
|
+
},
|
|
14
|
+
},
|
|
15
|
+
};
|
|
16
|
+
};
|
|
17
|
+
exports.RequestMimeType = RequestMimeType;
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.ResponseMimeType = void 0;
|
|
4
|
+
const utils_1 = require("../../utils");
|
|
5
|
+
const ResponseMimeType = ({ allowedValues }) => {
|
|
6
|
+
return {
|
|
7
|
+
DefinitionRoot(root, ctx) {
|
|
8
|
+
utils_1.validateMimeType({ type: 'produces', value: root }, ctx, allowedValues);
|
|
9
|
+
},
|
|
10
|
+
Operation: {
|
|
11
|
+
leave(operation, ctx) {
|
|
12
|
+
utils_1.validateMimeType({ type: 'produces', value: operation }, ctx, allowedValues);
|
|
13
|
+
},
|
|
14
|
+
},
|
|
15
|
+
};
|
|
16
|
+
};
|
|
17
|
+
exports.ResponseMimeType = ResponseMimeType;
|
|
@@ -4,4 +4,7 @@ export declare const rules: Oas3RuleSet;
|
|
|
4
4
|
export declare const preprocessors: {};
|
|
5
5
|
export declare const decorators: {
|
|
6
6
|
'registry-dependencies': Oas3Decorator;
|
|
7
|
+
'operation-description-override': Oas3Decorator;
|
|
8
|
+
'tag-description-override': Oas3Decorator;
|
|
9
|
+
'info-description-override': Oas3Decorator;
|
|
7
10
|
};
|
package/lib/rules/oas3/index.js
CHANGED
|
@@ -3,6 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.decorators = exports.preprocessors = exports.rules = void 0;
|
|
4
4
|
const spec_1 = require("../common/spec");
|
|
5
5
|
const operation_2xx_response_1 = require("../common/operation-2xx-response");
|
|
6
|
+
const operation_4xx_response_1 = require("../common/operation-4xx-response");
|
|
6
7
|
const operation_operationId_unique_1 = require("../common/operation-operationId-unique");
|
|
7
8
|
const operation_parameters_unique_1 = require("../common/operation-parameters-unique");
|
|
8
9
|
const path_params_defined_1 = require("../common/path-params-defined");
|
|
@@ -39,6 +40,14 @@ const operation_operationId_1 = require("../common/operation-operationId");
|
|
|
39
40
|
const operation_summary_1 = require("../common/operation-summary");
|
|
40
41
|
const no_ambiguous_paths_1 = require("../common/no-ambiguous-paths");
|
|
41
42
|
const no_servers_empty_enum_1 = require("./no-servers-empty-enum");
|
|
43
|
+
const no_http_verbs_in_paths_1 = require("../common/no-http-verbs-in-paths");
|
|
44
|
+
const request_mime_type_1 = require("./request-mime-type");
|
|
45
|
+
const response_mime_type_1 = require("./response-mime-type");
|
|
46
|
+
const path_segment_plural_1 = require("../common/path-segment-plural");
|
|
47
|
+
const operation_description_override_1 = require("../common/operation-description-override");
|
|
48
|
+
const tag_description_override_1 = require("../common/tag-description-override");
|
|
49
|
+
const info_description_override_1 = require("../common/info-description-override");
|
|
50
|
+
const path_excludes_patterns_1 = require("../common/path-excludes-patterns");
|
|
42
51
|
exports.rules = {
|
|
43
52
|
spec: spec_1.OasSpec,
|
|
44
53
|
'info-description': info_description_1.InfoDescription,
|
|
@@ -46,6 +55,7 @@ exports.rules = {
|
|
|
46
55
|
'info-license': info_license_url_1.InfoLicense,
|
|
47
56
|
'info-license-url': license_url_1.InfoLicenseUrl,
|
|
48
57
|
'operation-2xx-response': operation_2xx_response_1.Operation2xxResponse,
|
|
58
|
+
'operation-4xx-response': operation_4xx_response_1.Operation4xxResponse,
|
|
49
59
|
'operation-operationId-unique': operation_operationId_unique_1.OperationIdUnique,
|
|
50
60
|
'operation-parameters-unique': operation_parameters_unique_1.OperationParametersUnique,
|
|
51
61
|
'path-parameters-defined': path_params_defined_1.PathParamsDefined,
|
|
@@ -77,9 +87,17 @@ exports.rules = {
|
|
|
77
87
|
'no-identical-paths': no_identical_paths_1.NoIdenticalPaths,
|
|
78
88
|
'no-ambiguous-paths': no_ambiguous_paths_1.NoAmbiguousPaths,
|
|
79
89
|
'no-undefined-server-variable': no_undefined_server_variable_1.NoUndefinedServerVariable,
|
|
80
|
-
'no-servers-empty-enum': no_servers_empty_enum_1.NoEmptyEnumServers
|
|
90
|
+
'no-servers-empty-enum': no_servers_empty_enum_1.NoEmptyEnumServers,
|
|
91
|
+
'no-http-verbs-in-paths': no_http_verbs_in_paths_1.NoHttpVerbsInPaths,
|
|
92
|
+
'path-excludes-patterns': path_excludes_patterns_1.PathExcludesPatterns,
|
|
93
|
+
'request-mime-type': request_mime_type_1.RequestMimeType,
|
|
94
|
+
'response-mime-type': response_mime_type_1.ResponseMimeType,
|
|
95
|
+
'path-segment-plural': path_segment_plural_1.PathSegmentPlural,
|
|
81
96
|
};
|
|
82
97
|
exports.preprocessors = {};
|
|
83
98
|
exports.decorators = {
|
|
84
99
|
'registry-dependencies': registry_dependencies_1.RegistryDependencies,
|
|
100
|
+
'operation-description-override': operation_description_override_1.OperationDescriptionOverride,
|
|
101
|
+
'tag-description-override': tag_description_override_1.TagDescriptionOverride,
|
|
102
|
+
'info-description-override': info_description_override_1.InfoDescriptionOverride,
|
|
85
103
|
};
|
|
@@ -6,7 +6,7 @@ const NoServerTrailingSlash = () => {
|
|
|
6
6
|
Server(server, { report, location }) {
|
|
7
7
|
if (!server.url)
|
|
8
8
|
return;
|
|
9
|
-
if (server.url.endsWith('/')) {
|
|
9
|
+
if (server.url.endsWith('/') && server.url !== '/') {
|
|
10
10
|
report({
|
|
11
11
|
message: 'Server `url` should not have a trailing slash.',
|
|
12
12
|
location: location.child(['url']),
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.RequestMimeType = void 0;
|
|
4
|
+
const utils_1 = require("../../utils");
|
|
5
|
+
const RequestMimeType = ({ allowedValues }) => {
|
|
6
|
+
return {
|
|
7
|
+
PathMap: {
|
|
8
|
+
RequestBody: {
|
|
9
|
+
leave(requestBody, ctx) {
|
|
10
|
+
utils_1.validateMimeTypeOAS3({ type: 'consumes', value: requestBody }, ctx, allowedValues);
|
|
11
|
+
},
|
|
12
|
+
},
|
|
13
|
+
Callback: {
|
|
14
|
+
RequestBody() { },
|
|
15
|
+
Response: {
|
|
16
|
+
leave(response, ctx) {
|
|
17
|
+
utils_1.validateMimeTypeOAS3({ type: 'consumes', value: response }, ctx, allowedValues);
|
|
18
|
+
},
|
|
19
|
+
},
|
|
20
|
+
},
|
|
21
|
+
},
|
|
22
|
+
WebhooksMap: {
|
|
23
|
+
Response: {
|
|
24
|
+
leave(response, ctx) {
|
|
25
|
+
utils_1.validateMimeTypeOAS3({ type: 'consumes', value: response }, ctx, allowedValues);
|
|
26
|
+
},
|
|
27
|
+
},
|
|
28
|
+
},
|
|
29
|
+
};
|
|
30
|
+
};
|
|
31
|
+
exports.RequestMimeType = RequestMimeType;
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.ResponseMimeType = void 0;
|
|
4
|
+
const utils_1 = require("../../utils");
|
|
5
|
+
const ResponseMimeType = ({ allowedValues }) => {
|
|
6
|
+
return {
|
|
7
|
+
PathMap: {
|
|
8
|
+
Response: {
|
|
9
|
+
leave(response, ctx) {
|
|
10
|
+
utils_1.validateMimeTypeOAS3({ type: 'produces', value: response }, ctx, allowedValues);
|
|
11
|
+
},
|
|
12
|
+
},
|
|
13
|
+
Callback: {
|
|
14
|
+
Response() { },
|
|
15
|
+
RequestBody: {
|
|
16
|
+
leave(requestBody, ctx) {
|
|
17
|
+
utils_1.validateMimeTypeOAS3({ type: 'produces', value: requestBody }, ctx, allowedValues);
|
|
18
|
+
},
|
|
19
|
+
},
|
|
20
|
+
},
|
|
21
|
+
},
|
|
22
|
+
WebhooksMap: {
|
|
23
|
+
RequestBody: {
|
|
24
|
+
leave(requestBody, ctx) {
|
|
25
|
+
utils_1.validateMimeTypeOAS3({ type: 'produces', value: requestBody }, ctx, allowedValues);
|
|
26
|
+
},
|
|
27
|
+
},
|
|
28
|
+
},
|
|
29
|
+
};
|
|
30
|
+
};
|
|
31
|
+
exports.ResponseMimeType = ResponseMimeType;
|
package/lib/types/oas3_1.js
CHANGED
|
@@ -94,6 +94,12 @@ const Operation = {
|
|
|
94
94
|
};
|
|
95
95
|
const Schema = {
|
|
96
96
|
properties: {
|
|
97
|
+
$id: { type: 'string' },
|
|
98
|
+
id: { type: 'string' },
|
|
99
|
+
$schema: { type: 'string' },
|
|
100
|
+
definitions: 'NamedSchemas',
|
|
101
|
+
$defs: 'NamedSchemas',
|
|
102
|
+
$vocabulary: { type: 'string' },
|
|
97
103
|
externalDocs: 'ExternalDocs',
|
|
98
104
|
discriminator: 'Discriminator',
|
|
99
105
|
myArbitraryKeyword: { type: 'boolean' },
|
package/lib/utils.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { HttpResolveConfig } from './config/config';
|
|
2
|
+
import { UserContext } from './walk';
|
|
2
3
|
export { parseYaml, stringifyYaml } from './js-yaml';
|
|
3
4
|
export declare type StackFrame<T> = {
|
|
4
5
|
prev: StackFrame<T> | null;
|
|
@@ -22,3 +23,13 @@ export declare function readFileFromUrl(url: string, config: HttpResolveConfig):
|
|
|
22
23
|
export declare function match(url: string, pattern: string): boolean;
|
|
23
24
|
export declare function pickObjectProps<T extends Record<string, unknown>>(object: T, keys: Array<string>): T;
|
|
24
25
|
export declare function omitObjectProps<T extends Record<string, unknown>>(object: T, keys: Array<string>): T;
|
|
26
|
+
export declare function splitCamelCaseIntoWords(str: string): Set<string>;
|
|
27
|
+
export declare function validateMimeType({ type, value }: any, { report, location }: UserContext, allowedValues: string[]): void;
|
|
28
|
+
export declare function validateMimeTypeOAS3({ type, value }: any, { report, location }: UserContext, allowedValues: string[]): void;
|
|
29
|
+
export declare function isSingular(path: string): boolean;
|
|
30
|
+
export declare function readFileAsStringSync(filePath: string): string;
|
|
31
|
+
export declare function isPathParameter(pathSegment: string): boolean;
|
|
32
|
+
/**
|
|
33
|
+
* Convert Windows backslash paths to slash paths: foo\\bar ➔ foo/bar
|
|
34
|
+
*/
|
|
35
|
+
export declare function slash(path: string): string;
|
package/lib/utils.js
CHANGED
|
@@ -9,10 +9,11 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
|
|
|
9
9
|
});
|
|
10
10
|
};
|
|
11
11
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
|
-
exports.omitObjectProps = exports.pickObjectProps = exports.match = exports.readFileFromUrl = exports.isPlainObject = exports.notUndefined = exports.loadYaml = exports.popStack = exports.pushStack = exports.stringifyYaml = exports.parseYaml = void 0;
|
|
12
|
+
exports.slash = exports.isPathParameter = exports.readFileAsStringSync = exports.isSingular = exports.validateMimeTypeOAS3 = exports.validateMimeType = exports.splitCamelCaseIntoWords = exports.omitObjectProps = exports.pickObjectProps = exports.match = exports.readFileFromUrl = exports.isPlainObject = exports.notUndefined = exports.loadYaml = exports.popStack = exports.pushStack = exports.stringifyYaml = exports.parseYaml = void 0;
|
|
13
13
|
const fs = require("fs");
|
|
14
14
|
const minimatch = require("minimatch");
|
|
15
15
|
const node_fetch_1 = require("node-fetch");
|
|
16
|
+
const pluralize = require("pluralize");
|
|
16
17
|
const js_yaml_1 = require("./js-yaml");
|
|
17
18
|
var js_yaml_2 = require("./js-yaml");
|
|
18
19
|
Object.defineProperty(exports, "parseYaml", { enumerable: true, get: function () { return js_yaml_2.parseYaml; } });
|
|
@@ -76,3 +77,70 @@ function omitObjectProps(object, keys) {
|
|
|
76
77
|
return Object.fromEntries(Object.entries(object).filter(([key]) => !keys.includes(key)));
|
|
77
78
|
}
|
|
78
79
|
exports.omitObjectProps = omitObjectProps;
|
|
80
|
+
function splitCamelCaseIntoWords(str) {
|
|
81
|
+
const camel = str
|
|
82
|
+
.split(/(?:[-._])|([A-Z][a-z]+)/)
|
|
83
|
+
.filter(Boolean)
|
|
84
|
+
.map((item) => item.toLocaleLowerCase());
|
|
85
|
+
const caps = str
|
|
86
|
+
.split(/([A-Z]{2,})/)
|
|
87
|
+
.filter((e) => e && e === e.toUpperCase())
|
|
88
|
+
.map((item) => item.toLocaleLowerCase());
|
|
89
|
+
return new Set([...camel, ...caps]);
|
|
90
|
+
}
|
|
91
|
+
exports.splitCamelCaseIntoWords = splitCamelCaseIntoWords;
|
|
92
|
+
function validateMimeType({ type, value }, { report, location }, allowedValues) {
|
|
93
|
+
const ruleType = type === 'consumes' ? 'request' : 'response';
|
|
94
|
+
if (!allowedValues)
|
|
95
|
+
throw new Error(`Parameter "allowedValues" is not provided for "${ruleType}-mime-type" rule`);
|
|
96
|
+
if (!value[type])
|
|
97
|
+
return;
|
|
98
|
+
for (const mime of value[type]) {
|
|
99
|
+
if (!allowedValues.includes(mime)) {
|
|
100
|
+
report({
|
|
101
|
+
message: `Mime type "${mime}" is not allowed`,
|
|
102
|
+
location: location.child(value[type].indexOf(mime)).key(),
|
|
103
|
+
});
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
exports.validateMimeType = validateMimeType;
|
|
108
|
+
function validateMimeTypeOAS3({ type, value }, { report, location }, allowedValues) {
|
|
109
|
+
const ruleType = type === 'consumes' ? 'request' : 'response';
|
|
110
|
+
if (!allowedValues)
|
|
111
|
+
throw new Error(`Parameter "allowedValues" is not provided for "${ruleType}-mime-type" rule`);
|
|
112
|
+
if (!value.content)
|
|
113
|
+
return;
|
|
114
|
+
for (const mime of Object.keys(value.content)) {
|
|
115
|
+
if (!allowedValues.includes(mime)) {
|
|
116
|
+
report({
|
|
117
|
+
message: `Mime type "${mime}" is not allowed`,
|
|
118
|
+
location: location.child('content').child(mime).key(),
|
|
119
|
+
});
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
exports.validateMimeTypeOAS3 = validateMimeTypeOAS3;
|
|
124
|
+
function isSingular(path) {
|
|
125
|
+
return pluralize.isSingular(path);
|
|
126
|
+
}
|
|
127
|
+
exports.isSingular = isSingular;
|
|
128
|
+
function readFileAsStringSync(filePath) {
|
|
129
|
+
return fs.readFileSync(filePath, 'utf-8');
|
|
130
|
+
}
|
|
131
|
+
exports.readFileAsStringSync = readFileAsStringSync;
|
|
132
|
+
function isPathParameter(pathSegment) {
|
|
133
|
+
return pathSegment.startsWith('{') && pathSegment.endsWith('{');
|
|
134
|
+
}
|
|
135
|
+
exports.isPathParameter = isPathParameter;
|
|
136
|
+
/**
|
|
137
|
+
* Convert Windows backslash paths to slash paths: foo\\bar ➔ foo/bar
|
|
138
|
+
*/
|
|
139
|
+
function slash(path) {
|
|
140
|
+
const isExtendedLengthPath = /^\\\\\?\\/.test(path);
|
|
141
|
+
if (isExtendedLengthPath) {
|
|
142
|
+
return path;
|
|
143
|
+
}
|
|
144
|
+
return path.replace(/\\/g, '/');
|
|
145
|
+
}
|
|
146
|
+
exports.slash = slash;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@redocly/openapi-core",
|
|
3
|
-
"version": "1.0.0-beta.
|
|
3
|
+
"version": "1.0.0-beta.68",
|
|
4
4
|
"description": "",
|
|
5
5
|
"main": "lib/index.js",
|
|
6
6
|
"engines": {
|
|
@@ -30,7 +30,7 @@
|
|
|
30
30
|
"Andriy Leliv <andriy@redoc.ly> (https://redoc.ly/)"
|
|
31
31
|
],
|
|
32
32
|
"dependencies": {
|
|
33
|
-
"@redocly/ajv": "^8.6.
|
|
33
|
+
"@redocly/ajv": "^8.6.4",
|
|
34
34
|
"@types/node": "^14.11.8",
|
|
35
35
|
"colorette": "^1.2.0",
|
|
36
36
|
"js-levenshtein": "^1.1.6",
|
|
@@ -38,6 +38,7 @@
|
|
|
38
38
|
"lodash.isequal": "^4.5.0",
|
|
39
39
|
"minimatch": "^3.0.4",
|
|
40
40
|
"node-fetch": "^2.6.1",
|
|
41
|
+
"pluralize": "^8.0.0",
|
|
41
42
|
"yaml-ast-parser": "0.0.43"
|
|
42
43
|
},
|
|
43
44
|
"devDependencies": {
|
|
@@ -46,6 +47,7 @@
|
|
|
46
47
|
"@types/lodash.isequal": "^4.5.5",
|
|
47
48
|
"@types/minimatch": "^3.0.3",
|
|
48
49
|
"@types/node-fetch": "^2.5.7",
|
|
50
|
+
"@types/pluralize": "^0.0.29",
|
|
49
51
|
"typescript": "^4.0.5"
|
|
50
52
|
}
|
|
51
53
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { pickObjectProps, omitObjectProps } from '../utils';
|
|
1
|
+
import { pickObjectProps, omitObjectProps, slash } from '../utils';
|
|
2
2
|
|
|
3
3
|
describe('utils', () => {
|
|
4
4
|
const testObject = {
|
|
@@ -53,4 +53,22 @@ describe('utils', () => {
|
|
|
53
53
|
expect(omitObjectProps({}, ['d', 'e'])).toStrictEqual({});
|
|
54
54
|
});
|
|
55
55
|
});
|
|
56
|
+
|
|
57
|
+
describe('slash path', () => {
|
|
58
|
+
it('can correctly slash path', () => {
|
|
59
|
+
[
|
|
60
|
+
['foo\\bar', 'foo/bar'],
|
|
61
|
+
['foo/bar', 'foo/bar'],
|
|
62
|
+
['foo\\中文', 'foo/中文'],
|
|
63
|
+
['foo/中文', 'foo/中文'],
|
|
64
|
+
].forEach(([path, expectRes]) => {
|
|
65
|
+
expect(slash(path)).toBe(expectRes);
|
|
66
|
+
});
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
it('does not modify extended length paths', () => {
|
|
70
|
+
const extended = '\\\\?\\some\\path';
|
|
71
|
+
expect(slash(extended)).toBe(extended);
|
|
72
|
+
});
|
|
73
|
+
});
|
|
56
74
|
});
|
package/src/bundle.ts
CHANGED
|
@@ -7,7 +7,7 @@ import { Oas3_1Types } from './types/oas3_1';
|
|
|
7
7
|
import { NormalizedNodeType, normalizeTypes, NodeType } from './types';
|
|
8
8
|
import { WalkContext, walkDocument, UserContext, ResolveResult } from './walk';
|
|
9
9
|
import { detectOpenAPI, openAPIMajor, OasMajorVersion } from './oas-types';
|
|
10
|
-
import { Location, refBaseName } from './ref-utils';
|
|
10
|
+
import { isRef, Location, refBaseName } from './ref-utils';
|
|
11
11
|
import type { Config, LintConfig } from './config/config';
|
|
12
12
|
import { initRules } from './config/rules';
|
|
13
13
|
import { reportUnresolvedRef } from './rules/no-unresolved-refs';
|
|
@@ -40,7 +40,8 @@ export async function bundle(opts: {
|
|
|
40
40
|
throw new Error('Document or reference is required.\n');
|
|
41
41
|
}
|
|
42
42
|
|
|
43
|
-
const document =
|
|
43
|
+
const document =
|
|
44
|
+
doc !== undefined ? doc : await externalRefResolver.resolveDocument(base, ref!, true);
|
|
44
45
|
|
|
45
46
|
if (document instanceof Error) {
|
|
46
47
|
throw document;
|
|
@@ -69,7 +70,11 @@ export async function bundleDocument(opts: {
|
|
|
69
70
|
const rules = config.getRulesForOasVersion(oasMajorVersion);
|
|
70
71
|
const types = normalizeTypes(
|
|
71
72
|
config.extendTypes(
|
|
72
|
-
customTypes ?? oasMajorVersion === OasMajorVersion.Version3
|
|
73
|
+
customTypes ?? oasMajorVersion === OasMajorVersion.Version3
|
|
74
|
+
? oasVersion === OasVersion.Version3_1
|
|
75
|
+
? Oas3_1Types
|
|
76
|
+
: Oas3Types
|
|
77
|
+
: Oas2Types,
|
|
73
78
|
oasVersion,
|
|
74
79
|
),
|
|
75
80
|
config,
|
|
@@ -249,6 +254,21 @@ function makeBundleVisitor(version: OasMajorVersion, dereference: boolean, rootD
|
|
|
249
254
|
}
|
|
250
255
|
}
|
|
251
256
|
|
|
257
|
+
function isEqualOrEqualRef(
|
|
258
|
+
node: any,
|
|
259
|
+
target: { node: any; location: Location },
|
|
260
|
+
ctx: UserContext,
|
|
261
|
+
) {
|
|
262
|
+
if (
|
|
263
|
+
isRef(node) &&
|
|
264
|
+
ctx.resolve(node).location?.absolutePointer === target.location.absolutePointer
|
|
265
|
+
) {
|
|
266
|
+
return true;
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
return isEqual(node, target.node);
|
|
270
|
+
}
|
|
271
|
+
|
|
252
272
|
function getComponentName(
|
|
253
273
|
target: { node: any; location: Location },
|
|
254
274
|
componentType: string,
|
|
@@ -265,20 +285,20 @@ function makeBundleVisitor(version: OasMajorVersion, dereference: boolean, rootD
|
|
|
265
285
|
if (
|
|
266
286
|
!componentsGroup ||
|
|
267
287
|
!componentsGroup[name] ||
|
|
268
|
-
|
|
288
|
+
isEqualOrEqualRef(componentsGroup[name], target, ctx)
|
|
269
289
|
) {
|
|
270
290
|
return name;
|
|
271
291
|
}
|
|
272
292
|
}
|
|
273
293
|
|
|
274
294
|
name = refBaseName(fileRef) + (name ? `_${name}` : '');
|
|
275
|
-
if (!componentsGroup[name] ||
|
|
295
|
+
if (!componentsGroup[name] || isEqualOrEqualRef(componentsGroup[name], target, ctx)) {
|
|
276
296
|
return name;
|
|
277
297
|
}
|
|
278
298
|
|
|
279
299
|
const prevName = name;
|
|
280
300
|
let serialId = 2;
|
|
281
|
-
while (componentsGroup[name] && !
|
|
301
|
+
while (componentsGroup[name] && !isEqualOrEqualRef(componentsGroup[name], target, ctx)) {
|
|
282
302
|
name = `${prevName}-${serialId}`;
|
|
283
303
|
serialId++;
|
|
284
304
|
}
|
package/src/config/all.ts
CHANGED
|
@@ -12,11 +12,13 @@ export default {
|
|
|
12
12
|
'no-identical-paths': 'error',
|
|
13
13
|
'no-ambiguous-paths': 'error',
|
|
14
14
|
'no-path-trailing-slash': 'error',
|
|
15
|
+
'path-segment-plural': 'error',
|
|
15
16
|
'path-declaration-must-exist': 'error',
|
|
16
17
|
'path-not-include-query': 'error',
|
|
17
18
|
'path-parameters-defined': 'error',
|
|
18
19
|
'operation-description': 'error',
|
|
19
20
|
'operation-2xx-response': 'error',
|
|
21
|
+
'operation-4xx-response': 'error',
|
|
20
22
|
'operation-operationId': 'error',
|
|
21
23
|
'operation-summary': 'error',
|
|
22
24
|
'operation-operationId-unique': 'error',
|
|
@@ -29,6 +31,12 @@ export default {
|
|
|
29
31
|
'no-enum-type-mismatch': 'error',
|
|
30
32
|
'boolean-parameter-prefixes': 'error',
|
|
31
33
|
'paths-kebab-case': 'error',
|
|
34
|
+
'no-http-verbs-in-paths': 'error',
|
|
35
|
+
'path-excludes-patterns': {
|
|
36
|
+
severity: 'error',
|
|
37
|
+
patterns: [],
|
|
38
|
+
},
|
|
39
|
+
'request-mime-type': 'error',
|
|
32
40
|
spec: 'error',
|
|
33
41
|
},
|
|
34
42
|
oas3_0Rules: {
|
|
@@ -49,5 +57,5 @@ export default {
|
|
|
49
57
|
'no-unused-components': 'error',
|
|
50
58
|
'no-undefined-server-variable': 'error',
|
|
51
59
|
'no-servers-empty-enum': 'error',
|
|
52
|
-
}
|
|
60
|
+
},
|
|
53
61
|
} as LintRawConfig;
|
package/src/config/config.ts
CHANGED
|
@@ -4,7 +4,7 @@ import { dirname } from 'path';
|
|
|
4
4
|
import { red, blue } from 'colorette';
|
|
5
5
|
|
|
6
6
|
import { parseYaml, stringifyYaml } from '../js-yaml';
|
|
7
|
-
import { notUndefined } from '../utils';
|
|
7
|
+
import { notUndefined, slash } from '../utils';
|
|
8
8
|
|
|
9
9
|
import {
|
|
10
10
|
OasVersion,
|
|
@@ -211,7 +211,7 @@ export class LintConfig {
|
|
|
211
211
|
const ignoreFile = path.join(dir, IGNORE_FILE);
|
|
212
212
|
const mapped: Record<string, any> = {};
|
|
213
213
|
for (const absFileName of Object.keys(this.ignore)) {
|
|
214
|
-
const ignoredRules = (mapped[path.relative(dir, absFileName)] = this.ignore[absFileName]);
|
|
214
|
+
const ignoredRules = (mapped[slash(path.relative(dir, absFileName))] = this.ignore[absFileName]);
|
|
215
215
|
for (const ruleId of Object.keys(ignoredRules)) {
|
|
216
216
|
ignoredRules[ruleId] = Array.from(ignoredRules[ruleId]) as any;
|
|
217
217
|
}
|
package/src/config/minimal.ts
CHANGED
|
@@ -17,6 +17,7 @@ export default {
|
|
|
17
17
|
'path-parameters-defined': 'warn',
|
|
18
18
|
'operation-description': 'off',
|
|
19
19
|
'operation-2xx-response': 'warn',
|
|
20
|
+
'operation-4xx-response': 'off',
|
|
20
21
|
'operation-operationId': 'warn',
|
|
21
22
|
'operation-summary': 'warn',
|
|
22
23
|
'operation-operationId-unique': 'warn',
|
|
@@ -17,6 +17,7 @@ export default {
|
|
|
17
17
|
'path-parameters-defined': 'error',
|
|
18
18
|
'operation-description': 'off',
|
|
19
19
|
'operation-2xx-response': 'warn',
|
|
20
|
+
'operation-4xx-response': 'warn',
|
|
20
21
|
'operation-operationId': 'warn',
|
|
21
22
|
'operation-summary': 'error',
|
|
22
23
|
'operation-operationId-unique': 'error',
|