@redocly/openapi-core 1.0.0-beta.66 → 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 (84) hide show
  1. package/__tests__/__snapshots__/bundle.test.ts.snap +126 -0
  2. package/__tests__/bundle.test.ts +53 -1
  3. package/__tests__/fixtures/refs/definitions.yaml +3 -0
  4. package/__tests__/fixtures/refs/external-request-body.yaml +13 -0
  5. package/__tests__/fixtures/refs/externalref.yaml +35 -0
  6. package/__tests__/fixtures/refs/hosted.yaml +35 -0
  7. package/__tests__/fixtures/refs/rename.yaml +1 -0
  8. package/__tests__/fixtures/refs/requestBody.yaml +9 -0
  9. package/__tests__/fixtures/refs/simple.yaml +1 -0
  10. package/__tests__/fixtures/refs/vendor.schema.yaml +20 -0
  11. package/__tests__/lint.test.ts +1 -1
  12. package/__tests__/login.test.ts +17 -0
  13. package/lib/bundle.d.ts +4 -0
  14. package/lib/bundle.js +25 -7
  15. package/lib/config/all.js +3 -0
  16. package/lib/config/config.d.ts +10 -0
  17. package/lib/config/config.js +7 -1
  18. package/lib/config/load.js +17 -8
  19. package/lib/config/minimal.js +1 -0
  20. package/lib/config/recommended.js +1 -0
  21. package/lib/index.d.ts +2 -2
  22. package/lib/lint.js +2 -0
  23. package/lib/redocly/index.d.ts +25 -20
  24. package/lib/redocly/index.js +77 -214
  25. package/lib/redocly/registry-api-types.d.ts +28 -0
  26. package/lib/redocly/registry-api-types.js +2 -0
  27. package/lib/redocly/registry-api.d.ts +14 -0
  28. package/lib/redocly/registry-api.js +105 -0
  29. package/lib/ref-utils.js +1 -2
  30. package/lib/rules/common/no-invalid-parameter-examples.d.ts +1 -0
  31. package/lib/rules/common/no-invalid-parameter-examples.js +25 -0
  32. package/lib/rules/common/no-invalid-schema-examples.d.ts +1 -0
  33. package/lib/rules/common/no-invalid-schema-examples.js +23 -0
  34. package/lib/rules/common/operation-4xx-response.d.ts +2 -0
  35. package/lib/rules/common/operation-4xx-response.js +17 -0
  36. package/lib/rules/common/paths-kebab-case.js +1 -1
  37. package/lib/rules/common/registry-dependencies.js +4 -7
  38. package/lib/rules/oas2/index.d.ts +3 -0
  39. package/lib/rules/oas2/index.js +6 -0
  40. package/lib/rules/oas3/index.js +6 -0
  41. package/lib/rules/oas3/no-invalid-media-type-examples.js +5 -26
  42. package/lib/rules/oas3/no-server-trailing-slash.js +1 -1
  43. package/lib/rules/utils.d.ts +3 -0
  44. package/lib/rules/utils.js +26 -1
  45. package/lib/typings/openapi.d.ts +3 -0
  46. package/lib/utils.d.ts +1 -0
  47. package/lib/utils.js +5 -1
  48. package/lib/walk.d.ts +2 -0
  49. package/lib/walk.js +7 -0
  50. package/package.json +1 -1
  51. package/src/bundle.ts +51 -9
  52. package/src/config/__tests__/load.test.ts +35 -0
  53. package/src/config/all.ts +3 -0
  54. package/src/config/config.ts +11 -0
  55. package/src/config/load.ts +20 -9
  56. package/src/config/minimal.ts +1 -0
  57. package/src/config/recommended.ts +1 -0
  58. package/src/index.ts +2 -8
  59. package/src/lint.ts +2 -0
  60. package/src/redocly/__tests__/redocly-client.test.ts +114 -0
  61. package/src/redocly/index.ts +90 -227
  62. package/src/redocly/registry-api-types.ts +31 -0
  63. package/src/redocly/registry-api.ts +110 -0
  64. package/src/ref-utils.ts +1 -3
  65. package/src/rules/common/__tests__/operation-4xx-response.test.ts +108 -0
  66. package/src/rules/common/__tests__/paths-kebab-case.test.ts +23 -0
  67. package/src/rules/common/no-invalid-parameter-examples.ts +36 -0
  68. package/src/rules/common/no-invalid-schema-examples.ts +27 -0
  69. package/src/rules/common/operation-4xx-response.ts +17 -0
  70. package/src/rules/common/paths-kebab-case.ts +1 -1
  71. package/src/rules/common/registry-dependencies.ts +6 -8
  72. package/src/rules/oas2/index.ts +6 -0
  73. package/src/rules/oas3/__tests__/no-server-trailing-slash.test.ts +19 -0
  74. package/src/rules/oas3/index.ts +6 -0
  75. package/src/rules/oas3/no-invalid-media-type-examples.ts +16 -36
  76. package/src/rules/oas3/no-server-trailing-slash.ts +1 -1
  77. package/src/rules/utils.ts +43 -2
  78. package/src/typings/openapi.ts +4 -0
  79. package/src/utils.ts +5 -1
  80. package/src/walk.ts +10 -0
  81. package/tsconfig.tsbuildinfo +1 -1
  82. package/lib/redocly/query.d.ts +0 -4
  83. package/lib/redocly/query.js +0 -44
  84. package/src/redocly/query.ts +0 -38
@@ -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;
@@ -12,6 +14,7 @@ export declare const rules: {
12
14
  'boolean-parameter-prefixes': Oas2Rule;
13
15
  'no-path-trailing-slash': Oas2Rule;
14
16
  'operation-2xx-response': Oas2Rule;
17
+ 'operation-4xx-response': Oas2Rule;
15
18
  'operation-operationId-unique': Oas2Rule;
16
19
  'operation-parameters-unique': Oas2Rule;
17
20
  'path-parameters-defined': 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");
@@ -13,6 +15,7 @@ const paths_kebab_case_1 = require("../common/paths-kebab-case");
13
15
  const no_enum_type_mismatch_1 = require("../common/no-enum-type-mismatch");
14
16
  const no_path_trailing_slash_1 = require("../common/no-path-trailing-slash");
15
17
  const operation_2xx_response_1 = require("../common/operation-2xx-response");
18
+ const operation_4xx_response_1 = require("../common/operation-4xx-response");
16
19
  const operation_operationId_unique_1 = require("../common/operation-operationId-unique");
17
20
  const operation_parameters_unique_1 = require("../common/operation-parameters-unique");
18
21
  const path_params_defined_1 = require("../common/path-params-defined");
@@ -41,6 +44,8 @@ const tag_description_override_1 = require("../common/tag-description-override")
41
44
  const info_description_override_1 = require("../common/info-description-override");
42
45
  exports.rules = {
43
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,
44
49
  'info-description': info_description_1.InfoDescription,
45
50
  'info-contact': info_contact_1.InfoContact,
46
51
  'info-license': info_license_url_1.InfoLicense,
@@ -52,6 +57,7 @@ exports.rules = {
52
57
  'boolean-parameter-prefixes': boolean_parameter_prefixes_1.BooleanParameterPrefixes,
53
58
  'no-path-trailing-slash': no_path_trailing_slash_1.NoPathTrailingSlash,
54
59
  'operation-2xx-response': operation_2xx_response_1.Operation2xxResponse,
60
+ 'operation-4xx-response': operation_4xx_response_1.Operation4xxResponse,
55
61
  'operation-operationId-unique': operation_operationId_unique_1.OperationIdUnique,
56
62
  'operation-parameters-unique': operation_parameters_unique_1.OperationParametersUnique,
57
63
  'path-parameters-defined': path_params_defined_1.PathParamsDefined,
@@ -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");
@@ -47,6 +48,8 @@ const operation_description_override_1 = require("../common/operation-descriptio
47
48
  const tag_description_override_1 = require("../common/tag-description-override");
48
49
  const info_description_override_1 = require("../common/info-description-override");
49
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");
50
53
  exports.rules = {
51
54
  spec: spec_1.OasSpec,
52
55
  'info-description': info_description_1.InfoDescription,
@@ -54,6 +57,7 @@ exports.rules = {
54
57
  'info-license': info_license_url_1.InfoLicense,
55
58
  'info-license-url': license_url_1.InfoLicenseUrl,
56
59
  'operation-2xx-response': operation_2xx_response_1.Operation2xxResponse,
60
+ 'operation-4xx-response': operation_4xx_response_1.Operation4xxResponse,
57
61
  'operation-operationId-unique': operation_operationId_unique_1.OperationIdUnique,
58
62
  'operation-parameters-unique': operation_parameters_unique_1.OperationParametersUnique,
59
63
  'path-parameters-defined': path_params_defined_1.PathParamsDefined,
@@ -91,6 +95,8 @@ exports.rules = {
91
95
  'request-mime-type': request_mime_type_1.RequestMimeType,
92
96
  'response-mime-type': response_mime_type_1.ResponseMimeType,
93
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,
94
100
  };
95
101
  exports.preprocessors = {};
96
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
  },
@@ -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']),
@@ -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;
@@ -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;
@@ -144,6 +144,9 @@ export interface Oas3Schema {
144
144
  example?: any;
145
145
  xml?: Oas3Xml;
146
146
  }
147
+ export interface Oas3_1Schema extends Oas3Schema {
148
+ examples?: any[];
149
+ }
147
150
  export interface Oas3Discriminator {
148
151
  propertyName: string;
149
152
  mapping?: {
package/lib/utils.d.ts CHANGED
@@ -33,3 +33,4 @@ export declare function isPathParameter(pathSegment: string): boolean;
33
33
  * Convert Windows backslash paths to slash paths: foo\\bar ➔ foo/bar
34
34
  */
35
35
  export declare function slash(path: string): string;
36
+ export declare function isNotEmptyObject(obj: any): boolean;
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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@redocly/openapi-core",
3
- "version": "1.0.0-beta.66",
3
+ "version": "1.0.0-beta.70",
4
4
  "description": "",
5
5
  "main": "lib/index.js",
6
6
  "engines": {
package/src/bundle.ts CHANGED
@@ -7,12 +7,13 @@ 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';
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,
@@ -40,7 +42,8 @@ export async function bundle(opts: {
40
42
  throw new Error('Document or reference is required.\n');
41
43
  }
42
44
 
43
- const document = doc !== undefined ? doc : await externalRefResolver.resolveDocument(base, ref!, true);
45
+ const document =
46
+ doc !== undefined ? doc : await externalRefResolver.resolveDocument(base, ref!, true);
44
47
 
45
48
  if (document instanceof Error) {
46
49
  throw document;
@@ -62,14 +65,26 @@ export async function bundleDocument(opts: {
62
65
  customTypes?: Record<string, NodeType>;
63
66
  externalRefResolver: BaseResolver;
64
67
  dereference?: boolean;
68
+ skipRedoclyRegistryRefs?: boolean;
65
69
  }) {
66
- const { document, config, customTypes, externalRefResolver, dereference = false } = opts;
70
+ const {
71
+ document,
72
+ config,
73
+ customTypes,
74
+ externalRefResolver,
75
+ dereference = false,
76
+ skipRedoclyRegistryRefs = false,
77
+ } = opts;
67
78
  const oasVersion = detectOpenAPI(document.parsed);
68
79
  const oasMajorVersion = openAPIMajor(oasVersion);
69
80
  const rules = config.getRulesForOasVersion(oasMajorVersion);
70
81
  const types = normalizeTypes(
71
82
  config.extendTypes(
72
- customTypes ?? oasMajorVersion === OasMajorVersion.Version3 ? (oasVersion === OasVersion.Version3_1 ? Oas3_1Types : Oas3Types) : Oas2Types,
83
+ customTypes ?? oasMajorVersion === OasMajorVersion.Version3
84
+ ? oasVersion === OasVersion.Version3_1
85
+ ? Oas3_1Types
86
+ : Oas3Types
87
+ : Oas2Types,
73
88
  oasVersion,
74
89
  ),
75
90
  config,
@@ -81,6 +96,7 @@ export async function bundleDocument(opts: {
81
96
  problems: [],
82
97
  oasVersion: oasVersion,
83
98
  refTypes: new Map<string, NormalizedNodeType>(),
99
+ visitorsData: {},
84
100
  };
85
101
 
86
102
  const bundleVisitor = normalizeVisitors(
@@ -89,7 +105,7 @@ export async function bundleDocument(opts: {
89
105
  {
90
106
  severity: 'error',
91
107
  ruleId: 'bundler',
92
- visitor: makeBundleVisitor(oasMajorVersion, dereference, document),
108
+ visitor: makeBundleVisitor(oasMajorVersion, dereference, skipRedoclyRegistryRefs, document),
93
109
  },
94
110
  ...decorators,
95
111
  ],
@@ -116,6 +132,7 @@ export async function bundleDocument(opts: {
116
132
  fileDependencies: externalRefResolver.getFiles(),
117
133
  rootType: types.DefinitionRoot,
118
134
  refTypes: ctx.refTypes,
135
+ visitorsData: ctx.visitorsData,
119
136
  };
120
137
  }
121
138
 
@@ -160,7 +177,12 @@ function mapTypeToComponent(typeName: string, version: OasMajorVersion) {
160
177
 
161
178
  // function oas3Move
162
179
 
163
- function makeBundleVisitor(version: OasMajorVersion, dereference: boolean, rootDocument: Document) {
180
+ function makeBundleVisitor(
181
+ version: OasMajorVersion,
182
+ dereference: boolean,
183
+ skipRedoclyRegistryRefs: boolean,
184
+ rootDocument: Document,
185
+ ) {
164
186
  let components: Record<string, Record<string, any>>;
165
187
 
166
188
  const visitor: Oas3Visitor | Oas2Visitor = {
@@ -178,6 +200,11 @@ function makeBundleVisitor(version: OasMajorVersion, dereference: boolean, rootD
178
200
  ) {
179
201
  return;
180
202
  }
203
+
204
+ if (skipRedoclyRegistryRefs && isRedoclyRegistryURL(node.$ref)) {
205
+ return;
206
+ }
207
+
181
208
  const componentType = mapTypeToComponent(ctx.type.name, version);
182
209
  if (!componentType) {
183
210
  replaceRef(node, resolved, ctx);
@@ -249,6 +276,21 @@ function makeBundleVisitor(version: OasMajorVersion, dereference: boolean, rootD
249
276
  }
250
277
  }
251
278
 
279
+ function isEqualOrEqualRef(
280
+ node: any,
281
+ target: { node: any; location: Location },
282
+ ctx: UserContext,
283
+ ) {
284
+ if (
285
+ isRef(node) &&
286
+ ctx.resolve(node).location?.absolutePointer === target.location.absolutePointer
287
+ ) {
288
+ return true;
289
+ }
290
+
291
+ return isEqual(node, target.node);
292
+ }
293
+
252
294
  function getComponentName(
253
295
  target: { node: any; location: Location },
254
296
  componentType: string,
@@ -265,20 +307,20 @@ function makeBundleVisitor(version: OasMajorVersion, dereference: boolean, rootD
265
307
  if (
266
308
  !componentsGroup ||
267
309
  !componentsGroup[name] ||
268
- isEqual(componentsGroup[name], target.node)
310
+ isEqualOrEqualRef(componentsGroup[name], target, ctx)
269
311
  ) {
270
312
  return name;
271
313
  }
272
314
  }
273
315
 
274
316
  name = refBaseName(fileRef) + (name ? `_${name}` : '');
275
- if (!componentsGroup[name] || isEqual(componentsGroup[name], target.node)) {
317
+ if (!componentsGroup[name] || isEqualOrEqualRef(componentsGroup[name], target, ctx)) {
276
318
  return name;
277
319
  }
278
320
 
279
321
  const prevName = name;
280
322
  let serialId = 2;
281
- while (componentsGroup[name] && !isEqual(componentsGroup[name], target.node)) {
323
+ while (componentsGroup[name] && !isEqualOrEqualRef(componentsGroup[name], target, ctx)) {
282
324
  name = `${prevName}-${serialId}`;
283
325
  serialId++;
284
326
  }
@@ -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
@@ -18,6 +18,7 @@ export default {
18
18
  'path-parameters-defined': 'error',
19
19
  'operation-description': 'error',
20
20
  'operation-2xx-response': 'error',
21
+ 'operation-4xx-response': 'error',
21
22
  'operation-operationId': 'error',
22
23
  'operation-summary': 'error',
23
24
  'operation-operationId-unique': 'error',
@@ -37,6 +38,8 @@ export default {
37
38
  },
38
39
  'request-mime-type': 'error',
39
40
  spec: 'error',
41
+ 'no-invalid-schema-examples': 'error',
42
+ 'no-invalid-parameter-examples': 'error',
40
43
  },
41
44
  oas3_0Rules: {
42
45
  'no-invalid-media-type-examples': 'error',
@@ -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
 
@@ -1,8 +1,7 @@
1
1
  import * as fs from 'fs';
2
-
3
2
  import { RedoclyClient } from '../redocly';
4
3
  import { loadYaml } from '../utils';
5
- import { Config, RawConfig } from './config';
4
+ import { Config, DOMAINS, RawConfig, Region } from './config';
6
5
 
7
6
  import { defaultPlugin } from './builtIn';
8
7
 
@@ -25,19 +24,31 @@ export async function loadConfig(configPath?: string, customExtends?: string[]):
25
24
  }
26
25
 
27
26
  const redoclyClient = new RedoclyClient();
28
- if (redoclyClient.hasToken()) {
27
+ const tokens = await redoclyClient.getTokens();
28
+
29
+ if (tokens.length) {
29
30
  if (!rawConfig.resolve) rawConfig.resolve = {};
30
31
  if (!rawConfig.resolve.http) rawConfig.resolve.http = {};
31
- rawConfig.resolve.http.headers = [
32
- {
33
- matches: `https://api.${process.env.REDOCLY_DOMAIN || 'redoc.ly'}/registry/**`,
32
+ rawConfig.resolve.http.headers = [...(rawConfig.resolve.http.headers ?? [])];
33
+
34
+ for (const item of tokens) {
35
+ const domain = DOMAINS[item.region as Region];
36
+ rawConfig.resolve.http.headers.push({
37
+ matches: `https://api.${domain}/registry/**`,
34
38
  name: 'Authorization',
35
39
  envVariable: undefined,
36
- value: (redoclyClient && (await redoclyClient.getAuthorizationHeader())) || '',
40
+ value: item.token,
37
41
  },
38
- ...(rawConfig.resolve.http.headers ?? []),
39
- ];
42
+ //support redocly.com domain for future compatibility
43
+ ...(item.region === 'us' ? [{
44
+ matches: `https://api.redocly.com/registry/**`,
45
+ name: 'Authorization',
46
+ envVariable: undefined,
47
+ value: item.token,
48
+ }] : []));
49
+ }
40
50
  }
51
+
41
52
  return new Config(
42
53
  {
43
54
  ...rawConfig,
@@ -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',
package/src/index.ts CHANGED
@@ -17,7 +17,7 @@ export { StatsAccumulator, StatsName } from './typings/common';
17
17
  export { normalizeTypes } from './types';
18
18
  export { Stats } from './rules/other/stats';
19
19
 
20
- export { Config, LintConfig, RawConfig, IGNORE_FILE } from './config/config';
20
+ export { Config, LintConfig, RawConfig, IGNORE_FILE, Region } from './config/config';
21
21
  export { loadConfig } from './config/load';
22
22
  export { RedoclyClient } from './redocly';
23
23
  export {
@@ -46,11 +46,5 @@ export {
46
46
 
47
47
  export { getAstNodeByPointer, getLineColLocation } from './format/codeframes';
48
48
  export { formatProblems, OutputFormat, getTotals, Totals } from './format/format';
49
- export {
50
- lint,
51
- lint as validate,
52
- lintDocument,
53
- lintFromString,
54
- lintConfig,
55
- } from './lint';
49
+ export { lint, lint as validate, lintDocument, lintFromString, lintConfig } from './lint';
56
50
  export { bundle, bundleDocument } from './bundle';
package/src/lint.ts CHANGED
@@ -72,6 +72,7 @@ export async function lintDocument(opts: {
72
72
  const ctx: WalkContext = {
73
73
  problems: [],
74
74
  oasVersion: oasVersion,
75
+ visitorsData: {},
75
76
  };
76
77
 
77
78
  const preprocessors = initRules(rules as any, config, 'preprocessors', oasVersion);
@@ -101,6 +102,7 @@ export async function lintConfig(opts: {
101
102
  const ctx: WalkContext = {
102
103
  problems: [],
103
104
  oasVersion: OasVersion.Version3_0,
105
+ visitorsData: {},
104
106
  };
105
107
  const config = new LintConfig({
106
108
  plugins: [defaultPlugin],