Skip to content

Commit 8443232

Browse files
feat(rulesets): initial rulesets for the Arazzo Specification (#2672)
* feat(formats): add arazzo format * feat(rulesets): add initial arazzo rules * chore(rulesets): adjust Parameter Validation * feat(rulesets): add initial arazzo runtime expression validation * feat(rulesets): add workflow dependsOn validation for Arazzo * chore(rulesets): adjust null check * feat(rulesets): add Criterion Validation for Arazzo * feat(rulesets): add Step Request Body Validation for Arazzo * chore(rulesets): improve runtime validation for dependsOn * feat(rulesets): add criteria validation to success and failure actions for Arazzo * feat(rulesets): add Step Validation for Arazzo * chore(formats): bump package version * chore(rulesets): bump package version * feat(rulesets): add Arazzo schema, criterion, expression, outputs,and action validation * chore(rulesets): improve null check for linting * feat(rulesets): harden Parameter validation * chore(rulesets): harden InputExpressionValidation to deal with JSON Schema refs * feat(rulesets): harden Step Request Body Validation * feat(rulesets): initial Arazzo documentation * chore(rulesets): modified yarn.lock * chore(rulesets): add arazzoTypes and refactor validations and tests * feat(ruleset-migrator): add arazzo support * chore(rulesets): address PR review comments * chore(deps): bump eslint plugin * chore(rulesets): fix linting issue * chore(rulesets): fix prettier formatting
1 parent dc1a8ef commit 8443232

File tree

60 files changed

+6515
-56
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

60 files changed

+6515
-56
lines changed

.eslintignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,5 +2,6 @@
22
/test-harness/tests/
33
/packages/*/dist
44
/packages/rulesets/src/oas/schemas/validators.ts
5+
/packages/rulesets/src/arazzo/schemas/validators.ts
56
/packages/*/CHANGELOG.md
67
packages/formatters/src/html/templates.ts

README.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -40,10 +40,10 @@ There are also [additional installation options](https://meta.stoplight.io/docs/
4040

4141
Spectral, being a generic YAML/JSON linter, **needs a ruleset** to lint files. A ruleset is a JSON, YAML, or JavaScript/TypeScript file (often the file is called `.spectral.yaml` for a YAML ruleset) that contains a collection of rules, which can be used to lint other JSON or YAML files such as an API description.
4242

43-
To get started, run this command in your terminal to create a `.spectral.yaml` file that uses the Spectral predefined rulesets based on OpenAPI or AsyncAPI:
43+
To get started, run this command in your terminal to create a `.spectral.yaml` file that uses the Spectral predefined rulesets based on OpenAPI, Arazzo or AsyncAPI:
4444

4545
```bash
46-
echo 'extends: ["spectral:oas", "spectral:asyncapi"]' > .spectral.yaml
46+
echo 'extends: ["spectral:oas", "spectral:asyncapi", "spectral:arazzo"]' > .spectral.yaml
4747
```
4848

4949
If you would like to create your own rules, check out the [Custom Rulesets](https://meta.stoplight.io/docs/spectral/01baf06bdd05a-rulesets) page.

docs/getting-started/6-arazzo.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
# Arazzo Support
2+
3+
Spectral has a built-in [Arazzo v1](https://spec.openapis.org/arazzo/v1.0.0.html) ruleset that you can use to validate your Arazzo files.
4+
5+
Add `extends: "spectral:arazzo"` to your ruleset file to apply rules for Arazzo v1.
6+
7+
You can see a full list of the rules in this ruleset in [Arazzo Rules](../reference/arazzo-rules.md).

docs/reference/arazzo-rules.md

Lines changed: 307 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,307 @@
1+
# Arazzo Rules
2+
3+
Spectral has a built-in "arazzo" ruleset for the [Arazzo Specification](https://spec.openapis.org/arazzo/v1.0.0.html).
4+
5+
In your ruleset file you can add `extends: "spectral:arazzo"` and you'll get all of the following rules applied.
6+
7+
### arazzo-document-schema
8+
9+
Validate structure of an Arazzo Document against the schema of the Arazzo v1 specification.
10+
11+
**Recommended:** Yes
12+
13+
### arazzo-workflowId-unique
14+
15+
`workflowId` must be unique across all the workflows defined within an Arazzo Document.
16+
17+
**Recommended:** Yes
18+
19+
### arazzo-workflow-output-validation
20+
21+
Every workflow output must have unique name and its value must be a valid runtime expression.
22+
23+
Additionally, if output values use expressions like `$workflows.foo.steps.bar`, the rule will verify the existence of workflow `foo` and step `bar`.
24+
25+
**Recommended:** Yes
26+
27+
**Good Example**
28+
29+
```yaml
30+
// Assuming that `TokenStep` is a defined step and that it exposes an output of `tokenResponse`
31+
outputs:
32+
access_token: $steps.TokenStep.outputs.tokenResponse
33+
```
34+
35+
**Bad Example**
36+
37+
```yaml
38+
outputs:
39+
access_token: $foo
40+
```
41+
42+
### arazzo-workflow-stepId-unique
43+
44+
Every `stepId` defined within a workflow must be unique
45+
46+
**Recommended:** Yes
47+
48+
**Good Example**
49+
50+
```yaml
51+
workflows:
52+
- workflowId: someWorkflow
53+
parameters:
54+
- in: cookie
55+
name: workflowLevelParamOne
56+
value: someValue
57+
- in: header
58+
name: workflowLevelParamTwo
59+
value: someValue
60+
steps:
61+
- stepId: post-step
62+
parameters:
63+
- in: cookie
64+
name: foo
65+
value: some_value
66+
operationId: createResource
67+
- stepId: get-step
68+
operationId: getResource
69+
```
70+
71+
**Bad Example**
72+
73+
```yaml
74+
workflows:
75+
- workflowId: someWorkflow
76+
parameters:
77+
- in: cookie
78+
name: workflowLevelParamOne
79+
value: someValue
80+
- in: header
81+
name: workflowLevelParamTwo
82+
value: someValue
83+
steps:
84+
- stepId: post-step
85+
parameters:
86+
- in: cookie
87+
name: foo
88+
value: some_value
89+
operationId: createResource
90+
- stepId: post-step
91+
operationId: getResource
92+
```
93+
94+
### arazzo-step-output-validation
95+
96+
Every step output must have unique name and its value must be a valid runtime expression.
97+
98+
Then validating the expression the rule checks against known prefixes described in the [Arazzo Specification Runtime Expressions](https://spec.openapis.org/arazzo/v1.0.0.html#runtime-expressions). Additionally, if output values use expressions like `$workflows.foo.steps.bar`, the rule will verify the existence of workflow `foo` and step `bar`.
99+
100+
**Recommended:** Yes
101+
102+
### arazzo-step-parameters-validation
103+
104+
Parameters must be unique based on their combination of `name` and `in` properties when defined at the workflow level or within a step. Step parameters can override workflow level parameters.
105+
106+
Additionally rule checks:
107+
108+
- reusable parameter references must be a valid Arazzo runtime expression (e.g. `$components.parameters.<name>`) and the referenced parameter must be existing within the components parameters
109+
- parameter values using expressions, must be a valid Arazzo Specification Runtime Expressions](https://spec.openapis.org/arazzo/v1.0.0.html#runtime-expressions). Additionally, if parameter values use expressions like `$workflows.foo.steps.bar`, the rule will verify the existence of workflow `foo` and step `bar`.
110+
111+
**Recommended:** Yes
112+
113+
**Good Example**
114+
115+
```yaml
116+
parameters:
117+
- name: username
118+
in: query
119+
value: $inputs.username
120+
```
121+
122+
**Bad Example**
123+
124+
```yaml
125+
parameters:
126+
- name: username
127+
in: query
128+
value: $foo
129+
```
130+
131+
### arazzo-step-failure-actions-validation
132+
133+
Every failure action must have a unique `name`, and the fields `workflowId` and `stepId` are mutually exclusive. Any runtime expressions used for `workflowId` or `stepId` must be valid and resolve to a defined workflow or step respectively.
134+
135+
Additionally rule checks:
136+
137+
- reusable failure action references must be a valid Arazzo runtime expression (e.g. `$components.failureActions.<name>`) and the referenced action must be existing within the components parameters
138+
139+
**Recommended:** Yes
140+
141+
### arazzo-step-success-actions-validation
142+
143+
Every success action must have a unique `name`, and the fields `workflowId` and `stepId` are mutually exclusive. Any runtime expressions used for `workflowId` or `stepId` must be valid and resolve to a defined workflow or step respectively.
144+
145+
Additionally rule checks:
146+
147+
- reusable success action references must be a valid Arazzo runtime expression (e.g. `$components.successActions.<name>`) and the referenced action must be existing within the components parameters
148+
149+
**Recommended:** Yes
150+
151+
### arazzo-workflow-depends-on-validation
152+
153+
The list of defined workflows within the `dependsOn` property must be unique and must be valid (e.g. the runtime expression must resolve to a defined workflow).
154+
155+
**Recommended:** Yes
156+
157+
### arazzo-step-success-criteria-validation
158+
159+
Every success criteria must have a valid context, conditions, and types.
160+
161+
Rule checks:
162+
163+
- `condition` must be specified
164+
- if `type` is defined then a `context` must be provided
165+
- if `type` is an object then it must conform to an [Arazzo Specification Criterion Expression Type Object](https://spec.openapis.org/arazzo/v1.0.0.html#criterion-expression-type-object)
166+
- if `type` is specified as "regex", then the condition must be a valid regex
167+
- `context` must be a valid [Arazzo Specification Runtime Expressions](https://spec.openapis.org/arazzo/v1.0.0.html#runtime-expressions)
168+
169+
**Recommended:** Yes
170+
171+
**Good Example**
172+
173+
```yaml
174+
- context: $statusCode
175+
condition: "^200$"
176+
type: regex
177+
```
178+
179+
**Bad Example**
180+
181+
```yaml
182+
- context: hello
183+
condition: "^200$"
184+
type: regex
185+
```
186+
187+
### arazzo-step-validation
188+
189+
Every step must have a valid `stepId` and either a valid `operationId` or `operationPath` or `workflowId`. Defined runtime expressions are also validated.
190+
191+
**Recommended:** Yes
192+
193+
### arazzo-no-script-tags-in-markdown
194+
195+
This rule protects against a potential hack, for anyone bringing in Arazzo documents from third parties and then generating HTML documentation. If one of those third parties does something shady like injecting `<script>` tags, they could easily execute arbitrary code on your domain, which if it's the same as your main application could be all sorts of terrible.
196+
197+
**Recommended:** Yes
198+
199+
**Bad Example**
200+
201+
```yaml
202+
arazzo: "1.0.0"
203+
info:
204+
title: 'some title with <script>alert("You are Hacked");</script>',
205+
```
206+
207+
### arazzo-info-description
208+
209+
Arazzo object info `description` must be present and non-empty string.
210+
211+
Examples can contain Markdown so you can really go to town with them, implementing getting started information like what the workflows contained can do and how you can get up and running.
212+
213+
**Recommended:** Yes
214+
215+
**Good Example**
216+
217+
```yaml
218+
arazzo: 1.0.0
219+
info:
220+
title: BNPL Workflow Description
221+
version: 1.0.0
222+
description: |
223+
## Overview
224+
225+
This workflow guides the process of applying for a loan at checkout using a "Buy Now, Pay Later" (BNPL) platform. It orchestrates a series of API interactions to ensure a seamless and efficient loan application process, integrating multiple services across different API providers.
226+
227+
### Key Features
228+
- **Multi-step Loan Application:** The workflow includes multiple steps to check product eligibility, retrieve terms and conditions, create customer profiles, initiate the loan, and finalize the payment plan.
229+
- **Dynamic Decision Making:** Based on the API responses, the workflow adapts the flow, for example, skipping customer creation if the customer is already registered or ending the workflow if no eligible products are found.
230+
- **User-Centric:** The workflow handles both existing and new customers, providing a flexible approach to customer onboarding and loan authorization.
231+
```
232+
233+
### arazzo-source-descriptions-type
234+
235+
Source Description `type` should be present. This means that tooling does not need to immediately parse/resolve the `sourceDescriptions` to know what type of document they are.
236+
237+
**Recommended:** Yes
238+
239+
**Good Example**
240+
241+
```yaml
242+
sourceDescriptions:
243+
- name: BnplApi
244+
url: https://raw.githubusercontent.com/OAI/Arazzo-Specification/main/examples/1.0.0/bnpl-openapi.yaml
245+
type: openapi
246+
```
247+
248+
### arazzo-workflow-workflowId
249+
250+
Workflow `workflowId` defined should follow the pattern `^[A-Za-z0-9_\\-]+$`. This is good practice as tools and libraries can use the `workflowId` to uniquely identify a workflow.
251+
252+
**Recommended:** Yes
253+
254+
### arazzo-workflow-description
255+
256+
In order to improve consumer experience, Workflow `description` should be present and a non-empty string.
257+
258+
**Recommend:** Yes
259+
260+
### arazzo-workflow-summary
261+
262+
In order to improve consumer experience, Workflow `summary` should be present and a non-empty string.
263+
264+
**Recommend:** Yes
265+
266+
### arazzo-step-stepId
267+
268+
Step `stepId` defined should follow the pattern `^[A-Za-z0-9_\\-]+$`. This is good practice as tools and libraries can use the `stepId` to uniquely identify a step.
269+
270+
**Recommended:** Yes
271+
272+
### arazzo-step-description
273+
274+
In order to improve consumer experience, Step `description` should be present and a non-empty string.
275+
276+
**Recommend:** Yes
277+
278+
### arazzo-step-summary
279+
280+
In order to improve consumer experience, Step `summary` should be present and a non-empty string.
281+
282+
**Recommend:** Yes
283+
284+
### arazzo-step-operationPath
285+
286+
It is recommended to use `operationId` rather than `operationPath` within a step to reference an API operation.
287+
288+
**Recommended:** Yes
289+
290+
### arazzo-step-request-body-validation
291+
292+
Every step request body must have an expected `contentType` and expected use of runtime expressions.
293+
294+
The contentType value will be checked against the following regex:
295+
296+
```regex
297+
/^(application|audio|font|example|image|message|model|multipart|text|video)\/[a-zA-Z0-9!#$&^_.+-]{1,127}$/
298+
```
299+
300+
Rule Checks:
301+
302+
- if `payload` uses full runtime expression (e.g. $steps.steps1.outputs.responseBody) then it must be a valid/expected runtime expression
303+
- If `replacements` are specified, then if a `value` uses a runtime expression it must be valid.
304+
305+
> \_inline use of runtime expressions within `payload` are not yet validated
306+
307+
**Recommended:** Yes

package.json

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@
77
"validator",
88
"OpenAPI",
99
"Swagger",
10+
"Arazzo",
11+
"AsyncAPI",
1012
"schema",
1113
"API"
1214
],
@@ -32,7 +34,7 @@
3234
"lint": "yarn prelint && yarn lint.prettier && yarn lint.eslint",
3335
"lint.fix": "yarn lint.prettier --write && yarn lint.eslint --fix",
3436
"lint.eslint": "eslint --cache --cache-location .cache/.eslintcache --ext=.js,.mjs,.ts packages test-harness",
35-
"lint.prettier": "prettier --ignore-path .eslintignore --ignore-unknown --check packages/core/src/ruleset/meta/*.json packages/rulesets/src/{asyncapi,oas}/schemas/**/*.json docs/**/*.md README.md",
37+
"lint.prettier": "prettier --ignore-path .eslintignore --ignore-unknown --check packages/core/src/ruleset/meta/*.json packages/rulesets/src/{asyncapi,oas,arazzo}/schemas/**/*.json docs/**/*.md README.md",
3638
"pretest": "yarn workspaces foreach run pretest",
3739
"test": "yarn pretest && yarn test.karma && yarn test.jest",
3840
"pretest.harness": "ts-node -T test-harness/scripts/generate-tests.ts",
@@ -96,7 +98,7 @@
9698
"@types/node-fetch": "^2.5.12",
9799
"@types/node-powershell": "^3.1.1",
98100
"@types/text-table": "^0.2.2",
99-
"@typescript-eslint/eslint-plugin": "^5.34.0",
101+
"@typescript-eslint/eslint-plugin": "^5.35.1",
100102
"@typescript-eslint/parser": "^5.34.0",
101103
"eslint": "^8.22.0",
102104
"eslint-config-prettier": "^8.5.0",
@@ -138,7 +140,7 @@
138140
"packages/core/src/ruleset/meta/*.json": [
139141
"prettier --ignore-path .eslintignore --write"
140142
],
141-
"packages/rulesets/src/{asyncapi,oas}/schemas/**/*.json": [
143+
"packages/rulesets/src/{asyncapi,oas,arazzo}/schemas/**/*.json": [
142144
"prettier --ignore-path .eslintignore --write"
143145
]
144146
},

packages/cli/package.json

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@stoplight/spectral-cli",
3-
"version": "6.11.1",
3+
"version": "6.12.0",
44
"homepage": "https://github.com/stoplightio/spectral",
55
"bugs": "https://github.com/stoplightio/spectral/issues",
66
"author": "Stoplight <[email protected]>",
@@ -40,9 +40,9 @@
4040
"@stoplight/spectral-formatters": "^1.3.0",
4141
"@stoplight/spectral-parsers": "^1.0.3",
4242
"@stoplight/spectral-ref-resolver": "^1.0.4",
43-
"@stoplight/spectral-ruleset-bundler": "^1.5.2",
44-
"@stoplight/spectral-ruleset-migrator": "^1.9.5",
45-
"@stoplight/spectral-rulesets": ">=1",
43+
"@stoplight/spectral-ruleset-bundler": "^1.5.4",
44+
"@stoplight/spectral-ruleset-migrator": "^1.9.6",
45+
"@stoplight/spectral-rulesets": "1.20.2",
4646
"@stoplight/spectral-runtime": "^1.1.2",
4747
"@stoplight/types": "^13.6.0",
4848
"chalk": "4.1.2",

0 commit comments

Comments
 (0)