Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(rulesets): initial rulesets for the Arazzo Specification #2672

Merged
merged 27 commits into from
Sep 12, 2024
Merged
Show file tree
Hide file tree
Changes from 24 commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
720c760
feat(formats): add arazzo format
frankkilcommins Aug 2, 2024
2697fb3
Merge branch 'stoplightio:develop' into arazzo-format
frankkilcommins Aug 14, 2024
ea5ab31
feat(rulesets): add initial arazzo rules
frankkilcommins Aug 14, 2024
636c031
chore(rulesets): adjust Parameter Validation
frankkilcommins Aug 14, 2024
1803581
feat(rulesets): add initial arazzo runtime expression validation
frankkilcommins Aug 16, 2024
3a0a46a
feat(rulesets): add workflow dependsOn validation for Arazzo
frankkilcommins Aug 16, 2024
337b23f
chore(rulesets): adjust null check
frankkilcommins Aug 16, 2024
2b121c7
feat(rulesets): add Criterion Validation for Arazzo
frankkilcommins Aug 16, 2024
ceea177
feat(rulesets): add Step Request Body Validation for Arazzo
frankkilcommins Aug 16, 2024
2d10bb3
chore(rulesets): improve runtime validation for dependsOn
frankkilcommins Aug 16, 2024
f9bd5ad
feat(rulesets): add criteria validation to success and failure action…
frankkilcommins Aug 16, 2024
c5e9ab5
feat(rulesets): add Step Validation for Arazzo
frankkilcommins Aug 16, 2024
e6ccf21
chore(formats): bump package version
frankkilcommins Aug 19, 2024
bd8d2f7
chore(rulesets): bump package version
frankkilcommins Aug 19, 2024
613ca65
feat(rulesets): add Arazzo schema, criterion, expression, outputs,and…
frankkilcommins Aug 23, 2024
7a56be3
chore(rulesets): improve null check for linting
frankkilcommins Aug 23, 2024
f7d38a9
feat(rulesets): harden Parameter validation
frankkilcommins Aug 24, 2024
f4efce2
chore(rulesets): harden InputExpressionValidation to deal with JSON S…
frankkilcommins Aug 26, 2024
88c8250
feat(rulesets): harden Step Request Body Validation
frankkilcommins Aug 26, 2024
13ab7ec
feat(rulesets): initial Arazzo documentation
frankkilcommins Aug 26, 2024
6a3fd3d
chore(rulesets): modified yarn.lock
frankkilcommins Aug 27, 2024
98fcfe3
chore(rulesets): add arazzoTypes and refactor validations and tests
frankkilcommins Aug 27, 2024
ef86ef4
feat(ruleset-migrator): add arazzo support
frankkilcommins Aug 28, 2024
717103a
chore(rulesets): address PR review comments
frankkilcommins Sep 11, 2024
ff35105
chore(deps): bump eslint plugin
frankkilcommins Sep 12, 2024
7b6e01f
chore(rulesets): fix linting issue
frankkilcommins Sep 12, 2024
27c330e
chore(rulesets): fix prettier formatting
frankkilcommins Sep 12, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -40,10 +40,10 @@ There are also [additional installation options](https://meta.stoplight.io/docs/

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.

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:
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:

```bash
echo 'extends: ["spectral:oas", "spectral:asyncapi"]' > .spectral.yaml
echo 'extends: ["spectral:oas", "spectral:asyncapi", "spectral:arazzo"]' > .spectral.yaml
```

If you would like to create your own rules, check out the [Custom Rulesets](https://meta.stoplight.io/docs/spectral/01baf06bdd05a-rulesets) page.
Expand Down
7 changes: 7 additions & 0 deletions docs/getting-started/6-arazzo.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
# Arazzo Support

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.

Add `extends: "spectral:arazzo"` to your ruleset file to apply rules for Arazzo v1.

You can see a full list of the rules in this ruleset in [Arazzo Rules](../reference/arazzo-rules.md).
311 changes: 311 additions & 0 deletions docs/reference/arazzo-rules.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,311 @@
# Arazzo Rules

Spectral has a built-in "arazzo" ruleset for the [Arazzo Specification](https://spec.openapis.org/arazzo/v1.0.0.html).

In your ruleset file you can add `extends: "spectral:arazzo"` and you'll get all of the following rules applied.

### arazzo-document-schema

Validate structure of an Arazzo Document against the schema of the Arazzo v1 specification.

**Recommended:** Yes

### arazzo-workflowId-unique

`workflowId` must be unique across all the workflows defined within an Arazzo Document.

**Recommended:** Yes

### arazzo-workflow-output-validation

Every workflow output must have unique name and its value must be a valid runtime expression.

Additionally, if output values use expressions like `$workflows.foo.steps.bar`, the rule will verify the existence of workflow `foo` and step `bar`.

**Recommended:** Yes

**Good Example**

```yaml
// Assuming that `TokenStep` is a defined step and that it exposes an output of `tokenResponse`
outputs:
access_token: $steps.TokenStep.outputs.tokenResponse
```

**Bad Example**

```yaml
outputs:
access_token: $foo
```

### arazzo-workflow-stepId-unique

Every `stepId` defined within a workflow must be unique

**Recommended:** Yes

**Good Example**

```yaml
workflows:
- workflowId: someWorkflow
parameters:
- in: cookie
name: workflowLevelParamOne
value: someValue
- in: header
name: workflowLevelParamTwo
value: someValue
steps:
- stepId: post-step
parameters:
- in: cookie
name: foo
value: some_value
operationId: createResource
- stepId: get-step
operationId: getResource
```

**Bad Example**

```yaml
workflows:
- workflowId: someWorkflow
parameters:
- in: cookie
name: workflowLevelParamOne
value: someValue
- in: header
name: workflowLevelParamTwo
value: someValue
steps:
- stepId: post-step
parameters:
- in: cookie
name: foo
value: some_value
operationId: createResource
- stepId: post-step
operationId: getResource
```

### arazzo-step-output-validation

Every step output must have unique name and its value must be a valid runtime expression.

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`.

**Recommended:** Yes

### arazzo-step-parameters-validation

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.

Additionally rule checks:
- 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
- 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`.

**Recommended:** Yes

**Good Example**

```yaml
parameters:
- name: username
in: query
value: $inputs.username
```

**Bad Example**

```yaml
parameters:
- name: username
in: query
value: $foo
```

### arazzo-step-failure-actions-validation

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.

Additionally rule checks:
- 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

**Recommended:** Yes

### arazzo-step-success-actions-validation

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.

Additionally rule checks:
- 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

**Recommended:** Yes

### arazzo-workflow-depends-on-validation

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).

**Recommended:** Yes

### arazzo-step-success-criteria-validation

Every success criteria must have a valid context, conditions, and types.

Rule checks:
- `condition` must be specified
- if `type` is defined then a `context` must be provided
- 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)
- if `type` is specified as "regex", then the condition must be a valid regex
- `context` must be a valid [Arazzo Specification Runtime Expressions](https://spec.openapis.org/arazzo/v1.0.0.html#runtime-expressions)

**Recommended:** Yes

**Good Example**

```yaml
- context: $statusCode
condition: '^200$'
type: regex
```

**Bad Example**

```yaml

- context: hello
condition: '^200$'
type: regex
```

### arazzo-step-validation

Every step must have a valid `stepId` and either a valid `operationId` or `operationPath` or `workflowId`. Defined runtime expressions are also validated.

**Recommended:** Yes

### arazzo-no-script-tags-in-markdown

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.

**Recommended:** Yes

**Bad Example**

```yaml
arazzo: "1.0.0"
info:
title: 'some title with <script>alert("You are Hacked");</script>',
```

### arazzo-info-description
Arazzo object info `description` must be present and non-empty string.

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.

**Recommended:** Yes

**Good Example**

```yaml
arazzo: 1.0.0
info:
title: BNPL Workflow Description
version: 1.0.0
description: |
## Overview

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.

### Key Features
- **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.
- **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.
- **User-Centric:** The workflow handles both existing and new customers, providing a flexible approach to customer onboarding and loan authorization.

```

### arazzo-source-descriptions-type

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.

**Recommended:** Yes

**Good Example**

```yaml
sourceDescriptions:
- name: BnplApi
url: https://raw.githubusercontent.com/OAI/Arazzo-Specification/main/examples/1.0.0/bnpl-openapi.yaml
type: openapi
```

### arazzo-workflow-workflowId

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.

**Recommended:** Yes

### arazzo-workflow-description

In order to improve consumer experience, Workflow `description` should be present and a non-empty string.

**Recommend:** Yes

### arazzo-workflow-summary

In order to improve consumer experience, Workflow `summary` should be present and a non-empty string.

**Recommend:** Yes

### arazzo-step-stepId

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.

**Recommended:** Yes

### arazzo-step-description

In order to improve consumer experience, Step `description` should be present and a non-empty string.

**Recommend:** Yes

### arazzo-step-summary

In order to improve consumer experience, Step `summary` should be present and a non-empty string.

**Recommend:** Yes


### arazzo-step-operationPath

It is recommended to use `operationId` rather than `operationPath` within a step to reference an API operation.

**Recommended:** Yes

### arazzo-step-request-body-validation

Every step request body must have an expected `contentType` and expected use of runtime expressions.

The contentType value will be checked against the following regex:
```regex
/^(application|audio|font|example|image|message|model|multipart|text|video)\/[a-zA-Z0-9!#$&^_.+-]{1,127}$/
```

Rule Checks:
- if `payload` uses full runtime expression (e.g. $steps.steps1.outputs.responseBody) then it must be a valid/expected runtime expression
- If `replacements` are specified, then if a `value` uses a runtime expression it must be valid.

> _inline use of runtime expressions within `payload` are not yet validated


**Recommended:** Yes







6 changes: 4 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@
"validator",
"OpenAPI",
"Swagger",
"Arazzo",
"AsyncAPI",
"schema",
"API"
],
Expand All @@ -32,7 +34,7 @@
"lint": "yarn prelint && yarn lint.prettier && yarn lint.eslint",
"lint.fix": "yarn lint.prettier --write && yarn lint.eslint --fix",
"lint.eslint": "eslint --cache --cache-location .cache/.eslintcache --ext=.js,.mjs,.ts packages test-harness",
"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",
"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",
"pretest": "yarn workspaces foreach run pretest",
"test": "yarn pretest && yarn test.karma && yarn test.jest",
"pretest.harness": "ts-node -T test-harness/scripts/generate-tests.ts",
Expand Down Expand Up @@ -138,7 +140,7 @@
"packages/core/src/ruleset/meta/*.json": [
"prettier --ignore-path .eslintignore --write"
],
"packages/rulesets/src/{asyncapi,oas}/schemas/**/*.json": [
"packages/rulesets/src/{asyncapi,oas,arazzo}/schemas/**/*.json": [
"prettier --ignore-path .eslintignore --write"
]
},
Expand Down
8 changes: 4 additions & 4 deletions packages/cli/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@stoplight/spectral-cli",
"version": "6.11.1",
"version": "6.12.0",
"homepage": "https://github.com/stoplightio/spectral",
"bugs": "https://github.com/stoplightio/spectral/issues",
"author": "Stoplight <[email protected]>",
Expand Down Expand Up @@ -40,9 +40,9 @@
"@stoplight/spectral-formatters": "^1.3.0",
"@stoplight/spectral-parsers": "^1.0.3",
"@stoplight/spectral-ref-resolver": "^1.0.4",
"@stoplight/spectral-ruleset-bundler": "^1.5.2",
"@stoplight/spectral-ruleset-migrator": "^1.9.5",
"@stoplight/spectral-rulesets": ">=1",
"@stoplight/spectral-ruleset-bundler": "^1.5.4",
"@stoplight/spectral-ruleset-migrator": "^1.9.6",
"@stoplight/spectral-rulesets": "1.20.2",
"@stoplight/spectral-runtime": "^1.1.2",
"@stoplight/types": "^13.6.0",
"chalk": "4.1.2",
Expand Down
2 changes: 1 addition & 1 deletion packages/core/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@
"tslib": "^2.3.0"
},
"devDependencies": {
"@stoplight/spectral-formats": "*",
"@stoplight/spectral-formats": "^1.7.0",
"@stoplight/spectral-functions": "*",
"@stoplight/spectral-parsers": "*",
"@stoplight/yaml": "^4.2.2",
Expand Down
2 changes: 1 addition & 1 deletion packages/formats/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@stoplight/spectral-formats",
"version": "1.6.0",
"version": "1.7.0",
"sideEffects": false,
"homepage": "https://github.com/stoplightio/spectral",
"bugs": "https://github.com/stoplightio/spectral/issues",
Expand Down
Loading