@redocly/openapi-core 1.0.0-beta.69 → 1.0.0-beta.70

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (45) hide show
  1. package/__tests__/lint.test.ts +1 -1
  2. package/__tests__/login.test.ts +17 -0
  3. package/lib/config/all.js +2 -0
  4. package/lib/config/config.d.ts +10 -0
  5. package/lib/config/config.js +7 -1
  6. package/lib/config/load.js +17 -8
  7. package/lib/index.d.ts +2 -2
  8. package/lib/redocly/index.d.ts +22 -6
  9. package/lib/redocly/index.js +61 -31
  10. package/lib/redocly/registry-api.d.ts +8 -5
  11. package/lib/redocly/registry-api.js +31 -20
  12. package/lib/rules/common/no-invalid-parameter-examples.d.ts +1 -0
  13. package/lib/rules/common/no-invalid-parameter-examples.js +25 -0
  14. package/lib/rules/common/no-invalid-schema-examples.d.ts +1 -0
  15. package/lib/rules/common/no-invalid-schema-examples.js +23 -0
  16. package/lib/rules/common/paths-kebab-case.js +1 -1
  17. package/lib/rules/oas2/index.d.ts +2 -0
  18. package/lib/rules/oas2/index.js +4 -0
  19. package/lib/rules/oas3/index.js +4 -0
  20. package/lib/rules/oas3/no-invalid-media-type-examples.js +5 -26
  21. package/lib/rules/utils.d.ts +3 -0
  22. package/lib/rules/utils.js +26 -1
  23. package/lib/typings/openapi.d.ts +3 -0
  24. package/lib/utils.d.ts +1 -0
  25. package/lib/utils.js +5 -1
  26. package/package.json +2 -2
  27. package/src/config/__tests__/load.test.ts +35 -0
  28. package/src/config/all.ts +2 -0
  29. package/src/config/config.ts +11 -0
  30. package/src/config/load.ts +20 -9
  31. package/src/index.ts +2 -8
  32. package/src/redocly/__tests__/redocly-client.test.ts +114 -0
  33. package/src/redocly/index.ts +77 -37
  34. package/src/redocly/registry-api.ts +33 -29
  35. package/src/rules/common/__tests__/paths-kebab-case.test.ts +23 -0
  36. package/src/rules/common/no-invalid-parameter-examples.ts +36 -0
  37. package/src/rules/common/no-invalid-schema-examples.ts +27 -0
  38. package/src/rules/common/paths-kebab-case.ts +1 -1
  39. package/src/rules/oas2/index.ts +4 -0
  40. package/src/rules/oas3/index.ts +4 -0
  41. package/src/rules/oas3/no-invalid-media-type-examples.ts +16 -36
  42. package/src/rules/utils.ts +43 -2
  43. package/src/typings/openapi.ts +4 -0
  44. package/src/utils.ts +5 -1
  45. package/tsconfig.tsbuildinfo +1 -1
@@ -2,7 +2,7 @@ import outdent from 'outdent';
2
2
  import { detectOpenAPI } from '../src/oas-types';
3
3
  import { parseYamlToDocument } from './utils';
4
4
 
5
- describe.only('lint', () => {
5
+ describe('lint', () => {
6
6
  it('detect OpenAPI should throw an error when version is not string', () => {
7
7
 
8
8
  const testDocument = parseYamlToDocument(
@@ -0,0 +1,17 @@
1
+ import { RedoclyClient } from '../src/redocly';
2
+
3
+ describe('login', () => {
4
+ it('should call login with setAccessTokens function', async () => {
5
+ const client = new RedoclyClient();
6
+ Object.defineProperty(client, 'registryApi', {
7
+ value: {
8
+ setAccessTokens: jest.fn(),
9
+ authStatus: jest.fn(() => true)
10
+ },
11
+ writable: true,
12
+ configurable: true
13
+ });
14
+ await client.login('token');
15
+ expect(client.registryApi.setAccessTokens).toHaveBeenCalled();
16
+ });
17
+ });
package/lib/config/all.js CHANGED
@@ -38,6 +38,8 @@ exports.default = {
38
38
  },
39
39
  'request-mime-type': 'error',
40
40
  spec: 'error',
41
+ 'no-invalid-schema-examples': 'error',
42
+ 'no-invalid-parameter-examples': 'error',
41
43
  },
42
44
  oas3_0Rules: {
43
45
  'no-invalid-media-type-examples': 'error',
@@ -70,11 +70,20 @@ export declare type HttpResolveConfig = {
70
70
  export declare type ResolveConfig = {
71
71
  http: HttpResolveConfig;
72
72
  };
73
+ export declare const DEFAULT_REGION = "us";
74
+ export declare type Region = 'us' | 'eu';
75
+ export declare type AccessTokens = {
76
+ [region in Region]?: string;
77
+ };
78
+ export declare const DOMAINS: {
79
+ [region in Region]: string;
80
+ };
73
81
  export declare type RawConfig = {
74
82
  referenceDocs?: any;
75
83
  apiDefinitions?: Record<string, string>;
76
84
  lint?: LintRawConfig;
77
85
  resolve?: RawResolveConfig;
86
+ region?: Region;
78
87
  };
79
88
  export declare class LintConfig {
80
89
  rawConfig: LintRawConfig;
@@ -126,5 +135,6 @@ export declare class Config {
126
135
  lint: LintConfig;
127
136
  resolve: ResolveConfig;
128
137
  licenseKey?: string;
138
+ region?: Region;
129
139
  constructor(rawConfig: RawConfig, configFile?: string | undefined);
130
140
  }
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.Config = exports.LintConfig = exports.IGNORE_FILE = void 0;
3
+ exports.Config = exports.LintConfig = exports.DOMAINS = exports.DEFAULT_REGION = exports.IGNORE_FILE = void 0;
4
4
  const fs = require("fs");
5
5
  const path = require("path");
6
6
  const path_1 = require("path");
@@ -12,6 +12,11 @@ const recommended_1 = require("./recommended");
12
12
  exports.IGNORE_FILE = '.redocly.lint-ignore.yaml';
13
13
  const IGNORE_BANNER = `# This file instructs Redocly's linter to ignore the rules contained for specific parts of your API.\n` +
14
14
  `# See https://redoc.ly/docs/cli/ for more information.\n`;
15
+ exports.DEFAULT_REGION = 'us';
16
+ exports.DOMAINS = {
17
+ us: 'redoc.ly',
18
+ eu: 'eu.redocly.com',
19
+ };
15
20
  class LintConfig {
16
21
  constructor(rawConfig, configFile) {
17
22
  this.rawConfig = rawConfig;
@@ -233,6 +238,7 @@ class Config {
233
238
  customFetch: undefined,
234
239
  },
235
240
  };
241
+ this.region = rawConfig.region;
236
242
  }
237
243
  }
238
244
  exports.Config = Config;
@@ -35,20 +35,29 @@ function loadConfig(configPath, customExtends) {
35
35
  rawConfig.lint.extends = customExtends;
36
36
  }
37
37
  const redoclyClient = new redocly_1.RedoclyClient();
38
- if (redoclyClient.hasToken()) {
38
+ const tokens = yield redoclyClient.getTokens();
39
+ if (tokens.length) {
39
40
  if (!rawConfig.resolve)
40
41
  rawConfig.resolve = {};
41
42
  if (!rawConfig.resolve.http)
42
43
  rawConfig.resolve.http = {};
43
- rawConfig.resolve.http.headers = [
44
- {
45
- matches: `https://api.${process.env.REDOCLY_DOMAIN || 'redoc.ly'}/registry/**`,
44
+ rawConfig.resolve.http.headers = [...((_a = rawConfig.resolve.http.headers) !== null && _a !== void 0 ? _a : [])];
45
+ for (const item of tokens) {
46
+ const domain = config_1.DOMAINS[item.region];
47
+ rawConfig.resolve.http.headers.push({
48
+ matches: `https://api.${domain}/registry/**`,
46
49
  name: 'Authorization',
47
50
  envVariable: undefined,
48
- value: (redoclyClient && (yield redoclyClient.getAuthorizationHeader())) || '',
49
- },
50
- ...((_a = rawConfig.resolve.http.headers) !== null && _a !== void 0 ? _a : []),
51
- ];
51
+ value: item.token,
52
+ },
53
+ //support redocly.com domain for future compatibility
54
+ ...(item.region === 'us' ? [{
55
+ matches: `https://api.redocly.com/registry/**`,
56
+ name: 'Authorization',
57
+ envVariable: undefined,
58
+ value: item.token,
59
+ }] : []));
60
+ }
52
61
  }
53
62
  return new config_1.Config(Object.assign(Object.assign({}, rawConfig), { lint: Object.assign(Object.assign({}, rawConfig === null || rawConfig === void 0 ? void 0 : rawConfig.lint), { plugins: [...(((_b = rawConfig === null || rawConfig === void 0 ? void 0 : rawConfig.lint) === null || _b === void 0 ? void 0 : _b.plugins) || []), builtIn_1.defaultPlugin] }) }), configPath);
54
63
  });
package/lib/index.d.ts CHANGED
@@ -8,7 +8,7 @@ export { Oas2Definition } from './typings/swagger';
8
8
  export { StatsAccumulator, StatsName } from './typings/common';
9
9
  export { normalizeTypes } from './types';
10
10
  export { Stats } from './rules/other/stats';
11
- export { Config, LintConfig, RawConfig, IGNORE_FILE } from './config/config';
11
+ export { Config, LintConfig, RawConfig, IGNORE_FILE, Region } from './config/config';
12
12
  export { loadConfig } from './config/load';
13
13
  export { RedoclyClient } from './redocly';
14
14
  export { Source, BaseResolver, Document, resolveDocument, ResolveError, YamlParseError, makeDocumentFromString, } from './resolve';
@@ -19,5 +19,5 @@ export { normalizeVisitors } from './visitors';
19
19
  export { WalkContext, walkDocument, NormalizedProblem, ProblemSeverity, LineColLocationObject, LocationObject, Loc, } from './walk';
20
20
  export { getAstNodeByPointer, getLineColLocation } from './format/codeframes';
21
21
  export { formatProblems, OutputFormat, getTotals, Totals } from './format/format';
22
- export { lint, lint as validate, lintDocument, lintFromString, lintConfig, } from './lint';
22
+ export { lint, lint as validate, lintDocument, lintFromString, lintConfig } from './lint';
23
23
  export { bundle, bundleDocument } from './bundle';
@@ -1,13 +1,29 @@
1
1
  import { RegistryApi } from './registry-api';
2
+ import { AccessTokens, Region } from '../config/config';
2
3
  export declare class RedoclyClient {
3
- private accessToken;
4
+ private accessTokens;
5
+ private region;
6
+ domain: string;
4
7
  registryApi: RegistryApi;
5
- constructor();
6
- hasToken(): boolean;
7
- loadToken(): void;
8
+ constructor(region?: Region);
9
+ loadRegion(region?: Region): Region;
10
+ hasTokens(): boolean;
11
+ setAccessTokens(accessTokens: AccessTokens): void;
12
+ loadTokens(): void;
13
+ getValidTokens(): Promise<{
14
+ region: string;
15
+ token: string;
16
+ valid: boolean;
17
+ }[]>;
18
+ getTokens(): Promise<{
19
+ region: string;
20
+ token: string;
21
+ valid: boolean;
22
+ }[]>;
23
+ isAuthorizedWithRedoclyByRegion(): Promise<boolean>;
8
24
  isAuthorizedWithRedocly(): Promise<boolean>;
9
- verifyToken(accessToken: string, verbose?: boolean): Promise<boolean>;
10
- getAuthorizationHeader(): Promise<string | undefined>;
25
+ readCredentialsFile(credentialsPath: string): any;
26
+ verifyToken(accessToken: string, region: Region, verbose?: boolean): Promise<boolean>;
11
27
  login(accessToken: string, verbose?: boolean): Promise<void>;
12
28
  logout(): void;
13
29
  }
@@ -15,61 +15,91 @@ const path_1 = require("path");
15
15
  const os_1 = require("os");
16
16
  const colorette_1 = require("colorette");
17
17
  const registry_api_1 = require("./registry-api");
18
+ const config_1 = require("../config/config");
19
+ const utils_1 = require("../utils");
18
20
  const TOKEN_FILENAME = '.redocly-config.json';
19
21
  class RedoclyClient {
20
- constructor() {
21
- this.loadToken();
22
- this.registryApi = new registry_api_1.RegistryApi(this.accessToken);
22
+ constructor(region) {
23
+ this.accessTokens = {};
24
+ this.region = this.loadRegion(region);
25
+ this.loadTokens();
26
+ this.domain = region
27
+ ? config_1.DOMAINS[region]
28
+ : process.env.REDOCLY_DOMAIN || config_1.DOMAINS[config_1.DEFAULT_REGION];
29
+ this.registryApi = new registry_api_1.RegistryApi(this.accessTokens, this.region);
23
30
  }
24
- hasToken() {
25
- return !!this.accessToken;
26
- }
27
- loadToken() {
28
- if (process.env.REDOCLY_AUTHORIZATION) {
29
- this.accessToken = process.env.REDOCLY_AUTHORIZATION;
30
- return;
31
+ loadRegion(region) {
32
+ if (region && !config_1.DOMAINS[region]) {
33
+ process.stdout.write(colorette_1.red(`Invalid argument: region in config file.\nGiven: ${colorette_1.green(region)}, choices: "us", "eu".\n`));
34
+ process.exit(1);
31
35
  }
36
+ return region || config_1.DEFAULT_REGION;
37
+ }
38
+ hasTokens() {
39
+ return utils_1.isNotEmptyObject(this.accessTokens);
40
+ }
41
+ setAccessTokens(accessTokens) {
42
+ this.accessTokens = accessTokens;
43
+ }
44
+ loadTokens() {
32
45
  const credentialsPath = path_1.resolve(os_1.homedir(), TOKEN_FILENAME);
33
- if (fs_1.existsSync(credentialsPath)) {
34
- const credentials = JSON.parse(fs_1.readFileSync(credentialsPath, 'utf-8'));
35
- this.accessToken = credentials && credentials.token;
46
+ const credentials = this.readCredentialsFile(credentialsPath);
47
+ if (utils_1.isNotEmptyObject(credentials)) {
48
+ this.setAccessTokens(Object.assign(Object.assign({}, credentials), (credentials.token && !credentials[this.region] && {
49
+ [this.region]: credentials.token
50
+ })));
51
+ }
52
+ if (process.env.REDOCLY_AUTHORIZATION) {
53
+ this.setAccessTokens(Object.assign(Object.assign({}, this.accessTokens), { [this.region]: process.env.REDOCLY_AUTHORIZATION }));
36
54
  }
37
55
  }
38
- isAuthorizedWithRedocly() {
56
+ getValidTokens() {
39
57
  return __awaiter(this, void 0, void 0, function* () {
40
- return this.hasToken() && !!(yield this.getAuthorizationHeader());
58
+ return (yield Promise.all(Object.entries(this.accessTokens).map(([key, value]) => __awaiter(this, void 0, void 0, function* () {
59
+ return { region: key, token: value, valid: yield this.verifyToken(value, key) };
60
+ })))).filter(item => Boolean(item.valid));
41
61
  });
42
62
  }
43
- verifyToken(accessToken, verbose = false) {
63
+ getTokens() {
44
64
  return __awaiter(this, void 0, void 0, function* () {
45
- if (!accessToken)
65
+ return this.hasTokens() ? yield this.getValidTokens() : [];
66
+ });
67
+ }
68
+ isAuthorizedWithRedoclyByRegion() {
69
+ return __awaiter(this, void 0, void 0, function* () {
70
+ if (!this.hasTokens())
46
71
  return false;
47
- return this.registryApi.setAccessToken(accessToken).authStatus(verbose);
72
+ const accessToken = this.accessTokens[this.region];
73
+ return !!accessToken && (yield this.verifyToken(accessToken, this.region));
48
74
  });
49
75
  }
50
- getAuthorizationHeader() {
76
+ isAuthorizedWithRedocly() {
51
77
  return __awaiter(this, void 0, void 0, function* () {
52
- // print this only if there is token but invalid
53
- if (this.accessToken && !(yield this.verifyToken(this.accessToken))) {
54
- process.stderr.write(`${colorette_1.yellow('Warning:')} invalid Redocly API key. Use "npx @redocly/openapi-cli login" to provide your API key\n`);
55
- return undefined;
56
- }
57
- return this.accessToken;
78
+ return this.hasTokens() && utils_1.isNotEmptyObject(yield this.getValidTokens());
79
+ });
80
+ }
81
+ readCredentialsFile(credentialsPath) {
82
+ return fs_1.existsSync(credentialsPath) ? JSON.parse(fs_1.readFileSync(credentialsPath, 'utf-8')) : {};
83
+ }
84
+ verifyToken(accessToken, region, verbose = false) {
85
+ return __awaiter(this, void 0, void 0, function* () {
86
+ if (!accessToken)
87
+ return false;
88
+ return this.registryApi.authStatus(accessToken, region, verbose);
58
89
  });
59
90
  }
60
91
  login(accessToken, verbose = false) {
61
92
  return __awaiter(this, void 0, void 0, function* () {
62
93
  const credentialsPath = path_1.resolve(os_1.homedir(), TOKEN_FILENAME);
63
94
  process.stdout.write(colorette_1.gray('\n Logging in...\n'));
64
- const authorized = yield this.verifyToken(accessToken, verbose);
95
+ const authorized = yield this.verifyToken(accessToken, this.region, verbose);
65
96
  if (!authorized) {
66
97
  process.stdout.write(colorette_1.red('Authorization failed. Please check if you entered a valid API key.\n'));
67
98
  process.exit(1);
68
99
  }
69
- this.accessToken = accessToken;
70
- const credentials = {
71
- token: accessToken,
72
- };
100
+ const credentials = Object.assign(Object.assign({}, this.readCredentialsFile(credentialsPath)), { [this.region]: accessToken });
101
+ this.accessTokens = credentials;
102
+ this.registryApi.setAccessTokens(credentials);
73
103
  fs_1.writeFileSync(credentialsPath, JSON.stringify(credentials, null, 2));
74
104
  process.stdout.write(colorette_1.green(' Authorization confirmed. ✅\n\n'));
75
105
  });
@@ -84,7 +114,7 @@ class RedoclyClient {
84
114
  }
85
115
  exports.RedoclyClient = RedoclyClient;
86
116
  function isRedoclyRegistryURL(link) {
87
- const domain = process.env.REDOCLY_DOMAIN || 'redoc.ly';
117
+ const domain = process.env.REDOCLY_DOMAIN || config_1.DOMAINS[config_1.DEFAULT_REGION];
88
118
  if (!link.startsWith(`https://api.${domain}/registry/`))
89
119
  return false;
90
120
  const registryPath = link.replace(`https://api.${domain}/registry/`, '');
@@ -1,11 +1,14 @@
1
1
  import { RegistryApiTypes } from './registry-api-types';
2
+ import { AccessTokens, Region } from '../config/config';
2
3
  export declare class RegistryApi {
3
- private accessToken?;
4
- private readonly baseUrl;
5
- constructor(accessToken?: string | undefined);
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;
6
10
  private request;
7
- setAccessToken(accessToken: string): this;
8
- authStatus(verbose?: boolean): Promise<boolean>;
11
+ authStatus(accessToken: string, region: Region, verbose?: boolean): Promise<boolean>;
9
12
  prepareFileUpload({ organizationId, name, version, filesHash, filename, isUpsert, }: RegistryApiTypes.PrepareFileuploadParams): Promise<RegistryApiTypes.PrepareFileuploadOKResponse>;
10
13
  pushApi({ organizationId, name, version, rootFilePath, filePaths, branch, isUpsert, }: RegistryApiTypes.PushApiParams): Promise<void>;
11
14
  }
@@ -11,22 +11,31 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
11
11
  Object.defineProperty(exports, "__esModule", { value: true });
12
12
  exports.RegistryApi = void 0;
13
13
  const node_fetch_1 = require("node-fetch");
14
+ const config_1 = require("../config/config");
15
+ const utils_1 = require("../utils");
14
16
  const version = require('../../package.json').version;
15
17
  class RegistryApi {
16
- constructor(accessToken) {
17
- this.accessToken = accessToken;
18
- this.baseUrl = `https://api.${process.env.REDOCLY_DOMAIN || 'redoc.ly'}/registry`;
18
+ constructor(accessTokens, region) {
19
+ this.accessTokens = accessTokens;
20
+ this.region = region;
19
21
  }
20
- request(path = '', options = {}) {
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) {
21
33
  return __awaiter(this, void 0, void 0, function* () {
22
- if (!this.accessToken) {
34
+ const headers = Object.assign({}, options.headers || {}, { 'x-redocly-cli-version': version });
35
+ if (!headers.hasOwnProperty('authorization')) {
23
36
  throw new Error('Unauthorized');
24
37
  }
25
- const headers = Object.assign({}, options.headers || {}, {
26
- authorization: this.accessToken,
27
- 'x-redocly-cli-version': version,
28
- });
29
- const response = yield node_fetch_1.default(`${this.baseUrl}${path}`, Object.assign({}, options, { headers }));
38
+ const response = yield node_fetch_1.default(`${this.getBaseUrl(region)}${path}`, Object.assign({}, options, { headers }));
30
39
  if (response.status === 401) {
31
40
  throw new Error('Unauthorized');
32
41
  }
@@ -37,14 +46,10 @@ class RegistryApi {
37
46
  return response;
38
47
  });
39
48
  }
40
- setAccessToken(accessToken) {
41
- this.accessToken = accessToken;
42
- return this;
43
- }
44
- authStatus(verbose = false) {
49
+ authStatus(accessToken, region, verbose = false) {
45
50
  return __awaiter(this, void 0, void 0, function* () {
46
51
  try {
47
- const response = yield this.request();
52
+ const response = yield this.request('', { headers: { authorization: accessToken } }, region);
48
53
  return response.ok;
49
54
  }
50
55
  catch (error) {
@@ -59,13 +64,16 @@ class RegistryApi {
59
64
  return __awaiter(this, void 0, void 0, function* () {
60
65
  const response = yield this.request(`/${organizationId}/${name}/${version}/prepare-file-upload`, {
61
66
  method: 'POST',
62
- headers: { 'content-type': 'application/json' },
67
+ headers: {
68
+ 'content-type': 'application/json',
69
+ authorization: this.accessToken,
70
+ },
63
71
  body: JSON.stringify({
64
72
  filesHash,
65
73
  filename,
66
74
  isUpsert,
67
75
  }),
68
- });
76
+ }, this.region);
69
77
  if (response.ok) {
70
78
  return response.json();
71
79
  }
@@ -76,14 +84,17 @@ class RegistryApi {
76
84
  return __awaiter(this, void 0, void 0, function* () {
77
85
  const response = yield this.request(`/${organizationId}/${name}/${version}`, {
78
86
  method: 'PUT',
79
- headers: { 'content-type': 'application/json' },
87
+ headers: {
88
+ 'content-type': 'application/json',
89
+ authorization: this.accessToken
90
+ },
80
91
  body: JSON.stringify({
81
92
  rootFilePath,
82
93
  filePaths,
83
94
  branch,
84
95
  isUpsert,
85
96
  }),
86
- });
97
+ }, this.region);
87
98
  if (response.ok) {
88
99
  return;
89
100
  }
@@ -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.`,
@@ -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;
@@ -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,
@@ -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, { report, location, resolve }) {
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
  },
@@ -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;