Skip to content
Open
Show file tree
Hide file tree
Changes from 8 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 22 additions & 0 deletions linter/spectral.yml
Original file line number Diff line number Diff line change
Expand Up @@ -156,6 +156,7 @@ rules:
functionOptions:
match: ^https://.*

#/core/problem-details
nlgov:use-problem-schema:
severity: warn
message: "The content type of an error response should be application/problem+json or application/problem+xml to match RFC 9457."
Expand All @@ -168,6 +169,27 @@ rules:
- required: ["application/problem+json"]
- required: ["application/problem+xml"]

nlgov:problem-schema-members:
severity: warn
message: "These fields are required: status, title and detail. Additionally, only type and instance are allowed."
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Laten we nog testen of het toevoegen van {{value}} ook laat zien dat bijvoorbeeld het veld "extra" niet toegestaan is.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

{{value}} gaf [object Object].
{{error}} geeft een mooie zin met de value erin, zoals Property "extra" is not expected to be here.
Hiermee kwam ook aan het licht dat de test een niveau te hoog aan het kijken was voor de verwachte velden.

given: $..[responses][?(@property && @property.match(/(4|5)\d\d/))].content[?(@property=="application/problem+json" || @property=="application/problem+xml")]..schema
then:
function: schema
functionOptions:
schema:
type: object
properties:
status: {}
title: {}
detail: {}
type: {}
instance: {}
required:
- status
- title
- detail
additionalProperties: false

nlgov:property-casing:
severity: warn
given:
Expand Down
5 changes: 5 additions & 0 deletions linter/testcases/error-type-illegal-field/expected-output.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@

/testcases/error-type-illegal-field/openapi.json
60:42 warning nlgov:problem-schema-members These fields are required: status, title and detail. Additionally, only type and instance are allowed. paths./openapi.json.get.responses[400].content.application/problem+json.schema

✖ 1 problem (0 errors, 1 warning, 0 infos, 0 hints)
98 changes: 98 additions & 0 deletions linter/testcases/error-type-illegal-field/openapi.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
{
"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": "[email protected]"
},
"version": "1.0.0"
},
"servers": [
{
"url": "https://example.com/api/v1"
}
],
"security": [
{
"default": []
}
],
"tags": [
{
"name": "openapi"
}
],
"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"
}
}
}
},
"400": {
"description": "NOK",
"content": {
"application/problem+json": {
"schema": {
"type": "object",
"properties": {
"status": { "type": "integer" },
"title": { "type": "string" },
"detail": {"type": "string" },
"extra": {"type": "string" }
},
"required": ["status", "title", "detail"],
"additionalProperties": false
}
}
}
}
},
"security": [
{
"default": []
}
]
}
}
},
"components": {
"schemas": {
},
"securitySchemes": {
"default": {
"type": "oauth2",
"flows": {
"implicit": {
"authorizationUrl": "https://test.com",
"scopes": {}
}
}
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@

/testcases/error-type-missing-required/openapi.json
60:42 warning nlgov:problem-schema-members These fields are required: status, title and detail. Additionally, only type and instance are allowed. paths./openapi.json.get.responses[400].content.application/problem+json.schema

✖ 1 problem (0 errors, 1 warning, 0 infos, 0 hints)
96 changes: 96 additions & 0 deletions linter/testcases/error-type-missing-required/openapi.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
{
"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": "[email protected]"
},
"version": "1.0.0"
},
"servers": [
{
"url": "https://example.com/api/v1"
}
],
"security": [
{
"default": []
}
],
"tags": [
{
"name": "openapi"
}
],
"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"
}
}
}
},
"400": {
"description": "NOK",
"content": {
"application/problem+json": {
"schema": {
"type": "object",
"properties": {
"status": { "type": "integer" },
"title": { "type": "string" }
},
"required": ["status", "title"],
"additionalProperties": false
}
}
}
}
},
"security": [
{
"default": []
}
]
}
}
},
"components": {
"schemas": {
},
"securitySchemes": {
"default": {
"type": "oauth2",
"flows": {
"implicit": {
"authorizationUrl": "https://test.com",
"scopes": {}
}
}
}
}
}
}
31 changes: 31 additions & 0 deletions sections/designRules.md
Original file line number Diff line number Diff line change
Expand Up @@ -491,6 +491,37 @@ An API is as good as the accompanying documentation. The documentation has to be
</dl>
</div>

<div class="rule" id="/core/problem-details" data-type="technical">
<p class="rulelab">Include problem details with error responses</p>
<dl>
<dt>Statement</dt>
<dd>
<p>Error responses with HTTP status codes <code>4xx</code> or <code>5xx</code> MUST use either <code>application/problem+json</code> or <code>application/problem+xml</code> as the <code>Content-Type</code> header, and the response body MUST conform to the structure defined in [[rfc9457]].</p>
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hoe verhouden de media types zich tot content negotiation? Wat als ik Accept: application/json heb meegestuurd in mijn request, maar een fout-response krijg? Wordt deze dan "overruled"? En wat als ik zowel JSON als XML problem responses ondersteun, wanneer krijg je welke? Etc etc...

<p>The following fields MUST be present: <code>status</code>, <code>title</code>, and <code>detail</code>. Additionally, only these fields MAY be present: <code>type</code> and <code>instance</code>.</p>
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Er moet ook ruimte zijn voor additionele details, bijv validatie-feedback van de request body waarbij je vaak per property een validatie-boodschap wil teruggeven. Dit zouden we ook kunnen standaardiseren of evt additionele properties toestaan.

</dd>
<dt>Rationale</dt>
<dd>
<p>Providing problem details in a machine-readable format aids automation and debugging. By using a common error format, APIs do not need to define their own or misuse existing HTTP status codes.</p>
<div class="example">
The following example shows the head and body of a detailed error response.
<pre><code class="http">HTTP/1.1 404 Not Found
Content-Type: application/problem+json</code><code class="json">{
"type": "https://example.org/probs/not-found",
"title": "Resource Not Found",
"status": 404,
"detail": "No building found with id 12345.",
"instance": "/gebouwen/12345"
}
</code></pre>
</div>
</dd>
<dt>How to test</dt>
<dd>
Verify all responses with status code <code>4xx</code> or <code>5xx</code> have <code>Content-Type</code> set to <code>application/problem+json</code> or <code>application/problem+xml</code>.
</dd>
</dl>
</div>

## Versioning

Changes in APIs are inevitable. APIs should therefore always be versioned, facilitating the transition between changes.
Expand Down
Loading