Skip to content

Commit e63f6c7

Browse files
committed
add support for $ref
1 parent 5092809 commit e63f6c7

File tree

6 files changed

+45
-56
lines changed

6 files changed

+45
-56
lines changed

src/lib/SchemaForm.svelte

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212
import { FileNone, type CommonComponentParameters, type ValidationErrors } from './types/CommonComponentParameters';
1313
import Enum from './editors/Enum.svelte';
1414
import Array from './editors/Array.svelte';
15-
import { incr, nullOptionalsAllowed } from './utilities.js';
15+
import { incr, nullOptionalsAllowed, resolveRefs } from './utilities.js';
1616
import Boolean from './editors/Boolean.svelte';
1717
import Number from './editors/Number.svelte';
1818
import { errorMapper } from "./errorMapper";
@@ -34,15 +34,17 @@
3434
export let components: Record<string, new (...args: any[]) => any> = {};
3535
export let componentContext: Record<string, unknown> = {};
3636
37+
let resolvedSchema: any = resolveRefs(schema, schema)
38+
3739
const dispatch = createEventDispatcher();
3840
3941
let validationErrors = {} as ValidationErrors;
4042
4143
const revalidate = (newValue?: any) => {
42-
const validate = validator(nullOptionalsAllowed(schema), { includeErrors: true, allErrors: true, allowUnusedKeywords: true });
44+
const validate = validator(nullOptionalsAllowed(resolvedSchema), { includeErrors: true, allErrors: true, allowUnusedKeywords: true });
4345
const validatorResult = validate(newValue || value);
4446
validationErrors = Object.fromEntries(
45-
(validate.errors || []).map(ve => errorMapper(schema, value, ve.keywordLocation, ve.instanceLocation))
47+
(validate.errors || []).map(ve => errorMapper(resolvedSchema, value, ve.keywordLocation, ve.instanceLocation))
4648
);
4749
}
4850
@@ -87,7 +89,7 @@
8789
pathChanged,
8890
validationErrors,
8991
containerParent: "none",
90-
containerReadOnly: schema.readOnly || false,
92+
containerReadOnly: resolvedSchema.readOnly || false,
9193
showErrors,
9294
collapsible,
9395
idx: incr()
@@ -140,4 +142,4 @@
140142
};
141143
</script>
142144

143-
<SubSchemaForm {params} {value} bind:schema />
145+
<SubSchemaForm {params} {value} root={schema} bind:schema={resolvedSchema} />

src/lib/SubSchemaForm.svelte

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,21 @@
11
<script lang="ts">
22
import type { CommonComponentParameters } from "./types/CommonComponentParameters";
33
import { editorForSchema } from "./types/schema";
4+
import { resolveRefs } from "./utilities";
45
export let params: CommonComponentParameters;
56
export let schema: any;
7+
export let root: any;
68
export let value: any;
79
let { components } = params;
10+
$: resolvedSchema = resolveRefs(root, schema);
811
912
let typeComponent: any;
1013
1114
let component: new (...args: any[]) => any;
12-
$: component = components[editorForSchema(schema)];
15+
$: component = components[editorForSchema(resolvedSchema)];
1316
</script>
1417

15-
<svelte:component this={component} {params} {value} bind:schema />
18+
<svelte:component this={component} {params} {value} {root} bind:schema={resolvedSchema} />
1619

1720

1821

src/lib/editors/ListDetail.svelte

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
import { tick } from "svelte";
99
export let params: CommonComponentParameters;
1010
export let schema: any;
11+
export let root: any;
1112
export let value: any[];
1213
1314
interface SortSpec {
@@ -188,6 +189,7 @@
188189
}}
189190
value={selectedValue}
190191
bind:schema={schema.items}
192+
{root}
191193
/>
192194
</div>
193195
{/if}

src/lib/editors/Object.svelte

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
import { stringToHtml } from "../utilities.js";
77
export let params: CommonComponentParameters;
88
export let schema: any;
9+
export let root: any;
910
export let value: any;
1011
1112
let propnames: string[];
@@ -50,6 +51,7 @@
5051
}}
5152
value={value?.[propName]}
5253
bind:schema={schema.properties[propName]}
54+
{root}
5355
/>
5456
{/each}
5557
{/if}

src/lib/schemasafe-extended.d.ts

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,13 @@
11
declare module "@exodus/schemasafe/src/pointer" {
2-
let get: (obj: any, pointer: string, objpath?: string) => any;
3-
export { get };
4-
};
2+
export let get: (obj: any, pointer: string, objpath?: string) => any;
3+
export function resolveReference(
4+
root: Record<string, unknown>,
5+
schemas: Set<string>,
6+
ref: string,
7+
base?: string
8+
): [
9+
schema: Record<string, unknown>,
10+
root: Record<string, unknown>,
11+
basePath: string
12+
][];
13+
}

src/lib/utilities.ts

Lines changed: 17 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
1-
import { get } from "lodash-es";
2-
1+
import { get, cloneDeep } from "lodash-es";
2+
import { resolveReference } from "@exodus/schemasafe/src/pointer";
3+
34
export const upTo = (str: string, match: string, start?: number) => {
45
const pos = str.indexOf(match, start);
56
return pos < 0 ? str.substring(start || 0) : str.substring(start || 0, pos);
@@ -39,30 +40,34 @@ export function camelToTitle(camel: string): string {
3940
return camelToWords(camel).replace(/[a-z]/i, (ltr) => ltr.toUpperCase())
4041
}
4142

43+
export function resolveRefs(root: Record<string, unknown>, schema: Record<string, unknown>): Record<string, unknown> {
44+
return schema['$ref']
45+
? resolveReference(root, new Set(Object.keys(root.definitions as Record<string, unknown>[])), schema['$ref'] as string)[0][0]
46+
: cloneDeep(schema)
47+
}
48+
4249
/** manipulate the schema to allow any optional property to have a null value
4350
* which is appropriate for form input */
44-
export function nullOptionalsAllowed(schema: object): object {
51+
export function nullOptionalsAllowed(schema: Record<string, unknown>): object {
4552
if (schema === null || schema === undefined) schema = {};
46-
let newSchema = deepCopy(schema);
47-
nullOptionalsAllowedApply(newSchema as Record<string, unknown>);
48-
return newSchema;
53+
return nullOptionalsAllowedApply(schema, schema);
4954
}
5055

51-
function nullOptionalsAllowedApply(schema: Record<string, unknown>) {
56+
function nullOptionalsAllowedApply(root: Record<string, unknown>, unresolvedSchema: Record<string, unknown>): Record<string, unknown> {
57+
const schema = resolveRefs(root, unresolvedSchema)
5258
let req = (schema['required'] || []) as Array<string>;
53-
if (schema['$ref']) return;
5459
switch (schema['type']) {
5560
case 'object':
5661
const properties = (schema['properties'] || {}) as Record<string, unknown>;
5762
for (let prop in properties) {
5863
if (req.indexOf(prop) < 0) {
59-
nullOptionalsAllowedApply(properties[prop] as Record<string, unknown>);
64+
properties[prop] = nullOptionalsAllowedApply(root, properties[prop] as Record<string, unknown>);
6065
}
6166
}
6267
break;
6368
case 'array':
6469
const items = (schema['items'] || {}) as Record<string, unknown>;
65-
nullOptionalsAllowedApply(items);
70+
schema['items'] = nullOptionalsAllowedApply(root, items);
6671
if (items['oneOf'] && !(items['oneOf'] as any[]).some(subschema => subschema["type"] == "null")) {
6772
(items['oneOf'] as any[]).push({ type: 'null' });
6873
}
@@ -80,44 +85,10 @@ function nullOptionalsAllowedApply(schema: Record<string, unknown>) {
8085
const defns = schema['definitions'] as Record<string, unknown>;
8186
if (defns) {
8287
for (let defn in defns) {
83-
nullOptionalsAllowedApply(defns[defn] as Record<string, unknown>);
88+
defns[defn] = nullOptionalsAllowedApply(root, defns[defn] as Record<string, unknown>);
8489
}
8590
}
86-
}
87-
88-
export function deepCopy(obj: object): object {
89-
var copy;
90-
91-
// Handle the 3 simple types, and null or undefined
92-
if (null == obj || "object" != typeof obj) return obj;
93-
94-
// Handle Date
95-
if (obj instanceof Date) {
96-
copy = new Date();
97-
copy.setTime(obj.getTime());
98-
return copy;
99-
}
100-
101-
// Handle Array
102-
if (obj instanceof Array) {
103-
copy = [];
104-
for (var i = 0, len = obj.length; i < len; i++) {
105-
copy[i] = deepCopy(obj[i]);
106-
}
107-
return copy;
108-
}
109-
110-
// Handle Object
111-
if (obj instanceof Object) {
112-
copy = {} as Record<string, unknown>;
113-
const recObj = obj as Record<string, unknown>;
114-
for (var attr in recObj) {
115-
if (recObj.hasOwnProperty(attr)) copy[attr] = deepCopy(recObj[attr] as object);
116-
}
117-
return copy;
118-
}
119-
120-
throw new Error("Unable to copy obj! Its type isn't supported.");
91+
return schema;
12192
}
12293

12394
let incrVal = 0;

0 commit comments

Comments
 (0)