Skip to content

Commit 2b1d005

Browse files
Implemented spectral lint rule that validates that at any time, an endpoint can only have one API version marked as 'upcoming'
1 parent dfb1d0a commit 2b1d005

File tree

2 files changed

+52
-0
lines changed

2 files changed

+52
-0
lines changed

tools/spectral/.spectral.yaml

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,18 @@ aliases:
1515
- "#OperationObject.parameters[?(@ && @.in)]"
1616
- "$.components.schemas[*]..properties[?(@ && @.type)]"
1717

18+
functions:
19+
- acceptHeaderUpcomingVersionLimit
20+
1821
rules:
22+
accept-header-upcoming-version-limit:
23+
description: Ensure that each operation has at most one upcoming API Accept header.
24+
message: "An operation must not have more than one upcoming API Accept header (format: application/vnd.atlas.YYYY-MM-DD.upcoming+format)."
25+
severity: error
26+
given: $.paths[*][*]
27+
then:
28+
function: "acceptHeaderUpcomingVersionLimit"
29+
1930
xgen-schema-name-pascal-case:
2031
description: OpenAPI Schema names should use PascalCase. PascalCase ensures consistency with OpenAPI generated code.
2132
message: "`{{property}}` name must follow PascalCase. Please verify if you have provided valid @Schema(name='') annotation"
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
module.exports = function (input) {
2+
// Get operationId from context, if this fails return an error
3+
const operationId = input.operationId;
4+
if (!operationId) {
5+
return;
6+
}
7+
8+
// List of errors
9+
const errors = [];
10+
11+
// Validate versions in 200 responses
12+
const responseErr = validateContent(operationId, "response", input?.responses?.[200]?.content);
13+
if (responseErr != null) errors.push(responseErr);
14+
15+
// Validate versions in requests
16+
const requestErr = validateContent(operationId, "request", input?.requestBody?.content);
17+
if (requestErr != null) errors.push(requestErr);
18+
19+
return errors.length > 0 ? errors : undefined;
20+
};
21+
22+
// Check for upcoming API Accept headers
23+
const upcomingRegex = /^application\/vnd\.atlas\.\d{4}-\d{2}-\d{2}\.upcoming\+.+$/;
24+
25+
function validateContent(operationId, section, content) {
26+
if (content == null) {
27+
return null;
28+
}
29+
30+
const contentTypes = Object.keys(content);
31+
const upcomingContentTypes = contentTypes.filter(k => upcomingRegex.test(k));
32+
// If there's less than or equal to one upcoming header then the operation is valid
33+
if (upcomingContentTypes.length <= 1) {
34+
return null;
35+
}
36+
37+
// Return an error message
38+
return ({
39+
message: `OperationId: ${operationId} - Found ${upcomingContentTypes.length} upcoming API Accept headers (section: ${section}): ${upcomingContentTypes.join(', ')}`,
40+
});
41+
}

0 commit comments

Comments
 (0)