@redocly/openapi-core 1.23.0 → 1.24.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 (33) hide show
  1. package/CHANGELOG.md +13 -0
  2. package/lib/config/all.js +3 -0
  3. package/lib/config/config.js +4 -1
  4. package/lib/config/minimal.js +3 -0
  5. package/lib/config/recommended-strict.js +3 -0
  6. package/lib/config/recommended.js +3 -0
  7. package/lib/rules/arazzo/criteria-unique.d.ts +2 -0
  8. package/lib/rules/arazzo/criteria-unique.js +65 -0
  9. package/lib/rules/arazzo/index.js +6 -0
  10. package/lib/rules/spot/no-actions-type-end.d.ts +2 -0
  11. package/lib/rules/spot/no-actions-type-end.js +28 -0
  12. package/lib/rules/spot/no-criteria-xpath.d.ts +2 -0
  13. package/lib/rules/spot/no-criteria-xpath.js +21 -0
  14. package/lib/types/arazzo.js +1 -1
  15. package/lib/types/redocly-yaml.d.ts +1 -1
  16. package/lib/types/redocly-yaml.js +3 -0
  17. package/package.json +2 -2
  18. package/src/config/__tests__/__snapshots__/config-resolvers.test.ts.snap +6 -0
  19. package/src/config/all.ts +3 -0
  20. package/src/config/config.ts +4 -1
  21. package/src/config/minimal.ts +3 -0
  22. package/src/config/recommended-strict.ts +3 -0
  23. package/src/config/recommended.ts +3 -0
  24. package/src/rules/arazzo/__tests__/criteria-unique.test.ts +161 -0
  25. package/src/rules/arazzo/__tests__/no-actions-type-end.test.ts +122 -0
  26. package/src/rules/arazzo/__tests__/no-criteria-xpath.test.ts +127 -0
  27. package/src/rules/arazzo/criteria-unique.ts +63 -0
  28. package/src/rules/arazzo/index.ts +6 -0
  29. package/src/rules/spot/no-actions-type-end.ts +27 -0
  30. package/src/rules/spot/no-criteria-xpath.ts +20 -0
  31. package/src/types/arazzo.ts +1 -1
  32. package/src/types/redocly-yaml.ts +3 -0
  33. package/tsconfig.tsbuildinfo +1 -1
@@ -0,0 +1,127 @@
1
+ import { outdent } from 'outdent';
2
+ import { lintDocument } from '../../../lint';
3
+ import { parseYamlToDocument, replaceSourceWithRef, makeConfig } from '../../../../__tests__/utils';
4
+ import { BaseResolver } from '../../../resolve';
5
+
6
+ describe('Arazzo no-criteria-xpath', () => {
7
+ const document = parseYamlToDocument(
8
+ outdent`
9
+ arazzo: '1.0.0'
10
+ info:
11
+ title: Cool API
12
+ version: 1.0.0
13
+ description: A cool API
14
+ sourceDescriptions:
15
+ - name: museum-api
16
+ type: openapi
17
+ url: openapi.yaml
18
+ workflows:
19
+ - workflowId: get-museum-hours
20
+ description: This workflow demonstrates how to get the museum opening hours and buy tickets.
21
+ parameters:
22
+ - in: header
23
+ name: Authorization
24
+ value: Basic Og==
25
+ steps:
26
+ - stepId: create-event
27
+ description: >-
28
+ Create a new special event.
29
+ operationPath: $sourceDescriptions.museum-api#/paths/~1special-events/post
30
+ requestBody:
31
+ payload:
32
+ name: 'Mermaid Treasure Identification and Analysis'
33
+ location: 'Under the seaaa 🦀 🎶 🌊.'
34
+ eventDescription: 'Join us as we review and classify a rare collection of 20 thingamabobs, gadgets, gizmos, whoosits, and whatsits, kindly donated by Ariel.'
35
+ dates:
36
+ - '2023-09-05'
37
+ - '2023-09-08'
38
+ price: 0
39
+ successCriteria:
40
+ - condition: $statusCode == 201
41
+ - context: $response.body
42
+ condition: $.name == 'Mermaid Treasure Identification and Analysis'
43
+ type:
44
+ type: jsonpath
45
+ version: draft-goessner-dispatch-jsonpath-00
46
+ - context: $response.body
47
+ condition: $.name == 'Orca Identification and Analysis'
48
+ type: xpath
49
+ - context: $response.body
50
+ condition: $.name == 'Mermaid Treasure Identification and Analysis'
51
+ type:
52
+ type: xpath
53
+ version: xpath-30
54
+ outputs:
55
+ createdEventId: $response.body.eventId
56
+ name: $response.body.name
57
+ - workflowId: get-museum-hours-2
58
+ description: This workflow demonstrates how to get the museum opening hours and buy tickets.
59
+ parameters:
60
+ - in: header
61
+ name: Authorization
62
+ value: Basic Og==
63
+ steps:
64
+ - stepId: get-museum-hours
65
+ description: >-
66
+ Get museum hours by resolving request details with getMuseumHours operationId from openapi.yaml description.
67
+ operationId: museum-api.getMuseumHours
68
+ successCriteria:
69
+ - condition: $statusCode == 200
70
+ `,
71
+ 'arazzo.yaml'
72
+ );
73
+
74
+ it('should report when the `xpath` criteria exists', async () => {
75
+ const results = await lintDocument({
76
+ externalRefResolver: new BaseResolver(),
77
+ document,
78
+ config: await makeConfig({
79
+ rules: {},
80
+ arazzoRules: { 'no-criteria-xpath': 'error' },
81
+ }),
82
+ });
83
+
84
+ expect(replaceSourceWithRef(results)).toMatchInlineSnapshot(`
85
+ [
86
+ {
87
+ "location": [
88
+ {
89
+ "pointer": "#/workflows/0/steps/0/successCriteria/2/type",
90
+ "reportOnKey": false,
91
+ "source": "arazzo.yaml",
92
+ },
93
+ ],
94
+ "message": "The \`xpath\` type criteria is not supported by Spot.",
95
+ "ruleId": "no-criteria-xpath",
96
+ "severity": "error",
97
+ "suggest": [],
98
+ },
99
+ {
100
+ "location": [
101
+ {
102
+ "pointer": "#/workflows/0/steps/0/successCriteria/3/type",
103
+ "reportOnKey": false,
104
+ "source": "arazzo.yaml",
105
+ },
106
+ ],
107
+ "message": "The \`xpath\` type criteria is not supported by Spot.",
108
+ "ruleId": "no-criteria-xpath",
109
+ "severity": "error",
110
+ "suggest": [],
111
+ },
112
+ ]
113
+ `);
114
+ });
115
+
116
+ it('should not report when the `xpath` criteria exists', async () => {
117
+ const results = await lintDocument({
118
+ externalRefResolver: new BaseResolver(),
119
+ document,
120
+ config: await makeConfig({
121
+ rules: {},
122
+ }),
123
+ });
124
+
125
+ expect(replaceSourceWithRef(results)).toMatchInlineSnapshot(`[]`);
126
+ });
127
+ });
@@ -0,0 +1,63 @@
1
+ import type { ArazzoRule } from '../../visitors';
2
+ import type { UserContext } from '../../walk';
3
+
4
+ export const CriteriaUnique: ArazzoRule = () => {
5
+ return {
6
+ FailureActionObject: {
7
+ enter(action, { report, location }: UserContext) {
8
+ const criterias = action.criteria;
9
+ const seen = new Set<string>();
10
+ for (const criteria of criterias) {
11
+ const key = JSON.stringify(criteria);
12
+ if (seen.has(key)) {
13
+ report({
14
+ message: 'The FailureAction criteria items must be unique.',
15
+ location: location.child(['criteria', criterias.indexOf(criteria)]),
16
+ });
17
+ } else {
18
+ seen.add(key);
19
+ }
20
+ }
21
+ },
22
+ },
23
+ SuccessActionObject: {
24
+ enter(action, { report, location }: UserContext) {
25
+ const criterias = action.criteria;
26
+ const seen = new Set<string>();
27
+ for (const criteria of criterias) {
28
+ const key = JSON.stringify(criteria);
29
+ if (seen.has(key)) {
30
+ report({
31
+ message: 'The SuccessAction criteria items must be unique.',
32
+ location: location.child(['criteria', criterias.indexOf(criteria)]),
33
+ });
34
+ } else {
35
+ seen.add(key);
36
+ }
37
+ }
38
+ },
39
+ },
40
+ Step: {
41
+ enter(step, { report, location }: UserContext) {
42
+ if (!step.successCriteria) {
43
+ return;
44
+ }
45
+
46
+ const successCriterias = step.successCriteria;
47
+ const seen = new Set<string>();
48
+
49
+ for (const criteria of successCriterias) {
50
+ const key = JSON.stringify(criteria);
51
+ if (seen.has(key)) {
52
+ report({
53
+ message: 'The Step SuccessCriteria items must be unique.',
54
+ location: location.child(['successCriteria', successCriterias.indexOf(criteria)]),
55
+ });
56
+ } else {
57
+ seen.add(key);
58
+ }
59
+ }
60
+ },
61
+ },
62
+ };
63
+ };
@@ -11,6 +11,9 @@ import { ParametersUnique } from './parameters-unique';
11
11
  import { StepOnSuccessUnique } from './step-onSuccess-unique';
12
12
  import { StepOnFailureUnique } from './step-onFailure-unique';
13
13
  import { RequestBodyReplacementsUnique } from './requestBody-replacements-unique';
14
+ import { NoCriteriaXpath } from '../spot/no-criteria-xpath';
15
+ import { NoActionsTypeEnd } from '../spot/no-actions-type-end';
16
+ import { CriteriaUnique } from './criteria-unique';
14
17
 
15
18
  import type { ArazzoRule } from '../../visitors';
16
19
  import type { ArazzoRuleSet } from '../../oas-types';
@@ -29,6 +32,9 @@ export const rules: ArazzoRuleSet<'built-in'> = {
29
32
  'step-onSuccess-unique': StepOnSuccessUnique as ArazzoRule,
30
33
  'step-onFailure-unique': StepOnFailureUnique as ArazzoRule,
31
34
  'requestBody-replacements-unique': RequestBodyReplacementsUnique as ArazzoRule,
35
+ 'no-criteria-xpath': NoCriteriaXpath as ArazzoRule,
36
+ 'no-actions-type-end': NoActionsTypeEnd as ArazzoRule,
37
+ 'criteria-unique': CriteriaUnique as ArazzoRule,
32
38
  };
33
39
 
34
40
  export const preprocessors = {};
@@ -0,0 +1,27 @@
1
+ import type { ArazzoRule } from '../../visitors';
2
+ import type { UserContext } from '../../walk';
3
+
4
+ export const NoActionsTypeEnd: ArazzoRule = () => {
5
+ return {
6
+ FailureActionObject: {
7
+ enter(action, { report, location }: UserContext) {
8
+ if (action.type === 'end') {
9
+ report({
10
+ message: 'The `end` type action is not supported by Spot.',
11
+ location: location.child(['type']),
12
+ });
13
+ }
14
+ },
15
+ },
16
+ SuccessActionObject: {
17
+ enter(action, { report, location }: UserContext) {
18
+ if (action.type === 'end') {
19
+ report({
20
+ message: 'The `end` type action is not supported by Spot.',
21
+ location: location.child(['type']),
22
+ });
23
+ }
24
+ },
25
+ },
26
+ };
27
+ };
@@ -0,0 +1,20 @@
1
+ import type { ArazzoRule } from '../../visitors';
2
+ import type { UserContext } from '../../walk';
3
+
4
+ export const NoCriteriaXpath: ArazzoRule = () => {
5
+ return {
6
+ CriterionObject: {
7
+ enter(criteria, { report, location }: UserContext) {
8
+ if (!criteria.type) {
9
+ return;
10
+ }
11
+ if (criteria?.type?.type === 'xpath' || criteria?.type === 'xpath') {
12
+ report({
13
+ message: 'The `xpath` type criteria is not supported by Spot.',
14
+ location: location.child(['type']),
15
+ });
16
+ }
17
+ },
18
+ },
19
+ };
20
+ };
@@ -205,7 +205,7 @@ const CriterionObject: NodeType = {
205
205
  return undefined;
206
206
  } else if (typeof value === 'string') {
207
207
  return { enum: ['regex', 'jsonpath', 'simple', 'xpath'] };
208
- } else if (value.type === 'jsonpath') {
208
+ } else if (value?.type === 'jsonpath') {
209
209
  return 'JSONPathCriterion';
210
210
  } else {
211
211
  return 'XPathCriterion';
@@ -122,6 +122,9 @@ const builtInArazzoRules = [
122
122
  'step-onSuccess-unique',
123
123
  'step-onFailure-unique',
124
124
  'requestBody-replacements-unique',
125
+ 'no-criteria-xpath',
126
+ 'no-actions-type-end',
127
+ 'criteria-unique',
125
128
  ] as const;
126
129
 
127
130
  export type BuiltInArazzoRuleId = typeof builtInArazzoRules[number];