From 92d6480251e9253b21a5151132de3edaa4df9bd1 Mon Sep 17 00:00:00 2001 From: Lovisa Berggren Date: Fri, 21 Mar 2025 17:12:10 +0000 Subject: [PATCH 1/3] CLOUDP-304967: Add IPA rule for description format --- .../IPA117DescriptionUpperCasePeriod.test.js | 102 ++++++++++++++++++ tools/spectral/ipa/rulesets/IPA-117.yaml | 25 +++++ tools/spectral/ipa/rulesets/README.md | 14 +++ .../IPA117DescriptionUpperCasePeriod.js | 46 ++++++++ 4 files changed, 187 insertions(+) create mode 100644 tools/spectral/ipa/__tests__/IPA117DescriptionUpperCasePeriod.test.js create mode 100644 tools/spectral/ipa/rulesets/functions/IPA117DescriptionUpperCasePeriod.js diff --git a/tools/spectral/ipa/__tests__/IPA117DescriptionUpperCasePeriod.test.js b/tools/spectral/ipa/__tests__/IPA117DescriptionUpperCasePeriod.test.js new file mode 100644 index 0000000000..7e125eb8c3 --- /dev/null +++ b/tools/spectral/ipa/__tests__/IPA117DescriptionUpperCasePeriod.test.js @@ -0,0 +1,102 @@ +import testRule from './__helpers__/testRule'; +import { DiagnosticSeverity } from '@stoplight/types'; + +testRule('xgen-IPA-117-description-uppercase-period', [ + { + name: 'valid description', + document: { + components: { + schemas: { + Schema: { + properties: { + id: { + description: 'Description.', + }, + }, + }, + }, + }, + }, + errors: [], + }, + { + name: 'invalid descriptions', + document: { + components: { + schemas: { + Schema: { + properties: { + noPeriod: { + description: 'Description', + }, + noUpperCase: { + description: 'description.', + }, + noUpperCaseNoPeriod: { + description: 'description', + }, + }, + }, + }, + }, + }, + errors: [ + { + code: 'xgen-IPA-117-description-uppercase-period', + message: 'Descriptions must end with a full stop(.).', + path: ['components', 'schemas', 'Schema', 'properties', 'noPeriod'], + severity: DiagnosticSeverity.Warning, + }, + { + code: 'xgen-IPA-117-description-uppercase-period', + message: 'Descriptions must start with Uppercase.', + path: ['components', 'schemas', 'Schema', 'properties', 'noUpperCase'], + severity: DiagnosticSeverity.Warning, + }, + { + code: 'xgen-IPA-117-description-uppercase-period', + message: 'Descriptions must start with Uppercase.', + path: ['components', 'schemas', 'Schema', 'properties', 'noUpperCaseNoPeriod'], + severity: DiagnosticSeverity.Warning, + }, + { + code: 'xgen-IPA-117-description-uppercase-period', + message: 'Descriptions must end with a full stop(.).', + path: ['components', 'schemas', 'Schema', 'properties', 'noUpperCaseNoPeriod'], + severity: DiagnosticSeverity.Warning, + }, + ], + }, + { + name: 'invalid components with exceptions', + document: { + components: { + schemas: { + Schema: { + properties: { + noPeriod: { + description: 'Description', + 'x-xgen-IPA-exception': { + 'xgen-IPA-117-description-uppercase-period': 'reason', + }, + }, + noUpperCase: { + description: 'description.', + 'x-xgen-IPA-exception': { + 'xgen-IPA-117-description-uppercase-period': 'reason', + }, + }, + noUpperCaseNoPeriod: { + description: 'description', + 'x-xgen-IPA-exception': { + 'xgen-IPA-117-description-uppercase-period': 'reason', + }, + }, + }, + }, + }, + }, + }, + errors: [], + }, +]); diff --git a/tools/spectral/ipa/rulesets/IPA-117.yaml b/tools/spectral/ipa/rulesets/IPA-117.yaml index cab084d98a..2301acf1da 100644 --- a/tools/spectral/ipa/rulesets/IPA-117.yaml +++ b/tools/spectral/ipa/rulesets/IPA-117.yaml @@ -3,6 +3,7 @@ functions: - IPA117HasDescription + - IPA117DescriptionUpperCasePeriod rules: xgen-IPA-117-description: @@ -30,3 +31,27 @@ rules: - '$.components.parameters[*]' then: function: 'IPA117HasDescription' + xgen-IPA-117-description-uppercase-period: + description: | + Descriptions must start with Uppercase and end with a full stop(.) + + ##### Implementation details + Rule checks the format of the description property in the following components: + - Info object + - Tags + - Operation objects + - Inline schema properties for operation object requests and responses + - Parameter objects (in operations and components) + - Schema properties + message: '{{error}} https://mdb.link/mongodb-atlas-openapi-validation#xgen-IPA-117-description-uppercase-period' + severity: warn + given: + - '$.info' + - '$.tags[*]' + - '$.paths[*][get,put,post,delete,options,head,patch,trace]' + - '$.paths[*][get,put,post,delete,options,head,patch,trace].parameters[*]' + - '$.paths[*][get,put,post,delete,options,head,patch,trace]..content..properties[*]' + - '$.components.schemas..properties[*]' + - '$.components.parameters[*]' + then: + function: 'IPA117DescriptionUpperCasePeriod' diff --git a/tools/spectral/ipa/rulesets/README.md b/tools/spectral/ipa/rulesets/README.md index 3f0e20fb63..7031e54454 100644 --- a/tools/spectral/ipa/rulesets/README.md +++ b/tools/spectral/ipa/rulesets/README.md @@ -535,6 +535,20 @@ Rule checks for description property in the following components: - Schema properties The rule also fails if the description is an empty string. +#### xgen-IPA-117-description-uppercase-period + + ![warn](https://img.shields.io/badge/warning-yellow) +Descriptions must start with Uppercase and end with a full stop(.) + +##### Implementation details +Rule checks the format of the description property in the following components: + - Info object + - Tags + - Operation objects + - Inline schema properties for operation object requests and responses + - Parameter objects (in operations and components) + - Schema properties + ### IPA-123 diff --git a/tools/spectral/ipa/rulesets/functions/IPA117DescriptionUpperCasePeriod.js b/tools/spectral/ipa/rulesets/functions/IPA117DescriptionUpperCasePeriod.js new file mode 100644 index 0000000000..3fc57317b1 --- /dev/null +++ b/tools/spectral/ipa/rulesets/functions/IPA117DescriptionUpperCasePeriod.js @@ -0,0 +1,46 @@ +import { hasException } from './utils/exceptions.js'; +import { + collectAdoption, + collectAndReturnViolation, + collectException, + handleInternalError, +} from './utils/collectionUtils.js'; + +const RULE_NAME = 'xgen-IPA-117-description-uppercase-period'; +const ERROR_MESSAGE_PERIOD = 'Descriptions must end with a full stop(.).'; +const ERROR_MESSAGE_UPPER_CASE = 'Descriptions must start with Uppercase.'; + +export default (input, opts, { path }) => { + if (!input['description']) { + return; + } + + if (hasException(input, RULE_NAME)) { + collectException(input, RULE_NAME, path); + return; + } + + const errors = checkViolationsAndReturnErrors(input['description'], path); + if (errors.length !== 0) { + return collectAndReturnViolation(path, RULE_NAME, errors); + } + collectAdoption(path, RULE_NAME); +}; + +function checkViolationsAndReturnErrors(description, path) { + const upperCaseStart = new RegExp(`^[A-Z]`); + const periodEnd = new RegExp(`[.]$`); + const errors = []; + + try { + if (!upperCaseStart.test(description)) { + errors.push({ path, message: ERROR_MESSAGE_UPPER_CASE }); + } + if (!periodEnd.test(description)) { + errors.push({ path, message: ERROR_MESSAGE_PERIOD }); + } + return errors; + } catch (e) { + handleInternalError(RULE_NAME, path, e); + } +} From 96d33c6f85d2265fb4c882e00e8e11627209166d Mon Sep 17 00:00:00 2001 From: Lovisa Berggren Date: Fri, 21 Mar 2025 17:44:43 +0000 Subject: [PATCH 2/3] CLOUDP-304967: Ignore inline table ending in separate rule --- .../IPA117DescriptionEndsWithPeriod.test.js | 83 ++++++++++++++ ...A117DescriptionStartsWithUpperCase.test.js | 66 ++++++++++++ .../IPA117DescriptionUpperCasePeriod.test.js | 102 ------------------ tools/spectral/ipa/rulesets/IPA-117.yaml | 36 ++++++- tools/spectral/ipa/rulesets/README.md | 19 +++- .../IPA117DescriptionEndsWithPeriod.js | 41 +++++++ ...> IPA117DescriptionStartsWithUpperCase.js} | 12 +-- 7 files changed, 241 insertions(+), 118 deletions(-) create mode 100644 tools/spectral/ipa/__tests__/IPA117DescriptionEndsWithPeriod.test.js create mode 100644 tools/spectral/ipa/__tests__/IPA117DescriptionStartsWithUpperCase.test.js delete mode 100644 tools/spectral/ipa/__tests__/IPA117DescriptionUpperCasePeriod.test.js create mode 100644 tools/spectral/ipa/rulesets/functions/IPA117DescriptionEndsWithPeriod.js rename tools/spectral/ipa/rulesets/functions/{IPA117DescriptionUpperCasePeriod.js => IPA117DescriptionStartsWithUpperCase.js} (70%) diff --git a/tools/spectral/ipa/__tests__/IPA117DescriptionEndsWithPeriod.test.js b/tools/spectral/ipa/__tests__/IPA117DescriptionEndsWithPeriod.test.js new file mode 100644 index 0000000000..877bbab06d --- /dev/null +++ b/tools/spectral/ipa/__tests__/IPA117DescriptionEndsWithPeriod.test.js @@ -0,0 +1,83 @@ +import testRule from './__helpers__/testRule'; +import { DiagnosticSeverity } from '@stoplight/types'; + +testRule('xgen-IPA-117-description-ends-with-period', [ + { + name: 'valid description', + document: { + components: { + schemas: { + Schema: { + properties: { + id: { + description: 'Description.', + }, + }, + }, + }, + }, + }, + errors: [], + }, + { + name: 'invalid descriptions', + document: { + components: { + schemas: { + Schema: { + properties: { + noPeriod: { + description: 'Description', + }, + }, + }, + }, + }, + }, + errors: [ + { + code: 'xgen-IPA-117-description-ends-with-period', + message: 'Descriptions must end with a full stop(.).', + path: ['components', 'schemas', 'Schema', 'properties', 'noPeriod'], + severity: DiagnosticSeverity.Warning, + }, + ], + }, + { + name: 'ignores descriptions ending with table', + document: { + components: { + schemas: { + Schema: { + properties: { + noPeriod: { + description: 'Description\n| Table |', + }, + }, + }, + }, + }, + }, + errors: [], + }, + { + name: 'invalid components with exceptions', + document: { + components: { + schemas: { + Schema: { + properties: { + noPeriod: { + description: 'Description', + 'x-xgen-IPA-exception': { + 'xgen-IPA-117-description-ends-with-period': 'reason', + }, + }, + }, + }, + }, + }, + }, + errors: [], + }, +]); diff --git a/tools/spectral/ipa/__tests__/IPA117DescriptionStartsWithUpperCase.test.js b/tools/spectral/ipa/__tests__/IPA117DescriptionStartsWithUpperCase.test.js new file mode 100644 index 0000000000..e72777c061 --- /dev/null +++ b/tools/spectral/ipa/__tests__/IPA117DescriptionStartsWithUpperCase.test.js @@ -0,0 +1,66 @@ +import testRule from './__helpers__/testRule'; +import { DiagnosticSeverity } from '@stoplight/types'; + +testRule('xgen-IPA-117-description-starts-with-uppercase', [ + { + name: 'valid description', + document: { + components: { + schemas: { + Schema: { + properties: { + id: { + description: 'Description', + }, + }, + }, + }, + }, + }, + errors: [], + }, + { + name: 'invalid descriptions', + document: { + components: { + schemas: { + Schema: { + properties: { + noUpperCase: { + description: 'description', + }, + }, + }, + }, + }, + }, + errors: [ + { + code: 'xgen-IPA-117-description-starts-with-uppercase', + message: 'Descriptions must start with Uppercase.', + path: ['components', 'schemas', 'Schema', 'properties', 'noUpperCase'], + severity: DiagnosticSeverity.Warning, + }, + ], + }, + { + name: 'invalid components with exceptions', + document: { + components: { + schemas: { + Schema: { + properties: { + noUpperCase: { + description: 'description', + 'x-xgen-IPA-exception': { + 'xgen-IPA-117-description-starts-with-uppercase': 'reason', + }, + }, + }, + }, + }, + }, + }, + errors: [], + }, +]); diff --git a/tools/spectral/ipa/__tests__/IPA117DescriptionUpperCasePeriod.test.js b/tools/spectral/ipa/__tests__/IPA117DescriptionUpperCasePeriod.test.js deleted file mode 100644 index 7e125eb8c3..0000000000 --- a/tools/spectral/ipa/__tests__/IPA117DescriptionUpperCasePeriod.test.js +++ /dev/null @@ -1,102 +0,0 @@ -import testRule from './__helpers__/testRule'; -import { DiagnosticSeverity } from '@stoplight/types'; - -testRule('xgen-IPA-117-description-uppercase-period', [ - { - name: 'valid description', - document: { - components: { - schemas: { - Schema: { - properties: { - id: { - description: 'Description.', - }, - }, - }, - }, - }, - }, - errors: [], - }, - { - name: 'invalid descriptions', - document: { - components: { - schemas: { - Schema: { - properties: { - noPeriod: { - description: 'Description', - }, - noUpperCase: { - description: 'description.', - }, - noUpperCaseNoPeriod: { - description: 'description', - }, - }, - }, - }, - }, - }, - errors: [ - { - code: 'xgen-IPA-117-description-uppercase-period', - message: 'Descriptions must end with a full stop(.).', - path: ['components', 'schemas', 'Schema', 'properties', 'noPeriod'], - severity: DiagnosticSeverity.Warning, - }, - { - code: 'xgen-IPA-117-description-uppercase-period', - message: 'Descriptions must start with Uppercase.', - path: ['components', 'schemas', 'Schema', 'properties', 'noUpperCase'], - severity: DiagnosticSeverity.Warning, - }, - { - code: 'xgen-IPA-117-description-uppercase-period', - message: 'Descriptions must start with Uppercase.', - path: ['components', 'schemas', 'Schema', 'properties', 'noUpperCaseNoPeriod'], - severity: DiagnosticSeverity.Warning, - }, - { - code: 'xgen-IPA-117-description-uppercase-period', - message: 'Descriptions must end with a full stop(.).', - path: ['components', 'schemas', 'Schema', 'properties', 'noUpperCaseNoPeriod'], - severity: DiagnosticSeverity.Warning, - }, - ], - }, - { - name: 'invalid components with exceptions', - document: { - components: { - schemas: { - Schema: { - properties: { - noPeriod: { - description: 'Description', - 'x-xgen-IPA-exception': { - 'xgen-IPA-117-description-uppercase-period': 'reason', - }, - }, - noUpperCase: { - description: 'description.', - 'x-xgen-IPA-exception': { - 'xgen-IPA-117-description-uppercase-period': 'reason', - }, - }, - noUpperCaseNoPeriod: { - description: 'description', - 'x-xgen-IPA-exception': { - 'xgen-IPA-117-description-uppercase-period': 'reason', - }, - }, - }, - }, - }, - }, - }, - errors: [], - }, -]); diff --git a/tools/spectral/ipa/rulesets/IPA-117.yaml b/tools/spectral/ipa/rulesets/IPA-117.yaml index 2301acf1da..e2750ff485 100644 --- a/tools/spectral/ipa/rulesets/IPA-117.yaml +++ b/tools/spectral/ipa/rulesets/IPA-117.yaml @@ -3,7 +3,8 @@ functions: - IPA117HasDescription - - IPA117DescriptionUpperCasePeriod + - IPA117DescriptionStartsWithUpperCase + - IPA117DescriptionEndsWithPeriod rules: xgen-IPA-117-description: @@ -31,9 +32,9 @@ rules: - '$.components.parameters[*]' then: function: 'IPA117HasDescription' - xgen-IPA-117-description-uppercase-period: + xgen-IPA-117-description-starts-with-uppercase: description: | - Descriptions must start with Uppercase and end with a full stop(.) + Descriptions must start with Uppercase. ##### Implementation details Rule checks the format of the description property in the following components: @@ -43,7 +44,7 @@ rules: - Inline schema properties for operation object requests and responses - Parameter objects (in operations and components) - Schema properties - message: '{{error}} https://mdb.link/mongodb-atlas-openapi-validation#xgen-IPA-117-description-uppercase-period' + message: '{{error}} https://mdb.link/mongodb-atlas-openapi-validation#xgen-IPA-117-description-starts-with-uppercase' severity: warn given: - '$.info' @@ -54,4 +55,29 @@ rules: - '$.components.schemas..properties[*]' - '$.components.parameters[*]' then: - function: 'IPA117DescriptionUpperCasePeriod' + function: 'IPA117DescriptionStartsWithUpperCase' + xgen-IPA-117-description-ends-with-period: + description: | + Descriptions must end with a full stop(.). + + ##### Implementation details + Rule checks the format of the description property in the following components: + - Info object + - Tags + - Operation objects + - Inline schema properties for operation object requests and responses + - Parameter objects (in operations and components) + - Schema properties + The rule ignores descriptions that end with `|`, i.e. inline markdown tables + message: '{{error}} https://mdb.link/mongodb-atlas-openapi-validation#xgen-IPA-117-description-ends-with-period' + severity: warn + given: + - '$.info' + - '$.tags[*]' + - '$.paths[*][get,put,post,delete,options,head,patch,trace]' + - '$.paths[*][get,put,post,delete,options,head,patch,trace].parameters[*]' + - '$.paths[*][get,put,post,delete,options,head,patch,trace]..content..properties[*]' + - '$.components.schemas..properties[*]' + - '$.components.parameters[*]' + then: + function: 'IPA117DescriptionEndsWithPeriod' diff --git a/tools/spectral/ipa/rulesets/README.md b/tools/spectral/ipa/rulesets/README.md index 7031e54454..d7f37a128a 100644 --- a/tools/spectral/ipa/rulesets/README.md +++ b/tools/spectral/ipa/rulesets/README.md @@ -535,10 +535,10 @@ Rule checks for description property in the following components: - Schema properties The rule also fails if the description is an empty string. -#### xgen-IPA-117-description-uppercase-period +#### xgen-IPA-117-description-starts-with-uppercase ![warn](https://img.shields.io/badge/warning-yellow) -Descriptions must start with Uppercase and end with a full stop(.) +Descriptions must start with Uppercase. ##### Implementation details Rule checks the format of the description property in the following components: @@ -549,6 +549,21 @@ Rule checks the format of the description property in the following components: - Parameter objects (in operations and components) - Schema properties +#### xgen-IPA-117-description-ends-with-period + + ![warn](https://img.shields.io/badge/warning-yellow) +Descriptions must end with a full stop(.). + +##### Implementation details +Rule checks the format of the description property in the following components: + - Info object + - Tags + - Operation objects + - Inline schema properties for operation object requests and responses + - Parameter objects (in operations and components) + - Schema properties +The rule ignores descriptions that end with `|`, i.e. inline markdown tables + ### IPA-123 diff --git a/tools/spectral/ipa/rulesets/functions/IPA117DescriptionEndsWithPeriod.js b/tools/spectral/ipa/rulesets/functions/IPA117DescriptionEndsWithPeriod.js new file mode 100644 index 0000000000..9a01f148f7 --- /dev/null +++ b/tools/spectral/ipa/rulesets/functions/IPA117DescriptionEndsWithPeriod.js @@ -0,0 +1,41 @@ +import { hasException } from './utils/exceptions.js'; +import { + collectAdoption, + collectAndReturnViolation, + collectException, + handleInternalError, +} from './utils/collectionUtils.js'; + +const RULE_NAME = 'xgen-IPA-117-description-ends-with-period'; +const ERROR_MESSAGE_PERIOD = 'Descriptions must end with a full stop(.).'; + +export default (input, opts, { path }) => { + // Ignore missing descriptions or descriptions that ends with an inline table + if (!input['description'] || input['description'].endsWith('|')) { + return; + } + + if (hasException(input, RULE_NAME)) { + collectException(input, RULE_NAME, path); + return; + } + + const errors = checkViolationsAndReturnErrors(input['description'], path); + if (errors.length !== 0) { + return collectAndReturnViolation(path, RULE_NAME, errors); + } + collectAdoption(path, RULE_NAME); +}; + +function checkViolationsAndReturnErrors(description, path) { + const periodEnd = new RegExp(`[.]$`); + + try { + if (!periodEnd.test(description)) { + return [{ path, message: ERROR_MESSAGE_PERIOD }]; + } + return []; + } catch (e) { + handleInternalError(RULE_NAME, path, e); + } +} diff --git a/tools/spectral/ipa/rulesets/functions/IPA117DescriptionUpperCasePeriod.js b/tools/spectral/ipa/rulesets/functions/IPA117DescriptionStartsWithUpperCase.js similarity index 70% rename from tools/spectral/ipa/rulesets/functions/IPA117DescriptionUpperCasePeriod.js rename to tools/spectral/ipa/rulesets/functions/IPA117DescriptionStartsWithUpperCase.js index 3fc57317b1..61b964c660 100644 --- a/tools/spectral/ipa/rulesets/functions/IPA117DescriptionUpperCasePeriod.js +++ b/tools/spectral/ipa/rulesets/functions/IPA117DescriptionStartsWithUpperCase.js @@ -6,8 +6,7 @@ import { handleInternalError, } from './utils/collectionUtils.js'; -const RULE_NAME = 'xgen-IPA-117-description-uppercase-period'; -const ERROR_MESSAGE_PERIOD = 'Descriptions must end with a full stop(.).'; +const RULE_NAME = 'xgen-IPA-117-description-starts-with-uppercase'; const ERROR_MESSAGE_UPPER_CASE = 'Descriptions must start with Uppercase.'; export default (input, opts, { path }) => { @@ -29,17 +28,12 @@ export default (input, opts, { path }) => { function checkViolationsAndReturnErrors(description, path) { const upperCaseStart = new RegExp(`^[A-Z]`); - const periodEnd = new RegExp(`[.]$`); - const errors = []; try { if (!upperCaseStart.test(description)) { - errors.push({ path, message: ERROR_MESSAGE_UPPER_CASE }); + return [{ path, message: ERROR_MESSAGE_UPPER_CASE }]; } - if (!periodEnd.test(description)) { - errors.push({ path, message: ERROR_MESSAGE_PERIOD }); - } - return errors; + return []; } catch (e) { handleInternalError(RULE_NAME, path, e); } From 0a3d3d133f6c941e7b3bf74aba98801ec612fca0 Mon Sep 17 00:00:00 2001 From: Lovisa Berggren Date: Fri, 21 Mar 2025 17:50:07 +0000 Subject: [PATCH 3/3] CLOUDP-304967: Ignore inline table with linebreak --- .../ipa/rulesets/functions/IPA117DescriptionEndsWithPeriod.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/spectral/ipa/rulesets/functions/IPA117DescriptionEndsWithPeriod.js b/tools/spectral/ipa/rulesets/functions/IPA117DescriptionEndsWithPeriod.js index 9a01f148f7..2ac45dc343 100644 --- a/tools/spectral/ipa/rulesets/functions/IPA117DescriptionEndsWithPeriod.js +++ b/tools/spectral/ipa/rulesets/functions/IPA117DescriptionEndsWithPeriod.js @@ -11,7 +11,7 @@ const ERROR_MESSAGE_PERIOD = 'Descriptions must end with a full stop(.).'; export default (input, opts, { path }) => { // Ignore missing descriptions or descriptions that ends with an inline table - if (!input['description'] || input['description'].endsWith('|')) { + if (!input['description'] || input['description'].endsWith('|') || input['description'].endsWith('|\n')) { return; }