UNPKG

@ibm-cloud/openapi-ruleset

Version:

Spectral ruleset for validating IBM Cloud services

121 lines (109 loc) 4.42 kB
/** * Copyright 2017 - 2023 IBM Corporation. * SPDX-License-Identifier: Apache2.0 */ const { isArraySchema, isPrimitiveSchema, validateSubschemas, } = require('@ibm-cloud/openapi-ruleset-utilities'); const { isJsonMimeType, isEmptyObjectSchema, isRefSiblingSchema, } = require('../utils'); /** * Checks to make sure that nested object schemas are defined using a $ref rather than * an inline object schema. * When the SDK generator encounters a nested schema (property, additionalProperties, array items, etc.) * that is defined as an inline object schema, it must refacter the inline object schema by moving it to * the "components.schemas" section and replacing it with a $ref. * This is done so that the schema property will have an appropriate datatype that defines * the nested object. When the generator refactors a schema, it computes a name for the new schema by * combining the name of the containing schema (the schema that contains the nested schema) together * with the name of the property (e.g. property "foo" within schema MyModel would yield a new schema name * of "MyModelFoo", the "items" field within array schema MyList would yield a new schema name of * "MyListItems", etc.). * To avoid these sub-optimal name computations, the recommendation is to define each * nested object schema property to use a schema reference to avoid the refactoring performed by the generator. * @param {*} schema a schema from the unresolved API definition * @param {*} path the array of path segments indicating the location of "schema" within the API definition * @returns an array containing the violations found or [] if no violations */ module.exports = function (schema, options, { path }) { return validateSubschemas(schema, path, checkForInlineObjectSchemas); }; /** * Checks the specified schema to determine if it's an inline object schema. * If yes and it is in a "reportable" location, then return a warning for it. * * @param {*} schema the schema to be checked * @param {*} path the array of path segments indicating the "location" of "schema" within the API definition * @returns an array containing the violations found or [] if no violations */ function checkForInlineObjectSchemas(schema, path) { // If "schema" is a $ref or fits the ref-sibling pattern, // then bail out now to avoid a warning. if (schema.$ref || isPrimitiveSchema(schema) || isRefSiblingSchema(schema)) { return []; } // We need to avoid returning a warning for certain locations within the API, // because those are locations where it's ok to have an inline object schema. const pathString = path.join('!'); // It's ok for a named schema to be an inline object schema (duh!). if (/^components!schemas![^!]+$/.test(pathString)) { return []; } // We don't want to return a warning for an individual element of an // allOf composition because an individual list element // doesn't provide the full context of the inline object schema. // Any violations would be reported against the schema that contains the allOf. if (/allOf![^!]+$/.test(pathString)) { return []; } // At this point, we have an inline schema. // We'll do some additional checks according to the type of schema, // and potentially return a warning. // Request body schema. if ( /requestBody!content![^!]+!schema$/.test(pathString) || /components!requestBodies![^!]+!content![^!]+!schema$/.test(pathString) ) { const mimetype = path[path.length - 2]; if (!isJsonMimeType(mimetype) || isArraySchema(schema)) { return []; } return [ { message: 'Request body schemas should be defined as a $ref to a named schema', path, }, ]; } else if (/responses![^!]+!content![^!]+!schema$/.test(pathString)) { // Response schema. const mimetype = path[path.length - 2]; if (!isJsonMimeType(mimetype) || isArraySchema(schema)) { return []; } return [ { message: 'Response schemas should be defined as a $ref to a named schema', path, }, ]; } else { // Must be a property schema. if (!isEmptyObjectSchema(schema) && !isArraySchema(schema)) { return [ { message: 'Nested objects should be defined as a $ref to a named schema', path, }, ]; } } return []; }