@redocly/openapi-core 1.0.0-beta.68 → 1.0.0-beta.72
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__/lint.test.ts +1 -1
- package/__tests__/login.test.ts +17 -0
- package/lib/bundle.d.ts +4 -0
- package/lib/bundle.js +9 -3
- package/lib/config/all.js +2 -0
- package/lib/config/config.d.ts +10 -0
- package/lib/config/config.js +7 -1
- package/lib/config/load.js +17 -8
- package/lib/index.d.ts +2 -2
- package/lib/lint.js +2 -0
- package/lib/redocly/index.d.ts +26 -20
- package/lib/redocly/index.js +83 -214
- package/lib/redocly/registry-api-types.d.ts +28 -0
- package/lib/redocly/registry-api-types.js +2 -0
- package/lib/redocly/registry-api.d.ts +14 -0
- package/lib/redocly/registry-api.js +105 -0
- package/lib/rules/common/no-invalid-parameter-examples.d.ts +1 -0
- package/lib/rules/common/no-invalid-parameter-examples.js +25 -0
- package/lib/rules/common/no-invalid-schema-examples.d.ts +1 -0
- package/lib/rules/common/no-invalid-schema-examples.js +23 -0
- package/lib/rules/common/paths-kebab-case.js +1 -1
- package/lib/rules/common/registry-dependencies.js +4 -7
- package/lib/rules/oas2/index.d.ts +2 -0
- package/lib/rules/oas2/index.js +4 -0
- package/lib/rules/oas3/index.js +4 -0
- package/lib/rules/oas3/no-invalid-media-type-examples.js +5 -26
- package/lib/rules/utils.d.ts +3 -0
- package/lib/rules/utils.js +26 -1
- package/lib/typings/openapi.d.ts +3 -0
- package/lib/utils.d.ts +1 -0
- package/lib/utils.js +5 -1
- package/lib/walk.d.ts +2 -0
- package/lib/walk.js +7 -0
- package/package.json +1 -1
- package/src/bundle.ts +25 -3
- package/src/config/__tests__/load.test.ts +35 -0
- package/src/config/all.ts +2 -0
- package/src/config/config.ts +11 -0
- package/src/config/load.ts +20 -9
- package/src/index.ts +2 -8
- package/src/lint.ts +2 -0
- package/src/redocly/__tests__/redocly-client.test.ts +120 -0
- package/src/redocly/index.ts +101 -227
- package/src/redocly/registry-api-types.ts +31 -0
- package/src/redocly/registry-api.ts +110 -0
- package/src/rules/common/__tests__/paths-kebab-case.test.ts +23 -0
- package/src/rules/common/no-invalid-parameter-examples.ts +36 -0
- package/src/rules/common/no-invalid-schema-examples.ts +27 -0
- package/src/rules/common/paths-kebab-case.ts +1 -1
- package/src/rules/common/registry-dependencies.ts +6 -8
- package/src/rules/oas2/index.ts +4 -0
- package/src/rules/oas3/index.ts +4 -0
- package/src/rules/oas3/no-invalid-media-type-examples.ts +16 -36
- package/src/rules/utils.ts +43 -2
- package/src/typings/openapi.ts +4 -0
- package/src/utils.ts +5 -1
- package/src/walk.ts +10 -0
- package/tsconfig.tsbuildinfo +1 -1
- package/lib/redocly/query.d.ts +0 -4
- package/lib/redocly/query.js +0 -44
- package/src/redocly/query.ts +0 -38
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { RegistryApiTypes } from './registry-api-types';
|
|
2
|
+
import { AccessTokens, Region } from '../config/config';
|
|
3
|
+
export declare class RegistryApi {
|
|
4
|
+
private accessTokens;
|
|
5
|
+
private region;
|
|
6
|
+
constructor(accessTokens: AccessTokens, region: Region);
|
|
7
|
+
get accessToken(): string | false | undefined;
|
|
8
|
+
getBaseUrl(region?: Region): string;
|
|
9
|
+
setAccessTokens(accessTokens: AccessTokens): this;
|
|
10
|
+
private request;
|
|
11
|
+
authStatus(accessToken: string, region: Region, verbose?: boolean): Promise<boolean>;
|
|
12
|
+
prepareFileUpload({ organizationId, name, version, filesHash, filename, isUpsert, }: RegistryApiTypes.PrepareFileuploadParams): Promise<RegistryApiTypes.PrepareFileuploadOKResponse>;
|
|
13
|
+
pushApi({ organizationId, name, version, rootFilePath, filePaths, branch, isUpsert, }: RegistryApiTypes.PushApiParams): Promise<void>;
|
|
14
|
+
}
|
|
@@ -0,0 +1,105 @@
|
|
|
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
|
+
exports.RegistryApi = void 0;
|
|
13
|
+
const node_fetch_1 = require("node-fetch");
|
|
14
|
+
const config_1 = require("../config/config");
|
|
15
|
+
const utils_1 = require("../utils");
|
|
16
|
+
const version = require('../../package.json').version;
|
|
17
|
+
class RegistryApi {
|
|
18
|
+
constructor(accessTokens, region) {
|
|
19
|
+
this.accessTokens = accessTokens;
|
|
20
|
+
this.region = region;
|
|
21
|
+
}
|
|
22
|
+
get accessToken() {
|
|
23
|
+
return utils_1.isNotEmptyObject(this.accessTokens) && this.accessTokens[this.region];
|
|
24
|
+
}
|
|
25
|
+
getBaseUrl(region = config_1.DEFAULT_REGION) {
|
|
26
|
+
return `https://api.${config_1.DOMAINS[region]}/registry`;
|
|
27
|
+
}
|
|
28
|
+
setAccessTokens(accessTokens) {
|
|
29
|
+
this.accessTokens = accessTokens;
|
|
30
|
+
return this;
|
|
31
|
+
}
|
|
32
|
+
request(path = '', options = {}, region) {
|
|
33
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
34
|
+
const headers = Object.assign({}, options.headers || {}, { 'x-redocly-cli-version': version });
|
|
35
|
+
if (!headers.hasOwnProperty('authorization')) {
|
|
36
|
+
throw new Error('Unauthorized');
|
|
37
|
+
}
|
|
38
|
+
const response = yield node_fetch_1.default(`${this.getBaseUrl(region)}${path}`, Object.assign({}, options, { headers }));
|
|
39
|
+
if (response.status === 401) {
|
|
40
|
+
throw new Error('Unauthorized');
|
|
41
|
+
}
|
|
42
|
+
if (response.status === 404) {
|
|
43
|
+
const body = yield response.json();
|
|
44
|
+
throw new Error(body.code);
|
|
45
|
+
}
|
|
46
|
+
return response;
|
|
47
|
+
});
|
|
48
|
+
}
|
|
49
|
+
authStatus(accessToken, region, verbose = false) {
|
|
50
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
51
|
+
try {
|
|
52
|
+
const response = yield this.request('', { headers: { authorization: accessToken } }, region);
|
|
53
|
+
return response.ok;
|
|
54
|
+
}
|
|
55
|
+
catch (error) {
|
|
56
|
+
if (verbose) {
|
|
57
|
+
console.log(error);
|
|
58
|
+
}
|
|
59
|
+
return false;
|
|
60
|
+
}
|
|
61
|
+
});
|
|
62
|
+
}
|
|
63
|
+
prepareFileUpload({ organizationId, name, version, filesHash, filename, isUpsert, }) {
|
|
64
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
65
|
+
const response = yield this.request(`/${organizationId}/${name}/${version}/prepare-file-upload`, {
|
|
66
|
+
method: 'POST',
|
|
67
|
+
headers: {
|
|
68
|
+
'content-type': 'application/json',
|
|
69
|
+
authorization: this.accessToken,
|
|
70
|
+
},
|
|
71
|
+
body: JSON.stringify({
|
|
72
|
+
filesHash,
|
|
73
|
+
filename,
|
|
74
|
+
isUpsert,
|
|
75
|
+
}),
|
|
76
|
+
}, this.region);
|
|
77
|
+
if (response.ok) {
|
|
78
|
+
return response.json();
|
|
79
|
+
}
|
|
80
|
+
throw new Error('Could not prepare file upload');
|
|
81
|
+
});
|
|
82
|
+
}
|
|
83
|
+
pushApi({ organizationId, name, version, rootFilePath, filePaths, branch, isUpsert, }) {
|
|
84
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
85
|
+
const response = yield this.request(`/${organizationId}/${name}/${version}`, {
|
|
86
|
+
method: 'PUT',
|
|
87
|
+
headers: {
|
|
88
|
+
'content-type': 'application/json',
|
|
89
|
+
authorization: this.accessToken
|
|
90
|
+
},
|
|
91
|
+
body: JSON.stringify({
|
|
92
|
+
rootFilePath,
|
|
93
|
+
filePaths,
|
|
94
|
+
branch,
|
|
95
|
+
isUpsert,
|
|
96
|
+
}),
|
|
97
|
+
}, this.region);
|
|
98
|
+
if (response.ok) {
|
|
99
|
+
return;
|
|
100
|
+
}
|
|
101
|
+
throw new Error('Could not push api');
|
|
102
|
+
});
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
exports.RegistryApi = RegistryApi;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const NoInvalidParameterExamples: any;
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.NoInvalidParameterExamples = void 0;
|
|
4
|
+
const utils_1 = require("../utils");
|
|
5
|
+
const NoInvalidParameterExamples = (opts) => {
|
|
6
|
+
var _a;
|
|
7
|
+
const disallowAdditionalProperties = (_a = opts.disallowAdditionalProperties) !== null && _a !== void 0 ? _a : true;
|
|
8
|
+
return {
|
|
9
|
+
Parameter: {
|
|
10
|
+
leave(parameter, ctx) {
|
|
11
|
+
if (parameter.example) {
|
|
12
|
+
utils_1.validateExample(parameter.example, parameter.schema, ctx.location.child('example'), ctx, disallowAdditionalProperties);
|
|
13
|
+
}
|
|
14
|
+
if (parameter.examples) {
|
|
15
|
+
for (const [key, example] of Object.entries(parameter.examples)) {
|
|
16
|
+
if ('value' in example) {
|
|
17
|
+
utils_1.validateExample(example.value, parameter.schema, ctx.location.child(['examples', key]), ctx, false);
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
},
|
|
22
|
+
},
|
|
23
|
+
};
|
|
24
|
+
};
|
|
25
|
+
exports.NoInvalidParameterExamples = NoInvalidParameterExamples;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const NoInvalidSchemaExamples: any;
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.NoInvalidSchemaExamples = void 0;
|
|
4
|
+
const utils_1 = require("../utils");
|
|
5
|
+
const NoInvalidSchemaExamples = (opts) => {
|
|
6
|
+
var _a;
|
|
7
|
+
const disallowAdditionalProperties = (_a = opts.disallowAdditionalProperties) !== null && _a !== void 0 ? _a : true;
|
|
8
|
+
return {
|
|
9
|
+
Schema: {
|
|
10
|
+
leave(schema, ctx) {
|
|
11
|
+
if (schema.examples) {
|
|
12
|
+
for (const example of schema.examples) {
|
|
13
|
+
utils_1.validateExample(example, schema, ctx.location.child(['examples', schema.examples.indexOf(example)]), ctx, disallowAdditionalProperties);
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
if (schema.example) {
|
|
17
|
+
utils_1.validateExample(schema.example, schema, ctx.location.child('example'), ctx, false);
|
|
18
|
+
}
|
|
19
|
+
},
|
|
20
|
+
},
|
|
21
|
+
};
|
|
22
|
+
};
|
|
23
|
+
exports.NoInvalidSchemaExamples = NoInvalidSchemaExamples;
|
|
@@ -4,7 +4,7 @@ exports.PathsKebabCase = void 0;
|
|
|
4
4
|
const PathsKebabCase = () => {
|
|
5
5
|
return {
|
|
6
6
|
PathItem(_path, { report, key }) {
|
|
7
|
-
const segments = key.substr(1).split('/');
|
|
7
|
+
const segments = key.substr(1).split('/').filter(s => s !== ''); // filter out empty segments
|
|
8
8
|
if (!segments.every((segment) => /^{.+}$/.test(segment) || /^[a-z0-9-.]+$/.test(segment))) {
|
|
9
9
|
report({
|
|
10
10
|
message: `\`${key}\` does not use kebab-case.`,
|
|
@@ -3,21 +3,18 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.RegistryDependencies = void 0;
|
|
4
4
|
const redocly_1 = require("../../redocly");
|
|
5
5
|
const RegistryDependencies = () => {
|
|
6
|
-
let redoclyClient;
|
|
7
6
|
let registryDependencies = new Set();
|
|
8
7
|
return {
|
|
9
8
|
DefinitionRoot: {
|
|
10
|
-
leave() {
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
redoclyClient.updateDependencies(Array.from(registryDependencies.keys()));
|
|
14
|
-
}
|
|
9
|
+
leave(_, ctx) {
|
|
10
|
+
const data = ctx.getVisitorData();
|
|
11
|
+
data.links = Array.from(registryDependencies);
|
|
15
12
|
},
|
|
16
13
|
},
|
|
17
14
|
ref(node) {
|
|
18
15
|
if (node.$ref) {
|
|
19
16
|
const link = node.$ref.split('#/')[0];
|
|
20
|
-
if (redocly_1.
|
|
17
|
+
if (redocly_1.isRedoclyRegistryURL(link)) {
|
|
21
18
|
registryDependencies.add(link);
|
|
22
19
|
}
|
|
23
20
|
}
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import { Oas2Decorator, Oas2Rule } from '../../visitors';
|
|
2
2
|
export declare const rules: {
|
|
3
3
|
spec: Oas2Rule;
|
|
4
|
+
'no-invalid-schema-examples': any;
|
|
5
|
+
'no-invalid-parameter-examples': any;
|
|
4
6
|
'info-description': Oas2Rule;
|
|
5
7
|
'info-contact': Oas2Rule;
|
|
6
8
|
'info-license': Oas2Rule;
|
package/lib/rules/oas2/index.js
CHANGED
|
@@ -2,6 +2,8 @@
|
|
|
2
2
|
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
|
+
const no_invalid_schema_examples_1 = require("../common/no-invalid-schema-examples");
|
|
6
|
+
const no_invalid_parameter_examples_1 = require("../common/no-invalid-parameter-examples");
|
|
5
7
|
const info_description_1 = require("../common/info-description");
|
|
6
8
|
const info_contact_1 = require("../common/info-contact");
|
|
7
9
|
const info_license_url_1 = require("../common/info-license-url");
|
|
@@ -42,6 +44,8 @@ const tag_description_override_1 = require("../common/tag-description-override")
|
|
|
42
44
|
const info_description_override_1 = require("../common/info-description-override");
|
|
43
45
|
exports.rules = {
|
|
44
46
|
spec: spec_1.OasSpec,
|
|
47
|
+
'no-invalid-schema-examples': no_invalid_schema_examples_1.NoInvalidSchemaExamples,
|
|
48
|
+
'no-invalid-parameter-examples': no_invalid_parameter_examples_1.NoInvalidParameterExamples,
|
|
45
49
|
'info-description': info_description_1.InfoDescription,
|
|
46
50
|
'info-contact': info_contact_1.InfoContact,
|
|
47
51
|
'info-license': info_license_url_1.InfoLicense,
|
package/lib/rules/oas3/index.js
CHANGED
|
@@ -48,6 +48,8 @@ const operation_description_override_1 = require("../common/operation-descriptio
|
|
|
48
48
|
const tag_description_override_1 = require("../common/tag-description-override");
|
|
49
49
|
const info_description_override_1 = require("../common/info-description-override");
|
|
50
50
|
const path_excludes_patterns_1 = require("../common/path-excludes-patterns");
|
|
51
|
+
const no_invalid_schema_examples_1 = require("../common/no-invalid-schema-examples");
|
|
52
|
+
const no_invalid_parameter_examples_1 = require("../common/no-invalid-parameter-examples");
|
|
51
53
|
exports.rules = {
|
|
52
54
|
spec: spec_1.OasSpec,
|
|
53
55
|
'info-description': info_description_1.InfoDescription,
|
|
@@ -93,6 +95,8 @@ exports.rules = {
|
|
|
93
95
|
'request-mime-type': request_mime_type_1.RequestMimeType,
|
|
94
96
|
'response-mime-type': response_mime_type_1.ResponseMimeType,
|
|
95
97
|
'path-segment-plural': path_segment_plural_1.PathSegmentPlural,
|
|
98
|
+
'no-invalid-schema-examples': no_invalid_schema_examples_1.NoInvalidSchemaExamples,
|
|
99
|
+
'no-invalid-parameter-examples': no_invalid_parameter_examples_1.NoInvalidParameterExamples,
|
|
96
100
|
};
|
|
97
101
|
exports.preprocessors = {};
|
|
98
102
|
exports.decorators = {
|
|
@@ -1,18 +1,19 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.ValidContentExamples = void 0;
|
|
4
|
-
const ajv_1 = require("../ajv");
|
|
5
4
|
const ref_utils_1 = require("../../ref-utils");
|
|
5
|
+
const utils_1 = require("../utils");
|
|
6
6
|
const ValidContentExamples = (opts) => {
|
|
7
7
|
var _a;
|
|
8
8
|
const disallowAdditionalProperties = (_a = opts.disallowAdditionalProperties) !== null && _a !== void 0 ? _a : true;
|
|
9
9
|
return {
|
|
10
10
|
MediaType: {
|
|
11
|
-
leave(mediaType,
|
|
11
|
+
leave(mediaType, ctx) {
|
|
12
|
+
const { location, resolve } = ctx;
|
|
12
13
|
if (!mediaType.schema)
|
|
13
14
|
return;
|
|
14
15
|
if (mediaType.example) {
|
|
15
|
-
validateExample(mediaType.example, location.child('example'));
|
|
16
|
+
utils_1.validateExample(mediaType.example, mediaType.schema, location.child('example'), ctx, disallowAdditionalProperties);
|
|
16
17
|
}
|
|
17
18
|
else if (mediaType.examples) {
|
|
18
19
|
for (const exampleName of Object.keys(mediaType.examples)) {
|
|
@@ -25,29 +26,7 @@ const ValidContentExamples = (opts) => {
|
|
|
25
26
|
dataLoc = resolved.location.child('value');
|
|
26
27
|
example = resolved.node;
|
|
27
28
|
}
|
|
28
|
-
validateExample(example.value, dataLoc);
|
|
29
|
-
}
|
|
30
|
-
}
|
|
31
|
-
function validateExample(example, dataLoc) {
|
|
32
|
-
try {
|
|
33
|
-
const { valid, errors } = ajv_1.validateJsonSchema(example, mediaType.schema, location.child('schema'), dataLoc.pointer, resolve, disallowAdditionalProperties);
|
|
34
|
-
if (!valid) {
|
|
35
|
-
for (let error of errors) {
|
|
36
|
-
report({
|
|
37
|
-
message: `Example value must conform to the schema: ${error.message}.`,
|
|
38
|
-
location: Object.assign(Object.assign({}, new ref_utils_1.Location(dataLoc.source, error.instancePath)), { reportOnKey: error.keyword === 'additionalProperties' }),
|
|
39
|
-
from: location,
|
|
40
|
-
suggest: error.suggest,
|
|
41
|
-
});
|
|
42
|
-
}
|
|
43
|
-
}
|
|
44
|
-
}
|
|
45
|
-
catch (e) {
|
|
46
|
-
report({
|
|
47
|
-
message: `Example validation errored: ${e.message}.`,
|
|
48
|
-
location: location.child('schema'),
|
|
49
|
-
from: location
|
|
50
|
-
});
|
|
29
|
+
utils_1.validateExample(example.value, mediaType.schema, dataLoc, ctx, disallowAdditionalProperties);
|
|
51
30
|
}
|
|
52
31
|
}
|
|
53
32
|
},
|
package/lib/rules/utils.d.ts
CHANGED
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
import { UserContext } from '../walk';
|
|
2
|
+
import { Location } from '../ref-utils';
|
|
3
|
+
import { Oas3Schema, Referenced } from '../typings/openapi';
|
|
2
4
|
export declare function oasTypeOf(value: unknown): "string" | "number" | "bigint" | "boolean" | "symbol" | "undefined" | "object" | "function" | "array" | "null";
|
|
3
5
|
/**
|
|
4
6
|
* Checks if value matches specified JSON schema type
|
|
@@ -12,3 +14,4 @@ export declare function missingRequiredField(type: string, field: string): strin
|
|
|
12
14
|
export declare function fieldNonEmpty(type: string, field: string): string;
|
|
13
15
|
export declare function validateDefinedAndNonEmpty(fieldName: string, value: any, ctx: UserContext): void;
|
|
14
16
|
export declare function getSuggest(given: string, variants: string[]): string[];
|
|
17
|
+
export declare function validateExample(example: any, schema: Referenced<Oas3Schema>, dataLoc: Location, { resolve, location, report }: UserContext, disallowAdditionalProperties: boolean): void;
|
package/lib/rules/utils.js
CHANGED
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.getSuggest = exports.validateDefinedAndNonEmpty = exports.fieldNonEmpty = exports.missingRequiredField = exports.matchesJsonSchemaType = exports.oasTypeOf = void 0;
|
|
3
|
+
exports.validateExample = exports.getSuggest = exports.validateDefinedAndNonEmpty = exports.fieldNonEmpty = exports.missingRequiredField = exports.matchesJsonSchemaType = exports.oasTypeOf = void 0;
|
|
4
4
|
const levenshtein = require("js-levenshtein");
|
|
5
|
+
const ref_utils_1 = require("../ref-utils");
|
|
6
|
+
const ajv_1 = require("./ajv");
|
|
5
7
|
function oasTypeOf(value) {
|
|
6
8
|
if (Array.isArray(value)) {
|
|
7
9
|
return 'array';
|
|
@@ -80,3 +82,26 @@ function getSuggest(given, variants) {
|
|
|
80
82
|
return distances.map((d) => d.variant);
|
|
81
83
|
}
|
|
82
84
|
exports.getSuggest = getSuggest;
|
|
85
|
+
function validateExample(example, schema, dataLoc, { resolve, location, report }, disallowAdditionalProperties) {
|
|
86
|
+
try {
|
|
87
|
+
const { valid, errors } = ajv_1.validateJsonSchema(example, schema, location.child('schema'), dataLoc.pointer, resolve, disallowAdditionalProperties);
|
|
88
|
+
if (!valid) {
|
|
89
|
+
for (let error of errors) {
|
|
90
|
+
report({
|
|
91
|
+
message: `Example value must conform to the schema: ${error.message}.`,
|
|
92
|
+
location: Object.assign(Object.assign({}, new ref_utils_1.Location(dataLoc.source, error.instancePath)), { reportOnKey: error.keyword === 'additionalProperties' }),
|
|
93
|
+
from: location,
|
|
94
|
+
suggest: error.suggest,
|
|
95
|
+
});
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
catch (e) {
|
|
100
|
+
report({
|
|
101
|
+
message: `Example validation errored: ${e.message}.`,
|
|
102
|
+
location: location.child('schema'),
|
|
103
|
+
from: location,
|
|
104
|
+
});
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
exports.validateExample = validateExample;
|
package/lib/typings/openapi.d.ts
CHANGED
package/lib/utils.d.ts
CHANGED
package/lib/utils.js
CHANGED
|
@@ -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
|
-
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;
|
|
12
|
+
exports.isNotEmptyObject = 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");
|
|
@@ -144,3 +144,7 @@ function slash(path) {
|
|
|
144
144
|
return path.replace(/\\/g, '/');
|
|
145
145
|
}
|
|
146
146
|
exports.slash = slash;
|
|
147
|
+
function isNotEmptyObject(obj) {
|
|
148
|
+
return !!obj && Object.keys(obj).length > 0;
|
|
149
|
+
}
|
|
150
|
+
exports.isNotEmptyObject = isNotEmptyObject;
|
package/lib/walk.d.ts
CHANGED
|
@@ -36,6 +36,7 @@ export declare type UserContext = {
|
|
|
36
36
|
key: string | number;
|
|
37
37
|
parent: any;
|
|
38
38
|
oasVersion: OasVersion;
|
|
39
|
+
getVisitorData: () => Record<string, unknown>;
|
|
39
40
|
};
|
|
40
41
|
export declare type Loc = {
|
|
41
42
|
line: number;
|
|
@@ -72,6 +73,7 @@ export declare type NormalizedProblem = {
|
|
|
72
73
|
export declare type WalkContext = {
|
|
73
74
|
problems: NormalizedProblem[];
|
|
74
75
|
oasVersion: OasVersion;
|
|
76
|
+
visitorsData: Record<string, Record<string, unknown>>;
|
|
75
77
|
refTypes?: Map<string, NormalizedNodeType>;
|
|
76
78
|
};
|
|
77
79
|
export declare function walkDocument<T>(opts: {
|
package/lib/walk.js
CHANGED
|
@@ -50,6 +50,7 @@ function walkDocument(opts) {
|
|
|
50
50
|
key,
|
|
51
51
|
parentLocations: {},
|
|
52
52
|
oasVersion: ctx.oasVersion,
|
|
53
|
+
getVisitorData: getVisitorDataFn.bind(undefined, ruleId)
|
|
53
54
|
}, { node: resolvedNode, location: resolvedLocation, error });
|
|
54
55
|
if ((resolvedLocation === null || resolvedLocation === void 0 ? void 0 : resolvedLocation.source.absoluteRef) && ctx.refTypes) {
|
|
55
56
|
ctx.refTypes.set(resolvedLocation === null || resolvedLocation === void 0 ? void 0 : resolvedLocation.source.absoluteRef, type);
|
|
@@ -192,6 +193,7 @@ function walkDocument(opts) {
|
|
|
192
193
|
key,
|
|
193
194
|
parentLocations: {},
|
|
194
195
|
oasVersion: ctx.oasVersion,
|
|
196
|
+
getVisitorData: getVisitorDataFn.bind(undefined, ruleId)
|
|
195
197
|
}, { node: resolvedNode, location: resolvedLocation, error });
|
|
196
198
|
}
|
|
197
199
|
}
|
|
@@ -212,6 +214,7 @@ function walkDocument(opts) {
|
|
|
212
214
|
ignoreNextVisitorsOnNode: () => {
|
|
213
215
|
ignoreNextVisitorsOnNode = true;
|
|
214
216
|
},
|
|
217
|
+
getVisitorData: getVisitorDataFn.bind(undefined, ruleId),
|
|
215
218
|
}, collectParents(context), context);
|
|
216
219
|
return ignoreNextVisitorsOnNode;
|
|
217
220
|
}
|
|
@@ -244,6 +247,10 @@ function walkDocument(opts) {
|
|
|
244
247
|
return Object.assign(Object.assign(Object.assign({}, currentLocation), { reportOnKey: false }), loc);
|
|
245
248
|
}) }));
|
|
246
249
|
}
|
|
250
|
+
function getVisitorDataFn(ruleId) {
|
|
251
|
+
ctx.visitorsData[ruleId] = ctx.visitorsData[ruleId] || {};
|
|
252
|
+
return ctx.visitorsData[ruleId];
|
|
253
|
+
}
|
|
247
254
|
}
|
|
248
255
|
}
|
|
249
256
|
exports.walkDocument = walkDocument;
|
package/package.json
CHANGED
package/src/bundle.ts
CHANGED
|
@@ -13,6 +13,7 @@ import { initRules } from './config/rules';
|
|
|
13
13
|
import { reportUnresolvedRef } from './rules/no-unresolved-refs';
|
|
14
14
|
import { isPlainObject } from './utils';
|
|
15
15
|
import { OasRef } from './typings/openapi';
|
|
16
|
+
import { isRedoclyRegistryURL } from './redocly';
|
|
16
17
|
|
|
17
18
|
export type Oas3RuleSet = Record<string, Oas3Rule>;
|
|
18
19
|
|
|
@@ -29,6 +30,7 @@ export async function bundle(opts: {
|
|
|
29
30
|
config: Config;
|
|
30
31
|
dereference?: boolean;
|
|
31
32
|
base?: string;
|
|
33
|
+
skipRedoclyRegistryRefs?: boolean;
|
|
32
34
|
}) {
|
|
33
35
|
const {
|
|
34
36
|
ref,
|
|
@@ -63,8 +65,16 @@ export async function bundleDocument(opts: {
|
|
|
63
65
|
customTypes?: Record<string, NodeType>;
|
|
64
66
|
externalRefResolver: BaseResolver;
|
|
65
67
|
dereference?: boolean;
|
|
68
|
+
skipRedoclyRegistryRefs?: boolean;
|
|
66
69
|
}) {
|
|
67
|
-
const {
|
|
70
|
+
const {
|
|
71
|
+
document,
|
|
72
|
+
config,
|
|
73
|
+
customTypes,
|
|
74
|
+
externalRefResolver,
|
|
75
|
+
dereference = false,
|
|
76
|
+
skipRedoclyRegistryRefs = false,
|
|
77
|
+
} = opts;
|
|
68
78
|
const oasVersion = detectOpenAPI(document.parsed);
|
|
69
79
|
const oasMajorVersion = openAPIMajor(oasVersion);
|
|
70
80
|
const rules = config.getRulesForOasVersion(oasMajorVersion);
|
|
@@ -86,6 +96,7 @@ export async function bundleDocument(opts: {
|
|
|
86
96
|
problems: [],
|
|
87
97
|
oasVersion: oasVersion,
|
|
88
98
|
refTypes: new Map<string, NormalizedNodeType>(),
|
|
99
|
+
visitorsData: {},
|
|
89
100
|
};
|
|
90
101
|
|
|
91
102
|
const bundleVisitor = normalizeVisitors(
|
|
@@ -94,7 +105,7 @@ export async function bundleDocument(opts: {
|
|
|
94
105
|
{
|
|
95
106
|
severity: 'error',
|
|
96
107
|
ruleId: 'bundler',
|
|
97
|
-
visitor: makeBundleVisitor(oasMajorVersion, dereference, document),
|
|
108
|
+
visitor: makeBundleVisitor(oasMajorVersion, dereference, skipRedoclyRegistryRefs, document),
|
|
98
109
|
},
|
|
99
110
|
...decorators,
|
|
100
111
|
],
|
|
@@ -121,6 +132,7 @@ export async function bundleDocument(opts: {
|
|
|
121
132
|
fileDependencies: externalRefResolver.getFiles(),
|
|
122
133
|
rootType: types.DefinitionRoot,
|
|
123
134
|
refTypes: ctx.refTypes,
|
|
135
|
+
visitorsData: ctx.visitorsData,
|
|
124
136
|
};
|
|
125
137
|
}
|
|
126
138
|
|
|
@@ -165,7 +177,12 @@ function mapTypeToComponent(typeName: string, version: OasMajorVersion) {
|
|
|
165
177
|
|
|
166
178
|
// function oas3Move
|
|
167
179
|
|
|
168
|
-
function makeBundleVisitor(
|
|
180
|
+
function makeBundleVisitor(
|
|
181
|
+
version: OasMajorVersion,
|
|
182
|
+
dereference: boolean,
|
|
183
|
+
skipRedoclyRegistryRefs: boolean,
|
|
184
|
+
rootDocument: Document,
|
|
185
|
+
) {
|
|
169
186
|
let components: Record<string, Record<string, any>>;
|
|
170
187
|
|
|
171
188
|
const visitor: Oas3Visitor | Oas2Visitor = {
|
|
@@ -183,6 +200,11 @@ function makeBundleVisitor(version: OasMajorVersion, dereference: boolean, rootD
|
|
|
183
200
|
) {
|
|
184
201
|
return;
|
|
185
202
|
}
|
|
203
|
+
|
|
204
|
+
if (skipRedoclyRegistryRefs && isRedoclyRegistryURL(node.$ref)) {
|
|
205
|
+
return;
|
|
206
|
+
}
|
|
207
|
+
|
|
186
208
|
const componentType = mapTypeToComponent(ctx.type.name, version);
|
|
187
209
|
if (!componentType) {
|
|
188
210
|
replaceRef(node, resolved, ctx);
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { loadConfig } from '../load';
|
|
2
|
+
import { RedoclyClient } from '../../redocly';
|
|
3
|
+
|
|
4
|
+
describe('loadConfig', () => {
|
|
5
|
+
it('should resolve config http header by US region', async () => {
|
|
6
|
+
jest.spyOn(RedoclyClient.prototype, 'getTokens').mockImplementation(
|
|
7
|
+
() => Promise.resolve([{ region: 'us', token: "accessToken", valid: true }])
|
|
8
|
+
);
|
|
9
|
+
const config = await loadConfig();
|
|
10
|
+
expect(config.resolve.http.headers).toStrictEqual([{
|
|
11
|
+
"matches": 'https://api.redoc.ly/registry/**',
|
|
12
|
+
"name": "Authorization",
|
|
13
|
+
"envVariable": undefined,
|
|
14
|
+
"value": "accessToken"
|
|
15
|
+
}, {
|
|
16
|
+
"matches": 'https://api.redocly.com/registry/**',
|
|
17
|
+
"name": "Authorization",
|
|
18
|
+
"envVariable": undefined,
|
|
19
|
+
"value": "accessToken"
|
|
20
|
+
}]);
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
it('should resolve config http header by EU region', async () => {
|
|
24
|
+
jest.spyOn(RedoclyClient.prototype, 'getTokens').mockImplementation(
|
|
25
|
+
() => Promise.resolve([{ region: 'eu', token: "accessToken", valid: true }])
|
|
26
|
+
);
|
|
27
|
+
const config = await loadConfig();
|
|
28
|
+
expect(config.resolve.http.headers).toStrictEqual([{
|
|
29
|
+
"matches": 'https://api.eu.redocly.com/registry/**',
|
|
30
|
+
"name": "Authorization",
|
|
31
|
+
"envVariable": undefined,
|
|
32
|
+
"value": "accessToken"
|
|
33
|
+
}]);
|
|
34
|
+
});
|
|
35
|
+
});
|
package/src/config/all.ts
CHANGED
package/src/config/config.ts
CHANGED
|
@@ -123,11 +123,20 @@ export type ResolveConfig = {
|
|
|
123
123
|
http: HttpResolveConfig;
|
|
124
124
|
};
|
|
125
125
|
|
|
126
|
+
export const DEFAULT_REGION = 'us';
|
|
127
|
+
export type Region = 'us' | 'eu';
|
|
128
|
+
export type AccessTokens = {[region in Region]?: string };
|
|
129
|
+
export const DOMAINS: { [region in Region]: string } = {
|
|
130
|
+
us: 'redoc.ly',
|
|
131
|
+
eu: 'eu.redocly.com',
|
|
132
|
+
};
|
|
133
|
+
|
|
126
134
|
export type RawConfig = {
|
|
127
135
|
referenceDocs?: any;
|
|
128
136
|
apiDefinitions?: Record<string, string>;
|
|
129
137
|
lint?: LintRawConfig;
|
|
130
138
|
resolve?: RawResolveConfig;
|
|
139
|
+
region?: Region;
|
|
131
140
|
};
|
|
132
141
|
|
|
133
142
|
export class LintConfig {
|
|
@@ -385,6 +394,7 @@ export class Config {
|
|
|
385
394
|
lint: LintConfig;
|
|
386
395
|
resolve: ResolveConfig;
|
|
387
396
|
licenseKey?: string;
|
|
397
|
+
region?: Region;
|
|
388
398
|
constructor(public rawConfig: RawConfig, public configFile?: string) {
|
|
389
399
|
this.apiDefinitions = rawConfig.apiDefinitions || {};
|
|
390
400
|
this.lint = new LintConfig(rawConfig.lint || {}, configFile);
|
|
@@ -395,6 +405,7 @@ export class Config {
|
|
|
395
405
|
customFetch: undefined,
|
|
396
406
|
},
|
|
397
407
|
};
|
|
408
|
+
this.region = rawConfig.region;
|
|
398
409
|
}
|
|
399
410
|
}
|
|
400
411
|
|