diff --git a/tools/spectral/ipa/__tests__/IPA113SingletonHasNoDeleteMethod.test.js b/tools/spectral/ipa/__tests__/IPA113SingletonHasNoDeleteMethod.test.js new file mode 100644 index 0000000000..ebd790a9c4 --- /dev/null +++ b/tools/spectral/ipa/__tests__/IPA113SingletonHasNoDeleteMethod.test.js @@ -0,0 +1,59 @@ +import testRule from './__helpers__/testRule'; +import { DiagnosticSeverity } from '@stoplight/types'; + +testRule('xgen-IPA-113-singleton-must-not-have-delete-method', [ + { + name: 'valid resources', + document: { + paths: { + '/resource': { + post: {}, + get: {}, + }, + '/resource/{exampleId}': { + get: {}, + patch: {}, + delete: {}, + }, + '/resource/{exampleId}/singleton': { + get: {}, + patch: {}, + }, + }, + }, + errors: [], + }, + { + name: 'invalid resource', + document: { + paths: { + '/resource/{exampleId}/singleton': { + delete: {}, + }, + }, + }, + errors: [ + { + code: 'xgen-IPA-113-singleton-must-not-have-delete-method', + message: + 'Singleton resources must not define the Delete standard method. If this is not a singleton resource, please implement all CRUDL methods.', + path: ['paths', '/resource/{exampleId}/singleton'], + severity: DiagnosticSeverity.Warning, + }, + ], + }, + { + name: 'invalid resources with exceptions', + document: { + paths: { + '/resource/{exampleId}/singleton': { + delete: {}, + 'x-xgen-IPA-exception': { + 'xgen-IPA-113-singleton-must-not-have-delete-method': 'reason', + }, + }, + }, + }, + errors: [], + }, +]); diff --git a/tools/spectral/ipa/__tests__/IPA113SingletonHasUpdateMethod.test.js b/tools/spectral/ipa/__tests__/IPA113SingletonHasUpdateMethod.test.js new file mode 100644 index 0000000000..ce29ed75b9 --- /dev/null +++ b/tools/spectral/ipa/__tests__/IPA113SingletonHasUpdateMethod.test.js @@ -0,0 +1,69 @@ +import testRule from './__helpers__/testRule'; +import { DiagnosticSeverity } from '@stoplight/types'; + +testRule('xgen-IPA-113-singleton-should-have-update-method', [ + { + name: 'valid resources', + document: { + paths: { + '/resource/{exampleId}/singletonOne': { + patch: {}, + }, + '/resource/{exampleId}/singletonTwo': { + put: {}, + }, + '/resource/{exampleId}/singletonThree': { + patch: {}, + put: {}, + }, + }, + }, + errors: [], + }, + { + name: 'invalid resource', + document: { + paths: { + '/resource/{exampleId}/singletonOne': { + get: {}, + }, + '/resource/{exampleId}/singletonTwo': {}, + }, + }, + errors: [ + { + code: 'xgen-IPA-113-singleton-should-have-update-method', + message: + 'Singleton resources should define the Update method. If this is not a singleton resource, please implement all CRUDL methods.', + path: ['paths', '/resource/{exampleId}/singletonOne'], + severity: DiagnosticSeverity.Warning, + }, + { + code: 'xgen-IPA-113-singleton-should-have-update-method', + message: + 'Singleton resources should define the Update method. If this is not a singleton resource, please implement all CRUDL methods.', + path: ['paths', '/resource/{exampleId}/singletonTwo'], + severity: DiagnosticSeverity.Warning, + }, + ], + }, + { + name: 'invalid resources with exceptions', + document: { + paths: { + '/resource/{exampleId}/singletonOne': { + get: {}, + 'x-xgen-IPA-exception': { + 'xgen-IPA-113-singleton-should-have-update-method': 'reason', + }, + }, + '/resource/{exampleId}/singletonTwo': { + 'x-xgen-IPA-exception': { + 'xgen-IPA-113-singleton-should-have-update-method': 'reason', + }, + }, + }, + }, + errors: [], + }, +]); diff --git a/tools/spectral/ipa/rulesets/IPA-113.yaml b/tools/spectral/ipa/rulesets/IPA-113.yaml index fd54f81db6..8424b71d1d 100644 --- a/tools/spectral/ipa/rulesets/IPA-113.yaml +++ b/tools/spectral/ipa/rulesets/IPA-113.yaml @@ -3,6 +3,8 @@ functions: - IPA113SingletonHasNoId + - IPA113SingletonHasNoDeleteMethod + - IPA113SingletonHasUpdateMethod rules: xgen-IPA-113-singleton-must-not-have-id: @@ -21,3 +23,29 @@ rules: given: '$.paths[*]' then: function: 'IPA113SingletonHasNoId' + xgen-IPA-113-singleton-must-not-have-delete-method: + description: | + Singleton resources must not define the Delete standard method. + + ##### Implementation details + Rule checks for the following conditions: + - Applies only to singleton resources + - Checks that the resource does not have a DELETE method defined + message: '{{error}} https://mdb.link/mongodb-atlas-openapi-validation#xgen-IPA-113-singleton-must-not-have-delete-method' + severity: warn + given: '$.paths[*]' + then: + function: 'IPA113SingletonHasNoDeleteMethod' + xgen-IPA-113-singleton-should-have-update-method: + description: | + Singleton resources should define the Update method. Validation for the presence of Get method is covered by IPA-104 (see [xgen-IPA-104-resource-has-GET](https://mdb.link/mongodb-atlas-openapi-validation#xgen-IPA-104-resource-has-GET)). + + ##### Implementation details + Rule checks for the following conditions: + - Applies only to singleton resources + - Checks that the resource has the PUT and/or PATCH methods defined + message: '{{error}} https://mdb.link/mongodb-atlas-openapi-validation#xgen-IPA-113-singleton-should-have-update-method' + severity: warn + given: '$.paths[*]' + then: + function: 'IPA113SingletonHasUpdateMethod' diff --git a/tools/spectral/ipa/rulesets/README.md b/tools/spectral/ipa/rulesets/README.md index b99bc25806..4455e6e6bd 100644 --- a/tools/spectral/ipa/rulesets/README.md +++ b/tools/spectral/ipa/rulesets/README.md @@ -482,6 +482,26 @@ Rule checks for the following conditions: - Verifies that no schema contains 'id' or '_id' properties in their object definitions - Fails if any response schema contains these identifier properties +#### xgen-IPA-113-singleton-must-not-have-delete-method + + ![warn](https://img.shields.io/badge/warning-yellow) +Singleton resources must not define the Delete standard method. + +##### Implementation details +Rule checks for the following conditions: + - Applies only to singleton resources + - Checks that the resource does not have a DELETE method defined + +#### xgen-IPA-113-singleton-should-have-update-method + + ![warn](https://img.shields.io/badge/warning-yellow) +Singleton resources should define the Update method. Validation for the presence of Get method is covered by IPA-104 (see [xgen-IPA-104-resource-has-GET](https://mdb.link/mongodb-atlas-openapi-validation#xgen-IPA-104-resource-has-GET)). + +##### Implementation details +Rule checks for the following conditions: + - Applies only to singleton resources + - Checks that the resource has the PUT and/or PATCH methods defined + ### IPA-123 diff --git a/tools/spectral/ipa/rulesets/functions/IPA113SingletonHasNoDeleteMethod.js b/tools/spectral/ipa/rulesets/functions/IPA113SingletonHasNoDeleteMethod.js new file mode 100644 index 0000000000..d53eb86646 --- /dev/null +++ b/tools/spectral/ipa/rulesets/functions/IPA113SingletonHasNoDeleteMethod.js @@ -0,0 +1,49 @@ +import { + getResourcePathItems, + isSingletonResource, + isResourceCollectionIdentifier, + hasDeleteMethod, +} from './utils/resourceEvaluation.js'; +import { hasException } from './utils/exceptions.js'; +import { + collectAdoption, + collectAndReturnViolation, + collectException, + handleInternalError, +} from './utils/collectionUtils.js'; + +const RULE_NAME = 'xgen-IPA-113-singleton-must-not-have-delete-method'; +const ERROR_MESSAGE = + 'Singleton resources must not define the Delete standard method. If this is not a singleton resource, please implement all CRUDL methods.'; + +export default (input, opts, { path, documentInventory }) => { + const oas = documentInventory.resolved; + const resourcePath = path[1]; + const resourcePathItems = getResourcePathItems(resourcePath, oas.paths); + + if (!(isResourceCollectionIdentifier(resourcePath) && isSingletonResource(resourcePathItems))) { + return; + } + + if (hasException(input, RULE_NAME)) { + collectException(input, RULE_NAME, path); + return; + } + + const errors = checkViolationsAndReturnErrors(input, path); + if (errors.length !== 0) { + return collectAndReturnViolation(path, RULE_NAME, errors); + } + collectAdoption(path, RULE_NAME); +}; + +function checkViolationsAndReturnErrors(input, path) { + try { + if (hasDeleteMethod(input)) { + return [{ path, message: ERROR_MESSAGE }]; + } + return []; + } catch (e) { + handleInternalError(RULE_NAME, path, e); + } +} diff --git a/tools/spectral/ipa/rulesets/functions/IPA113SingletonHasUpdateMethod.js b/tools/spectral/ipa/rulesets/functions/IPA113SingletonHasUpdateMethod.js new file mode 100644 index 0000000000..fa31bad858 --- /dev/null +++ b/tools/spectral/ipa/rulesets/functions/IPA113SingletonHasUpdateMethod.js @@ -0,0 +1,50 @@ +import { + getResourcePathItems, + isSingletonResource, + isResourceCollectionIdentifier, + hasPutMethod, + hasPatchMethod, +} from './utils/resourceEvaluation.js'; +import { hasException } from './utils/exceptions.js'; +import { + collectAdoption, + collectAndReturnViolation, + collectException, + handleInternalError, +} from './utils/collectionUtils.js'; + +const RULE_NAME = 'xgen-IPA-113-singleton-should-have-update-method'; +const ERROR_MESSAGE = + 'Singleton resources should define the Update method. If this is not a singleton resource, please implement all CRUDL methods.'; + +export default (input, opts, { path, documentInventory }) => { + const oas = documentInventory.resolved; + const resourcePath = path[1]; + const resourcePathItems = getResourcePathItems(resourcePath, oas.paths); + + if (!(isResourceCollectionIdentifier(resourcePath) && isSingletonResource(resourcePathItems))) { + return; + } + + if (hasException(input, RULE_NAME)) { + collectException(input, RULE_NAME, path); + return; + } + + const errors = checkViolationsAndReturnErrors(input, path); + if (errors.length !== 0) { + return collectAndReturnViolation(path, RULE_NAME, errors); + } + collectAdoption(path, RULE_NAME); +}; + +function checkViolationsAndReturnErrors(input, path) { + try { + if (!(hasPutMethod(input) || hasPatchMethod(input))) { + return [{ path, message: ERROR_MESSAGE }]; + } + return []; + } catch (e) { + handleInternalError(RULE_NAME, path, e); + } +} diff --git a/tools/spectral/ipa/rulesets/functions/utils/resourceEvaluation.js b/tools/spectral/ipa/rulesets/functions/utils/resourceEvaluation.js index 9912c9bcc3..361fe0caff 100644 --- a/tools/spectral/ipa/rulesets/functions/utils/resourceEvaluation.js +++ b/tools/spectral/ipa/rulesets/functions/utils/resourceEvaluation.js @@ -102,6 +102,36 @@ export function hasPostMethod(pathObject) { return Object.keys(pathObject).includes('post'); } +/** + * Checks if a path object has a DELETE method + * + * @param pathObject the path object to evaluate + * @returns {boolean} + */ +export function hasDeleteMethod(pathObject) { + return Object.keys(pathObject).includes('delete'); +} + +/** + * Checks if a path object has a PUT method + * + * @param pathObject the path object to evaluate + * @returns {boolean} + */ +export function hasPutMethod(pathObject) { + return Object.keys(pathObject).includes('put'); +} + +/** + * Checks if a path object has a PATCH method + * + * @param pathObject the path object to evaluate + * @returns {boolean} + */ +export function hasPatchMethod(pathObject) { + return Object.keys(pathObject).includes('patch'); +} + /** * Get all path items for a resource based on the path for the resource collection * For example, resource collection path '/resource' may return path items for ['/resource', '/resource{id}', '/resource{id}:customMethod']