From 3b057dfe9e6c90ebc2f20125882dc1bf3f4d8813 Mon Sep 17 00:00:00 2001 From: Konrad Bosak Date: Fri, 11 Aug 2023 16:41:01 +0200 Subject: [PATCH 1/4] Stop evaluating initial value for empty array --- .../uniforms-bridge-json-schema/src/JSONSchemaBridge.ts | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/packages/uniforms-bridge-json-schema/src/JSONSchemaBridge.ts b/packages/uniforms-bridge-json-schema/src/JSONSchemaBridge.ts index 13677e51d..689eb98fc 100644 --- a/packages/uniforms-bridge-json-schema/src/JSONSchemaBridge.ts +++ b/packages/uniforms-bridge-json-schema/src/JSONSchemaBridge.ts @@ -273,13 +273,15 @@ export default class JSONSchemaBridge extends Bridge { } if (type === 'array') { + if (!field.minItems){ + return []; + } const item = this.getInitialValue(joinName(name, '$')); if (item === undefined) { return []; } - const length = field.minItems || 0; - return Array.from({ length }, () => item); + return Array.from({ length: field.minItems }, () => item); } if (type === 'object') { From 07bc65185d19261f4278be539e5161301b41a48b Mon Sep 17 00:00:00 2001 From: Adrian Mucha Date: Sun, 7 Apr 2024 11:24:15 +0200 Subject: [PATCH 2/4] Run prettier --- packages/uniforms-bridge-json-schema/src/JSONSchemaBridge.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/uniforms-bridge-json-schema/src/JSONSchemaBridge.ts b/packages/uniforms-bridge-json-schema/src/JSONSchemaBridge.ts index 689eb98fc..3e7dc430b 100644 --- a/packages/uniforms-bridge-json-schema/src/JSONSchemaBridge.ts +++ b/packages/uniforms-bridge-json-schema/src/JSONSchemaBridge.ts @@ -273,7 +273,7 @@ export default class JSONSchemaBridge extends Bridge { } if (type === 'array') { - if (!field.minItems){ + if (!field.minItems) { return []; } const item = this.getInitialValue(joinName(name, '$')); From 582369c915c8407f6b116cd463a25d5baf288c31 Mon Sep 17 00:00:00 2001 From: Konrad Bosak Date: Fri, 19 Apr 2024 16:11:54 +0200 Subject: [PATCH 3/4] Add JSONSchemaBridge tests for recursive arrays resolution --- .../__tests__/JSONSchemaBridge.ts | 49 ++++++++++++++++--- 1 file changed, 41 insertions(+), 8 deletions(-) diff --git a/packages/uniforms-bridge-json-schema/__tests__/JSONSchemaBridge.ts b/packages/uniforms-bridge-json-schema/__tests__/JSONSchemaBridge.ts index 40c4398b2..9c32a42f4 100644 --- a/packages/uniforms-bridge-json-schema/__tests__/JSONSchemaBridge.ts +++ b/packages/uniforms-bridge-json-schema/__tests__/JSONSchemaBridge.ts @@ -33,11 +33,25 @@ describe('JSONSchemaBridge', () => { firstName: { type: 'string', default: 'John' }, middleName: { $ref: '#/definitions/lastName' }, lastName: { type: 'string' }, - recursive: { + recursiveObject: { type: 'object', properties: { field: { type: 'string' }, - recursive: { $ref: '#/definitions/recursive' }, + recursiveObject: { $ref: '#/definitions/recursiveObject' }, + }, + }, + recursiveArray: { + type: 'array', + items: { $ref: '#/definitions/recursiveArray' }, + }, + bloodlineNode: { + type: 'object', + properties: { + name: { type: 'string' }, + children: { + type: 'array', + items: { $ref: '#/definitions/bloodlineNode' }, + }, }, }, }, @@ -135,7 +149,13 @@ describe('JSONSchemaBridge', () => { minimum: 1000, multipleOf: 3, }, - recursive: { $ref: '#/definitions/recursive' }, + recursiveObject: { $ref: '#/definitions/recursiveObject' }, + recursiveArray: { + $ref: '#/definitions/recursiveArray', + }, + bloodline: { + $ref: '#/definitions/bloodlineNode', + }, arrayWithAllOf: { type: 'array', items: { @@ -889,7 +909,9 @@ describe('JSONSchemaBridge', () => { 'complexNames', 'password', 'passwordNumeric', - 'recursive', + 'recursiveObject', + 'recursiveArray', + 'bloodline', 'arrayWithAllOf', 'nonObjectAnyOf', 'nonObjectAnyOfRequired', @@ -912,13 +934,24 @@ describe('JSONSchemaBridge', () => { ]); }); - it('works with recursive types', () => { - expect(bridge.getSubfields('recursive')).toEqual(['field', 'recursive']); - expect(bridge.getSubfields('recursive.recursive')).toEqual([ + it('works with recursive types - object', () => { + expect(bridge.getSubfields('recursiveObject')).toEqual([ + 'field', + 'recursiveObject', + ]); + expect(bridge.getSubfields('recursiveObject.recursiveObject')).toEqual([ 'field', - 'recursive', + 'recursiveObject', ]); }); + it('works with recursive types - array', () => { + expect(bridge.getSubfields('recursiveArray')).toEqual([]); + }); + + it('works with recursive types - mixed', () => { + expect(bridge.getSubfields('bloodline')).toEqual(['name', 'children']); + expect(bridge.getSubfields('bloodline.children')).toEqual([]); + }); it('works with primitives', () => { expect(bridge.getSubfields('personalData.firstName')).toEqual([]); From 4891bd9ee2e13bd61889d06c619b4c86d7b98a1e Mon Sep 17 00:00:00 2001 From: Konrad Bosak Date: Fri, 9 Aug 2024 13:12:50 +0200 Subject: [PATCH 4/4] detect cycle wip --- package.json | 1 + .../uniforms-bridge-json-schema/package.json | 1 + .../src/JSONSchemaBridge.ts | 42 +++++++++++++++++++ 3 files changed, 44 insertions(+) diff --git a/package.json b/package.json index b957156df..a2f0f0e86 100644 --- a/package.json +++ b/package.json @@ -52,6 +52,7 @@ "husky": "8.0.1", "invariant": "^2.0.0", "jest": "27.0.6", + "json-schema-traverse": "1.0.0", "lerna": "6.0.1", "lint-staged": "13.0.3", "lodash": "^4.0.0", diff --git a/packages/uniforms-bridge-json-schema/package.json b/packages/uniforms-bridge-json-schema/package.json index 4084529b0..dfc24cd90 100644 --- a/packages/uniforms-bridge-json-schema/package.json +++ b/packages/uniforms-bridge-json-schema/package.json @@ -34,6 +34,7 @@ ], "dependencies": { "invariant": "^2.0.0", + "json-schema-traverse": "^1.0.0", "lodash": "^4.0.0", "tslib": "^2.2.0", "uniforms": "^4.0.0-alpha.5" diff --git a/packages/uniforms-bridge-json-schema/src/JSONSchemaBridge.ts b/packages/uniforms-bridge-json-schema/src/JSONSchemaBridge.ts index 3e7dc430b..d69af998f 100644 --- a/packages/uniforms-bridge-json-schema/src/JSONSchemaBridge.ts +++ b/packages/uniforms-bridge-json-schema/src/JSONSchemaBridge.ts @@ -6,6 +6,8 @@ import lowerCase from 'lodash/lowerCase'; import memoize from 'lodash/memoize'; import upperFirst from 'lodash/upperFirst'; import { Bridge, UnknownObject, joinName } from 'uniforms'; +import traverse from "json-schema-traverse"; + function fieldInvariant(name: string, condition: boolean): asserts condition { invariant(condition, 'Field not found in schema: "%s"', name); @@ -410,4 +412,44 @@ export default class JSONSchemaBridge extends Bridge { getValidator() { return this.validator; } + + detectCycle() { + const schema = this.schema; + const visited = new Set(); + const detectCycle = (name: string) => { + if (visited.has(name)) { + return true; + } + visited.add(name); + const field = this.getField(name); + if (field.type === Object) { + return Object.keys(field.properties).some(detectCycle); + } + if (field.type === Array) { + return field.items.some(detectCycle); + } + return false; + }; + return (name: string) => detectCycle(name); + } + + private buildGraph() { + const schema; + const graph = new Map(); + + function pre(schema, jsonPointer, rootSchema, parentJSON, parentKeyword, parentSchema, indexOrProperty) { + if (schema && schema.$ref) { + const ref = schema.$ref; + const parentPointer = jsonPointer.split('/').slice(0, -2).join('/') || '#'; + if (!graph.has(parentPointer)) { + graph.set(parentPointer, []); + } + graph.get(parentPointer).push(removeHashPrefix(ref)); + } + } + + traverse(schema, { cb: { pre } }); + + return graph; + } }