Skip to content
Original file line number Diff line number Diff line change
@@ -0,0 +1,168 @@
import testRule from './__helpers__/testRule';
import { DiagnosticSeverity } from '@stoplight/types';

const validDocument = {
components: {
schemas: {
ApiError: {
properties: {
badRequestDetail: {
$ref: '#/components/schemas/BadRequestDetail',
},
detail: { type: 'string' },
},
},
BadRequestDetail: {
properties: {
fields: {
type: 'array',
items: {
$ref: '#/components/schemas/FieldViolation',
},
},
},
},
FieldViolation: {
properties: {
description: { type: 'string' },
field: { type: 'string' },
},
required: ['description', 'field'],
},
},
},
};

testRule('xgen-IPA-114-api-error-has-bad-request-detail', [
{
name: 'valid ApiError schema passes validation',
document: validDocument,
errors: [],
},
{
name: 'missing properties in ApiError fails',
document: {
components: {
schemas: {
ApiError: {},
},
},
},
errors: [
{
code: 'xgen-IPA-114-api-error-has-bad-request-detail',
message: 'ApiError schema must have badRequestDetail field.',
path: ['components', 'schemas', 'ApiError'],
severity: DiagnosticSeverity.Warning,
},
],
},
{
name: 'missing badRequestDetail field fails',
document: {
components: {
schemas: {
ApiError: {
properties: {
detail: { type: 'string' },
},
},
},
},
},
errors: [
{
code: 'xgen-IPA-114-api-error-has-bad-request-detail',
message: 'ApiError schema must have badRequestDetail field.',
path: ['components', 'schemas', 'ApiError'],
severity: DiagnosticSeverity.Warning,
},
],
},
{
name: 'badRequestDetail without fields property fails',
document: {
components: {
schemas: {
ApiError: {
properties: {
badRequestDetail: {
properties: {
someOtherProperty: { type: 'string' },
},
},
},
},
},
},
},
errors: [
{
code: 'xgen-IPA-114-api-error-has-bad-request-detail',
message: 'badRequestDetail must include an array of fields.',
path: ['components', 'schemas', 'ApiError'],
severity: DiagnosticSeverity.Warning,
},
],
},
{
name: 'fields not being an array fails',
document: {
components: {
schemas: {
ApiError: {
properties: {
badRequestDetail: {
properties: {
fields: {
type: 'object',
},
},
},
},
},
},
},
},
errors: [
{
code: 'xgen-IPA-114-api-error-has-bad-request-detail',
message: 'badRequestDetail must include an array of fields.',
path: ['components', 'schemas', 'ApiError'],
severity: DiagnosticSeverity.Warning,
},
],
},
{
name: 'missing description or field properties fails',
document: {
components: {
schemas: {
ApiError: {
properties: {
badRequestDetail: {
properties: {
fields: {
type: 'array',
properties: {
// Missing description and field properties
otherProperty: { type: 'string' },
},
},
},
},
},
},
},
},
},
errors: [
{
code: 'xgen-IPA-114-api-error-has-bad-request-detail',
message: 'Each field must include description and field properties.',
path: ['components', 'schemas', 'ApiError'],
severity: DiagnosticSeverity.Warning,
},
],
},
]);
16 changes: 16 additions & 0 deletions tools/spectral/ipa/rulesets/IPA-114.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@

functions:
- IPA114ErrorResponsesReferToApiError
- IPA114ApiErrorHasBadRequestDetail

rules:
xgen-IPA-114-error-responses-refer-to-api-error:
Expand All @@ -16,3 +17,18 @@ rules:
given: '$.paths[*][*].responses[?(@property.match(/^[45]\d\d$/))]'
then:
function: 'IPA114ErrorResponsesReferToApiError'
xgen-IPA-114-api-error-has-bad-request-detail:
description: |
ApiError schema should have badRequestDetail field with proper structure.

##### Implementation details
Rule checks that:
- ApiError schema has badRequestDetail field
- badRequestDetail must include an array of fields
- Each field must include description and field properties
- This rule does not allow exceptions
message: '{{error}} https://mdb.link/mongodb-atlas-openapi-validation#xgen-IPA-114-api-error-has-bad-request-detail'
severity: warn
given: $.components.schemas.ApiError
then:
function: 'IPA114ApiErrorHasBadRequestDetail'
12 changes: 12 additions & 0 deletions tools/spectral/ipa/rulesets/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -615,6 +615,18 @@ APIs must return ApiError when errors occur
##### Implementation details
This rule checks that all 4xx and 5xx error responses reference the ApiError schema.

#### xgen-IPA-114-api-error-has-bad-request-detail

![warn](https://img.shields.io/badge/warning-yellow)
ApiError schema should have badRequestDetail field with proper structure.

##### Implementation details
Rule checks that:
- ApiError schema has badRequestDetail field
- badRequestDetail must include an array of fields
- Each field must include description and field properties
- This rule does not allow exceptions



### IPA-117
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
import { collectAdoption, collectAndReturnViolation, handleInternalError } from './utils/collectionUtils.js';

const RULE_NAME = 'xgen-IPA-114-api-error-has-bad-request-detail';

/**
* Verifies that ApiError schema has badRequestDetail field with proper structure
*
* @param {object} input - The ApiError schema object
* @param {object} _ - Rule options (unused)
* @param {object} context - The context object containing path and document information
*/
export default (input, _, { path, documentInventory }) => {
const errors = checkViolationsAndReturnErrors(input, documentInventory, path);
if (errors.length > 0) {
return collectAndReturnViolation(path, RULE_NAME, errors);
}

collectAdoption(path, RULE_NAME);
};

/**
* Check for violations in ApiError schema structure
*
* @param {object} apiErrorSchema - The ApiError schema object to validate
* @param {object} documentInventory - Contains document information
* @param {Array} path - Path to the schema in the document
* @returns {Array} - Array of error objects
*/
function checkViolationsAndReturnErrors(apiErrorSchema, documentInventory, path) {
try {
// ApiError should have badRequestDetail property
if (!apiErrorSchema.properties?.badRequestDetail) {
return [
{
path,
message: 'ApiError schema must have badRequestDetail field.',
},
];
}

//badRequestDetail must include an array of fields
const badRequestDetail = apiErrorSchema.properties.badRequestDetail;
if (badRequestDetail.properties?.fields?.type !== 'array') {
return [
{
path,
message: 'badRequestDetail must include an array of fields.',
},
];
}

//Each field must include description and field properties
const fields = badRequestDetail.properties.fields;
if (!fields.items?.properties?.description && !fields.items?.properties?.field) {
return [
{
path,
message: 'Each field must include description and field properties.',
},
];
}

return [];
} catch (e) {
handleInternalError(RULE_NAME, path, e);
return [{ path, message: `Internal error during validation: ${e.message}` }];
}
}
Loading