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): add new rule that requires sibling items field for type array #2632

Merged
merged 5 commits into from
Jun 7, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
2 changes: 1 addition & 1 deletion .nvmrc
Original file line number Diff line number Diff line change
@@ -1 +1 @@
lts/*
18.18.2
31 changes: 31 additions & 0 deletions docs/reference/openapi-rules.md
Original file line number Diff line number Diff line change
Expand Up @@ -448,6 +448,37 @@ TheBadModel:
- 8
```

### array-items

Schemas with `type: array`, require a sibling `items` field.

**Recommended:** Yes

**Good Example**

```yaml
TheGoodModel:
type: object
properties:
favoriteColorSets:
type: array
items:
type: array
items: {}
```

**Bad Example**

```yaml
TheBadModel:
type: object
properties:
favoriteColorSets:
type: array
items:
type: array
```

## OpenAPI v2.0-only

These rules will only apply to OpenAPI v2.0 documents.
Expand Down
81 changes: 81 additions & 0 deletions packages/rulesets/src/oas/__tests__/array-items.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
import { DiagnosticSeverity } from '@stoplight/types';

import testRule from '../../__tests__/__helpers__/tester';

testRule('array-items', [
{
name: 'valid case',
document: {
swagger: '2.0',
securityDefinitions: {
apikey: {},
},
paths: {
'/path': {
get: {
security: [
{
apikey: [],
},
],
},
},
},
},
errors: [],
},

{
name: 'array items sibling is present',
document: {
$ref: '#/',
responses: {
200: {
type: 'array',
items: {},
},
201: {
type: 'array',
items: {
type: 'array',
items: {},
},
},
},
openapi: '3.0.0',
},
errors: [],
},
{
name: 'array items sibling is missing',
document: {
$ref: '#/',
responses: {
200: {
type: 'array',
},
201: {
type: 'array',
items: {
type: 'array',
},
},
},
openapi: '3.0.0',
},
errors: [
{
code: 'array-items',
message: 'Schemas with "type: array", require a sibling "items" field',
path: ['responses', '200'],
severity: DiagnosticSeverity.Error,
},
{
code: 'array-items',
message: 'Schemas with "type: array", require a sibling "items" field',
path: ['responses', '201', 'items'],
severity: DiagnosticSeverity.Error,
},
],
},
]);
12 changes: 12 additions & 0 deletions packages/rulesets/src/oas/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -361,6 +361,18 @@ const ruleset = {
function: refSiblings,
},
},
'array-items': {
formats: [oas3_0],

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

should this be 3.1 as well?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, we discussed this with @mnaumanali94. We agreed to fix one version at a time. We can address 3.1 if there will be any interest in the community.

message: 'Schemas with "type: array", require a sibling "items" field',
severity: 0,
recommended: true,
resolved: false,
given: "$..[?(@.type === 'array')]",
then: {
function: truthy,

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

should this detect a child schema?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

field: 'items',
},
},
'typed-enum': {
description: 'Enum values must respect the specified type.',
message: '{{error}}',
Expand Down
15 changes: 10 additions & 5 deletions test-harness/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,19 +3,24 @@
## Prerequisites

* Install the project dependencies with `yarn`
* Generate the binary for your platform with `yarn build.binary`. This will *also* compile the project from TS -> JS
* Generate the binary for your platform with `yarn workspace @stoplight/spectral-cli build.binary`. This will *also* compile the project from TS -> JS

## Running the suite

Run `yarn test.harness` from your terminal

### Running a selected tests

You can run one or selected tests using `TESTS` env variable.
If you want multiple test files to be run separate them with commas.
Use paths relative to the `./scenarios` directory.
Test Harness uses Jest under the hood. You can use all CLI options that Jest supports: https://jestjs.io/docs/cli

E.g. run `TESTS=parameters-ac1.oas2.scenario,validate-body-params/form-byte-format-fail.oas2.scenario yarn test.harness`
You can run one or multiple tests by passing a path to `test-harness` command.
All scenarios are converted to `.js` files under the `./tests` directory.
Hence you must use paths relative to the `./tests` directory, like in the following example:

```bash
# path to scenario file: `test-harness/scenarios/require-module.scenario`
yarn test.harness test-harness/tests/require-module
```

### Matching test files

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
====test====
Schemas with "type: array", require a sibling "items" field
====document====
openapi: 3.0.3
info:
title: test
description: Test specification file
version: '1.0'
contact:
name: John Doe
url: 'https://example.com'
email: john_doe@example.com
license:
name: Apache 2.0
url: 'https://www.apache.org/licenses/LICENSE-2.0'
servers:
- url: 'http://localhost:3000'
tags:
- name: list-endpoint
description: Endpoint for listing objects
paths:
/users:
get:
summary: List Users
operationId: get-users
description: List all Users
tags:
- list-endpoint
responses:
'200':
description: OK
content:
application/json:
schema:
type: object
properties:
favoriteColorSets:
type: array
items:
type: array

====asset:ruleset====
const { oas } = require('@stoplight/spectral-rulesets');
module.exports = oas;
====command====
{bin} lint {document} --ruleset "{asset:ruleset}"
====stdout====
{document}
36:27 error array-items Schemas with "type: array", require a sibling "items" field paths./users.get.responses[200].content.application/json.schema.properties.favoriteColorSets.items

✖ 1 problem (1 error, 0 warnings, 0 infos, 0 hints)
10 changes: 8 additions & 2 deletions test-harness/src/suite.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,14 @@ if (scenario.command === null) {
// executing Date() before or after spawnNode were constantly leading to occasional mismatches,
// as the success of that approach was highly bound to the time spent on the actual spawnNode call
// this is a tad smarter, because instead of naively hoping the date will match, we try to extract the date from the actual output
// this regular expression matches "00:43:59" in "Thu Jul 08 2021 00:43:59 GMT+0200 (Central European Summer Time)"
const date = RegExp(escapeRegExp(String(Date())).replace(/(\d\d:){2}\d\d/, '(\\d\\d:){2}\\d\\d'));
// this regular expression matches "00:43:59" in "Thu Jul 08 2021 00:43:59 GMT+0200 (Central European Summer Time)".
// this regular expression is locale agnostic: it will match "Fri May 31 2024 18:26:32 GMT+0300 (за східноєвропейським літнім часом)"
const date = RegExp(
escapeRegExp(String(Date()))
.replace(/(\d\d:){2}\d\d/, '(\\d\\d:){2}\\d\\d')
.replace(/\s\\\(.+\\\)/, '\\s+\\([\\w\\s]+\\)'),
);

Reflect.defineProperty(env, 'date', {
configurable: true,
enumerable: true,
Expand Down