Skip to content

Commit 24198bc

Browse files
rainumVazha Omanashvili
andauthored
feat(rulesets): add new rule that requires sibling items field for type array (#2632)
* feat(test-harness): make test-harness locale agnostic * chore(deps): use node 18.18.2 * chore(test-harness): updated test-harness readme * feat(rulesets): add new rule that requires sibling items field for type array * chore(repo): update documentation --------- Co-authored-by: Vazha Omanashvili <[email protected]>
1 parent 9e906ea commit 24198bc

File tree

7 files changed

+194
-8
lines changed

7 files changed

+194
-8
lines changed

.nvmrc

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
lts/*
1+
18.18.2

docs/reference/openapi-rules.md

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -448,6 +448,37 @@ TheBadModel:
448448
- 8
449449
```
450450

451+
### array-items
452+
453+
Schemas with `type: array`, require a sibling `items` field.
454+
455+
**Recommended:** Yes
456+
457+
**Good Example**
458+
459+
```yaml
460+
TheGoodModel:
461+
type: object
462+
properties:
463+
favoriteColorSets:
464+
type: array
465+
items:
466+
type: array
467+
items: {}
468+
```
469+
470+
**Bad Example**
471+
472+
```yaml
473+
TheBadModel:
474+
type: object
475+
properties:
476+
favoriteColorSets:
477+
type: array
478+
items:
479+
type: array
480+
```
481+
451482
## OpenAPI v2.0-only
452483

453484
These rules will only apply to OpenAPI v2.0 documents.
Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
import { DiagnosticSeverity } from '@stoplight/types';
2+
3+
import testRule from '../../__tests__/__helpers__/tester';
4+
5+
testRule('array-items', [
6+
{
7+
name: 'valid case',
8+
document: {
9+
swagger: '2.0',
10+
securityDefinitions: {
11+
apikey: {},
12+
},
13+
paths: {
14+
'/path': {
15+
get: {
16+
security: [
17+
{
18+
apikey: [],
19+
},
20+
],
21+
},
22+
},
23+
},
24+
},
25+
errors: [],
26+
},
27+
28+
{
29+
name: 'array items sibling is present',
30+
document: {
31+
$ref: '#/',
32+
responses: {
33+
200: {
34+
type: 'array',
35+
items: {},
36+
},
37+
201: {
38+
type: 'array',
39+
items: {
40+
type: 'array',
41+
items: {},
42+
},
43+
},
44+
},
45+
openapi: '3.0.0',
46+
},
47+
errors: [],
48+
},
49+
{
50+
name: 'array items sibling is missing',
51+
document: {
52+
$ref: '#/',
53+
responses: {
54+
200: {
55+
type: 'array',
56+
},
57+
201: {
58+
type: 'array',
59+
items: {
60+
type: 'array',
61+
},
62+
},
63+
},
64+
openapi: '3.0.0',
65+
},
66+
errors: [
67+
{
68+
code: 'array-items',
69+
message: 'Schemas with "type: array", require a sibling "items" field',
70+
path: ['responses', '200'],
71+
severity: DiagnosticSeverity.Error,
72+
},
73+
{
74+
code: 'array-items',
75+
message: 'Schemas with "type: array", require a sibling "items" field',
76+
path: ['responses', '201', 'items'],
77+
severity: DiagnosticSeverity.Error,
78+
},
79+
],
80+
},
81+
]);

packages/rulesets/src/oas/index.ts

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -361,6 +361,18 @@ const ruleset = {
361361
function: refSiblings,
362362
},
363363
},
364+
'array-items': {
365+
formats: [oas3_0],
366+
message: 'Schemas with "type: array", require a sibling "items" field',
367+
severity: 0,
368+
recommended: true,
369+
resolved: false,
370+
given: "$..[?(@.type === 'array')]",
371+
then: {
372+
function: truthy,
373+
field: 'items',
374+
},
375+
},
364376
'typed-enum': {
365377
description: 'Enum values must respect the specified type.',
366378
message: '{{error}}',

test-harness/README.md

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,19 +3,24 @@
33
## Prerequisites
44

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

88
## Running the suite
99

1010
Run `yarn test.harness` from your terminal
1111

1212
### Running a selected tests
1313

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

18-
E.g. run `TESTS=parameters-ac1.oas2.scenario,validate-body-params/form-byte-format-fail.oas2.scenario yarn test.harness`
16+
You can run one or multiple tests by passing a path to `test-harness` command.
17+
All scenarios are converted to `.js` files under the `./tests` directory.
18+
Hence you must use paths relative to the `./tests` directory, like in the following example:
19+
20+
```bash
21+
# path to scenario file: `test-harness/scenarios/require-module.scenario`
22+
yarn test.harness test-harness/tests/require-module
23+
```
1924

2025
### Matching test files
2126

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
====test====
2+
Schemas with "type: array", require a sibling "items" field
3+
====document====
4+
openapi: 3.0.3
5+
info:
6+
title: test
7+
description: Test specification file
8+
version: '1.0'
9+
contact:
10+
name: John Doe
11+
url: 'https://example.com'
12+
13+
license:
14+
name: Apache 2.0
15+
url: 'https://www.apache.org/licenses/LICENSE-2.0'
16+
servers:
17+
- url: 'http://localhost:3000'
18+
tags:
19+
- name: list-endpoint
20+
description: Endpoint for listing objects
21+
paths:
22+
/users:
23+
get:
24+
summary: List Users
25+
operationId: get-users
26+
description: List all Users
27+
tags:
28+
- list-endpoint
29+
responses:
30+
'200':
31+
description: OK
32+
content:
33+
application/json:
34+
schema:
35+
type: object
36+
properties:
37+
favoriteColorSets:
38+
type: array
39+
items:
40+
type: array
41+
42+
====asset:ruleset====
43+
const { oas } = require('@stoplight/spectral-rulesets');
44+
module.exports = oas;
45+
====command====
46+
{bin} lint {document} --ruleset "{asset:ruleset}"
47+
====stdout====
48+
{document}
49+
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
50+
51+
✖ 1 problem (1 error, 0 warnings, 0 infos, 0 hints)

test-harness/src/suite.ts

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,14 @@ if (scenario.command === null) {
1919
// executing Date() before or after spawnNode were constantly leading to occasional mismatches,
2020
// as the success of that approach was highly bound to the time spent on the actual spawnNode call
2121
// this is a tad smarter, because instead of naively hoping the date will match, we try to extract the date from the actual output
22-
// this regular expression matches "00:43:59" in "Thu Jul 08 2021 00:43:59 GMT+0200 (Central European Summer Time)"
23-
const date = RegExp(escapeRegExp(String(Date())).replace(/(\d\d:){2}\d\d/, '(\\d\\d:){2}\\d\\d'));
22+
// this regular expression matches "00:43:59" in "Thu Jul 08 2021 00:43:59 GMT+0200 (Central European Summer Time)".
23+
// this regular expression is locale agnostic: it will match "Fri May 31 2024 18:26:32 GMT+0300 (за східноєвропейським літнім часом)"
24+
const date = RegExp(
25+
escapeRegExp(String(Date()))
26+
.replace(/(\d\d:){2}\d\d/, '(\\d\\d:){2}\\d\\d')
27+
.replace(/\s\\\(.+\\\)/, '\\s+\\([\\w\\s]+\\)'),
28+
);
29+
2430
Reflect.defineProperty(env, 'date', {
2531
configurable: true,
2632
enumerable: true,

0 commit comments

Comments
 (0)