Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 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
11 changes: 11 additions & 0 deletions tools/spectral/.spectral.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,18 @@ aliases:
- "#OperationObject.parameters[?(@ && @.in)]"
- "$.components.schemas[*]..properties[?(@ && @.type)]"

functions:
- acceptHeaderUpcomingVersionLimit

rules:
accept-header-upcoming-version-limit:
description: Ensure that each operation has at most one upcoming API Accept header.
message: "An operation must not have more than one upcoming API Accept header (format: application/vnd.atlas.YYYY-MM-DD.upcoming+format)."
severity: error
given: $.paths[*][*]
then:
function: "acceptHeaderUpcomingVersionLimit"

xgen-schema-name-pascal-case:
description: OpenAPI Schema names should use PascalCase. PascalCase ensures consistency with OpenAPI generated code.
message: "`{{property}}` name must follow PascalCase. Please verify if you have provided valid @Schema(name='') annotation"
Expand Down
41 changes: 41 additions & 0 deletions tools/spectral/functions/acceptHeaderUpcomingVersionLimit.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
module.exports = function (input) {
Copy link
Collaborator

Choose a reason for hiding this comment

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

can we have unit tests like we have for ipa rule sets?

https://github.com/mongodb/openapi/tree/main/tools/spectral/ipa/__tests__

Copy link
Member Author

Choose a reason for hiding this comment

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

I missed those, didn't know it was possible! Thanks

// Get operationId from context, if this fails return an error
const operationId = input.operationId;
if (!operationId) {
return;
}

// List of errors
const errors = [];

// Validate versions in 200 responses
const responseErr = validateContent(operationId, 'response', input?.responses?.[200]?.content);
if (responseErr != null) errors.push(responseErr);

// Validate versions in requests
const requestErr = validateContent(operationId, 'request', input?.requestBody?.content);
if (requestErr != null) errors.push(requestErr);

return errors.length > 0 ? errors : undefined;
};

// Check for upcoming API Accept headers
const upcomingRegex = /^application\/vnd\.atlas\.\d{4}-\d{2}-\d{2}\.upcoming\+.+$/;

function validateContent(operationId, section, content) {
if (content == null) {
return null;
}

const contentTypes = Object.keys(content);
const upcomingContentTypes = contentTypes.filter((k) => upcomingRegex.test(k));
// If there's less than or equal to one upcoming header then the operation is valid
if (upcomingContentTypes.length <= 1) {
return null;
}

// Return an error message
return {
message: `OperationId: ${operationId} - Found ${upcomingContentTypes.length} upcoming API Accept headers (section: ${section}): ${upcomingContentTypes.join(', ')}`,
};
}
Loading