diff --git a/package.json b/package.json index 9c3a0a0d..dd126106 100644 --- a/package.json +++ b/package.json @@ -46,7 +46,7 @@ }, "dependencies": { "@stoplight/json": "^3.20.1", - "@stoplight/json-schema-tree": "^2.3.0", + "@stoplight/json-schema-tree": "^3.0.0", "@stoplight/react-error-boundary": "^2.0.0", "@types/json-schema": "^7.0.7", "classnames": "^2.2.6", diff --git a/src/__fixtures__/references/allOfReference.json b/src/__fixtures__/references/allOfReference.json new file mode 100644 index 00000000..1784f8f9 --- /dev/null +++ b/src/__fixtures__/references/allOfReference.json @@ -0,0 +1,20 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "allOf": [ + { + "type": "object", + "properties": { + "AAAAA": { "allOf": [{ "description": "AAAAA", "type": "string" }, { "examples": ["AAAAA"] }] }, + "BBBBB": { + "allOf": [ + { + "$ref": "#/paths/~1operation/post/requestBody/content/application~1json/schema/allOf/0/properties/AAAAA/allOf/0", + "description": "BBBBB" + }, + { "examples": ["BBBBB"] } + ] + } + } + } + ] +} diff --git a/src/__fixtures__/references/fullAllOfReference.json b/src/__fixtures__/references/fullAllOfReference.json new file mode 100644 index 00000000..8bc91e8a --- /dev/null +++ b/src/__fixtures__/references/fullAllOfReference.json @@ -0,0 +1,40 @@ +{ + "paths": { + "/operation": { + "post": { + "summary": "operation", + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "allOf": [ + { + "type": "object", + "properties": { + "AAAAA": { "allOf": [{ "description": "AAAAA", "type": "string" }, { "examples": ["AAAAA"] }] }, + "BBBBB": { + "allOf": [ + { + "$ref": "#/paths/~1operation/post/requestBody/content/application~1json/schema/allOf/0/properties/AAAAA/allOf/0", + "description": "BBBBB" + }, + { "examples": ["BBBBB"] } + ] + } + } + } + ] + } + } + } + }, + "operationId": "operationId", + "security": [] + } + } + }, + "servers": [{ "url": "http://example.com" }], + "openapi": "3.1.0", + "info": { "title": "Test", "version": "version" } +} diff --git a/src/__stories__/Refs.tsx b/src/__stories__/Refs.tsx index c3ed58f0..88c27b1f 100644 --- a/src/__stories__/Refs.tsx +++ b/src/__stories__/Refs.tsx @@ -1,13 +1,16 @@ import { Story } from '@storybook/react'; -import { JSONSchema4 } from 'json-schema'; +import { JSONSchema4, JSONSchema7 } from 'json-schema'; import React from 'react'; import { JsonSchemaProps, JsonSchemaViewer } from '../components/JsonSchemaViewer'; +import { defaultResolver } from '../utils/refResolver'; const defaultSchema = require('../__fixtures__/default-schema.json'); const refSchema = require('../__fixtures__/references/base.json'); const nullRefSchema = require('../__fixtures__/references/nullish.json'); const brokenRefArraySchema = require('../__fixtures__/arrays/of-refs.json'); +const allOfRefSchema = require('../__fixtures__/references/allOfReference.json'); +const fullAllOfRefDoc = require('../__fixtures__/references/fullAllOfReference.json'); export default { component: JsonSchemaViewer, @@ -26,3 +29,9 @@ Nullish.args = { schema: nullRefSchema as JSONSchema4 }; export const Broken = Template.bind({}); Broken.args = { schema: brokenRefArraySchema as JSONSchema4 }; + +export const RefResolverAllOfRef = Template.bind({}); +RefResolverAllOfRef.args = { + schema: allOfRefSchema as JSONSchema7, + resolveRef: defaultResolver(fullAllOfRefDoc), +}; diff --git a/src/utils/refResolver.ts b/src/utils/refResolver.ts new file mode 100644 index 00000000..a729a389 --- /dev/null +++ b/src/utils/refResolver.ts @@ -0,0 +1,29 @@ +import { resolveInlineRef } from '@stoplight/json'; +import { Dictionary } from '@stoplight/types'; + +export type ReferenceInfo = { + source: string | null; + pointer: string | null; +}; + +export type ReferenceResolver = (ref: ReferenceInfo, propertyPath: string[] | null, currentObject?: object) => any; + +export const defaultResolver = + (contextObject: object): ReferenceResolver => + ({ pointer }, _, currentObject) => { + const activeObject = contextObject ?? currentObject; + + if (pointer === null) { + return null; + } + + if (pointer === '#') { + return activeObject; + } + + const resolved = resolveInlineRef(activeObject as Dictionary, pointer); + + if (resolved) return resolved; + + throw new ReferenceError(`Could not resolve '${pointer}`); + }; diff --git a/yarn.lock b/yarn.lock index f6d67686..9db3d6c4 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2383,22 +2383,22 @@ dependencies: eslint-config-prettier "^8.3.0" -"@stoplight/json-schema-merge-allof@^0.7.8": - version "0.7.8" - resolved "https://registry.yarnpkg.com/@stoplight/json-schema-merge-allof/-/json-schema-merge-allof-0.7.8.tgz#7efe5e0086dff433eb011f617e82f7295c3de061" - integrity sha512-JTDt6GYpCWQSb7+UW1P91IAp/pcLWis0mmEzWVFcLsrNgtUYK7JLtYYz0ZPSR4QVL0fJ0YQejM+MPq5iNDFO4g== +"@stoplight/json-schema-merge-allof@^0.8.0": + version "0.8.0" + resolved "https://registry.yarnpkg.com/@stoplight/json-schema-merge-allof/-/json-schema-merge-allof-0.8.0.tgz#62f8116f59d9df5a910d037b1965decd2efab472" + integrity sha512-g8e0s43v96Xbzvd8d6KKUuJTO16CS2oJglJrviUi8ASIUxzFvAJqTHWLtGmpTryisQopqg1evXGJfi0+164+Qw== dependencies: compute-lcm "^1.1.0" json-schema-compare "^0.2.2" lodash "^4.17.4" -"@stoplight/json-schema-tree@^2.3.0": - version "2.3.0" - resolved "https://registry.yarnpkg.com/@stoplight/json-schema-tree/-/json-schema-tree-2.3.0.tgz#cdad7f7571700016d8e0e5bad59d287e9bb782d0" - integrity sha512-IH4SYuvV0C4maYQEPftduDBG1qVV4hy5ZXzS9rgq3V7zCT34I7xVwX9Vmpl0mOQTh2IZaTcyPWXkngt21ShJaQ== +"@stoplight/json-schema-tree@^3.0.0": + version "3.0.0" + resolved "https://registry.yarnpkg.com/@stoplight/json-schema-tree/-/json-schema-tree-3.0.0.tgz#967cecda59b0dd8efa2275e580d5a8c71868cc3c" + integrity sha512-lxALWJtl7ev+iTNbW+QHDr66+nmspwrDyVEhNSIjWjp8Vd6+qYxqk3r9zr8Ktfrx4pREfG7iTq6rwNSxYdP+Nw== dependencies: "@stoplight/json" "^3.12.0" - "@stoplight/json-schema-merge-allof" "^0.7.8" + "@stoplight/json-schema-merge-allof" "^0.8.0" "@stoplight/lifecycle" "^2.3.2" "@types/json-schema" "^7.0.7" magic-error "0.0.1"