diff --git a/README.md b/README.md index 8edc5697b..56171e48b 100644 --- a/README.md +++ b/README.md @@ -34,28 +34,28 @@ Think of a set of **rules** and **functions** as a flexible and customizable sty Spectral has a built-in set of functions which you can reference in your rules. This example uses the `RuleFunction.PATTERN` to create a rule that checks that all property values are in snake case. ```javascript -const { RuleFunction, Spectral } = require("@stoplight/spectral"); +const { RuleFunction, Spectral } = require('@stoplight/spectral'); const spectral = new Spectral(); spectral.addRules({ snake_case: { - summary: "Checks for snake case pattern", + summary: 'Checks for snake case pattern', // evaluate every property - given: "$..*", + given: '$..*', then: { function: RuleFunction.PATTERN, functionOptions: { - match: "^[a-z]+[a-z0-9_]*[a-z0-9]+$" - } - } - } + match: '^[a-z]+[a-z0-9_]*[a-z0-9]+$', + }, + }, + }, }); const results = spectral.run({ - name: "helloWorld" + name: 'helloWorld', }); console.log(JSON.stringify(results, null, 4)); @@ -66,8 +66,8 @@ console.log(JSON.stringify(results, null, 4)); // { // "name": "snake_case", // "message": "must match the pattern '^[a-z]+[a-z0-9_]*[a-z0-9]+$'", -// "severity": 50, -// "severityLabel": "error", +// "severity": 40, +// "severityLabel": "warn", // "path": [ // "name" // ] @@ -81,7 +81,7 @@ console.log(JSON.stringify(results, null, 4)); Sometimes the built-in functions don't cover your use case. This example creates a custom function, `customNotThatFunction`, and then uses it within a rule, `openapi_not_swagger`. The custom function checks that you are not using a specific string (e.g., "Swagger") and suggests what to use instead (e.g., "OpenAPI"). ```javascript -const { Spectral } = require("@stoplight/spectral"); +const { Spectral } = require('@stoplight/spectral'); // custom function const customNotThatFunction = (targetValue, options) => { @@ -91,8 +91,8 @@ const customNotThatFunction = (targetValue, options) => { // return the single error return [ { - message: `Use ${suggestion} instead of ${match}!` - } + message: `Use ${suggestion} instead of ${match}!`, + }, ]; } }; @@ -100,31 +100,31 @@ const customNotThatFunction = (targetValue, options) => { const spectral = new Spectral(); spectral.addFunctions({ - notThat: customNotThatFunction + notThat: customNotThatFunction, }); spectral.addRules({ openapi_not_swagger: { - summary: "Checks for use of Swagger, and suggests OpenAPI.", + summary: 'Checks for use of Swagger, and suggests OpenAPI.', // check every property - given: "$..*", + given: '$..*', then: { // reference the function we added! - function: "notThat", + function: 'notThat', // pass it the options it needs functionOptions: { - match: "Swagger", - suggestion: "OpenAPI" - } - } - } + match: 'Swagger', + suggestion: 'OpenAPI', + }, + }, + }, }); const results = spectral.run({ - description: "Swagger is pretty cool!" + description: 'Swagger is pretty cool!', }); console.log(JSON.stringify(results, null, 4)); @@ -135,8 +135,8 @@ console.log(JSON.stringify(results, null, 4)); // { // "name": "openapi_not_swagger", // "message": "Use OpenAPI instead of Swagger!", -// "severity": 50, -// "severityLabel": "error", +// "severity": 40, +// "severityLabel": "warn", // "path": [ // "description" // ] @@ -152,23 +152,20 @@ Spectral also includes a number of ready made rules and functions for OpenAPI Sp You can also add to these rules to create a customized linting style guide for your OAS documents. ```javascript -const { Spectral } = require("@stoplight/spectral"); -const { - oas2Functions, - oas2Rules -} = require("@stoplight/spectral/rulesets/oas2"); +const { Spectral } = require('@stoplight/spectral'); +const { oas2Functions, oas2Rules } = require('@stoplight/spectral/rulesets/oas2'); // an OASv2 document var myOAS = { // ... properties in your document responses: { - "200": { - description: "", + '200': { + description: '', schema: { - $ref: "#/definitions/error-response" - } - } - } + $ref: '#/definitions/error-response', + }, + }, + }, // ... properties in your document }; diff --git a/src/__tests__/linter.ts b/src/__tests__/linter.ts index 973dce472..7b9d8de88 100644 --- a/src/__tests__/linter.ts +++ b/src/__tests__/linter.ts @@ -88,8 +88,8 @@ describe('linter', () => { expect(result.results[0]).toEqual({ name: 'rule1', message, - severity: ValidationSeverity.Error, - severityLabel: ValidationSeverityLabel.Error, + severity: ValidationSeverity.Warn, + severityLabel: ValidationSeverityLabel.Warn, path: ['responses', '404', 'description'], }); }); @@ -124,6 +124,35 @@ describe('linter', () => { expect(result.results[0].severityLabel).toEqual(ValidationSeverityLabel.Info); }); + test('should default severityLabel based on rule severity', () => { + spectral.addFunctions({ + func1: () => { + return [ + { + message: 'foo', + }, + ]; + }, + }); + + spectral.addRules({ + rule1: { + given: '$.x', + severity: ValidationSeverity.Info, + then: { + function: 'func1', + }, + }, + }); + + const result = spectral.run({ + x: true, + }); + + expect(result.results[0].severity).toEqual(ValidationSeverity.Info); + expect(result.results[0].severityLabel).toEqual(ValidationSeverityLabel.Info); + }); + describe('functional tests for the given property', () => { let fakeLintingFunction: any; diff --git a/src/linter.ts b/src/linter.ts index f883a9f5c..4e1e1c425 100644 --- a/src/linter.ts +++ b/src/linter.ts @@ -2,6 +2,7 @@ import { ObjPath, ValidationSeverity, ValidationSeverityLabel } from '@stoplight import * as jp from 'jsonpath'; const get = require('lodash/get'); const has = require('lodash/has'); +const invert = require('lodash/invert'); import { IFunction, IGivenNode, IRuleResult, IRunOpts, IRunRule, IThen } from './types'; @@ -83,15 +84,19 @@ export const lintNode = ( } ) || []; + const severity = rule.severity || ValidationSeverity.Warn; + const severityLabel = + rule.severityLabel || (ValidationSeverityLabel[invert(ValidationSeverity)[severity]] as ValidationSeverityLabel); + results = results.concat( targetResults.map(result => { return { name: rule.name, summary: rule.summary, message: result.message, - severity: rule.severity || ValidationSeverity.Error, - severityLabel: rule.severityLabel || ValidationSeverityLabel.Error, path: result.path || targetPath, + severity, + severityLabel, }; }) ); diff --git a/src/rulesets/oas/__tests__/__snapshots__/contact-properties.ts.snap b/src/rulesets/oas/__tests__/__snapshots__/contact-properties.ts.snap index c795e43b4..96ea956bf 100644 --- a/src/rulesets/oas/__tests__/__snapshots__/contact-properties.ts.snap +++ b/src/rulesets/oas/__tests__/__snapshots__/contact-properties.ts.snap @@ -10,8 +10,8 @@ Array [ "contact", "name", ], - "severity": 50, - "severityLabel": "error", + "severity": 40, + "severityLabel": "warn", "summary": "Contact object should have \`name\`, \`url\` and \`email\`.", }, Object { @@ -22,8 +22,8 @@ Array [ "contact", "url", ], - "severity": 50, - "severityLabel": "error", + "severity": 40, + "severityLabel": "warn", "summary": "Contact object should have \`name\`, \`url\` and \`email\`.", }, Object { @@ -34,8 +34,8 @@ Array [ "contact", "email", ], - "severity": 50, - "severityLabel": "error", + "severity": 40, + "severityLabel": "warn", "summary": "Contact object should have \`name\`, \`url\` and \`email\`.", }, ] diff --git a/src/rulesets/oas/__tests__/__snapshots__/example-value-or-externalValue.ts.snap b/src/rulesets/oas/__tests__/__snapshots__/example-value-or-externalValue.ts.snap index ff9eed6a6..3cbbe2192 100644 --- a/src/rulesets/oas/__tests__/__snapshots__/example-value-or-externalValue.ts.snap +++ b/src/rulesets/oas/__tests__/__snapshots__/example-value-or-externalValue.ts.snap @@ -8,8 +8,8 @@ Array [ "path": Array [ "example", ], - "severity": 50, - "severityLabel": "error", + "severity": 40, + "severityLabel": "warn", "summary": "Example should have either a \`value\` or \`externalValue\` field.", }, ] @@ -23,8 +23,8 @@ Array [ "path": Array [ "example", ], - "severity": 50, - "severityLabel": "error", + "severity": 40, + "severityLabel": "warn", "summary": "Example should have either a \`value\` or \`externalValue\` field.", }, ] diff --git a/src/rulesets/oas/__tests__/__snapshots__/info-contact.ts.snap b/src/rulesets/oas/__tests__/__snapshots__/info-contact.ts.snap index 96a4664f1..c1f4d464b 100644 --- a/src/rulesets/oas/__tests__/__snapshots__/info-contact.ts.snap +++ b/src/rulesets/oas/__tests__/__snapshots__/info-contact.ts.snap @@ -9,8 +9,8 @@ Array [ "info", "contact", ], - "severity": 50, - "severityLabel": "error", + "severity": 40, + "severityLabel": "warn", "summary": "Info object should contain \`contact\` object.", }, ] diff --git a/src/rulesets/oas/__tests__/__snapshots__/info-description.ts.snap b/src/rulesets/oas/__tests__/__snapshots__/info-description.ts.snap index 2802bd31c..a426dcc0e 100644 --- a/src/rulesets/oas/__tests__/__snapshots__/info-description.ts.snap +++ b/src/rulesets/oas/__tests__/__snapshots__/info-description.ts.snap @@ -9,8 +9,8 @@ Array [ "info", "description", ], - "severity": 50, - "severityLabel": "error", + "severity": 40, + "severityLabel": "warn", "summary": "OpenAPI object info \`description\` must be present and non-empty string.", }, ] diff --git a/src/rulesets/oas/__tests__/__snapshots__/info-license.ts.snap b/src/rulesets/oas/__tests__/__snapshots__/info-license.ts.snap index 9742ae1ca..04e92e8cb 100644 --- a/src/rulesets/oas/__tests__/__snapshots__/info-license.ts.snap +++ b/src/rulesets/oas/__tests__/__snapshots__/info-license.ts.snap @@ -9,8 +9,8 @@ Array [ "info", "license", ], - "severity": 50, - "severityLabel": "error", + "severity": 40, + "severityLabel": "warn", "summary": "OpenAPI object info \`license\` must be present and non-empty string.", }, ] diff --git a/src/rulesets/oas/__tests__/__snapshots__/license-url.ts.snap b/src/rulesets/oas/__tests__/__snapshots__/license-url.ts.snap index d27c4239f..017c9ce59 100644 --- a/src/rulesets/oas/__tests__/__snapshots__/license-url.ts.snap +++ b/src/rulesets/oas/__tests__/__snapshots__/license-url.ts.snap @@ -10,8 +10,8 @@ Array [ "license", "url", ], - "severity": 50, - "severityLabel": "error", + "severity": 40, + "severityLabel": "warn", "summary": "License object should include \`url\`.", }, ] diff --git a/src/rulesets/oas/__tests__/__snapshots__/no-eval-in-markdown.ts.snap b/src/rulesets/oas/__tests__/__snapshots__/no-eval-in-markdown.ts.snap index 52b7b2888..2add34e60 100644 --- a/src/rulesets/oas/__tests__/__snapshots__/no-eval-in-markdown.ts.snap +++ b/src/rulesets/oas/__tests__/__snapshots__/no-eval-in-markdown.ts.snap @@ -9,8 +9,8 @@ Array [ "info", "description", ], - "severity": 50, - "severityLabel": "error", + "severity": 40, + "severityLabel": "warn", "summary": "Markdown descriptions should not contain \`eval(\`.", }, Object { @@ -20,8 +20,8 @@ Array [ "info", "title", ], - "severity": 50, - "severityLabel": "error", + "severity": 40, + "severityLabel": "warn", "summary": "Markdown descriptions should not contain \`eval(\`.", }, ] diff --git a/src/rulesets/oas/__tests__/__snapshots__/no-script-tags-in-markdown.ts.snap b/src/rulesets/oas/__tests__/__snapshots__/no-script-tags-in-markdown.ts.snap index 27680b163..b0eaccd1a 100644 --- a/src/rulesets/oas/__tests__/__snapshots__/no-script-tags-in-markdown.ts.snap +++ b/src/rulesets/oas/__tests__/__snapshots__/no-script-tags-in-markdown.ts.snap @@ -9,8 +9,8 @@ Array [ "info", "description", ], - "severity": 50, - "severityLabel": "error", + "severity": 40, + "severityLabel": "warn", "summary": "Markdown descriptions should not contain \`