@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,110 @@
|
|
|
1
|
+
import fetch, { RequestInit, HeadersInit } from 'node-fetch';
|
|
2
|
+
import { RegistryApiTypes } from './registry-api-types';
|
|
3
|
+
import { AccessTokens, Region, DEFAULT_REGION, DOMAINS } from '../config/config';
|
|
4
|
+
import { isNotEmptyObject } from '../utils';
|
|
5
|
+
const version = require('../../package.json').version;
|
|
6
|
+
|
|
7
|
+
export class RegistryApi {
|
|
8
|
+
constructor(private accessTokens: AccessTokens, private region: Region) {}
|
|
9
|
+
|
|
10
|
+
get accessToken() {
|
|
11
|
+
return isNotEmptyObject(this.accessTokens) && this.accessTokens[this.region];
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
getBaseUrl(region: Region = DEFAULT_REGION) {
|
|
15
|
+
return `https://api.${DOMAINS[region]}/registry`;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
setAccessTokens(accessTokens: AccessTokens) {
|
|
19
|
+
this.accessTokens = accessTokens;
|
|
20
|
+
return this;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
private async request(path = '', options: RequestInit = {}, region?: Region) {
|
|
24
|
+
const headers = Object.assign({}, options.headers || {}, { 'x-redocly-cli-version': version });
|
|
25
|
+
if (!headers.hasOwnProperty('authorization')) { throw new Error('Unauthorized'); }
|
|
26
|
+
const response = await fetch(`${this.getBaseUrl(region)}${path}`, Object.assign({}, options, { headers }));
|
|
27
|
+
if (response.status === 401) { throw new Error('Unauthorized'); }
|
|
28
|
+
if (response.status === 404) {
|
|
29
|
+
const body: RegistryApiTypes.NotFoundProblemResponse = await response.json();
|
|
30
|
+
throw new Error(body.code);
|
|
31
|
+
}
|
|
32
|
+
return response;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
async authStatus(accessToken: string, region: Region, verbose = false) {
|
|
36
|
+
try {
|
|
37
|
+
const response = await this.request('', { headers: { authorization: accessToken }}, region);
|
|
38
|
+
return response.ok;
|
|
39
|
+
} catch (error) {
|
|
40
|
+
if (verbose) {
|
|
41
|
+
console.log(error);
|
|
42
|
+
}
|
|
43
|
+
return false;
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
async prepareFileUpload({
|
|
48
|
+
organizationId,
|
|
49
|
+
name,
|
|
50
|
+
version,
|
|
51
|
+
filesHash,
|
|
52
|
+
filename,
|
|
53
|
+
isUpsert,
|
|
54
|
+
}: RegistryApiTypes.PrepareFileuploadParams): Promise<RegistryApiTypes.PrepareFileuploadOKResponse> {
|
|
55
|
+
const response = await this.request(
|
|
56
|
+
`/${organizationId}/${name}/${version}/prepare-file-upload`,
|
|
57
|
+
{
|
|
58
|
+
method: 'POST',
|
|
59
|
+
headers: {
|
|
60
|
+
'content-type': 'application/json',
|
|
61
|
+
authorization: this.accessToken,
|
|
62
|
+
} as HeadersInit,
|
|
63
|
+
body: JSON.stringify({
|
|
64
|
+
filesHash,
|
|
65
|
+
filename,
|
|
66
|
+
isUpsert,
|
|
67
|
+
}),
|
|
68
|
+
},
|
|
69
|
+
this.region
|
|
70
|
+
);
|
|
71
|
+
|
|
72
|
+
if (response.ok) {
|
|
73
|
+
return response.json();
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
throw new Error('Could not prepare file upload');
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
async pushApi({
|
|
80
|
+
organizationId,
|
|
81
|
+
name,
|
|
82
|
+
version,
|
|
83
|
+
rootFilePath,
|
|
84
|
+
filePaths,
|
|
85
|
+
branch,
|
|
86
|
+
isUpsert,
|
|
87
|
+
}: RegistryApiTypes.PushApiParams) {
|
|
88
|
+
const response = await this.request(`/${organizationId}/${name}/${version}`, {
|
|
89
|
+
method: 'PUT',
|
|
90
|
+
headers: {
|
|
91
|
+
'content-type': 'application/json',
|
|
92
|
+
authorization: this.accessToken
|
|
93
|
+
} as HeadersInit,
|
|
94
|
+
body: JSON.stringify({
|
|
95
|
+
rootFilePath,
|
|
96
|
+
filePaths,
|
|
97
|
+
branch,
|
|
98
|
+
isUpsert,
|
|
99
|
+
}),
|
|
100
|
+
},
|
|
101
|
+
this.region
|
|
102
|
+
);
|
|
103
|
+
|
|
104
|
+
if (response.ok) {
|
|
105
|
+
return;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
throw new Error('Could not push api');
|
|
109
|
+
}
|
|
110
|
+
}
|
|
@@ -83,4 +83,27 @@ describe('Oas3 paths-kebab-case', () => {
|
|
|
83
83
|
]
|
|
84
84
|
`);
|
|
85
85
|
});
|
|
86
|
+
|
|
87
|
+
it('should allow trailing slash in path with "paths-kebab-case" rule', async () => {
|
|
88
|
+
const document = parseYamlToDocument(
|
|
89
|
+
outdent`
|
|
90
|
+
openapi: 3.0.0
|
|
91
|
+
paths:
|
|
92
|
+
/some/:
|
|
93
|
+
get:
|
|
94
|
+
summary: List all pets
|
|
95
|
+
`,
|
|
96
|
+
'foobar.yaml',
|
|
97
|
+
);
|
|
98
|
+
|
|
99
|
+
const results = await lintDocument({
|
|
100
|
+
externalRefResolver: new BaseResolver(),
|
|
101
|
+
document,
|
|
102
|
+
config: makeConfig({
|
|
103
|
+
'paths-kebab-case': 'error',
|
|
104
|
+
'no-path-trailing-slash': 'off',
|
|
105
|
+
}),
|
|
106
|
+
});
|
|
107
|
+
expect(replaceSourceWithRef(results)).toMatchInlineSnapshot(`Array []`);
|
|
108
|
+
});
|
|
86
109
|
});
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { UserContext } from '../../walk';
|
|
2
|
+
import { Oas3Parameter } from '../../typings/openapi';
|
|
3
|
+
import { validateExample } from '../utils';
|
|
4
|
+
|
|
5
|
+
export const NoInvalidParameterExamples: any = (opts: any) => {
|
|
6
|
+
const disallowAdditionalProperties = opts.disallowAdditionalProperties ?? true;
|
|
7
|
+
return {
|
|
8
|
+
Parameter: {
|
|
9
|
+
leave(parameter: Oas3Parameter, ctx: UserContext) {
|
|
10
|
+
if (parameter.example) {
|
|
11
|
+
validateExample(
|
|
12
|
+
parameter.example,
|
|
13
|
+
parameter.schema!,
|
|
14
|
+
ctx.location.child('example'),
|
|
15
|
+
ctx,
|
|
16
|
+
disallowAdditionalProperties,
|
|
17
|
+
);
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
if (parameter.examples) {
|
|
21
|
+
for (const [key, example] of Object.entries(parameter.examples)) {
|
|
22
|
+
if ('value' in example) {
|
|
23
|
+
validateExample(
|
|
24
|
+
example.value,
|
|
25
|
+
parameter.schema!,
|
|
26
|
+
ctx.location.child(['examples', key]),
|
|
27
|
+
ctx,
|
|
28
|
+
false,
|
|
29
|
+
);
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
},
|
|
34
|
+
},
|
|
35
|
+
};
|
|
36
|
+
};
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { UserContext } from '../../walk';
|
|
2
|
+
import { Oas3_1Schema } from '../../typings/openapi';
|
|
3
|
+
import { validateExample } from '../utils';
|
|
4
|
+
|
|
5
|
+
export const NoInvalidSchemaExamples: any = (opts: any) => {
|
|
6
|
+
const disallowAdditionalProperties = opts.disallowAdditionalProperties ?? true;
|
|
7
|
+
return {
|
|
8
|
+
Schema: {
|
|
9
|
+
leave(schema: Oas3_1Schema, ctx: UserContext) {
|
|
10
|
+
if (schema.examples) {
|
|
11
|
+
for (const example of schema.examples) {
|
|
12
|
+
validateExample(
|
|
13
|
+
example,
|
|
14
|
+
schema,
|
|
15
|
+
ctx.location.child(['examples', schema.examples.indexOf(example)]),
|
|
16
|
+
ctx,
|
|
17
|
+
disallowAdditionalProperties,
|
|
18
|
+
);
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
if (schema.example) {
|
|
22
|
+
validateExample(schema.example, schema, ctx.location.child('example'), ctx, false);
|
|
23
|
+
}
|
|
24
|
+
},
|
|
25
|
+
},
|
|
26
|
+
};
|
|
27
|
+
};
|
|
@@ -4,7 +4,7 @@ import { UserContext } from '../../walk';
|
|
|
4
4
|
export const PathsKebabCase: Oas3Rule | Oas2Rule = () => {
|
|
5
5
|
return {
|
|
6
6
|
PathItem(_path: object, { report, key }: UserContext) {
|
|
7
|
-
const segments = (key as string).substr(1).split('/');
|
|
7
|
+
const segments = (key as string).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.`,
|
|
@@ -1,24 +1,22 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { UserContext } from '../../walk';
|
|
2
|
+
import { isRedoclyRegistryURL } from '../../redocly';
|
|
2
3
|
|
|
3
4
|
import { Oas3Decorator, Oas2Decorator } from '../../visitors';
|
|
4
5
|
|
|
5
6
|
export const RegistryDependencies: Oas3Decorator | Oas2Decorator = () => {
|
|
6
|
-
let redoclyClient: RedoclyClient;
|
|
7
7
|
let registryDependencies = new Set<string>();
|
|
8
8
|
|
|
9
9
|
return {
|
|
10
10
|
DefinitionRoot: {
|
|
11
|
-
leave() {
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
redoclyClient.updateDependencies(Array.from(registryDependencies.keys()));
|
|
15
|
-
}
|
|
11
|
+
leave(_: any, ctx: UserContext) {
|
|
12
|
+
const data = ctx.getVisitorData();
|
|
13
|
+
data.links = Array.from(registryDependencies);
|
|
16
14
|
},
|
|
17
15
|
},
|
|
18
16
|
ref(node) {
|
|
19
17
|
if (node.$ref) {
|
|
20
18
|
const link = node.$ref.split('#/')[0];
|
|
21
|
-
if (
|
|
19
|
+
if (isRedoclyRegistryURL(link)) {
|
|
22
20
|
registryDependencies.add(link);
|
|
23
21
|
}
|
|
24
22
|
}
|
package/src/rules/oas2/index.ts
CHANGED
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
import { OasSpec } from '../common/spec';
|
|
2
|
+
import { NoInvalidSchemaExamples } from '../common/no-invalid-schema-examples';
|
|
3
|
+
import { NoInvalidParameterExamples } from '../common/no-invalid-parameter-examples';
|
|
2
4
|
import { InfoDescription } from '../common/info-description';
|
|
3
5
|
import { InfoContact } from '../common/info-contact';
|
|
4
6
|
import { InfoLicense } from '../common/info-license-url';
|
|
@@ -41,6 +43,8 @@ import { InfoDescriptionOverride } from '../common/info-description-override';
|
|
|
41
43
|
|
|
42
44
|
export const rules = {
|
|
43
45
|
spec: OasSpec as Oas2Rule,
|
|
46
|
+
'no-invalid-schema-examples': NoInvalidSchemaExamples,
|
|
47
|
+
'no-invalid-parameter-examples': NoInvalidParameterExamples,
|
|
44
48
|
'info-description': InfoDescription as Oas2Rule,
|
|
45
49
|
'info-contact': InfoContact as Oas2Rule,
|
|
46
50
|
'info-license': InfoLicense as Oas2Rule,
|
package/src/rules/oas3/index.ts
CHANGED
|
@@ -47,6 +47,8 @@ import { OperationDescriptionOverride } from '../common/operation-description-ov
|
|
|
47
47
|
import { TagDescriptionOverride } from '../common/tag-description-override';
|
|
48
48
|
import { InfoDescriptionOverride } from '../common/info-description-override';
|
|
49
49
|
import { PathExcludesPatterns } from '../common/path-excludes-patterns';
|
|
50
|
+
import { NoInvalidSchemaExamples } from '../common/no-invalid-schema-examples';
|
|
51
|
+
import { NoInvalidParameterExamples } from '../common/no-invalid-parameter-examples';
|
|
50
52
|
|
|
51
53
|
export const rules = {
|
|
52
54
|
spec: OasSpec,
|
|
@@ -93,6 +95,8 @@ export const rules = {
|
|
|
93
95
|
'request-mime-type': RequestMimeType,
|
|
94
96
|
'response-mime-type': ResponseMimeType,
|
|
95
97
|
'path-segment-plural': PathSegmentPlural,
|
|
98
|
+
'no-invalid-schema-examples': NoInvalidSchemaExamples,
|
|
99
|
+
'no-invalid-parameter-examples': NoInvalidParameterExamples,
|
|
96
100
|
} as Oas3RuleSet;
|
|
97
101
|
|
|
98
102
|
export const preprocessors = {};
|
|
@@ -1,18 +1,25 @@
|
|
|
1
1
|
import { Oas3Rule } from '../../visitors';
|
|
2
|
-
import { validateJsonSchema } from '../ajv';
|
|
3
2
|
import { Location, isRef } from '../../ref-utils';
|
|
4
3
|
import { Oas3Example } from '../../typings/openapi';
|
|
4
|
+
import { validateExample } from '../utils';
|
|
5
|
+
import { UserContext } from '../../walk';
|
|
5
6
|
|
|
6
7
|
export const ValidContentExamples: Oas3Rule = (opts) => {
|
|
7
8
|
const disallowAdditionalProperties = opts.disallowAdditionalProperties ?? true;
|
|
8
9
|
|
|
9
10
|
return {
|
|
10
11
|
MediaType: {
|
|
11
|
-
leave(mediaType,
|
|
12
|
+
leave(mediaType, ctx: UserContext) {
|
|
13
|
+
const { location, resolve } = ctx;
|
|
12
14
|
if (!mediaType.schema) return;
|
|
13
|
-
|
|
14
15
|
if (mediaType.example) {
|
|
15
|
-
validateExample(
|
|
16
|
+
validateExample(
|
|
17
|
+
mediaType.example,
|
|
18
|
+
mediaType.schema,
|
|
19
|
+
location.child('example'),
|
|
20
|
+
ctx,
|
|
21
|
+
disallowAdditionalProperties,
|
|
22
|
+
);
|
|
16
23
|
} else if (mediaType.examples) {
|
|
17
24
|
for (const exampleName of Object.keys(mediaType.examples)) {
|
|
18
25
|
let example = mediaType.examples[exampleName];
|
|
@@ -23,40 +30,13 @@ export const ValidContentExamples: Oas3Rule = (opts) => {
|
|
|
23
30
|
dataLoc = resolved.location.child('value');
|
|
24
31
|
example = resolved.node;
|
|
25
32
|
}
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
function validateExample(example: any, dataLoc: Location) {
|
|
32
|
-
try {
|
|
33
|
-
const { valid, errors } = validateJsonSchema(
|
|
34
|
-
example,
|
|
35
|
-
mediaType.schema!,
|
|
36
|
-
location.child('schema'),
|
|
37
|
-
dataLoc.pointer,
|
|
38
|
-
resolve,
|
|
33
|
+
validateExample(
|
|
34
|
+
example.value,
|
|
35
|
+
mediaType.schema,
|
|
36
|
+
dataLoc,
|
|
37
|
+
ctx,
|
|
39
38
|
disallowAdditionalProperties,
|
|
40
39
|
);
|
|
41
|
-
if (!valid) {
|
|
42
|
-
for (let error of errors) {
|
|
43
|
-
report({
|
|
44
|
-
message: `Example value must conform to the schema: ${error.message}.`,
|
|
45
|
-
location: {
|
|
46
|
-
...new Location(dataLoc.source, error.instancePath),
|
|
47
|
-
reportOnKey: error.keyword === 'additionalProperties',
|
|
48
|
-
},
|
|
49
|
-
from: location,
|
|
50
|
-
suggest: error.suggest,
|
|
51
|
-
});
|
|
52
|
-
}
|
|
53
|
-
}
|
|
54
|
-
} catch(e) {
|
|
55
|
-
report({
|
|
56
|
-
message: `Example validation errored: ${e.message}.`,
|
|
57
|
-
location: location.child('schema'),
|
|
58
|
-
from: location
|
|
59
|
-
});
|
|
60
40
|
}
|
|
61
41
|
}
|
|
62
42
|
},
|
package/src/rules/utils.ts
CHANGED
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
import levenshtein = require('js-levenshtein');
|
|
2
2
|
import { UserContext } from '../walk';
|
|
3
|
+
import { Location } from '../ref-utils';
|
|
4
|
+
import { validateJsonSchema } from './ajv';
|
|
5
|
+
import { Oas3Schema, Referenced } from '../typings/openapi';
|
|
3
6
|
|
|
4
7
|
export function oasTypeOf(value: unknown) {
|
|
5
8
|
if (Array.isArray(value)) {
|
|
@@ -20,7 +23,7 @@ export function oasTypeOf(value: unknown) {
|
|
|
20
23
|
*/
|
|
21
24
|
export function matchesJsonSchemaType(value: unknown, type: string, nullable: boolean): boolean {
|
|
22
25
|
if (nullable && value === null) {
|
|
23
|
-
return value === null
|
|
26
|
+
return value === null;
|
|
24
27
|
}
|
|
25
28
|
|
|
26
29
|
switch (type) {
|
|
@@ -79,4 +82,42 @@ export function getSuggest(given: string, variants: string[]): string[] {
|
|
|
79
82
|
|
|
80
83
|
// if (bestMatch.distance <= 4) return bestMatch.string;
|
|
81
84
|
return distances.map((d) => d.variant);
|
|
82
|
-
}
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
export function validateExample(
|
|
88
|
+
example: any,
|
|
89
|
+
schema: Referenced<Oas3Schema>,
|
|
90
|
+
dataLoc: Location,
|
|
91
|
+
{ resolve, location, report }: UserContext,
|
|
92
|
+
disallowAdditionalProperties: boolean,
|
|
93
|
+
) {
|
|
94
|
+
try {
|
|
95
|
+
const { valid, errors } = validateJsonSchema(
|
|
96
|
+
example,
|
|
97
|
+
schema,
|
|
98
|
+
location.child('schema'),
|
|
99
|
+
dataLoc.pointer,
|
|
100
|
+
resolve,
|
|
101
|
+
disallowAdditionalProperties,
|
|
102
|
+
);
|
|
103
|
+
if (!valid) {
|
|
104
|
+
for (let error of errors) {
|
|
105
|
+
report({
|
|
106
|
+
message: `Example value must conform to the schema: ${error.message}.`,
|
|
107
|
+
location: {
|
|
108
|
+
...new Location(dataLoc.source, error.instancePath),
|
|
109
|
+
reportOnKey: error.keyword === 'additionalProperties',
|
|
110
|
+
},
|
|
111
|
+
from: location,
|
|
112
|
+
suggest: error.suggest,
|
|
113
|
+
});
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
} catch (e) {
|
|
117
|
+
report({
|
|
118
|
+
message: `Example validation errored: ${e.message}.`,
|
|
119
|
+
location: location.child('schema'),
|
|
120
|
+
from: location,
|
|
121
|
+
});
|
|
122
|
+
}
|
|
123
|
+
}
|
package/src/typings/openapi.ts
CHANGED
|
@@ -150,6 +150,10 @@ export interface Oas3Schema {
|
|
|
150
150
|
xml?: Oas3Xml;
|
|
151
151
|
}
|
|
152
152
|
|
|
153
|
+
export interface Oas3_1Schema extends Oas3Schema {
|
|
154
|
+
examples?: any[];
|
|
155
|
+
}
|
|
156
|
+
|
|
153
157
|
export interface Oas3Discriminator {
|
|
154
158
|
propertyName: string;
|
|
155
159
|
mapping?: { [name: string]: string };
|
package/src/utils.ts
CHANGED
package/src/walk.ts
CHANGED
|
@@ -33,6 +33,7 @@ export type UserContext = {
|
|
|
33
33
|
key: string | number;
|
|
34
34
|
parent: any;
|
|
35
35
|
oasVersion: OasVersion;
|
|
36
|
+
getVisitorData: () => Record<string, unknown>;
|
|
36
37
|
};
|
|
37
38
|
|
|
38
39
|
export type Loc = {
|
|
@@ -77,6 +78,7 @@ export type NormalizedProblem = {
|
|
|
77
78
|
export type WalkContext = {
|
|
78
79
|
problems: NormalizedProblem[];
|
|
79
80
|
oasVersion: OasVersion;
|
|
81
|
+
visitorsData: Record<string, Record<string, unknown>>; // custom data store that visitors can use for various purposes
|
|
80
82
|
refTypes?: Map<string, NormalizedNodeType>;
|
|
81
83
|
};
|
|
82
84
|
|
|
@@ -141,6 +143,7 @@ export function walkDocument<T>(opts: {
|
|
|
141
143
|
key,
|
|
142
144
|
parentLocations: {},
|
|
143
145
|
oasVersion: ctx.oasVersion,
|
|
146
|
+
getVisitorData: getVisitorDataFn.bind(undefined, ruleId)
|
|
144
147
|
},
|
|
145
148
|
{ node: resolvedNode, location: resolvedLocation, error },
|
|
146
149
|
);
|
|
@@ -325,6 +328,7 @@ export function walkDocument<T>(opts: {
|
|
|
325
328
|
key,
|
|
326
329
|
parentLocations: {},
|
|
327
330
|
oasVersion: ctx.oasVersion,
|
|
331
|
+
getVisitorData: getVisitorDataFn.bind(undefined, ruleId)
|
|
328
332
|
},
|
|
329
333
|
{ node: resolvedNode, location: resolvedLocation, error },
|
|
330
334
|
);
|
|
@@ -357,6 +361,7 @@ export function walkDocument<T>(opts: {
|
|
|
357
361
|
ignoreNextVisitorsOnNode: () => {
|
|
358
362
|
ignoreNextVisitorsOnNode = true;
|
|
359
363
|
},
|
|
364
|
+
getVisitorData: getVisitorDataFn.bind(undefined, ruleId),
|
|
360
365
|
},
|
|
361
366
|
collectParents(context),
|
|
362
367
|
context,
|
|
@@ -408,5 +413,10 @@ export function walkDocument<T>(opts: {
|
|
|
408
413
|
}),
|
|
409
414
|
});
|
|
410
415
|
}
|
|
416
|
+
|
|
417
|
+
function getVisitorDataFn(ruleId: string) {
|
|
418
|
+
ctx.visitorsData[ruleId] = ctx.visitorsData[ruleId] || {};
|
|
419
|
+
return ctx.visitorsData[ruleId];
|
|
420
|
+
}
|
|
411
421
|
}
|
|
412
422
|
}
|