From bc1bb6072cd062aadfcfa6f48ae9633515a96329 Mon Sep 17 00:00:00 2001 From: Yeliz Henden Date: Tue, 11 Mar 2025 15:56:20 +0000 Subject: [PATCH 1/4] IPA 106: Create : The response status code must be 201 Created --- ...eateResponseCodeShouldBe201Created.test.js | 117 ++++++++++++++++++ tools/spectral/ipa/rulesets/IPA-106.yaml | 7 ++ .../createResponseCodeShouldBe201Created.js | 52 ++++++++ 3 files changed, 176 insertions(+) create mode 100644 tools/spectral/ipa/__tests__/createResponseCodeShouldBe201Created.test.js create mode 100644 tools/spectral/ipa/rulesets/functions/createResponseCodeShouldBe201Created.js diff --git a/tools/spectral/ipa/__tests__/createResponseCodeShouldBe201Created.test.js b/tools/spectral/ipa/__tests__/createResponseCodeShouldBe201Created.test.js new file mode 100644 index 0000000000..ddff93637f --- /dev/null +++ b/tools/spectral/ipa/__tests__/createResponseCodeShouldBe201Created.test.js @@ -0,0 +1,117 @@ +import testRule from './__helpers__/testRule'; +import { DiagnosticSeverity } from '@stoplight/types'; + +testRule('xgen-IPA-106-create-method-response-code-is-201', [ + { + name: 'valid methods', + document: { + paths: { + '/resource': { + post: { + responses: { + 201: {}, + 400: {}, + 500: {}, + }, + }, + }, + '/resource/{id}/subresource': { + post: { + responses: { + 201: {}, + 400: {}, + 500: {}, + }, + }, + }, + '/resource/{id}:customMethod': { + post: { + responses: { + 200: {}, + 400: {}, + 500: {}, + }, + }, + }, + }, + }, + errors: [], + }, + { + name: 'invalid methods', + document: { + paths: { + '/resourceOne': { + post: { + responses: { + 200: {}, + 400: {}, + 500: {}, + }, + }, + }, + '/resourceTwo': { + post: { + responses: { + 400: {}, + 500: {}, + }, + }, + }, + '/resourceThree': { + post: { + responses: { + 201: {}, + 200: {}, + 400: {}, + 500: {}, + }, + }, + }, + }, + }, + errors: [ + { + code: 'xgen-IPA-106-create-method-response-code-is-201', + message: + 'The Create method must return a 201 Created response. This method either lacks a 201 Created response or defines a different 2xx status code. http://go/ipa/106', + path: ['paths', '/resourceOne', 'post'], + severity: DiagnosticSeverity.Warning, + }, + { + code: 'xgen-IPA-106-create-method-response-code-is-201', + message: + 'The Create method must return a 201 Created response. This method either lacks a 201 Created response or defines a different 2xx status code. http://go/ipa/106', + path: ['paths', '/resourceTwo', 'post'], + severity: DiagnosticSeverity.Warning, + }, + { + code: 'xgen-IPA-106-create-method-response-code-is-201', + message: + 'The Create method must return a 201 Created response. This method either lacks a 201 Created response or defines a different 2xx status code. http://go/ipa/106', + path: ['paths', '/resourceThree', 'post'], + severity: DiagnosticSeverity.Warning, + }, + ], + }, + { + name: 'invalid method with exception', + document: { + paths: { + '/resourceOne': { + post: { + responses: { + 200: {}, + 400: {}, + 500: {}, + }, + 'x-xgen-IPA-exception': { + 'xgen-IPA-106-create-method-response-code-is-201': 'Reason', + }, + }, + }, + }, + }, + errors: [], + }, +]); \ No newline at end of file diff --git a/tools/spectral/ipa/rulesets/IPA-106.yaml b/tools/spectral/ipa/rulesets/IPA-106.yaml index 238be3dbe9..28040f25d9 100644 --- a/tools/spectral/ipa/rulesets/IPA-106.yaml +++ b/tools/spectral/ipa/rulesets/IPA-106.yaml @@ -30,3 +30,10 @@ rules: then: field: '@key' function: 'createMethodRequestBodyIsGetResponse' + xgen-IPA-106-create-method-response-code-is-201: + description: 'Create methods must return a 201 Created response code. http://go/ipa/106' + message: '{{error}} http://go/ipa/106' + severity: warn + given: '$.paths[*].post' + then: + function: 'createMethodResponseCodeIs201Created' diff --git a/tools/spectral/ipa/rulesets/functions/createResponseCodeShouldBe201Created.js b/tools/spectral/ipa/rulesets/functions/createResponseCodeShouldBe201Created.js new file mode 100644 index 0000000000..1fdc7c90dc --- /dev/null +++ b/tools/spectral/ipa/rulesets/functions/createResponseCodeShouldBe201Created.js @@ -0,0 +1,52 @@ +import { isCustomMethodIdentifier } from './utils/resourceEvaluation.js'; +import { hasException } from './utils/exceptions.js'; +import { + collectAdoption, + collectAndReturnViolation, + collectException, + handleInternalError, +} from './utils/collectionUtils.js'; + +const RULE_NAME = 'xgen-IPA-106-create-method-response-code-is-201'; +const ERROR_MESSAGE = + 'The Create method must return a 201 Created response. This method either lacks a 201 Created response or defines a different 2xx status code.'; + +export default (input, _, { path}) => { + const resourcePath = path[1]; + + // Skip custom methods + if (isCustomMethodIdentifier(resourcePath)) { + 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 { + const responses = input.responses; + + // If there is no 201 response, return a violation + if (!responses || !responses['201']) { + return [{ path, message: ERROR_MESSAGE }]; + } + + // If there are other 2xx responses that are not 201, return a violation + if (Object.keys(responses).some((key) => key.startsWith('2') && key !== '201')) { + return [{ path, message: ERROR_MESSAGE }]; + } + + return []; + } catch (e) { + handleInternalError(RULE_NAME, path, e); + } +} \ No newline at end of file From 6eb895087a8c5d23bd2814464e42bd008bb986e2 Mon Sep 17 00:00:00 2001 From: Yeliz Henden Date: Wed, 12 Mar 2025 09:20:37 +0000 Subject: [PATCH 2/4] refactor fixes --- ...d.test.js => createMethodResponseCodeIs201Created.test.js} | 2 +- tools/spectral/ipa/rulesets/IPA-106.yaml | 1 + tools/spectral/ipa/rulesets/README.md | 1 + ...e201Created.js => createMethodResponseCodeIs201Created.js} | 4 ++-- 4 files changed, 5 insertions(+), 3 deletions(-) rename tools/spectral/ipa/__tests__/{createResponseCodeShouldBe201Created.test.js => createMethodResponseCodeIs201Created.test.js} (99%) rename tools/spectral/ipa/rulesets/functions/{createResponseCodeShouldBe201Created.js => createMethodResponseCodeIs201Created.js} (97%) diff --git a/tools/spectral/ipa/__tests__/createResponseCodeShouldBe201Created.test.js b/tools/spectral/ipa/__tests__/createMethodResponseCodeIs201Created.test.js similarity index 99% rename from tools/spectral/ipa/__tests__/createResponseCodeShouldBe201Created.test.js rename to tools/spectral/ipa/__tests__/createMethodResponseCodeIs201Created.test.js index ddff93637f..2cfc382847 100644 --- a/tools/spectral/ipa/__tests__/createResponseCodeShouldBe201Created.test.js +++ b/tools/spectral/ipa/__tests__/createMethodResponseCodeIs201Created.test.js @@ -114,4 +114,4 @@ testRule('xgen-IPA-106-create-method-response-code-is-201', [ }, errors: [], }, -]); \ No newline at end of file +]); diff --git a/tools/spectral/ipa/rulesets/IPA-106.yaml b/tools/spectral/ipa/rulesets/IPA-106.yaml index eaf4f44b45..6de7ab99b1 100644 --- a/tools/spectral/ipa/rulesets/IPA-106.yaml +++ b/tools/spectral/ipa/rulesets/IPA-106.yaml @@ -5,6 +5,7 @@ functions: - createMethodRequestBodyIsRequestSuffixedObject - createMethodShouldNotHaveQueryParameters - createMethodRequestBodyIsGetResponse + - createMethodResponseCodeIs201Created rules: xgen-IPA-106-create-method-request-body-is-request-suffixed-object: diff --git a/tools/spectral/ipa/rulesets/README.md b/tools/spectral/ipa/rulesets/README.md index 8cb53308eb..b74531fdfc 100644 --- a/tools/spectral/ipa/rulesets/README.md +++ b/tools/spectral/ipa/rulesets/README.md @@ -60,6 +60,7 @@ For rule definitions, see [IPA-106.yaml](https://github.com/mongodb/openapi/blob | xgen-IPA-106-create-method-request-body-is-get-method-response | Request body content of the Create method and response content of the Get method should refer to the same resource. readOnly/writeOnly properties will be ignored. http://go/ipa/106 | warn | +| xgen-IPA-106-create-method-response-code-is-201 | Create methods must return a 201 Created response code. http://go/ipa/106 | warn | ### IPA-108 diff --git a/tools/spectral/ipa/rulesets/functions/createResponseCodeShouldBe201Created.js b/tools/spectral/ipa/rulesets/functions/createMethodResponseCodeIs201Created.js similarity index 97% rename from tools/spectral/ipa/rulesets/functions/createResponseCodeShouldBe201Created.js rename to tools/spectral/ipa/rulesets/functions/createMethodResponseCodeIs201Created.js index 1fdc7c90dc..d9419ca290 100644 --- a/tools/spectral/ipa/rulesets/functions/createResponseCodeShouldBe201Created.js +++ b/tools/spectral/ipa/rulesets/functions/createMethodResponseCodeIs201Created.js @@ -11,7 +11,7 @@ const RULE_NAME = 'xgen-IPA-106-create-method-response-code-is-201'; const ERROR_MESSAGE = 'The Create method must return a 201 Created response. This method either lacks a 201 Created response or defines a different 2xx status code.'; -export default (input, _, { path}) => { +export default (input, _, { path }) => { const resourcePath = path[1]; // Skip custom methods @@ -49,4 +49,4 @@ function checkViolationsAndReturnErrors(input, path) { } catch (e) { handleInternalError(RULE_NAME, path, e); } -} \ No newline at end of file +} From 45b238f3e6c65a30c84d883ccfa75875db153f86 Mon Sep 17 00:00:00 2001 From: Yeliz Henden Date: Wed, 12 Mar 2025 11:45:43 +0000 Subject: [PATCH 3/4] Create method checks should apply resource collection URIs --- .../createMethodResponseCodeIs201Created.js | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/tools/spectral/ipa/rulesets/functions/createMethodResponseCodeIs201Created.js b/tools/spectral/ipa/rulesets/functions/createMethodResponseCodeIs201Created.js index d9419ca290..6a5940ec1e 100644 --- a/tools/spectral/ipa/rulesets/functions/createMethodResponseCodeIs201Created.js +++ b/tools/spectral/ipa/rulesets/functions/createMethodResponseCodeIs201Created.js @@ -1,4 +1,7 @@ -import { isCustomMethodIdentifier } from './utils/resourceEvaluation.js'; +import { + getResourcePathItems, + isCustomMethodIdentifier, isResourceCollectionIdentifier, isSingletonResource, +} from './utils/resourceEvaluation.js'; import { hasException } from './utils/exceptions.js'; import { collectAdoption, @@ -11,11 +14,13 @@ const RULE_NAME = 'xgen-IPA-106-create-method-response-code-is-201'; const ERROR_MESSAGE = 'The Create method must return a 201 Created response. This method either lacks a 201 Created response or defines a different 2xx status code.'; -export default (input, _, { path }) => { +export default (input, _, { path, documentInventory }) => { + const oas = documentInventory.resolved; const resourcePath = path[1]; + const resourcePaths = getResourcePathItems(resourcePath, oas.paths); - // Skip custom methods - if (isCustomMethodIdentifier(resourcePath)) { + const isResourceCollection = isResourceCollectionIdentifier(resourcePath) && !isSingletonResource(resourcePaths); + if (isCustomMethodIdentifier(resourcePath) || !isResourceCollection) { return; } From bf5d6cee06445b7691e789d550df0a5a930a558b Mon Sep 17 00:00:00 2001 From: Yeliz Henden Date: Wed, 12 Mar 2025 11:47:03 +0000 Subject: [PATCH 4/4] prettier fix --- .../functions/createMethodResponseCodeIs201Created.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/tools/spectral/ipa/rulesets/functions/createMethodResponseCodeIs201Created.js b/tools/spectral/ipa/rulesets/functions/createMethodResponseCodeIs201Created.js index 6a5940ec1e..b079469536 100644 --- a/tools/spectral/ipa/rulesets/functions/createMethodResponseCodeIs201Created.js +++ b/tools/spectral/ipa/rulesets/functions/createMethodResponseCodeIs201Created.js @@ -1,6 +1,8 @@ import { getResourcePathItems, - isCustomMethodIdentifier, isResourceCollectionIdentifier, isSingletonResource, + isCustomMethodIdentifier, + isResourceCollectionIdentifier, + isSingletonResource, } from './utils/resourceEvaluation.js'; import { hasException } from './utils/exceptions.js'; import {