@redocly/openapi-core 1.26.0 → 1.27.0

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/CHANGELOG.md +17 -0
  2. package/lib/config/all.js +2 -4
  3. package/lib/config/config-resolvers.d.ts +3 -1
  4. package/lib/config/config-resolvers.js +7 -5
  5. package/lib/config/minimal.js +1 -3
  6. package/lib/config/recommended-strict.js +2 -4
  7. package/lib/config/recommended.js +2 -4
  8. package/lib/config/rules.js +3 -0
  9. package/lib/config/spec.js +2 -4
  10. package/lib/config/types.d.ts +2 -3
  11. package/lib/rules/arazzo/index.js +2 -6
  12. package/lib/rules/spot/spot-supported-versions.d.ts +2 -0
  13. package/lib/rules/spot/{version-enum.js → spot-supported-versions.js} +3 -3
  14. package/lib/types/redocly-yaml.d.ts +1 -1
  15. package/lib/types/redocly-yaml.js +1 -3
  16. package/lib/visitors.d.ts +3 -0
  17. package/lib/visitors.js +2 -2
  18. package/lib/walk.js +14 -11
  19. package/package.json +1 -3
  20. package/src/__tests__/walk.test.ts +51 -0
  21. package/src/config/__tests__/__snapshots__/config-resolvers.test.ts.snap +4 -8
  22. package/src/config/all.ts +2 -4
  23. package/src/config/config-resolvers.ts +28 -36
  24. package/src/config/minimal.ts +1 -3
  25. package/src/config/recommended-strict.ts +2 -4
  26. package/src/config/recommended.ts +2 -4
  27. package/src/config/rules.ts +3 -1
  28. package/src/config/spec.ts +2 -4
  29. package/src/config/types.ts +2 -6
  30. package/src/rules/arazzo/__tests__/{version-enum.test.ts → spot-supported-versions.test.ts} +3 -3
  31. package/src/rules/arazzo/index.ts +2 -6
  32. package/src/rules/spot/{version-enum.ts → spot-supported-versions.ts} +1 -1
  33. package/src/types/redocly-yaml.ts +1 -3
  34. package/src/visitors.ts +5 -2
  35. package/src/walk.ts +20 -11
  36. package/tsconfig.tsbuildinfo +1 -1
  37. package/lib/rules/spot/no-actions-type-end.d.ts +0 -2
  38. package/lib/rules/spot/no-actions-type-end.js +0 -28
  39. package/lib/rules/spot/parameters-not-in-body.d.ts +0 -2
  40. package/lib/rules/spot/parameters-not-in-body.js +0 -18
  41. package/lib/rules/spot/version-enum.d.ts +0 -2
  42. package/src/rules/arazzo/__tests__/no-actions-type-end.test.ts +0 -121
  43. package/src/rules/arazzo/__tests__/parameters-not-in-body.test.ts +0 -73
  44. package/src/rules/spot/no-actions-type-end.ts +0 -27
  45. package/src/rules/spot/parameters-not-in-body.ts +0 -17
package/CHANGELOG.md CHANGED
@@ -1,5 +1,22 @@
1
1
  # @redocly/openapi-core
2
2
 
3
+ ## 1.27.0
4
+
5
+ ### Minor Changes
6
+
7
+ - Added the ability to override default problem messages for built-in rules.
8
+
9
+ ### Patch Changes
10
+
11
+ - Updated Spot validation rules.
12
+
13
+ ## 1.26.1
14
+
15
+ ### Patch Changes
16
+
17
+ - Removed the `no-actions-type-end` Spot rule.
18
+ - Removed unused lodash.isequal dependency.
19
+
3
20
  ## 1.26.0
4
21
 
5
22
  ### Minor Changes
package/lib/config/all.js CHANGED
@@ -210,9 +210,7 @@ const all = {
210
210
  },
211
211
  arazzo1Rules: {
212
212
  'criteria-unique': 'error',
213
- 'no-criteria-xpath': 'error',
214
- 'no-actions-type-end': 'error',
215
- 'parameters-not-in-body': 'error',
213
+ 'no-criteria-xpath': 'off',
216
214
  'parameters-unique': 'error',
217
215
  'requestBody-replacements-unique': 'error',
218
216
  'sourceDescription-type': 'error',
@@ -221,7 +219,7 @@ const all = {
221
219
  'stepId-unique': 'error',
222
220
  'sourceDescription-name-unique': 'error',
223
221
  'sourceDescriptions-not-empty': 'error',
224
- 'version-enum': 'error',
222
+ 'spot-supported-versions': 'off',
225
223
  'workflowId-unique': 'error',
226
224
  'workflow-dependsOn': 'error',
227
225
  },
@@ -24,5 +24,7 @@ export declare function resolveStyleguideConfig(opts: {
24
24
  styleguideConfig?: StyleguideRawConfig;
25
25
  configPath?: string;
26
26
  resolver?: BaseResolver;
27
- }, parentConfigPaths?: string[], extendPaths?: string[]): Promise<ResolvedStyleguideConfig>;
27
+ parentConfigPaths?: string[];
28
+ extendPaths?: string[];
29
+ }): Promise<ResolvedStyleguideConfig>;
28
30
  export declare function resolvePreset(presetName: string, plugins: Plugin[]): ResolvedStyleguideConfig;
@@ -257,7 +257,7 @@ async function resolveApis({ rawConfig, configPath = '', resolver, }) {
257
257
  }
258
258
  return resolvedApis;
259
259
  }
260
- async function resolveAndMergeNestedStyleguideConfig({ styleguideConfig, configPath = '', resolver = new resolve_1.BaseResolver(), }, parentConfigPaths = [], extendPaths = []) {
260
+ async function resolveAndMergeNestedStyleguideConfig({ styleguideConfig, configPath = '', resolver = new resolve_1.BaseResolver(), parentConfigPaths = [], extendPaths = [], }) {
261
261
  if (parentConfigPaths.includes(configPath)) {
262
262
  throw new Error(`Circular dependency in config file: "${configPath}"`);
263
263
  }
@@ -284,8 +284,10 @@ async function resolveAndMergeNestedStyleguideConfig({ styleguideConfig, configP
284
284
  return await resolveAndMergeNestedStyleguideConfig({
285
285
  styleguideConfig: extendedStyleguideConfig,
286
286
  configPath: pathItem,
287
- resolver: resolver,
288
- }, [...parentConfigPaths, resolvedConfigPath], extendPaths);
287
+ resolver,
288
+ parentConfigPaths: [...parentConfigPaths, resolvedConfigPath],
289
+ extendPaths,
290
+ });
289
291
  }) || []);
290
292
  const { plugins: mergedPlugins = [], ...styleguide } = (0, utils_2.mergeExtends)([
291
293
  ...extendConfigs,
@@ -305,8 +307,8 @@ async function resolveAndMergeNestedStyleguideConfig({ styleguideConfig, configP
305
307
  doNotResolveExamples: styleguideConfig?.doNotResolveExamples,
306
308
  };
307
309
  }
308
- async function resolveStyleguideConfig(opts, parentConfigPaths = [], extendPaths = []) {
309
- const resolvedStyleguideConfig = await resolveAndMergeNestedStyleguideConfig(opts, parentConfigPaths, extendPaths);
310
+ async function resolveStyleguideConfig(opts) {
311
+ const resolvedStyleguideConfig = await resolveAndMergeNestedStyleguideConfig(opts);
310
312
  return {
311
313
  ...resolvedStyleguideConfig,
312
314
  rules: resolvedStyleguideConfig.rules && groupStyleguideAssertionRules(resolvedStyleguideConfig),
@@ -187,8 +187,6 @@ const minimal = {
187
187
  arazzo1Rules: {
188
188
  'criteria-unique': 'off',
189
189
  'no-criteria-xpath': 'off',
190
- 'no-actions-type-end': 'off',
191
- 'parameters-not-in-body': 'off',
192
190
  'parameters-unique': 'off',
193
191
  'requestBody-replacements-unique': 'off',
194
192
  'sourceDescription-type': 'off',
@@ -197,7 +195,7 @@ const minimal = {
197
195
  'step-onFailure-unique': 'off',
198
196
  'stepId-unique': 'error',
199
197
  'sourceDescription-name-unique': 'off',
200
- 'version-enum': 'off',
198
+ 'spot-supported-versions': 'off',
201
199
  'workflowId-unique': 'error',
202
200
  'workflow-dependsOn': 'off',
203
201
  },
@@ -186,9 +186,7 @@ const recommendedStrict = {
186
186
  },
187
187
  arazzo1Rules: {
188
188
  'criteria-unique': 'error',
189
- 'no-criteria-xpath': 'error',
190
- 'no-actions-type-end': 'error',
191
- 'parameters-not-in-body': 'error',
189
+ 'no-criteria-xpath': 'off',
192
190
  'parameters-unique': 'error',
193
191
  'requestBody-replacements-unique': 'error',
194
192
  'sourceDescription-type': 'error',
@@ -197,7 +195,7 @@ const recommendedStrict = {
197
195
  'stepId-unique': 'error',
198
196
  'sourceDescription-name-unique': 'error',
199
197
  'sourceDescriptions-not-empty': 'error',
200
- 'version-enum': 'error',
198
+ 'spot-supported-versions': 'off',
201
199
  'workflowId-unique': 'error',
202
200
  'workflow-dependsOn': 'error',
203
201
  },
@@ -186,9 +186,7 @@ const recommended = {
186
186
  },
187
187
  arazzo1Rules: {
188
188
  'criteria-unique': 'warn',
189
- 'no-criteria-xpath': 'warn',
190
- 'no-actions-type-end': 'warn',
191
- 'parameters-not-in-body': 'warn',
189
+ 'no-criteria-xpath': 'off',
192
190
  'parameters-unique': 'error',
193
191
  'requestBody-replacements-unique': 'warn',
194
192
  'sourceDescription-type': 'error',
@@ -197,7 +195,7 @@ const recommended = {
197
195
  'stepId-unique': 'error',
198
196
  'sourceDescription-name-unique': 'error',
199
197
  'sourceDescriptions-not-empty': 'error',
200
- 'version-enum': 'warn',
198
+ 'spot-supported-versions': 'off',
201
199
  'workflowId-unique': 'error',
202
200
  'workflow-dependsOn': 'error',
203
201
  },
@@ -15,16 +15,19 @@ function initRules(rules, config, type, oasVersion) {
15
15
  return undefined;
16
16
  }
17
17
  const severity = ruleSettings.severity;
18
+ const message = ruleSettings.message;
18
19
  const visitors = rule(ruleSettings);
19
20
  if (Array.isArray(visitors)) {
20
21
  return visitors.map((visitor) => ({
21
22
  severity,
22
23
  ruleId,
24
+ message,
23
25
  visitor: visitor,
24
26
  }));
25
27
  }
26
28
  return {
27
29
  severity,
30
+ message,
28
31
  ruleId,
29
32
  visitor: visitors, // note: actually it is only one visitor object
30
33
  };
@@ -11,9 +11,8 @@ const spec = {
11
11
  async2Rules: {},
12
12
  async3Rules: {},
13
13
  arazzo1Rules: {
14
- 'parameters-not-in-body': 'error',
15
14
  'sourceDescription-type': 'error',
16
- 'version-enum': 'error',
15
+ 'spot-supported-versions': 'off',
17
16
  'workflowId-unique': 'error',
18
17
  'stepId-unique': 'error',
19
18
  'sourceDescription-name-unique': 'error',
@@ -23,8 +22,7 @@ const spec = {
23
22
  'step-onSuccess-unique': 'error',
24
23
  'step-onFailure-unique': 'error',
25
24
  'requestBody-replacements-unique': 'error',
26
- 'no-criteria-xpath': 'error',
27
- 'no-actions-type-end': 'error',
25
+ 'no-criteria-xpath': 'off',
28
26
  'criteria-unique': 'error',
29
27
  },
30
28
  };
@@ -7,11 +7,10 @@ import type { JSONSchema } from 'json-schema-to-ts';
7
7
  export type RuleSeverity = ProblemSeverity | 'off';
8
8
  export type RuleSettings = {
9
9
  severity: RuleSeverity;
10
+ message?: string;
10
11
  };
11
12
  export type PreprocessorSeverity = RuleSeverity | 'on';
12
- export type RuleConfig = RuleSeverity | ({
13
- severity?: ProblemSeverity;
14
- } & Record<string, any>);
13
+ export type RuleConfig = RuleSeverity | (Partial<RuleSettings> & Record<string, any>);
15
14
  export type PreprocessorConfig = PreprocessorSeverity | ({
16
15
  severity?: ProblemSeverity;
17
16
  } & Record<string, any>);
@@ -3,10 +3,9 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.preprocessors = exports.rules = void 0;
4
4
  const struct_1 = require("../common/struct");
5
5
  const assertions_1 = require("../common/assertions");
6
- const parameters_not_in_body_1 = require("../spot/parameters-not-in-body");
7
6
  const sourceDescription_type_1 = require("../arazzo/sourceDescription-type");
8
7
  const sourceDescriptions_not_empty_1 = require("./sourceDescriptions-not-empty");
9
- const version_enum_1 = require("../spot/version-enum");
8
+ const spot_supported_versions_1 = require("../spot/spot-supported-versions");
10
9
  const workflowId_unique_1 = require("./workflowId-unique");
11
10
  const stepId_unique_1 = require("./stepId-unique");
12
11
  const sourceDescriptions_name_unique_1 = require("./sourceDescriptions-name-unique");
@@ -16,14 +15,12 @@ const step_onSuccess_unique_1 = require("./step-onSuccess-unique");
16
15
  const step_onFailure_unique_1 = require("./step-onFailure-unique");
17
16
  const requestBody_replacements_unique_1 = require("./requestBody-replacements-unique");
18
17
  const no_criteria_xpath_1 = require("../spot/no-criteria-xpath");
19
- const no_actions_type_end_1 = require("../spot/no-actions-type-end");
20
18
  const criteria_unique_1 = require("./criteria-unique");
21
19
  exports.rules = {
22
20
  struct: struct_1.Struct,
23
21
  assertions: assertions_1.Assertions,
24
- 'parameters-not-in-body': parameters_not_in_body_1.ParametersNotInBody,
25
22
  'sourceDescription-type': sourceDescription_type_1.SourceDescriptionType,
26
- 'version-enum': version_enum_1.VersionEnum,
23
+ 'spot-supported-versions': spot_supported_versions_1.SpotSupportedVersions,
27
24
  'workflowId-unique': workflowId_unique_1.WorkflowIdUnique,
28
25
  'stepId-unique': stepId_unique_1.StepIdUnique,
29
26
  'sourceDescription-name-unique': sourceDescriptions_name_unique_1.SourceDescriptionsNameUnique,
@@ -34,7 +31,6 @@ exports.rules = {
34
31
  'step-onFailure-unique': step_onFailure_unique_1.StepOnFailureUnique,
35
32
  'requestBody-replacements-unique': requestBody_replacements_unique_1.RequestBodyReplacementsUnique,
36
33
  'no-criteria-xpath': no_criteria_xpath_1.NoCriteriaXpath,
37
- 'no-actions-type-end': no_actions_type_end_1.NoActionsTypeEnd,
38
34
  'criteria-unique': criteria_unique_1.CriteriaUnique,
39
35
  };
40
36
  exports.preprocessors = {};
@@ -0,0 +1,2 @@
1
+ import type { Arazzo1Rule } from '../../visitors';
2
+ export declare const SpotSupportedVersions: Arazzo1Rule;
@@ -1,9 +1,9 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.VersionEnum = void 0;
3
+ exports.SpotSupportedVersions = void 0;
4
4
  const arazzo_1 = require("../../typings/arazzo");
5
5
  const utils_1 = require("../../utils");
6
- const VersionEnum = () => {
6
+ const SpotSupportedVersions = () => {
7
7
  const supportedVersions = arazzo_1.ARAZZO_VERSIONS_SUPPORTED_BY_SPOT.join(', ');
8
8
  return {
9
9
  Root: {
@@ -18,4 +18,4 @@ const VersionEnum = () => {
18
18
  },
19
19
  };
20
20
  };
21
- exports.VersionEnum = VersionEnum;
21
+ exports.SpotSupportedVersions = SpotSupportedVersions;
@@ -9,7 +9,7 @@ declare const builtInAsync2Rules: readonly ["info-contact", "info-license-strict
9
9
  declare const builtInAsync3Rules: readonly ["info-contact", "info-license-strict", "operation-operationId", "tag-description", "tags-alphabetical", "channels-kebab-case", "no-channel-trailing-slash"];
10
10
  export type BuiltInAsync2RuleId = typeof builtInAsync2Rules[number];
11
11
  export type BuiltInAsync3RuleId = typeof builtInAsync3Rules[number];
12
- declare const builtInArazzo1Rules: readonly ["parameters-not-in-body", "sourceDescription-type", "version-enum", "workflowId-unique", "stepId-unique", "sourceDescription-name-unique", "sourceDescriptions-not-empty", "workflow-dependsOn", "parameters-unique", "step-onSuccess-unique", "step-onFailure-unique", "requestBody-replacements-unique", "no-criteria-xpath", "no-actions-type-end", "criteria-unique"];
12
+ declare const builtInArazzo1Rules: readonly ["sourceDescription-type", "workflowId-unique", "stepId-unique", "sourceDescription-name-unique", "sourceDescriptions-not-empty", "workflow-dependsOn", "parameters-unique", "step-onSuccess-unique", "step-onFailure-unique", "spot-supported-versions", "requestBody-replacements-unique", "no-criteria-xpath", "criteria-unique"];
13
13
  export type BuiltInArazzo1RuleId = typeof builtInArazzo1Rules[number];
14
14
  declare const oas2NodeTypesList: readonly ["Root", "Tag", "TagList", "ExternalDocs", "SecurityRequirement", "SecurityRequirementList", "Info", "Contact", "License", "Paths", "PathItem", "Parameter", "ParameterList", "ParameterItems", "Operation", "Example", "ExamplesMap", "Examples", "Header", "Responses", "Response", "Schema", "Xml", "SchemaProperties", "NamedSchemas", "NamedResponses", "NamedParameters", "NamedSecuritySchemes", "SecurityScheme", "TagGroup", "TagGroups", "EnumDescriptions", "Logo", "XCodeSample", "XCodeSampleList", "XServer", "XServerList"];
15
15
  export type Oas2NodeType = typeof oas2NodeTypesList[number];
@@ -128,9 +128,7 @@ const builtInAsync3Rules = [
128
128
  'no-channel-trailing-slash',
129
129
  ];
130
130
  const builtInArazzo1Rules = [
131
- 'parameters-not-in-body',
132
131
  'sourceDescription-type',
133
- 'version-enum',
134
132
  'workflowId-unique',
135
133
  'stepId-unique',
136
134
  'sourceDescription-name-unique',
@@ -139,9 +137,9 @@ const builtInArazzo1Rules = [
139
137
  'parameters-unique',
140
138
  'step-onSuccess-unique',
141
139
  'step-onFailure-unique',
140
+ 'spot-supported-versions',
142
141
  'requestBody-replacements-unique',
143
142
  'no-criteria-xpath',
144
- 'no-actions-type-end',
145
143
  'criteria-unique',
146
144
  ];
147
145
  const builtInRules = [
package/lib/visitors.d.ts CHANGED
@@ -23,6 +23,7 @@ type VisitFunctionOrObject<T> = VisitFunction<T> | VisitObject<T>;
23
23
  export type VisitorNode<T> = {
24
24
  ruleId: string;
25
25
  severity: ProblemSeverity;
26
+ message?: string;
26
27
  context: VisitorLevelContext | VisitorSkippedLevelContext;
27
28
  depth: number;
28
29
  visit: VisitFunction<T>;
@@ -31,6 +32,7 @@ export type VisitorNode<T> = {
31
32
  type VisitorRefNode = {
32
33
  ruleId: string;
33
34
  severity: ProblemSeverity;
35
+ message?: string;
34
36
  context: VisitorLevelContext;
35
37
  depth: number;
36
38
  visit: VisitRefFunction;
@@ -219,6 +221,7 @@ export type OasDecorator = Oas3Decorator;
219
221
  export type RuleInstanceConfig = {
220
222
  ruleId: string;
221
223
  severity: ProblemSeverity;
224
+ message?: string;
222
225
  };
223
226
  export declare function normalizeVisitors<T extends BaseVisitor>(visitorsConfig: (RuleInstanceConfig & {
224
227
  visitor: NestedVisitObject<unknown, T>;
package/lib/visitors.js CHANGED
@@ -31,8 +31,8 @@ function normalizeVisitors(visitorsConfig, types) {
31
31
  enter: [],
32
32
  leave: [],
33
33
  };
34
- for (const { ruleId, severity, visitor } of visitorsConfig) {
35
- normalizeVisitorLevel({ ruleId, severity }, visitor, null);
34
+ for (const { ruleId, severity, message, visitor } of visitorsConfig) {
35
+ normalizeVisitorLevel({ ruleId, severity, message }, visitor, null);
36
36
  }
37
37
  for (const v of Object.keys(normalizedVisitors)) {
38
38
  normalizedVisitors[v].enter.sort((a, b) => b.depth - a.depth);
package/lib/walk.js CHANGED
@@ -54,9 +54,9 @@ function walkDocument(opts) {
54
54
  const enteredContexts = new Set();
55
55
  if ((0, ref_utils_1.isRef)(node)) {
56
56
  const refEnterVisitors = normalizedVisitors.ref.enter;
57
- for (const { visit: visitor, ruleId, severity, context } of refEnterVisitors) {
57
+ for (const { visit: visitor, ruleId, severity, message, context } of refEnterVisitors) {
58
58
  enteredContexts.add(context);
59
- const report = reportFn.bind(undefined, ruleId, severity);
59
+ const report = reportFn.bind(undefined, ruleId, severity, message);
60
60
  visitor(node, {
61
61
  report,
62
62
  resolve,
@@ -82,7 +82,7 @@ function walkDocument(opts) {
82
82
  const anyEnterVisitors = normalizedVisitors.any.enter;
83
83
  const currentEnterVisitors = anyEnterVisitors.concat(normalizedVisitors[type.name]?.enter || []);
84
84
  const activatedContexts = [];
85
- for (const { context, visit, skip, ruleId, severity } of currentEnterVisitors) {
85
+ for (const { context, visit, skip, ruleId, severity, message } of currentEnterVisitors) {
86
86
  if (ignoredNodes.has(`${currentLocation.absolutePointer}${currentLocation.pointer}`))
87
87
  break;
88
88
  if (context.isSkippedLevel) {
@@ -127,7 +127,7 @@ function walkDocument(opts) {
127
127
  if (!activatedOn.skipped) {
128
128
  visitedBySome = true;
129
129
  enteredContexts.add(context);
130
- visitWithContext(visit, resolvedNode, node, context, ruleId, severity);
130
+ visitWithContext(visit, resolvedNode, node, context, ruleId, severity, message);
131
131
  }
132
132
  }
133
133
  }
@@ -209,18 +209,18 @@ function walkDocument(opts) {
209
209
  }
210
210
  }
211
211
  }
212
- for (const { context, visit, ruleId, severity } of currentLeaveVisitors) {
212
+ for (const { context, visit, ruleId, severity, message } of currentLeaveVisitors) {
213
213
  if (!context.isSkippedLevel && enteredContexts.has(context)) {
214
- visitWithContext(visit, resolvedNode, node, context, ruleId, severity);
214
+ visitWithContext(visit, resolvedNode, node, context, ruleId, severity, message);
215
215
  }
216
216
  }
217
217
  }
218
218
  currentLocation = location;
219
219
  if ((0, ref_utils_1.isRef)(node)) {
220
220
  const refLeaveVisitors = normalizedVisitors.ref.leave;
221
- for (const { visit: visitor, ruleId, severity, context } of refLeaveVisitors) {
221
+ for (const { visit: visitor, ruleId, severity, context, message } of refLeaveVisitors) {
222
222
  if (enteredContexts.has(context)) {
223
- const report = reportFn.bind(undefined, ruleId, severity);
223
+ const report = reportFn.bind(undefined, ruleId, severity, message);
224
224
  visitor(node, {
225
225
  report,
226
226
  resolve,
@@ -238,8 +238,8 @@ function walkDocument(opts) {
238
238
  }
239
239
  }
240
240
  // returns true ignores all the next visitors on the specific node
241
- function visitWithContext(visit, resolvedNode, node, context, ruleId, severity) {
242
- const report = reportFn.bind(undefined, ruleId, severity);
241
+ function visitWithContext(visit, resolvedNode, node, context, ruleId, severity, customMessage) {
242
+ const report = reportFn.bind(undefined, ruleId, severity, customMessage);
243
243
  visit(resolvedNode, {
244
244
  report,
245
245
  resolve,
@@ -257,7 +257,7 @@ function walkDocument(opts) {
257
257
  getVisitorData: getVisitorDataFn.bind(undefined, ruleId),
258
258
  }, collectParents(context), context);
259
259
  }
260
- function reportFn(ruleId, severity, opts) {
260
+ function reportFn(ruleId, severity, customMessage, opts) {
261
261
  const normalizedLocation = opts.location
262
262
  ? Array.isArray(opts.location)
263
263
  ? opts.location
@@ -274,6 +274,9 @@ function walkDocument(opts) {
274
274
  ruleId: opts.ruleId || ruleId,
275
275
  severity: ruleSeverity,
276
276
  ...opts,
277
+ message: customMessage
278
+ ? customMessage.replace('{{message}}', opts.message)
279
+ : opts.message,
277
280
  suggest: opts.suggest || [],
278
281
  location,
279
282
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@redocly/openapi-core",
3
- "version": "1.26.0",
3
+ "version": "1.27.0",
4
4
  "description": "",
5
5
  "main": "lib/index.js",
6
6
  "engines": {
@@ -41,7 +41,6 @@
41
41
  "https-proxy-agent": "^7.0.4",
42
42
  "js-levenshtein": "^1.1.6",
43
43
  "js-yaml": "^4.1.0",
44
- "lodash.isequal": "^4.5.0",
45
44
  "minimatch": "^5.0.1",
46
45
  "node-fetch": "^2.6.1",
47
46
  "pluralize": "^8.0.0",
@@ -50,7 +49,6 @@
50
49
  "devDependencies": {
51
50
  "@types/js-levenshtein": "^1.1.0",
52
51
  "@types/js-yaml": "^4.0.3",
53
- "@types/lodash.isequal": "^4.5.5",
54
52
  "@types/minimatch": "^3.0.5",
55
53
  "@types/node": "^20.11.5",
56
54
  "@types/node-fetch": "^2.5.7",
@@ -12,6 +12,7 @@ import {
12
12
  import { BaseResolver, Document } from '../resolve';
13
13
  import { listOf } from '../types';
14
14
  import { Oas3RuleSet } from '../oas-types';
15
+ import { createConfig } from '../config';
15
16
 
16
17
  describe('walk order', () => {
17
18
  it('should run visitors', async () => {
@@ -1338,6 +1339,56 @@ describe('context.report', () => {
1338
1339
  ]
1339
1340
  `);
1340
1341
  });
1342
+
1343
+ it('should report errors with custom messages', async () => {
1344
+ const document = parseYamlToDocument(
1345
+ outdent`
1346
+ openapi: 3.0.0
1347
+ info:
1348
+ license: {}
1349
+ paths: {}
1350
+ `,
1351
+ 'foobar.yaml'
1352
+ );
1353
+
1354
+ const config = await createConfig(`
1355
+ rules:
1356
+ info-contact:
1357
+ message: "MY ERR DESCRIPTION: {{message}}"
1358
+ severity: "error"
1359
+ `);
1360
+
1361
+ const results = await lintDocument({
1362
+ externalRefResolver: new BaseResolver(),
1363
+ document,
1364
+ config: config.styleguide,
1365
+ });
1366
+
1367
+ expect(results).toMatchInlineSnapshot(`
1368
+ [
1369
+ {
1370
+ "location": [
1371
+ {
1372
+ "pointer": "#/info/contact",
1373
+ "reportOnKey": true,
1374
+ "source": Source {
1375
+ "absoluteRef": "foobar.yaml",
1376
+ "body": "openapi: 3.0.0
1377
+ info:
1378
+ license: {}
1379
+ paths: {}",
1380
+ "mimeType": undefined,
1381
+ },
1382
+ },
1383
+ ],
1384
+ "message": "MY ERR DESCRIPTION: Info object should contain \`contact\` field.",
1385
+ "ruleId": "info-contact",
1386
+ "severity": "error",
1387
+ "suggest": [],
1388
+ },
1389
+ ]
1390
+ `);
1391
+ });
1341
1392
  });
1342
1393
 
1343
1394
  describe('context.resolve', () => {
@@ -6,18 +6,16 @@ exports[`resolveConfig should ignore minimal from the root and read local file 1
6
6
  "arazzo1Preprocessors": {},
7
7
  "arazzo1Rules": {
8
8
  "criteria-unique": "warn",
9
- "no-actions-type-end": "warn",
10
- "no-criteria-xpath": "warn",
11
- "parameters-not-in-body": "warn",
9
+ "no-criteria-xpath": "off",
12
10
  "parameters-unique": "error",
13
11
  "requestBody-replacements-unique": "warn",
14
12
  "sourceDescription-name-unique": "error",
15
13
  "sourceDescription-type": "error",
16
14
  "sourceDescriptions-not-empty": "error",
15
+ "spot-supported-versions": "off",
17
16
  "step-onFailure-unique": "warn",
18
17
  "step-onSuccess-unique": "warn",
19
18
  "stepId-unique": "error",
20
- "version-enum": "warn",
21
19
  "workflow-dependsOn": "error",
22
20
  "workflowId-unique": "error",
23
21
  },
@@ -234,18 +232,16 @@ exports[`resolveStyleguideConfig should resolve extends with local file config w
234
232
  "arazzo1Preprocessors": {},
235
233
  "arazzo1Rules": {
236
234
  "criteria-unique": "warn",
237
- "no-actions-type-end": "warn",
238
- "no-criteria-xpath": "warn",
239
- "parameters-not-in-body": "warn",
235
+ "no-criteria-xpath": "off",
240
236
  "parameters-unique": "error",
241
237
  "requestBody-replacements-unique": "warn",
242
238
  "sourceDescription-name-unique": "error",
243
239
  "sourceDescription-type": "error",
244
240
  "sourceDescriptions-not-empty": "error",
241
+ "spot-supported-versions": "off",
245
242
  "step-onFailure-unique": "warn",
246
243
  "step-onSuccess-unique": "warn",
247
244
  "stepId-unique": "error",
248
- "version-enum": "warn",
249
245
  "workflow-dependsOn": "error",
250
246
  "workflowId-unique": "error",
251
247
  },
package/src/config/all.ts CHANGED
@@ -210,9 +210,7 @@ const all: PluginStyleguideConfig<'built-in'> = {
210
210
  },
211
211
  arazzo1Rules: {
212
212
  'criteria-unique': 'error',
213
- 'no-criteria-xpath': 'error',
214
- 'no-actions-type-end': 'error',
215
- 'parameters-not-in-body': 'error',
213
+ 'no-criteria-xpath': 'off',
216
214
  'parameters-unique': 'error',
217
215
  'requestBody-replacements-unique': 'error',
218
216
  'sourceDescription-type': 'error',
@@ -221,7 +219,7 @@ const all: PluginStyleguideConfig<'built-in'> = {
221
219
  'stepId-unique': 'error',
222
220
  'sourceDescription-name-unique': 'error',
223
221
  'sourceDescriptions-not-empty': 'error',
224
- 'version-enum': 'error',
222
+ 'spot-supported-versions': 'off',
225
223
  'workflowId-unique': 'error',
226
224
  'workflow-dependsOn': 'error',
227
225
  },
@@ -370,19 +370,19 @@ export async function resolveApis({
370
370
  return resolvedApis;
371
371
  }
372
372
 
373
- async function resolveAndMergeNestedStyleguideConfig(
374
- {
375
- styleguideConfig,
376
- configPath = '',
377
- resolver = new BaseResolver(),
378
- }: {
379
- styleguideConfig?: StyleguideRawConfig;
380
- configPath?: string;
381
- resolver?: BaseResolver;
382
- },
383
- parentConfigPaths: string[] = [],
384
- extendPaths: string[] = []
385
- ): Promise<ResolvedStyleguideConfig> {
373
+ async function resolveAndMergeNestedStyleguideConfig({
374
+ styleguideConfig,
375
+ configPath = '',
376
+ resolver = new BaseResolver(),
377
+ parentConfigPaths = [],
378
+ extendPaths = [],
379
+ }: {
380
+ styleguideConfig?: StyleguideRawConfig;
381
+ configPath?: string;
382
+ resolver?: BaseResolver;
383
+ parentConfigPaths?: string[];
384
+ extendPaths?: string[];
385
+ }): Promise<ResolvedStyleguideConfig> {
386
386
  if (parentConfigPaths.includes(configPath)) {
387
387
  throw new Error(`Circular dependency in config file: "${configPath}"`);
388
388
  }
@@ -414,15 +414,13 @@ async function resolveAndMergeNestedStyleguideConfig(
414
414
  ? new URL(presetItem, configPath).href
415
415
  : path.resolve(path.dirname(configPath), presetItem);
416
416
  const extendedStyleguideConfig = await loadExtendStyleguideConfig(pathItem, resolver);
417
- return await resolveAndMergeNestedStyleguideConfig(
418
- {
419
- styleguideConfig: extendedStyleguideConfig,
420
- configPath: pathItem,
421
- resolver: resolver,
422
- },
423
- [...parentConfigPaths, resolvedConfigPath],
424
- extendPaths
425
- );
417
+ return await resolveAndMergeNestedStyleguideConfig({
418
+ styleguideConfig: extendedStyleguideConfig,
419
+ configPath: pathItem,
420
+ resolver,
421
+ parentConfigPaths: [...parentConfigPaths, resolvedConfigPath],
422
+ extendPaths,
423
+ });
426
424
  }) || []
427
425
  );
428
426
 
@@ -446,20 +444,14 @@ async function resolveAndMergeNestedStyleguideConfig(
446
444
  };
447
445
  }
448
446
 
449
- export async function resolveStyleguideConfig(
450
- opts: {
451
- styleguideConfig?: StyleguideRawConfig;
452
- configPath?: string;
453
- resolver?: BaseResolver;
454
- },
455
- parentConfigPaths: string[] = [],
456
- extendPaths: string[] = []
457
- ): Promise<ResolvedStyleguideConfig> {
458
- const resolvedStyleguideConfig = await resolveAndMergeNestedStyleguideConfig(
459
- opts,
460
- parentConfigPaths,
461
- extendPaths
462
- );
447
+ export async function resolveStyleguideConfig(opts: {
448
+ styleguideConfig?: StyleguideRawConfig;
449
+ configPath?: string;
450
+ resolver?: BaseResolver;
451
+ parentConfigPaths?: string[];
452
+ extendPaths?: string[];
453
+ }): Promise<ResolvedStyleguideConfig> {
454
+ const resolvedStyleguideConfig = await resolveAndMergeNestedStyleguideConfig(opts);
463
455
 
464
456
  return {
465
457
  ...resolvedStyleguideConfig,