diff --git a/tools/spectral/ipa/__tests__/IPA104ValidOperationID.test.js b/tools/spectral/ipa/__tests__/IPA104ValidOperationID.test.js new file mode 100644 index 0000000000..cff141067a --- /dev/null +++ b/tools/spectral/ipa/__tests__/IPA104ValidOperationID.test.js @@ -0,0 +1,70 @@ +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-104-valid-operation-id', [ + { + name: 'valid methods', + document: { + paths: { + '/groups/{groupId}/cluster/{clusterName}': { + get: { + operationId: 'getGroupCluster', + }, + }, + }, + }, + errors: [], + }, + // This test will be enable when the xgen-IPA-104-valid-operation-id is set to warning severity - CLOUDP-329722 + /* { + name: 'invalid methods', + document: { + paths: { + '/api/atlas/v2/groups/{groupId}/accessList/{entryValue}/status': { + get: { + operationId: 'getProjectIpAccessListStatus', + }, + }, + '/api/atlas/v2/groups/{groupId}/dataFederation/{tenantName}/limits/{limitName}': { + get: { + operationId: 'returnFederatedDatabaseQueryLimit', + }, + }, + }, + }, + errors: [ + { + 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.', + 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.', + path: ['paths', '/api/atlas/v2/groups/{groupId}/dataFederation/{tenantName}/limits/{limitName}', 'get'], + severity: DiagnosticSeverity.Warning, + }, + ], + }, */ + { + name: 'invalid methods with exceptions', + document: { + paths: { + '/api/atlas/v2/groups/{groupId}/clusters/{clusterName}/index ': { + get: { + operationId: 'getRollingIndex', + 'x-xgen-IPA-exception': { + 'xgen-IPA-104-valid-operation-id': 'Reason', + }, + }, + }, + }, + }, + errors: [], + }, +]); diff --git a/tools/spectral/ipa/__tests__/IPA105ValidOperationID.test.js b/tools/spectral/ipa/__tests__/IPA105ValidOperationID.test.js new file mode 100644 index 0000000000..eda4656182 --- /dev/null +++ b/tools/spectral/ipa/__tests__/IPA105ValidOperationID.test.js @@ -0,0 +1,70 @@ +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-105-valid-operation-id', [ + { + name: 'valid methods', + document: { + paths: { + '/groups/{groupId}/clusters': { + get: { + operationId: 'listGroupClusters', + }, + }, + }, + }, + errors: [], + }, + // This test will be enable when the xgen-IPA-105-valid-operation-id is set to warning severity - CLOUDP-329722 + /* { + name: 'invalid methods', + document: { + paths: { + '/api/atlas/v2/orgs/{orgId}/teams/{teamId}/users': { + get: { + operationId: 'listTeamUsers', + }, + }, + '/api/atlas/v2/orgs/{orgId}/events': { + get: { + operationId: 'listOrganizationEvents', + }, + }, + }, + }, + errors: [ + { + 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.', + 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.', + path: ['paths', '/api/atlas/v2/orgs/{orgId}/events', 'get'], + severity: DiagnosticSeverity.Warning, + }, + ], + }, */ + { + name: 'invalid methods with exceptions', + document: { + paths: { + '/api/atlas/v2/groups/{groupId}/clusters/{clusterName}/outageSimulation': { + get: { + operationId: 'getOutageSimulation', + 'x-xgen-IPA-exception': { + 'xgen-IPA-105-valid-operation-id': 'Reason', + }, + }, + }, + }, + }, + errors: [], + }, +]); diff --git a/tools/spectral/ipa/rulesets/IPA-104.yaml b/tools/spectral/ipa/rulesets/IPA-104.yaml index cc63863991..c365c5ea82 100644 --- a/tools/spectral/ipa/rulesets/IPA-104.yaml +++ b/tools/spectral/ipa/rulesets/IPA-104.yaml @@ -8,6 +8,7 @@ functions: - IPA104GetResponseCodeShouldBe200OK - IPA104GetMethodResponseHasNoInputFields - IPA104GetMethodHasNoRequestBody + - IPA104ValidOperationID aliases: GetOperationObject: @@ -97,3 +98,21 @@ rules: given: '#GetOperationObject' then: function: 'IPA104GetMethodHasNoRequestBody' + xgen-IPA-104-valid-operation-id: + description: | + The Operation ID must start with the verb “get” 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. + If the resource is a singleton resource, the last noun may be the plural form of the collection identifier. + + ##### Implementation details + Rule checks for the following conditions: + - Applies only to GET methods on single resources or singleton resources + - Generates the expected OperationId given the resource identifier + - Confirms that the existing operationId is compliant with generated IPA Compliant OperationId + message: '{{error}} https://mdb.link/mongodb-atlas-openapi-validation#xgen-IPA-104-valid-operation-id' + severity: off + given: '#GetOperationObject' + then: + function: 'IPA104ValidOperationID' + functionOptions: + methodName: 'get' diff --git a/tools/spectral/ipa/rulesets/IPA-105.yaml b/tools/spectral/ipa/rulesets/IPA-105.yaml index 9e5ac87c2d..0b19ddd787 100644 --- a/tools/spectral/ipa/rulesets/IPA-105.yaml +++ b/tools/spectral/ipa/rulesets/IPA-105.yaml @@ -6,6 +6,7 @@ functions: - IPA105ListMethodHasNoRequestBody - IPA105EachResourceHasListMethod - IPA105ListMethodResponseIsGetMethodResponse + - IPA105ValidOperationID aliases: GetOperationObject: @@ -77,3 +78,21 @@ rules: then: field: '@key' function: 'IPA105ListMethodResponseIsGetMethodResponse' + xgen-IPA-105-valid-operation-id: + description: | + The Operation ID must start with the verb “list” 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, where the last noun is in plural form. + + ##### Implementation details + Rule checks for the following conditions: + - Applies only to GET methods on resource collection paths + - Ignores singleton resources + - Generates the expected OperationId given the resource identifier + - Confirms that the existing operationId is compliant with generated IPA Compliant OperationId + message: '{{error}} https://mdb.link/mongodb-atlas-openapi-validation#xgen-IPA-105-valid-operation-id' + severity: off + given: '#GetOperationObject' + then: + function: 'IPA105ValidOperationID' + functionOptions: + methodName: 'list' diff --git a/tools/spectral/ipa/rulesets/README.md b/tools/spectral/ipa/rulesets/README.md index 7d452db1eb..3c613bf5b3 100644 --- a/tools/spectral/ipa/rulesets/README.md +++ b/tools/spectral/ipa/rulesets/README.md @@ -139,6 +139,19 @@ Rule checks for the following conditions: - Applies only to GET methods on single resources or singleton resources - Verifies that the operation object does not contain a requestBody property +#### xgen-IPA-104-valid-operation-id + + `off` +The Operation ID must start with the verb “get” 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. +If the resource is a singleton resource, the last noun may be the plural form of the collection identifier. + +##### Implementation details +Rule checks for the following conditions: + - Applies only to GET methods on single resources or singleton resources + - Generates the expected OperationId given the resource identifier + - Confirms that the existing operationId is compliant with generated IPA Compliant OperationId + ### IPA-105 @@ -193,6 +206,19 @@ The response body of the List method should consist of the same resource object - Fails if the Get method doesn't have a schema reference or if the schemas don't match - Validation ignores resources without a Get method - Paths with `x-xgen-IPA-exception` for this rule are excluded from validation +#### xgen-IPA-105-valid-operation-id + + `off` +The Operation ID must start with the verb “list” 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, where the last noun is in plural form. + +##### Implementation details +Rule checks for the following conditions: + - Applies only to GET methods on resource collection paths + - Ignores singleton resources + - Generates the expected OperationId given the resource identifier + - Confirms that the existing operationId is compliant with generated IPA Compliant OperationId + ### IPA-106 diff --git a/tools/spectral/ipa/rulesets/functions/IPA104ValidOperationID.js b/tools/spectral/ipa/rulesets/functions/IPA104ValidOperationID.js new file mode 100644 index 0000000000..f2583bfbb4 --- /dev/null +++ b/tools/spectral/ipa/rulesets/functions/IPA104ValidOperationID.js @@ -0,0 +1,45 @@ +import { generateOperationID } from './utils/operationIdGeneration.js'; +import { collectAdoption, collectAndReturnViolation, collectException } from './utils/collectionUtils.js'; +import { hasException } from './utils/exceptions.js'; +import { + isSingleResourceIdentifier, + isResourceCollectionIdentifier, + isSingletonResource, + getResourcePathItems, + isCustomMethodIdentifier, +} from './utils/resourceEvaluation.js'; + +const RULE_NAME = 'xgen-IPA-104-valid-operation-id'; +const ERROR_MESSAGE = 'Invalid OperationID.'; + +export default (input, { methodName }, { path, documentInventory }) => { + const resourcePath = path[1]; + const oas = documentInventory.resolved; + const resourcePaths = getResourcePathItems(resourcePath, oas.paths); + + if ( + isCustomMethodIdentifier(resourcePath) || + (!isSingleResourceIdentifier(resourcePath) && + !(isResourceCollectionIdentifier(resourcePath) && isSingletonResource(resourcePaths))) + ) { + return; + } + + if (hasException(input, RULE_NAME)) { + collectException(input, RULE_NAME, path); + return; + } + + const expectedOperationId = generateOperationID(methodName, resourcePath); + if (expectedOperationId !== input.operationId) { + const errors = [ + { + path, + message: `${ERROR_MESSAGE} Found ${input.operationId}, expected ${expectedOperationId}.`, + }, + ]; + return collectAndReturnViolation(path, RULE_NAME, errors); + } + + return collectAdoption(path, RULE_NAME); +}; diff --git a/tools/spectral/ipa/rulesets/functions/IPA105ValidOperationID.js b/tools/spectral/ipa/rulesets/functions/IPA105ValidOperationID.js new file mode 100644 index 0000000000..f35a694631 --- /dev/null +++ b/tools/spectral/ipa/rulesets/functions/IPA105ValidOperationID.js @@ -0,0 +1,43 @@ +import { hasException } from './utils/exceptions.js'; +import { collectAdoption, collectAndReturnViolation, collectException } from './utils/collectionUtils.js'; +import { + getResourcePathItems, + isCustomMethodIdentifier, + isResourceCollectionIdentifier, + isSingletonResource, +} from './utils/resourceEvaluation.js'; +import { generateOperationID } from './utils/operationIdGeneration.js'; + +const RULE_NAME = 'xgen-IPA-105-valid-operation-id'; +const ERROR_MESSAGE = 'Invalid OperationID.'; + +export default (input, { methodName }, { path, documentInventory }) => { + const resourcePath = path[1]; + const oas = documentInventory.resolved; + + if ( + isCustomMethodIdentifier(resourcePath) || + !isResourceCollectionIdentifier(resourcePath) || + isSingletonResource(getResourcePathItems(resourcePath, oas.paths)) + ) { + return; + } + + if (hasException(input, RULE_NAME)) { + collectException(input, RULE_NAME, path); + return; + } + + const expectedOperationId = generateOperationID(methodName, resourcePath); + if (expectedOperationId !== input.operationId) { + const errors = [ + { + path, + message: `${ERROR_MESSAGE} Found ${input.operationId}, expected ${expectedOperationId}.`, + }, + ]; + return collectAndReturnViolation(path, RULE_NAME, errors); + } + + return collectAdoption(path, RULE_NAME); +}; diff --git a/tools/spectral/ipa/rulesets/functions/IPA106ValidOperationID.js b/tools/spectral/ipa/rulesets/functions/IPA106ValidOperationID.js index c50a5c6cdd..0b4c5039ad 100644 --- a/tools/spectral/ipa/rulesets/functions/IPA106ValidOperationID.js +++ b/tools/spectral/ipa/rulesets/functions/IPA106ValidOperationID.js @@ -9,8 +9,7 @@ import { import { generateOperationID } from './utils/operationIdGeneration.js'; const RULE_NAME = 'xgen-IPA-106-valid-operation-id'; -const ERROR_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.'; +const ERROR_MESSAGE = 'Invalid OperationID.'; export default (input, { methodName }, { path, documentInventory }) => { const resourcePath = path[1]; diff --git a/tools/spectral/ipa/rulesets/functions/IPA107ValidOperationID.js b/tools/spectral/ipa/rulesets/functions/IPA107ValidOperationID.js index 526b2a4660..5e95184694 100644 --- a/tools/spectral/ipa/rulesets/functions/IPA107ValidOperationID.js +++ b/tools/spectral/ipa/rulesets/functions/IPA107ValidOperationID.js @@ -10,8 +10,7 @@ import { import { generateOperationID } from './utils/operationIdGeneration.js'; const RULE_NAME = 'xgen-IPA-107-valid-operation-id'; -const ERROR_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.'; +const ERROR_MESSAGE = 'Invalid OperationID.'; export default (input, { methodName }, { path, documentInventory }) => { const resourcePath = path[1]; @@ -33,9 +32,6 @@ export default (input, { methodName }, { path, documentInventory }) => { const expectedOperationID = generateOperationID(methodName, resourcePath); if (expectedOperationID !== input.operationId) { - console.log( - `${input.operationId}, ${expectedOperationID}, ${resourcePath}, ${input.deprecated ? 'TRUE' : 'FALSE'}, ${(resourcePath, input['x-xgen-owner-team'])}` - ); const errors = [ { path, diff --git a/tools/spectral/ipa/rulesets/functions/IPA108ValidOperationID.js b/tools/spectral/ipa/rulesets/functions/IPA108ValidOperationID.js index 03aa2eab2c..3f4dfef420 100644 --- a/tools/spectral/ipa/rulesets/functions/IPA108ValidOperationID.js +++ b/tools/spectral/ipa/rulesets/functions/IPA108ValidOperationID.js @@ -4,8 +4,7 @@ import { isCustomMethodIdentifier, isSingleResourceIdentifier } from './utils/re import { generateOperationID } from './utils/operationIdGeneration.js'; const RULE_NAME = 'xgen-IPA-108-valid-operation-id'; -const ERROR_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.'; +const ERROR_MESSAGE = 'Invalid OperationID.'; export default (input, { methodName }, { path }) => { const resourcePath = path[1];