From be2181d447959fa6e388b7a601fed6b31b5a218e Mon Sep 17 00:00:00 2001 From: Tim van der Lippe Date: Tue, 23 Sep 2025 11:11:34 +0200 Subject: [PATCH 1/3] Sta expliciet niet toe dat query keys met een getal beginnen Er zijn in het API register ook geen API's die dit hebben. Bij het afgelopen TO is gevraagd om deze verscherping. Tevens is de linter regel weer toegevoegd. Die is onverhoopt bij het mergen van de vorige PR verdwenen. Nu staat hij er weer, inclusief wat test cases. --- linter/spectral.yml | 15 +- linter/testcases/cor-api/expected-output.txt | 4 +- .../paths-kebab-incorrect/expected-output.txt | 4 +- .../query-keys-camel-case/expected-output.txt | 9 + .../query-keys-camel-case/openapi.json | 155 ++++++++++++++++++ sections/designRules.md | 4 +- 6 files changed, 185 insertions(+), 6 deletions(-) create mode 100644 linter/testcases/query-keys-camel-case/expected-output.txt create mode 100644 linter/testcases/query-keys-camel-case/openapi.json diff --git a/linter/spectral.yml b/linter/spectral.yml index 7cca73a..d22503c 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-z0-9]*([A-Z][\w0-9]*)*$ + 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..f8dbed6 --- /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 _startMetSlash 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..0527218 --- /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": "_startMetSlash", + "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..369d9ec 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,6 +178,8 @@ 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
From c7332dd20a0b4b458f320d2865f287661ff60b19 Mon Sep 17 00:00:00 2001 From: Tim van der Lippe Date: Tue, 23 Sep 2025 11:23:40 +0200 Subject: [PATCH 2/3] Fix regex in how to test --- sections/designRules.md | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/sections/designRules.md b/sections/designRules.md index 369d9ec..eda5589 100644 --- a/sections/designRules.md +++ b/sections/designRules.md @@ -184,10 +184,9 @@ https://api.example.org/v1/vergunningen/d285e05c-6b01-45c3-92d8-5e19a946b66f
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:
From e8997ecee0dcb78a97f4a8d2b32e986b729f0434 Mon Sep 17 00:00:00 2001 From: Tim van der Lippe Date: Wed, 24 Sep 2025 11:30:00 +0200 Subject: [PATCH 3/3] Fix code review comments --- linter/spectral.yml | 2 +- .../query-keys-camel-case/expected-output.txt | 10 +++++----- linter/testcases/query-keys-camel-case/openapi.json | 2 +- sections/designRules.md | 2 +- 4 files changed, 8 insertions(+), 8 deletions(-) diff --git a/linter/spectral.yml b/linter/spectral.yml index d22503c..da7089a 100644 --- a/linter/spectral.yml +++ b/linter/spectral.yml @@ -157,7 +157,7 @@ rules: function: pattern field: name functionOptions: - match: ^\$?[a-z][a-z0-9]*([A-Z][\w0-9]*)*$ + match: ^\$?[a-z][a-z\d]*([A-Z][a-z\d]*)*$ nlgov:schema-camel-case: severity: warn diff --git a/linter/testcases/query-keys-camel-case/expected-output.txt b/linter/testcases/query-keys-camel-case/expected-output.txt index f8dbed6..3e99836 100644 --- a/linter/testcases/query-keys-camel-case/expected-output.txt +++ b/linter/testcases/query-keys-camel-case/expected-output.txt @@ -1,9 +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 _startMetSlash 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 + 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 index 0527218..5c9ccbe 100644 --- a/linter/testcases/query-keys-camel-case/openapi.json +++ b/linter/testcases/query-keys-camel-case/openapi.json @@ -88,7 +88,7 @@ }, { "in": "query", - "name": "_startMetSlash", + "name": "_startMetUnderscore", "schema": { "type": "string" } diff --git a/sections/designRules.md b/sections/designRules.md index eda5589..13a1200 100644 --- a/sections/designRules.md +++ b/sections/designRules.md @@ -186,7 +186,7 @@ https://api.example.org/v1/vergunningen/d285e05c-6b01-45c3-92d8-5e19a946b66f 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: