-
Couldn't load subscription status.
- Fork 14
CLOUDP-304960: Errors (APIs must return ApiError when errors occur) #630
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from 10 commits
Commits
Show all changes
11 commits
Select commit
Hold shift + click to select a range
1df15ce
CLOUDP-304960: Errors (APIs must return ApiError when errors occur)
yelizhenden-mdb 2d43dbc
docs fix
yelizhenden-mdb be645e7
additional checks
yelizhenden-mdb c2eb288
additional checks
yelizhenden-mdb d2ff6fa
test log
yelizhenden-mdb ca46436
Merge branch 'main' into CLOUDP-304960
yelizhenden-mdb 46f534e
test log
yelizhenden-mdb 408e836
test log
yelizhenden-mdb 7cc8e06
remove test logs
yelizhenden-mdb 281b54c
prettier fix
yelizhenden-mdb a717d0e
address the comments
yelizhenden-mdb File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
230 changes: 230 additions & 0 deletions
230
tools/spectral/ipa/__tests__/IPA114ErrorResponsesReferToApiError.test.js
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,230 @@ | ||
| import testRule from './__helpers__/testRule'; | ||
| import { DiagnosticSeverity } from '@stoplight/types'; | ||
|
|
||
| const components = { | ||
| responses: { | ||
| badRequest: { | ||
| content: { | ||
| 'application/json': { | ||
| schema: { | ||
| $ref: '#/components/schemas/ApiError', | ||
| }, | ||
| }, | ||
| }, | ||
| description: 'Bad Request.', | ||
| }, | ||
| notFound: { | ||
| content: { | ||
| 'application/json': { | ||
| schema: { | ||
| $ref: '#/components/schemas/ApiError', | ||
| }, | ||
| }, | ||
| }, | ||
| description: 'Not Found.', | ||
| }, | ||
| internalServerError: { | ||
| content: { | ||
| 'application/json': { | ||
| schema: { | ||
| $ref: '#/components/schemas/ApiError', | ||
| }, | ||
| }, | ||
| }, | ||
| description: 'Internal Error.', | ||
| }, | ||
| }, | ||
| schemas: { | ||
| ApiError: { | ||
| type: 'object', | ||
| properties: { | ||
| error: { | ||
| type: 'string', | ||
| }, | ||
| }, | ||
| }, | ||
| }, | ||
| }; | ||
|
|
||
| testRule('xgen-IPA-114-error-responses-refer-to-api-error', [ | ||
| { | ||
| name: 'valid error responses with ApiError schema', | ||
| document: { | ||
| paths: { | ||
| '/resources': { | ||
| get: { | ||
| responses: { | ||
| 400: { | ||
| $ref: '#/components/responses/badRequest', | ||
| }, | ||
| 404: { | ||
| $ref: '#/components/responses/notFound', | ||
| }, | ||
| 500: { | ||
| $ref: '#/components/responses/internalServerError', | ||
| }, | ||
| }, | ||
| }, | ||
| }, | ||
| }, | ||
| components: components, | ||
| }, | ||
| errors: [], | ||
| }, | ||
| { | ||
| name: 'invalid error responses missing schema', | ||
| document: { | ||
| paths: { | ||
| '/resources': { | ||
| get: { | ||
| responses: { | ||
| 400: { | ||
| content: { | ||
| 'application/json': {}, | ||
| }, | ||
| }, | ||
| }, | ||
| }, | ||
| }, | ||
| }, | ||
| }, | ||
| errors: [ | ||
| { | ||
| code: 'xgen-IPA-114-error-responses-refer-to-api-error', | ||
| message: '400 response must define a schema referencing ApiError.', | ||
| path: ['paths', '/resources', 'get', 'responses', '400', 'content', 'application/json'], | ||
| severity: DiagnosticSeverity.Warning, | ||
| }, | ||
| ], | ||
| }, | ||
| { | ||
| name: 'invalid error responses missing content', | ||
| document: { | ||
| paths: { | ||
| '/resources': { | ||
| get: { | ||
| responses: { | ||
| 400: {}, | ||
| }, | ||
| }, | ||
| }, | ||
| }, | ||
| }, | ||
| errors: [ | ||
| { | ||
| code: 'xgen-IPA-114-error-responses-refer-to-api-error', | ||
| message: '400 response must define content with ApiError schema reference.', | ||
| path: ['paths', '/resources', 'get', 'responses', '400'], | ||
| severity: DiagnosticSeverity.Warning, | ||
| }, | ||
| ], | ||
| }, | ||
| { | ||
| name: 'invalid error responses referencing wrong schema', | ||
| document: { | ||
| paths: { | ||
| '/resources': { | ||
| get: { | ||
| responses: { | ||
| 500: { | ||
| content: { | ||
| 'application/json': { | ||
| schema: { | ||
| $ref: '#/components/schemas/Error', | ||
| }, | ||
| }, | ||
| }, | ||
| }, | ||
| }, | ||
| }, | ||
| }, | ||
| }, | ||
| components: { | ||
| schemas: { | ||
| Error: { | ||
| type: 'object', | ||
| properties: { | ||
| message: { | ||
| type: 'string', | ||
| }, | ||
| }, | ||
| }, | ||
| }, | ||
| }, | ||
| }, | ||
| errors: [ | ||
| { | ||
| code: 'xgen-IPA-114-error-responses-refer-to-api-error', | ||
| message: '500 response must reference ApiError schema.', | ||
| path: ['paths', '/resources', 'get', 'responses', '500', 'content', 'application/json'], | ||
| severity: DiagnosticSeverity.Warning, | ||
| }, | ||
| ], | ||
| }, | ||
| { | ||
| name: 'invalid error responses with inline schema', | ||
| document: { | ||
| paths: { | ||
| '/resources': { | ||
| get: { | ||
| responses: { | ||
| 404: { | ||
| content: { | ||
| 'application/json': { | ||
| schema: { | ||
| type: 'object', | ||
| properties: { | ||
| error: { | ||
| type: 'string', | ||
| }, | ||
| }, | ||
| }, | ||
| }, | ||
| }, | ||
| }, | ||
| }, | ||
| }, | ||
| }, | ||
| }, | ||
| }, | ||
| errors: [ | ||
| { | ||
| code: 'xgen-IPA-114-error-responses-refer-to-api-error', | ||
| message: '404 response must reference ApiError schema.', | ||
| path: ['paths', '/resources', 'get', 'responses', '404', 'content', 'application/json'], | ||
| severity: DiagnosticSeverity.Warning, | ||
| }, | ||
| ], | ||
| }, | ||
| { | ||
| name: 'error responses with exception', | ||
| document: { | ||
| paths: { | ||
| '/resources': { | ||
| get: { | ||
| responses: { | ||
| 400: { | ||
| content: { | ||
| 'application/json': { | ||
| schema: { | ||
| type: 'object', | ||
| }, | ||
| 'x-xgen-IPA-exception': { | ||
| 'xgen-IPA-114-error-responses-refer-to-api-error': 'Reason', | ||
| }, | ||
| }, | ||
| }, | ||
| }, | ||
| 500: { | ||
| 'x-xgen-IPA-exception': { | ||
| 'xgen-IPA-114-error-responses-refer-to-api-error': 'Reason', | ||
| }, | ||
| }, | ||
| }, | ||
| }, | ||
| }, | ||
| }, | ||
| }, | ||
| errors: [], | ||
| }, | ||
| ]); |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,18 @@ | ||
| # IPA-114: Errors | ||
| # http://go/ipa/114 | ||
|
|
||
| functions: | ||
| - IPA114ErrorResponsesReferToApiError | ||
|
|
||
| rules: | ||
| xgen-IPA-114-error-responses-refer-to-api-error: | ||
| description: | | ||
| APIs must return ApiError when errors occur | ||
|
|
||
| ##### Implementation details | ||
| This rule checks that all 4xx and 5xx error responses reference the ApiError schema. | ||
| message: '{{error}} https://mdb.link/mongodb-atlas-openapi-validation#xgen-IPA-114-error-responses-refer-to-api-error' | ||
| severity: warn | ||
| given: '$.paths[*][*].responses[?(@property.match(/^[45]\d\d$/))]' | ||
| then: | ||
| function: 'IPA114ErrorResponsesReferToApiError' |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
89 changes: 89 additions & 0 deletions
89
tools/spectral/ipa/rulesets/functions/IPA114ErrorResponsesReferToApiError.js
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,89 @@ | ||
| import { hasException } from './utils/exceptions.js'; | ||
| import { | ||
| collectAdoption, | ||
| collectAndReturnViolation, | ||
| collectException, | ||
| handleInternalError, | ||
| } from './utils/collectionUtils.js'; | ||
| import { resolveObject } from './utils/componentUtils.js'; | ||
| import { getSchemaNameFromRef } from './utils/methodUtils.js'; | ||
|
|
||
| const RULE_NAME = 'xgen-IPA-114-error-responses-refer-to-api-error'; | ||
|
|
||
| /** | ||
| * Verifies that 4xx and 5xx responses reference the ApiError schema | ||
| * | ||
| * @param {object} input - The response object to check | ||
| * @param {object} _ - Rule options (unused) | ||
| * @param {object} context - The context object containing path and document information | ||
| */ | ||
| export default (input, _, { path, documentInventory }) => { | ||
| const oas = documentInventory.unresolved; | ||
| const apiResponseObject = resolveObject(oas, path); | ||
| const errorCode = path[path.length - 1]; | ||
|
|
||
| // Check for exception at response level | ||
| if (hasException(apiResponseObject, RULE_NAME)) { | ||
| collectException(apiResponseObject, RULE_NAME, path); | ||
| return; | ||
| } | ||
|
|
||
| const errors = checkViolationsAndReturnErrors(apiResponseObject, oas, path, errorCode); | ||
| if (errors.length !== 0) { | ||
| return collectAndReturnViolation(path, RULE_NAME, errors); | ||
| } | ||
|
|
||
| collectAdoption(path, RULE_NAME); | ||
| }; | ||
|
|
||
| function checkViolationsAndReturnErrors(apiResponseObject, oas, path, errorCode) { | ||
| try { | ||
| const errors = []; | ||
| let content; | ||
|
|
||
| if (apiResponseObject.content) { | ||
| content = apiResponseObject.content; | ||
| } else if (apiResponseObject.$ref) { | ||
| const schemaName = getSchemaNameFromRef(apiResponseObject.$ref); | ||
| const responseSchema = resolveObject(oas, ['components', 'responses', schemaName]); | ||
| content = responseSchema.content; | ||
| } else { | ||
| return [{ path, message: `${errorCode} response must define content with ApiError schema reference.` }]; | ||
| } | ||
|
|
||
| for (const [mediaType, mediaTypeObj] of Object.entries(content)) { | ||
| if (!mediaType.endsWith('json')) { | ||
| continue; | ||
| } | ||
|
|
||
| if (hasException(mediaTypeObj, RULE_NAME)) { | ||
| collectException(mediaTypeObj, RULE_NAME, [...path, 'content', mediaType]); | ||
| continue; | ||
| } | ||
|
|
||
| const contentPath = [...path, 'content', mediaType]; | ||
|
|
||
| // Check if schema exists | ||
| if (!mediaTypeObj.schema) { | ||
| errors.push({ | ||
| path: contentPath, | ||
| message: `${errorCode} response must define a schema referencing ApiError.`, | ||
| }); | ||
| continue; | ||
| } | ||
|
|
||
| // Check if schema references ApiError | ||
| const schema = mediaTypeObj.schema; | ||
|
|
||
| if (!schema.$ref || !schema.$ref.endsWith('/ApiError')) { | ||
| errors.push({ | ||
| path: contentPath, | ||
| message: `${errorCode} response must reference ApiError schema.`, | ||
| }); | ||
| } | ||
| } | ||
| return errors; | ||
| } catch (e) { | ||
| handleInternalError(RULE_NAME, path, e); | ||
| } | ||
| } | ||
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.