diff --git a/linter/spectral.yml b/linter/spectral.yml index 7cca73a..da7089a 100644 --- a/linter/spectral.yml +++ b/linter/spectral.yml @@ -132,7 +132,7 @@ rules: #/core/path-segments-kebab-case nlgov:paths-kebab-case: - severity: warn + severity: error message: "{{property}} is not kebab-case." given: $.paths[?(@property && !@property.match(/\/openapi\.json/))]~ then: @@ -146,6 +146,19 @@ rules: # - Een pad mag eindigen met een `/`. Dat is volgens een andere regel niet toegestaan, maar we willen niet twee errors genereren match: ^(\/|(\/_[a-z0-9]+|\/(([a-z0-9\-]+|{[^}]+})(\/([a-z0-9\-\.]+|{[^}]+}))*)(\/_[a-z]+)?)\/?)$ + #/core/query-keys-camel-case + nlgov:query-keys-camel-case: + severity: error + message: "{{value}} is not lower camelCase." + given: + - $.paths.*.*.parameters[?(@.in=='query')] + - $.components.securitySchemes[?(@.in=='query')] + then: + function: pattern + field: name + functionOptions: + match: ^\$?[a-z][a-z\d]*([A-Z][a-z\d]*)*$ + nlgov:schema-camel-case: severity: warn message: "Schema name should be UpperCamelCase in {{path}}" diff --git a/linter/testcases/cor-api/expected-output.txt b/linter/testcases/cor-api/expected-output.txt index 5c46649..e88c108 100644 --- a/linter/testcases/cor-api/expected-output.txt +++ b/linter/testcases/cor-api/expected-output.txt @@ -2,7 +2,7 @@ /testcases/cor-api/openapi.json 70:35 warning nlgov:use-problem-schema The content type of an error response should be application/problem+json or application/problem+xml to match RFC 9457. paths./heartbeat.get.responses[429].content 80:35 warning nlgov:use-problem-schema The content type of an error response should be application/problem+json or application/problem+xml to match RFC 9457. paths./heartbeat.get.responses[503].content - 181:29 warning nlgov:paths-kebab-case /laatsteWijziging is not kebab-case. paths./laatsteWijziging + 181:29 error nlgov:paths-kebab-case /laatsteWijziging is not kebab-case. paths./laatsteWijziging 211:35 warning nlgov:use-problem-schema The content type of an error response should be application/problem+json or application/problem+xml to match RFC 9457. paths./laatsteWijziging.get.responses[400].content 221:35 warning nlgov:use-problem-schema The content type of an error response should be application/problem+json or application/problem+xml to match RFC 9457. paths./laatsteWijziging.get.responses[404].content 231:35 warning nlgov:use-problem-schema The content type of an error response should be application/problem+json or application/problem+xml to match RFC 9457. paths./laatsteWijziging.get.responses[405].content @@ -25,4 +25,4 @@ 734:35 warning nlgov:use-problem-schema The content type of an error response should be application/problem+json or application/problem+xml to match RFC 9457. paths./organisaties/{oin}.get.responses[500].content 744:35 warning nlgov:use-problem-schema The content type of an error response should be application/problem+json or application/problem+xml to match RFC 9457. paths./organisaties/{oin}.get.responses[503].content -✖ 24 problems (0 errors, 24 warnings, 0 infos, 0 hints) +✖ 24 problems (1 error, 23 warnings, 0 infos, 0 hints) diff --git a/linter/testcases/paths-kebab-incorrect/expected-output.txt b/linter/testcases/paths-kebab-incorrect/expected-output.txt index 3ece0b7..90b3405 100644 --- a/linter/testcases/paths-kebab-incorrect/expected-output.txt +++ b/linter/testcases/paths-kebab-incorrect/expected-output.txt @@ -1,5 +1,5 @@ /testcases/paths-kebab-incorrect/openapi.json - 67:25 warning nlgov:paths-kebab-case /camelCasePad is not kebab-case. paths./camelCasePad + 67:25 error nlgov:paths-kebab-case /camelCasePad is not kebab-case. paths./camelCasePad -✖ 1 problem (0 errors, 1 warning, 0 infos, 0 hints) +✖ 1 problem (1 error, 0 warnings, 0 infos, 0 hints) diff --git a/linter/testcases/query-keys-camel-case/expected-output.txt b/linter/testcases/query-keys-camel-case/expected-output.txt new file mode 100644 index 0000000..3e99836 --- /dev/null +++ b/linter/testcases/query-keys-camel-case/expected-output.txt @@ -0,0 +1,9 @@ + +/testcases/query-keys-camel-case/openapi.json + 84:33 error nlgov:query-keys-camel-case kebab-case is not lower camelCase. paths./resource.get.parameters[1].name + 91:33 error nlgov:query-keys-camel-case _startMetUnderscore is not lower camelCase. paths./resource.get.parameters[2].name + 98:33 error nlgov:query-keys-camel-case 9startMetGetal is not lower camelCase. paths./resource.get.parameters[3].name + 105:33 error nlgov:query-keys-camel-case snake_case is not lower camelCase. paths./resource.get.parameters[4].name + 112:33 error nlgov:query-keys-camel-case UpperCamelCase is not lower camelCase. paths./resource.get.parameters[5].name + +✖ 5 problems (5 errors, 0 warnings, 0 infos, 0 hints) diff --git a/linter/testcases/query-keys-camel-case/openapi.json b/linter/testcases/query-keys-camel-case/openapi.json new file mode 100644 index 0000000..5c9ccbe --- /dev/null +++ b/linter/testcases/query-keys-camel-case/openapi.json @@ -0,0 +1,155 @@ +{ + "openapi": "3.0.3", + "info": { + "title": "Baseline", + "description": "Deze OpenAPI specification bevat het minimale om aan alle regels te voldoen.", + "contact": { + "name": "Beheerder", + "url": "https://www.example.com", + "email": "mail@example.com" + }, + "version": "1.0.0" + }, + "servers": [ + { + "url": "https://example.com/api/v1" + } + ], + "security": [ + { + "default": [] + } + ], + "tags": [ + { + "name": "openapi" + }, + { + "name": "resource" + } + ], + "paths": { + "/openapi.json": { + "get": { + "tags": [ + "openapi" + ], + "description": "OpenAPI document", + "operationId": "getOpenapiJSON", + "parameters": [], + "responses": { + "200": { + "description": "OK", + "headers": { + "API-Version": { + "description": "De huidige versie van de applicatie", + "style": "simple", + "schema": { + "type": "string" + } + }, + "access-control-allow-origin": { + "description": "Alle origins mogen bij deze resource", + "schema": { + "type": "string" + } + } + } + } + }, + "security": [ + { + "default": [] + } + ] + } + }, + "/resource": { + "get": { + "tags": [ + "resource" + ], + "description": "resource", + "operationId": "getResource", + "parameters": [ + { + "in": "query", + "name": "lowerCamelCase", + "schema": { + "type": "string" + } + }, + { + "in": "query", + "name": "kebab-case", + "schema": { + "type": "string" + } + }, + { + "in": "query", + "name": "_startMetUnderscore", + "schema": { + "type": "string" + } + }, + { + "in": "query", + "name": "9startMetGetal", + "schema": { + "type": "string" + } + }, + { + "in": "query", + "name": "snake_case", + "schema": { + "type": "string" + } + }, + { + "in": "query", + "name": "UpperCamelCase", + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "OK", + "headers": { + "API-Version": { + "description": "De huidige versie van de applicatie", + "style": "simple", + "schema": { + "type": "string" + } + } + } + } + }, + "security": [ + { + "default": [] + } + ] + } + } + }, + "components": { + "schemas": { + }, + "securitySchemes": { + "default": { + "type": "oauth2", + "flows": { + "implicit": { + "authorizationUrl": "https://test.com", + "scopes": {} + } + } + } + } + } +} \ No newline at end of file diff --git a/sections/designRules.md b/sections/designRules.md index 999959a..13a1200 100644 --- a/sections/designRules.md +++ b/sections/designRules.md @@ -167,7 +167,7 @@ https://api.example.org/v1/vergunningen/d285e05c-6b01-45c3-92d8-5e19a946b66fStatement
-

Query keys in a [=URI=] MUST only contain letters and digits, where the first letter of each word is capitalized, except for the first letter of the entire compound word. This is also known as lower camelCase. This also implies that diacritics MUST be normalized and special characters MUST be omitted. +

Query keys in a [=URI=] MUST only contain letters and digits, where the first letter of each word is capitalized, except for the first letter (MUST NOT be a digit) of the entire compound word. This is also known as lower camelCase. This also implies that diacritics MUST be normalized and special characters MUST be omitted.

Rationale
@@ -178,14 +178,15 @@ https://api.example.org/v1/vergunningen/d285e05c-6b01-45c3-92d8-5e19a946b66fhttps://api.example.org/v1/gebouwen?typeGebouw=woning

URI query key not using camelCase (incorrect):

https://api.example.org/v1/gebouwen?type-gebouw=woning
+

URI query key starts with digit (incorrect):

+
https://api.example.org/v1/gebouwen?2ndReviewer=alice
How to test
- Loop all resource paths in the OpenAPI Description and check that all query keys use letters, digits in camelCase. + Loop all resource paths in the OpenAPI Description and check that all query keys use letters, digits in camelCase. You can use the following regex for each query key: