diff --git a/tools/spectral/ipa/__tests__/IPA104ValidOperationID.test.js b/tools/spectral/ipa/__tests__/IPA104ValidOperationID.test.js index cff141067a..5794894f5c 100644 --- a/tools/spectral/ipa/__tests__/IPA104ValidOperationID.test.js +++ b/tools/spectral/ipa/__tests__/IPA104ValidOperationID.test.js @@ -38,14 +38,14 @@ testRule('xgen-IPA-104-valid-operation-id', [ { code: 'xgen-IPA-104-valid-operation-id', message: - 'Invalid OperationID. The Operation ID must start with the verb “get” and should be followed by a noun or compound noun. The noun(s) should be the collection identifiers from the resource identifier in singular form.', + 'Invalid OperationID. ', path: ['paths', '/api/atlas/v2/groups/{groupId}/accessList/{entryValue}/status', 'get'], severity: DiagnosticSeverity.Warning, }, { code: 'xgen-IPA-104-valid-operation-id', message: - 'Invalid OperationID. The Operation ID must start with the verb “get” and should be followed by a noun or compound noun. The noun(s) should be the collection identifiers from the resource identifier in singular form.', + 'Invalid OperationID. ', path: ['paths', '/api/atlas/v2/groups/{groupId}/dataFederation/{tenantName}/limits/{limitName}', 'get'], severity: DiagnosticSeverity.Warning, }, diff --git a/tools/spectral/ipa/__tests__/IPA105ValidOperationID.test.js b/tools/spectral/ipa/__tests__/IPA105ValidOperationID.test.js index eda4656182..4b057da052 100644 --- a/tools/spectral/ipa/__tests__/IPA105ValidOperationID.test.js +++ b/tools/spectral/ipa/__tests__/IPA105ValidOperationID.test.js @@ -38,14 +38,14 @@ testRule('xgen-IPA-105-valid-operation-id', [ { code: 'xgen-IPA-105-valid-operation-id', message: - 'Invalid OperationID. The Operation ID must start with the verb “list” and should be followed by a noun or compound noun. The noun(s) should be the collection identifiers from the resource identifier in singular form, where the last noun is in plural form.', + 'Invalid OperationID. ', path: ['paths', '/api/atlas/v2/groups/{groupId}/databaseUsers/{username}/certs', 'get'], severity: DiagnosticSeverity.Warning, }, { code: 'xgen-IPA-105-valid-operation-id', message: - 'Invalid OperationID. The Operation ID must start with the verb “list” and should be followed by a noun or compound noun. The noun(s) should be the collection identifiers from the resource identifier in singular form, where the last noun is in plural form.', + 'Invalid OperationID. ', path: ['paths', '/api/atlas/v2/orgs/{orgId}/events', 'get'], severity: DiagnosticSeverity.Warning, }, diff --git a/tools/spectral/ipa/__tests__/IPA106ValidOperationID.test.js b/tools/spectral/ipa/__tests__/IPA106ValidOperationID.test.js index 4d03acc02b..717622120c 100644 --- a/tools/spectral/ipa/__tests__/IPA106ValidOperationID.test.js +++ b/tools/spectral/ipa/__tests__/IPA106ValidOperationID.test.js @@ -38,14 +38,14 @@ testRule('xgen-IPA-106-valid-operation-id', [ { code: 'xgen-IPA-106-valid-operation-id', message: - 'Invalid OperationID. The Operation ID must start with the verb “create” and should be followed by a noun or compound noun. The noun(s) in the Operation ID should be the collection identifiers from the resource identifier in singular form. ', + 'Invalid OperationID. ', path: ['paths', '/api/atlas/v2/groups/{groupId}/access', 'post'], severity: DiagnosticSeverity.Warning, }, { code: 'xgen-IPA-106-valid-operation-id', message: - 'Invalid OperationID. The Operation ID must start with the verb “create” and should be followed by a noun or compound noun. The noun(s) in the Operation ID should be the collection identifiers from the resource identifier in singular form. ', + 'Invalid OperationID. ', path: ['paths', '/api/atlas/v2/groups/{groupId}/invites', 'post'], severity: DiagnosticSeverity.Warning, }, diff --git a/tools/spectral/ipa/__tests__/IPA107ValidOperationID.test.js b/tools/spectral/ipa/__tests__/IPA107ValidOperationID.test.js index a157366ba2..abc58e5fce 100644 --- a/tools/spectral/ipa/__tests__/IPA107ValidOperationID.test.js +++ b/tools/spectral/ipa/__tests__/IPA107ValidOperationID.test.js @@ -43,14 +43,14 @@ testRule('xgen-IPA-107-valid-operation-id', [ { code: 'xgen-IPA-107-valid-operation-id', message: - 'Invalid OperationID. The Operation ID must start with the verb “update” and should be followed by a noun or compound noun. The noun(s) in the Operation ID should be the collection identifiers from the resource identifier in singular form. For singleton resources - the last noun may be in plural form.', + 'Invalid OperationID. ', path: ['paths', '/api/atlas/v2/groups/{groupId}/limits/{limitName}', 'patch'], severity: DiagnosticSeverity.Warning, }, { code: 'xgen-IPA-107-valid-operation-id', message: - 'Invalid OperationID. The Operation ID must start with the verb “update” and should be followed by a noun or compound noun. The noun(s) in the Operation ID should be the collection identifiers from the resource identifier in singular form. For singleton resources - the last noun may be in plural form.', + 'Invalid OperationID. ', path: ['paths', '/api/atlas/v2/groups/{groupId}/settings', 'put'], severity: DiagnosticSeverity.Warning, }, diff --git a/tools/spectral/ipa/__tests__/IPA108ValidOperationID.test.js b/tools/spectral/ipa/__tests__/IPA108ValidOperationID.test.js index 7a311e0f65..442da86325 100644 --- a/tools/spectral/ipa/__tests__/IPA108ValidOperationID.test.js +++ b/tools/spectral/ipa/__tests__/IPA108ValidOperationID.test.js @@ -37,15 +37,13 @@ testRule('xgen-IPA-108-valid-operation-id', [ errors: [ { code: 'xgen-IPA-108-valid-operation-id', - message: - 'Invalid OperationID. The Operation ID must start with the verb “delete” and should be followed by a noun or compound noun. The noun(s) in the Operation ID should be the collection identifiers from the resource identifier in singular form. ', + message: 'Invalid OperationID. ', path: ['paths', '/api/atlas/v2/groups/{groupId}/apiKeys/{apiUserId}', 'delete'], severity: DiagnosticSeverity.Warning, }, { code: 'xgen-IPA-108-valid-operation-id', - message: - 'Invalid OperationID. The Operation ID must start with the verb “delete” and should be followed by a noun or compound noun. The noun(s) in the Operation ID should be the collection identifiers from the resource identifier in singular form. ', + message: 'Invalid OperationID. ', path: ['paths', '/api/atlas/v2/groups/{groupId}', 'delete'], severity: DiagnosticSeverity.Warning, }, diff --git a/tools/spectral/ipa/__tests__/IPA109ValidOperationID.test.js b/tools/spectral/ipa/__tests__/IPA109ValidOperationID.test.js new file mode 100644 index 0000000000..378d29fa5b --- /dev/null +++ b/tools/spectral/ipa/__tests__/IPA109ValidOperationID.test.js @@ -0,0 +1,75 @@ +import testRule from './__helpers__/testRule'; + +// TODO: add tests for xgen-custom-method extension - CLOUDP-306294 +// TOOD: enable tests for invalid methods (after rules are upgraded to warning) - CLOUDP-329722 + +testRule('xgen-IPA-109-valid-operation-id', [ + { + name: 'valid methods', + document: { + paths: { + '/groups/{groupId}/clusters/{clusterName}:pause': { + post: { + operationId: 'pauseGroupCluster', + }, + }, + '/groups/{groupId}/clusters/{clusterName}:addNode': { + post: { + operationId: 'addGroupClusterNode', + }, + }, + }, + }, + errors: [], + }, + // This test will be enable when the xgen-IPA-109-valid-operation-id is set to warning severity - CLOUDP-329722 + /* { + name: 'invalid methods', + document: { + paths: { + '/api/atlas/v2/groups/{groupId}/clusters:search': { + post: { + operationId: 'searchClusters', + }, + }, + '/api/atlas/v2/groups/{groupId}:migrate': { + post: { + operationId: 'migrateProjectToAnotherOrg', + }, + }, + }, + }, + errors: [ + { + code: 'xgen-IPA-109-valid-operation-id', + message: + 'Invalid OperationID. ', + path: ['paths', '/api/atlas/v2/groups/{groupId}/clusters:search'], + severity: DiagnosticSeverity.Warning, + }, + { + code: 'xgen-IPA-109-valid-operation-id', + message: + 'Invalid OperationID. ', + path: ['paths', '/api/atlas/v2/groups/{groupId}:migrate'], + severity: DiagnosticSeverity.Warning, + }, + ], + }, */ + { + name: 'invalid methods with exceptions', + document: { + paths: { + '/api/atlas/v2/orgs/{orgId}/users/{userId}:addRole': { + post: { + operationId: 'addOrgRole', + 'x-xgen-IPA-exception': { + 'xgen-IPA-109-valid-operation-id': 'Reason', + }, + }, + }, + }, + }, + errors: [], + }, +]); diff --git a/tools/spectral/ipa/rulesets/IPA-109.yaml b/tools/spectral/ipa/rulesets/IPA-109.yaml index 7433a0da69..57ef4a97c2 100644 --- a/tools/spectral/ipa/rulesets/IPA-109.yaml +++ b/tools/spectral/ipa/rulesets/IPA-109.yaml @@ -5,6 +5,7 @@ functions: - IPA109EachCustomMethodMustBeGetOrPost - IPA109EachCustomMethodMustUseCamelCase - IPA109CustomMethodIdentifierFormat + - IPA109ValidOperationID rules: xgen-IPA-109-custom-method-must-be-GET-or-POST: @@ -56,3 +57,22 @@ rules: given: '$.paths[*]' then: function: 'IPA109CustomMethodIdentifierFormat' + xgen-IPA-109-valid-operation-id: + description: | + The Operation ID must start with the custom method verb (the custom method path section delimited by the colon (:) character) and should be followed by a noun or compound noun. + If the custom Operation ID has a verb + noun, the Operation ID should end with the noun. + The noun(s) in the Operation ID should be the collection identifiers from the resource identifier. + The noun(s) in the Operation ID should be the collection identifiers from the resource identifier in singular form, where the last noun: + - Is in plural form if the method applies to a collection of resources + - Is in singular form if the method applies to a single resource + + ##### Implementation details + Rule checks for the following conditions: + - Applies only to paths containing custom method identifiers (with colon format) + - Generates the expected OperationId given the resource identifier and the method name portion following the colon + - Confirms that the existing operationId is compliant with generated IPA Compliant OperationId + message: '{{error}} https://mdb.link/mongodb-atlas-openapi-validation#xgen-IPA-109-valid-operation-id' + severity: off + given: '$.paths[*]' + then: + function: 'IPA109ValidOperationID' diff --git a/tools/spectral/ipa/rulesets/README.md b/tools/spectral/ipa/rulesets/README.md index 3c613bf5b3..877c58ae12 100644 --- a/tools/spectral/ipa/rulesets/README.md +++ b/tools/spectral/ipa/rulesets/README.md @@ -496,6 +496,22 @@ Rule checks for the following conditions: - Fails if multiple colons appear in the path - Fails if other than an alphabetical character or a closing curly brace appears before a colon +#### xgen-IPA-109-valid-operation-id + + `off` +The Operation ID must start with the custom method verb (the custom method path section delimited by the colon (:) character) and should be followed by a noun or compound noun. +If the custom Operation ID has a verb + noun, the Operation ID should end with the noun. +The noun(s) in the Operation ID should be the collection identifiers from the resource identifier. +The noun(s) in the Operation ID should be the collection identifiers from the resource identifier in singular form, where the last noun: + - Is in plural form if the method applies to a collection of resources + - Is in singular form if the method applies to a single resource + +##### Implementation details +Rule checks for the following conditions: + - Applies only to paths containing custom method identifiers (with colon format) + - Generates the expected OperationId given the resource identifier and the method name portion following the colon + - Confirms that the existing operationId is compliant with generated IPA Compliant OperationId + ### IPA-110 diff --git a/tools/spectral/ipa/rulesets/functions/IPA109ValidOperationID.js b/tools/spectral/ipa/rulesets/functions/IPA109ValidOperationID.js new file mode 100644 index 0000000000..ec3a15f47d --- /dev/null +++ b/tools/spectral/ipa/rulesets/functions/IPA109ValidOperationID.js @@ -0,0 +1,46 @@ +import { hasException } from './utils/exceptions.js'; +import { collectAdoption, collectException, collectAndReturnViolation } from './utils/collectionUtils.js'; +import { isCustomMethodIdentifier, getCustomMethodName, stripCustomMethodName } from './utils/resourceEvaluation.js'; +import { generateOperationID } from './utils/operationIdGeneration.js'; + +const RULE_NAME = 'xgen-IPA-109-valid-operation-id'; +const ERROR_MESSAGE = 'Invalid OperationID.'; + +export default (input, _, { path }) => { + let resourcePath = path[1]; + const methodName = getCustomMethodName(resourcePath); + + if (!isCustomMethodIdentifier(resourcePath)) { + return; + } + + // TODO detect custom method extension - CLOUDP-306294 + + let obj; + if (input.post) { + obj = input.post; + } else if (input.get) { + obj = input.get; + } else { + return; + } + + if (hasException(obj, RULE_NAME)) { + collectException(obj, RULE_NAME, path); + return; + } + + const operationId = obj.operationId; + const expectedOperationID = generateOperationID(methodName, stripCustomMethodName(resourcePath)); + if (expectedOperationID !== operationId) { + const errors = [ + { + path, + message: `${ERROR_MESSAGE} Found ${operationId}, expected ${expectedOperationID}.`, + }, + ]; + return collectAndReturnViolation(path, RULE_NAME, errors); + } + + collectAdoption(path, RULE_NAME); +};