From 30b9273ffca7a02c1237346cf6a92f847b71217c Mon Sep 17 00:00:00 2001 From: David Weber Date: Sat, 25 May 2024 17:15:12 +0200 Subject: [PATCH] feat: add rule 169 --- README.md | 2 +- ...rmats-for-date-and-time-properties.test.ts | 87 +++++++++++++++++++ tests/fixtures/base-openapi.yml | 4 + zalando.yml | 25 ++++++ 4 files changed, 117 insertions(+), 1 deletion(-) create mode 100644 tests/169-MUST-use-standard-formats-for-date-and-time-properties.test.ts diff --git a/README.md b/README.md index d82ed5b..104bc03 100644 --- a/README.md +++ b/README.md @@ -168,7 +168,7 @@ npm run lint-fix # format source files | [#166][#166] | [MUST not use link headers with JSON entities][#166] | - | - | - | | [#167][#167] | [MUST use JSON to encode structured data][#167] | - | - | - | | [#168][#168] | [MAY use non JSON media types for binary data or alternative content representations][#168] | :grey_exclamation: | :grey_exclamation: | - | -| [#169][#169] | [MUST use standard date and time formats][#169] | :grey_exclamation: | :grey_exclamation: | A | +| [#169][#169] | [MUST use standard date and time formats][#169] | :heavy_check_mark: | - | A | | [#170][#170] | [SHOULD use standards for country, language and currency codes][#170] | - | - | B | | [#171][#171] | [MUST define format for number and integer types][#171] | :heavy_check_mark: | - | - | | [#172][#172] | [SHOULD prefer standard media type name `application/json`][#172] | :heavy_check_mark: | :heavy_check_mark: | B | diff --git a/tests/169-MUST-use-standard-formats-for-date-and-time-properties.test.ts b/tests/169-MUST-use-standard-formats-for-date-and-time-properties.test.ts new file mode 100644 index 0000000..9d48aad --- /dev/null +++ b/tests/169-MUST-use-standard-formats-for-date-and-time-properties.test.ts @@ -0,0 +1,87 @@ +import { DiagnosticSeverity } from '@stoplight/types'; +import { loadOpenApiSpec, lint } from './helpers'; + +describe('MUST use standard formats for date and time properties [169]', () => { + test('Providing an example should not warn', async () => { + const openApi = await loadOpenApiSpec('base-openapi.yml'); + const result = await lint(openApi); + expect(result).toEqual([]); + }); + + test('Not providing an example should warn', async () => { + const openApi = await loadOpenApiSpec('base-openapi.yml'); + delete openApi.paths['/example'].patch.requestBody.content['application/json'].schema.properties.rule169.example; + const result = await lint(openApi); + expect(result).toEqual([ + expect.objectContaining({ + code: 'must-use-standard-formats-for-date-and-time-properties-example', + message: 'You should provide an example for rule169.example', + severity: DiagnosticSeverity.Warning, + }), + ]); + }); + + test('Not providing an example in a nested structure should warn', async () => { + const openApi = await loadOpenApiSpec('base-openapi.yml'); + + openApi.paths['/example'].patch.requestBody.content['application/json'].schema.properties = { + ...openApi.paths['/example'].patch.requestBody.content['application/json'].schema.properties, + rule169nested: { + type: 'object', + properties: { + second: { + type: 'string', + format: 'date-time', + }, + }, + }, + }; + + const result = await lint(openApi); + expect(result).toEqual([ + expect.objectContaining({ + code: 'must-use-standard-formats-for-date-and-time-properties-example', + message: 'You should provide an example for second.example', + severity: DiagnosticSeverity.Warning, + }), + ]); + }); + + test.each(['date-time', 'date', 'time', 'duration', 'period'])( + 'Not providing an example for format %s should warn', + async (format) => { + const openApi = await loadOpenApiSpec('base-openapi.yml'); + + openApi.paths['/example'].patch.requestBody.content['application/json'].schema.properties = { + ...openApi.paths['/example'].patch.requestBody.content['application/json'].schema.properties, + rule169format: { + type: 'string', + format, + }, + }; + + const result = await lint(openApi); + expect(result).toEqual([ + expect.objectContaining({ + code: 'must-use-standard-formats-for-date-and-time-properties-example', + message: 'You should provide an example for rule169format.example', + severity: DiagnosticSeverity.Warning, + }), + ]); + }, + ); + + test('Not using UTC should warn', async () => { + const openApi = await loadOpenApiSpec('base-openapi.yml'); + openApi.paths['/example'].patch.requestBody.content['application/json'].schema.properties.rule169.example = + '2015-05-28T14:07:17+00:00'; + const result = await lint(openApi); + expect(result).toEqual([ + expect.objectContaining({ + code: 'must-use-standard-formats-for-date-and-time-properties-utc', + message: 'You should UTC for example', + severity: DiagnosticSeverity.Warning, + }), + ]); + }); +}); diff --git a/tests/fixtures/base-openapi.yml b/tests/fixtures/base-openapi.yml index 8410b7f..324a221 100644 --- a/tests/fixtures/base-openapi.yml +++ b/tests/fixtures/base-openapi.yml @@ -68,6 +68,10 @@ paths: properties: name: type: string + rule169: + type: string + format: date-time + example: 2015-05-28T14:07:17Z responses: '200': description: ok diff --git a/zalando.yml b/zalando.yml index 6e57c98..2cfc418 100644 --- a/zalando.yml +++ b/zalando.yml @@ -309,6 +309,31 @@ rules: field: default function: truthy + # MUST use standard formats for date and time properties [169] + # => https://opensource.zalando.com/restful-api-guidelines/#169 + + must-use-standard-formats-for-date-and-time-properties-example: + message: "You should provide an example for {{property}}" + description: MUST use standard formats for date and time properties [169] + documentationUrl: https://opensource.zalando.com/restful-api-guidelines/#169 + severity: warn # Not an error as you only should provide an example to help your consumers + given: $.paths..[?(@.type === 'string' && (@.format === 'date-time' || @.format === 'date' || @.format === 'time' || @.format === 'duration' || @.format === 'period'))] + then: + field: example + function: truthy + + must-use-standard-formats-for-date-and-time-properties-utc: + message: "You should UTC for {{property}}" + description: MUST use standard formats for date and time properties [169] + documentationUrl: https://opensource.zalando.com/restful-api-guidelines/#169 + severity: warn # Not an error as you only should provide an example to help your consumers + given: $.paths..[?(@.type === 'string' && @.format === 'date-time')] + then: + field: example + function: pattern + functionOptions: + match: "Z$" + must-use-problem-json-as-default-response: message: Operation must use problem json as default response description: MUST specify success and error responses [151]