Skip to content

Commit

Permalink
feat(rulesets): improve {oas2,oas3}-valid-schema rule (#2574)
Browse files Browse the repository at this point in the history
* feat(rulesets): improve {oas2,oas3}-valid-schema rule

* build(rulesets): try sth

* build(rulesets): abandon props mangling

* feat(rulesets): improve oas3.1 schema

* docs(repo): mention oas3-schema

* chore(rulesets): remove ajv-merge-patch dep

---------

Co-authored-by: Nauman <[email protected]>
  • Loading branch information
P0lip and mnaumanali94 authored May 3, 2024
1 parent a851c98 commit 8df2c36
Show file tree
Hide file tree
Showing 28 changed files with 2,126 additions and 989 deletions.
2 changes: 1 addition & 1 deletion .eslintignore
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
**/__fixtures__/**
/test-harness/**/*.yaml
/test-harness/tests/
/packages/*/dist
/packages/rulesets/src/oas/schemas/validators.ts
/packages/*/CHANGELOG.md
packages/formatters/src/html/templates.ts
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ node_modules
!.yarn/versions

packages/formatters/src/html/templates.ts
packages/rulesets/src/oas/schemas/validators.ts
packages/cli/binaries
packages/*/src/version.ts
/test-harness/tmp/
Expand Down
2 changes: 2 additions & 0 deletions docs/reference/openapi-rules.md
Original file line number Diff line number Diff line change
Expand Up @@ -665,6 +665,8 @@ Parameter objects should have a `description`.
### oas3-schema

Validate structure of OpenAPI v3 specification.
If OpenAPI 3.1.0 is used, `jsonSchemaDialect` is not respected and the draft 2020-12 is applied.
If you define your own `jsonSchemaDialect`, you'll most likely want to disable this rule.

**Recommended:** Yes

Expand Down
7 changes: 4 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -23,15 +23,16 @@
"node": "^12.20 || >= 14.13"
},
"scripts": {
"clean": "rimraf .cache packages/*/{dist,.cache}",
"preclean": "yarn workspaces foreach run preclean",
"clean": "yarn preclean && rimraf .cache packages/*/{dist,.cache}",
"prebuild": "yarn workspaces foreach run prebuild",
"build": "yarn prebuild && tsc --build ./tsconfig.build.json && yarn postbuild",
"postbuild": "yarn workspaces foreach run postbuild",
"prelint": "yarn workspaces foreach run prelint",
"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}/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 @@ -137,7 +138,7 @@
"packages/core/src/ruleset/meta/*.json": [
"prettier --ignore-path .eslintignore --write"
],
"packages/rulesets/src/{asyncapi,oas}/schemas/*.json": [
"packages/rulesets/src/{asyncapi,oas}/schemas/**/*.json": [
"prettier --ignore-path .eslintignore --write"
]
},
Expand Down
13 changes: 11 additions & 2 deletions packages/rulesets/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -27,16 +27,25 @@
"@stoplight/spectral-runtime": "^1.1.1",
"@stoplight/types": "^13.6.0",
"@types/json-schema": "^7.0.7",
"ajv": "^8.8.2",
"ajv": "^8.12.0",
"ajv-formats": "~2.1.0",
"json-schema-traverse": "^1.0.0",
"leven": "3.1.0",
"lodash": "~4.17.21",
"tslib": "^2.3.0"
},
"devDependencies": {
"@stoplight/path": "^1.3.2",
"@stoplight/spectral-parsers": "*",
"@stoplight/spectral-ref-resolver": "*",
"immer": "^9.0.6"
"gzip-size": "^6.0.0",
"immer": "^9.0.6",
"terser": "^5.26.0"
},
"scripts": {
"compile-schemas": "ts-node -T ./scripts/compile-schemas.ts",
"prelint": "yarn compile-schemas --quiet",
"pretest": "yarn compile-schemas --quiet",
"prebuild": "yarn compile-schemas --quiet"
}
}
89 changes: 89 additions & 0 deletions packages/rulesets/scripts/compile-schemas.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
/* eslint-disable no-console */
import * as fs from 'fs';
import * as path from 'path';
import * as process from 'process';
import Ajv2020 from 'ajv/dist/2020.js';
import standaloneCode from 'ajv/dist/standalone/index.js';
import ajvErrors from 'ajv-errors';
import ajvFormats from 'ajv-formats';
import chalk from 'chalk';
import { minify } from 'terser';
import { sync } from 'gzip-size';

const cwd = path.join(__dirname, '../src');

const schemas = [
'oas/schemas/json-schema/draft-04.json',
'oas/schemas/json-schema/draft-2020-12/index.json',
'oas/schemas/json-schema/draft-2020-12/validation.json',
'oas/schemas/oas/v2.0.json',
'oas/schemas/oas/v3.0.json',
'oas/schemas/oas/v3.1/dialect.schema.json',
'oas/schemas/oas/v3.1/meta.schema.json',
'oas/schemas/oas/v3.1/index.json',
].map(async schema => JSON.parse(await fs.promises.readFile(path.join(cwd, schema), 'utf8')));

const log = process.argv.includes('--quiet')
? (): void => {
/* no-op */
}
: console.log.bind(console);

Promise.all(schemas)
.then(async schemas => {
const ajv = new Ajv2020({
schemas,
allErrors: true,
messages: true,
strict: false,
inlineRefs: false,
formats: {
'media-range': true,
},
code: {
esm: true,
source: true,
},
});

ajvFormats(ajv);
ajvErrors(ajv);

const target = path.join(cwd, 'oas/schemas/validators.ts');
const basename = path.basename(target);
const code = standaloneCode(ajv, {
oas2_0: 'http://swagger.io/v2/schema.json',
oas3_0: 'https://spec.openapis.org/oas/3.0/schema/2019-04-02',
oas3_1: 'https://spec.openapis.org/oas/3.1/schema/2021-09-28',
});

const minified = (
await minify(code, {
compress: {
passes: 2,
},
ecma: 2020,
module: true,
format: {
comments: false,
},
})
).code as string;

log(
'writing %s size is %dKB (original), %dKB (minified) %dKB (minified + gzipped)',
path.join(target, '..', basename),
Math.round((code.length / 1024) * 100) / 100,
Math.round((minified.length / 1024) * 100) / 100,
Math.round((sync(minified) / 1024) * 100) / 100,
);

await fs.promises.writeFile(path.join(target, '..', basename), ['// @ts-nocheck', minified].join('\n'));
})
.then(() => {
log(chalk.green('Validators generated.'));
})
.catch(e => {
console.error(chalk.red('Error generating validators %s'), e.message);
process.exit(1);
});
32 changes: 32 additions & 0 deletions packages/rulesets/src/oas/__tests__/oas2-schema.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,4 +25,36 @@ testRule('oas2-schema', [
},
],
},

{
name: 'validate security definitions',
document: {
swagger: '2.0',
info: {
title: 'response example',
version: '1.0',
},
paths: {
'/user': {
get: {
responses: {
200: {
description: 'dummy description',
},
},
},
},
},
securityDefinitions: {
basic: null,
},
},
errors: [
{
message: 'Invalid basic authentication security definition.',
path: ['securityDefinitions', 'basic'],
severity: DiagnosticSeverity.Error,
},
],
},
]);
Loading

0 comments on commit 8df2c36

Please sign in to comment.