From acadb647854dfc405d9a9ba4a8fc7d57a1cd7fe9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=ADdia=20Tarcza?= <100163235+diatrcz@users.noreply.github.com> Date: Tue, 30 Jun 2026 16:16:33 +0200 Subject: [PATCH 1/2] fix(ibm-validate-schema-example): support circular references in schemas MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Lídia Tarcza <100163235+diatrcz@users.noreply.github.com> --- .../src/functions/valid-schema-example.js | 68 +++++-------------- 1 file changed, 18 insertions(+), 50 deletions(-) diff --git a/packages/ruleset/src/functions/valid-schema-example.js b/packages/ruleset/src/functions/valid-schema-example.js index eb53d3e6..d9a0b4c5 100644 --- a/packages/ruleset/src/functions/valid-schema-example.js +++ b/packages/ruleset/src/functions/valid-schema-example.js @@ -4,11 +4,15 @@ */ const { validate } = require('jsonschema'); -const { validateSubschemas } = require('@ibm-cloud/openapi-ruleset-utilities'); -const { nestedSchemaKeys, LoggerFactory } = require('../utils'); +const { + validateSubschemas, + getResolvedSpec, +} = require('@ibm-cloud/openapi-ruleset-utilities'); +const { LoggerFactory } = require('../utils'); let ruleId; let logger; +let openapi; module.exports = function (schema, _opts, context) { if (!logger) { @@ -16,6 +20,7 @@ module.exports = function (schema, _opts, context) { logger = LoggerFactory.getInstance().getLogger(ruleId); } + openapi = getResolvedSpec(context); return validateSubschemas(schema, context.path, checkSchemaExamples); }; @@ -50,18 +55,20 @@ function checkSchemaExamples(schema, path) { function validateExamples(examples) { return examples .map(({ schema, example, path }) => { - if (hasUnresolvedRefs(schema)) { - logger.debug( - `Skipping example validation at path ${path.join('.')}: schema contains unresolved $ref references` - ); - // Skip validation for schemas with unresolved references. - return undefined; - } + // If the spec includes circular references, there may be unresolved + // references in the schema. The JSON Schema validator needs to be + // able to look those up, so include all of the components in the schema. + const schemaWithComponents = { + ...schema, + components: openapi.components, + }; // Setting required: true prevents undefined values from passing validation. - const { valid, errors } = validate(example, schema, { required: true }); + const { valid, errors } = validate(example, schemaWithComponents, { + required: true, + }); if (!valid) { - const message = getMessage(errors, example, schema); + const message = getMessage(errors, example, schemaWithComponents); return { message: `Schema example is not valid: ${message}`, path, @@ -71,45 +78,6 @@ function validateExamples(examples) { .filter(e => isDefined(e)); } -/** - * Recursively checks if a schema or any of its nested schemas contain unresolved $ref references. - * @param {object} schema - The schema to check - * @returns {boolean} - True if the schema contains unresolved $ref references - */ -function hasUnresolvedRefs(schema) { - if (!schema || typeof schema !== 'object') { - return false; - } - - if (schema.$ref) { - return true; - } - - // Recursively check nested schemas in common locations. - for (const key of nestedSchemaKeys) { - if (schema[key]) { - if (Array.isArray(schema[key])) { - // Check each item in arrays (allOf, anyOf, oneOf). - if (schema[key].some(item => hasUnresolvedRefs(item))) { - return true; - } - } else if (key === 'properties') { - // Check each property in properties object. - if (Object.values(schema[key]).some(prop => hasUnresolvedRefs(prop))) { - return true; - } - } else { - // Check single nested schema (items, additionalProperties, not). - if (hasUnresolvedRefs(schema[key])) { - return true; - } - } - } - } - - return false; -} - function isDefined(x) { return x !== undefined; } From e70bfca4cdaf34a4974c9b9dcb21756263182eee Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=ADdia=20Tarcza?= <100163235+diatrcz@users.noreply.github.com> Date: Tue, 30 Jun 2026 16:29:03 +0200 Subject: [PATCH 2/2] chore: add minor fixes MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Lídia Tarcza <100163235+diatrcz@users.noreply.github.com> --- .../src/functions/valid-schema-example.js | 1 - packages/ruleset/src/utils/index.js | 1 - packages/ruleset/src/utils/nested-schema-keys.js | 16 ---------------- 3 files changed, 18 deletions(-) delete mode 100644 packages/ruleset/src/utils/nested-schema-keys.js diff --git a/packages/ruleset/src/functions/valid-schema-example.js b/packages/ruleset/src/functions/valid-schema-example.js index d9a0b4c5..4ef9ae30 100644 --- a/packages/ruleset/src/functions/valid-schema-example.js +++ b/packages/ruleset/src/functions/valid-schema-example.js @@ -55,7 +55,6 @@ function checkSchemaExamples(schema, path) { function validateExamples(examples) { return examples .map(({ schema, example, path }) => { - // If the spec includes circular references, there may be unresolved // references in the schema. The JSON Schema validator needs to be // able to look those up, so include all of the components in the schema. diff --git a/packages/ruleset/src/utils/index.js b/packages/ruleset/src/utils/index.js index fd8b0447..5ea3ea17 100644 --- a/packages/ruleset/src/utils/index.js +++ b/packages/ruleset/src/utils/index.js @@ -18,7 +18,6 @@ module.exports = { isRequestBodyExploded: require('./is-requestbody-exploded'), LoggerFactory: require('./logger-factory'), mergeAllOfSchemaProperties: require('./merge-allof-schema-properties'), - nestedSchemaKeys: require('./nested-schema-keys'), operationMethods: require('./constants'), pathHasMinimallyRepresentedResource: require('./path-has-minimally-represented-resource'), pathMatchesRegexp: require('./path-matches-regexp'), diff --git a/packages/ruleset/src/utils/nested-schema-keys.js b/packages/ruleset/src/utils/nested-schema-keys.js deleted file mode 100644 index 3ef61583..00000000 --- a/packages/ruleset/src/utils/nested-schema-keys.js +++ /dev/null @@ -1,16 +0,0 @@ -/** - * Copyright 2017 - 2026 IBM Corporation. - * SPDX-License-Identifier: Apache2.0 - */ - -const nestedSchemaKeys = [ - 'items', - 'additionalProperties', - 'properties', - 'allOf', - 'anyOf', - 'oneOf', - 'not', -]; - -module.exports = nestedSchemaKeys;