@ng-formworks/core 17.2.7 → 18.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +834 -0
- package/esm2022/lib/framework-library/framework-library.service.mjs +175 -0
- package/esm2022/lib/framework-library/framework.mjs +15 -0
- package/esm2022/lib/framework-library/no-framework.component.mjs +18 -0
- package/esm2022/lib/framework-library/no-framework.module.mjs +27 -0
- package/esm2022/lib/framework-library/no.framework.mjs +19 -0
- package/esm2022/lib/json-schema-form.component.mjs +765 -0
- package/esm2022/lib/json-schema-form.module.mjs +26 -0
- package/esm2022/lib/json-schema-form.service.mjs +676 -0
- package/esm2022/lib/locale/de-validation-messages.mjs +60 -0
- package/esm2022/lib/locale/en-validation-messages.mjs +60 -0
- package/esm2022/lib/locale/es-validation-messages.mjs +57 -0
- package/esm2022/lib/locale/fr-validation-messages.mjs +60 -0
- package/esm2022/lib/locale/index.mjs +8 -0
- package/esm2022/lib/locale/it-validation-messages.mjs +60 -0
- package/esm2022/lib/locale/pt-validation-messages.mjs +60 -0
- package/esm2022/lib/locale/zh-validation-messages.mjs +60 -0
- package/esm2022/lib/shared/convert-schema-to-draft6.function.mjs +300 -0
- package/esm2022/lib/shared/form-group.functions.mjs +442 -0
- package/esm2022/lib/shared/format-regex.constants.mjs +54 -0
- package/esm2022/lib/shared/index.mjs +12 -0
- package/esm2022/lib/shared/json-schema.functions.mjs +784 -0
- package/esm2022/lib/shared/json.validators.mjs +884 -0
- package/esm2022/lib/shared/jsonpointer.functions.mjs +1026 -0
- package/esm2022/lib/shared/layout.functions.mjs +1158 -0
- package/esm2022/lib/shared/merge-schemas.function.mjs +345 -0
- package/esm2022/lib/shared/utility.functions.mjs +380 -0
- package/esm2022/lib/shared/validator.functions.mjs +584 -0
- package/esm2022/lib/widget-library/add-reference.component.mjs +61 -0
- package/esm2022/lib/widget-library/button.component.mjs +72 -0
- package/esm2022/lib/widget-library/checkbox.component.mjs +105 -0
- package/esm2022/lib/widget-library/checkboxes.component.mjs +147 -0
- package/esm2022/lib/widget-library/file.component.mjs +35 -0
- package/esm2022/lib/widget-library/hidden.component.mjs +54 -0
- package/esm2022/lib/widget-library/index.mjs +55 -0
- package/esm2022/lib/widget-library/input.component.mjs +119 -0
- package/esm2022/lib/widget-library/message.component.mjs +38 -0
- package/esm2022/lib/widget-library/none.component.mjs +21 -0
- package/esm2022/lib/widget-library/number.component.mjs +123 -0
- package/esm2022/lib/widget-library/one-of.component.mjs +35 -0
- package/esm2022/lib/widget-library/orderable.directive.mjs +123 -0
- package/esm2022/lib/widget-library/radios.component.mjs +153 -0
- package/esm2022/lib/widget-library/root.component.mjs +79 -0
- package/esm2022/lib/widget-library/section.component.mjs +199 -0
- package/esm2022/lib/widget-library/select-framework.component.mjs +51 -0
- package/esm2022/lib/widget-library/select-widget.component.mjs +46 -0
- package/esm2022/lib/widget-library/select.component.mjs +150 -0
- package/esm2022/lib/widget-library/submit.component.mjs +82 -0
- package/esm2022/lib/widget-library/tab.component.mjs +41 -0
- package/esm2022/lib/widget-library/tabs.component.mjs +108 -0
- package/esm2022/lib/widget-library/template.component.mjs +46 -0
- package/esm2022/lib/widget-library/textarea.component.mjs +104 -0
- package/esm2022/lib/widget-library/widget-library.module.mjs +42 -0
- package/esm2022/lib/widget-library/widget-library.service.mjs +226 -0
- package/esm2022/ng-formworks-core.mjs +5 -0
- package/esm2022/public_api.mjs +13 -0
- package/fesm2022/ng-formworks-core.mjs +10151 -0
- package/fesm2022/ng-formworks-core.mjs.map +1 -0
- package/index.d.ts +5 -0
- package/lib/framework-library/framework-library.service.d.ts +55 -0
- package/lib/framework-library/framework.d.ts +13 -0
- package/lib/framework-library/no-framework.component.d.ts +8 -0
- package/lib/framework-library/no-framework.module.d.ts +9 -0
- package/lib/framework-library/no.framework.d.ts +10 -0
- package/lib/json-schema-form.component.d.ts +218 -0
- package/lib/json-schema-form.module.d.ts +11 -0
- package/lib/json-schema-form.service.d.ts +115 -0
- package/lib/locale/de-validation-messages.d.ts +1 -0
- package/lib/locale/en-validation-messages.d.ts +1 -0
- package/lib/locale/es-validation-messages.d.ts +1 -0
- package/lib/locale/fr-validation-messages.d.ts +1 -0
- package/{src/lib/locale/index.ts → lib/locale/index.d.ts} +7 -7
- package/lib/locale/it-validation-messages.d.ts +1 -0
- package/lib/locale/pt-validation-messages.d.ts +1 -0
- package/lib/locale/zh-validation-messages.d.ts +1 -0
- package/lib/shared/convert-schema-to-draft6.function.d.ts +21 -0
- package/lib/shared/form-group.functions.d.ts +100 -0
- package/lib/shared/format-regex.constants.d.ts +19 -0
- package/lib/shared/index.d.ts +9 -0
- package/lib/shared/json-schema.functions.d.ts +193 -0
- package/lib/shared/json.validators.d.ts +441 -0
- package/lib/shared/jsonpointer.functions.d.ts +416 -0
- package/lib/shared/layout.functions.d.ts +83 -0
- package/lib/shared/merge-schemas.function.d.ts +19 -0
- package/lib/shared/utility.functions.d.ts +165 -0
- package/{src/lib/shared/validator.functions.ts → lib/shared/validator.functions.d.ts} +364 -601
- package/lib/widget-library/add-reference.component.d.ts +20 -0
- package/lib/widget-library/button.component.d.ts +21 -0
- package/lib/widget-library/checkbox.component.d.ts +24 -0
- package/lib/widget-library/checkboxes.component.d.ts +24 -0
- package/lib/widget-library/file.component.d.ts +21 -0
- package/lib/widget-library/hidden.component.d.ts +19 -0
- package/{src/lib/widget-library/index.ts → lib/widget-library/index.d.ts} +47 -56
- package/lib/widget-library/input.component.d.ts +22 -0
- package/lib/widget-library/message.component.d.ts +15 -0
- package/lib/widget-library/none.component.d.ts +8 -0
- package/lib/widget-library/number.component.d.ts +25 -0
- package/lib/widget-library/one-of.component.d.ts +21 -0
- package/lib/widget-library/orderable.directive.d.ts +41 -0
- package/lib/widget-library/radios.component.d.ts +23 -0
- package/lib/widget-library/root.component.d.ts +17 -0
- package/lib/widget-library/section.component.d.ts +19 -0
- package/lib/widget-library/select-framework.component.d.ts +18 -0
- package/lib/widget-library/select-widget.component.d.ts +18 -0
- package/lib/widget-library/select.component.d.ts +24 -0
- package/lib/widget-library/submit.component.d.ts +24 -0
- package/lib/widget-library/tab.component.d.ts +14 -0
- package/lib/widget-library/tabs.component.d.ts +20 -0
- package/lib/widget-library/template.component.d.ts +18 -0
- package/lib/widget-library/textarea.component.d.ts +21 -0
- package/lib/widget-library/widget-library.module.d.ts +31 -0
- package/lib/widget-library/widget-library.service.d.ts +22 -0
- package/package.json +66 -53
- package/{src/public_api.ts → public_api.d.ts} +9 -21
- package/karma.conf.js +0 -46
- package/ng-package.json +0 -11
- package/src/lib/framework-library/framework-library.service.ts +0 -195
- package/src/lib/framework-library/framework.ts +0 -11
- package/src/lib/framework-library/no-framework.component.html +0 -2
- package/src/lib/framework-library/no-framework.component.ts +0 -11
- package/src/lib/framework-library/no-framework.module.ts +0 -18
- package/src/lib/framework-library/no.framework.ts +0 -11
- package/src/lib/json-schema-form.component.html +0 -7
- package/src/lib/json-schema-form.component.ts +0 -809
- package/src/lib/json-schema-form.module.ts +0 -17
- package/src/lib/json-schema-form.service.ts +0 -907
- package/src/lib/locale/de-validation-messages.ts +0 -58
- package/src/lib/locale/en-validation-messages.ts +0 -58
- package/src/lib/locale/es-validation-messages.ts +0 -55
- package/src/lib/locale/fr-validation-messages.ts +0 -58
- package/src/lib/locale/it-validation-messages.ts +0 -58
- package/src/lib/locale/pt-validation-messages.ts +0 -58
- package/src/lib/locale/zh-validation-messages.ts +0 -58
- package/src/lib/locale-dates/en-US.ts +0 -5
- package/src/lib/shared/convert-schema-to-draft6.function.ts +0 -321
- package/src/lib/shared/form-group.functions.ts +0 -522
- package/src/lib/shared/format-regex.constants.ts +0 -73
- package/src/lib/shared/index.ts +0 -40
- package/src/lib/shared/json-schema.functions.ts +0 -788
- package/src/lib/shared/json.validators.ts +0 -878
- package/src/lib/shared/jsonpointer.functions.ts +0 -1012
- package/src/lib/shared/jspointer.functions.json.spec.ts +0 -103
- package/src/lib/shared/layout.functions.ts +0 -1233
- package/src/lib/shared/merge-schemas.function.ts +0 -329
- package/src/lib/shared/utility.functions.ts +0 -373
- package/src/lib/shared/validator.functions.spec.ts +0 -55
- package/src/lib/widget-library/add-reference.component.ts +0 -59
- package/src/lib/widget-library/button.component.ts +0 -54
- package/src/lib/widget-library/checkbox.component.ts +0 -74
- package/src/lib/widget-library/checkboxes.component.ts +0 -104
- package/src/lib/widget-library/file.component.ts +0 -36
- package/src/lib/widget-library/hidden.component.ts +0 -39
- package/src/lib/widget-library/input.component.ts +0 -76
- package/src/lib/widget-library/message.component.ts +0 -29
- package/src/lib/widget-library/none.component.ts +0 -12
- package/src/lib/widget-library/number.component.ts +0 -79
- package/src/lib/widget-library/one-of.component.ts +0 -36
- package/src/lib/widget-library/orderable.directive.ts +0 -130
- package/src/lib/widget-library/radios.component.ts +0 -101
- package/src/lib/widget-library/root.component.ts +0 -78
- package/src/lib/widget-library/section.component.ts +0 -133
- package/src/lib/widget-library/select-framework.component.ts +0 -50
- package/src/lib/widget-library/select-widget.component.ts +0 -46
- package/src/lib/widget-library/select.component.ts +0 -96
- package/src/lib/widget-library/submit.component.ts +0 -68
- package/src/lib/widget-library/tab.component.ts +0 -29
- package/src/lib/widget-library/tabs.component.ts +0 -83
- package/src/lib/widget-library/template.component.ts +0 -52
- package/src/lib/widget-library/textarea.component.ts +0 -68
- package/src/lib/widget-library/widget-library.module.ts +0 -13
- package/src/lib/widget-library/widget-library.service.ts +0 -234
- package/src/test.ts +0 -18
- package/tsconfig.lib.json +0 -25
- package/tsconfig.lib.prod.json +0 -9
- package/tsconfig.spec.json +0 -17
- package/tslint.json +0 -11
|
@@ -1,788 +0,0 @@
|
|
|
1
|
-
import cloneDeep from 'lodash/cloneDeep';
|
|
2
|
-
import { JsonPointer } from './jsonpointer.functions';
|
|
3
|
-
import { mergeSchemas } from './merge-schemas.function';
|
|
4
|
-
import { forEach, hasOwn, mergeFilteredObject } from './utility.functions';
|
|
5
|
-
import {
|
|
6
|
-
getType,
|
|
7
|
-
hasValue,
|
|
8
|
-
inArray,
|
|
9
|
-
isArray,
|
|
10
|
-
isNumber,
|
|
11
|
-
isObject,
|
|
12
|
-
isString
|
|
13
|
-
} from './validator.functions';
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
/**
|
|
17
|
-
* JSON Schema function library:
|
|
18
|
-
*
|
|
19
|
-
* buildSchemaFromLayout: TODO: Write this function
|
|
20
|
-
*
|
|
21
|
-
* buildSchemaFromData:
|
|
22
|
-
*
|
|
23
|
-
* getFromSchema:
|
|
24
|
-
*
|
|
25
|
-
* removeRecursiveReferences:
|
|
26
|
-
*
|
|
27
|
-
* getInputType:
|
|
28
|
-
*
|
|
29
|
-
* checkInlineType:
|
|
30
|
-
*
|
|
31
|
-
* isInputRequired:
|
|
32
|
-
*
|
|
33
|
-
* updateInputOptions:
|
|
34
|
-
*
|
|
35
|
-
* getTitleMapFromOneOf:
|
|
36
|
-
*
|
|
37
|
-
* getControlValidators:
|
|
38
|
-
*
|
|
39
|
-
* resolveSchemaReferences:
|
|
40
|
-
*
|
|
41
|
-
* getSubSchema:
|
|
42
|
-
*
|
|
43
|
-
* combineAllOf:
|
|
44
|
-
*
|
|
45
|
-
* fixRequiredArrayProperties:
|
|
46
|
-
*/
|
|
47
|
-
|
|
48
|
-
/**
|
|
49
|
-
* 'buildSchemaFromLayout' function
|
|
50
|
-
*
|
|
51
|
-
* TODO: Build a JSON Schema from a JSON Form layout
|
|
52
|
-
*
|
|
53
|
-
* // layout - The JSON Form layout
|
|
54
|
-
* // - The new JSON Schema
|
|
55
|
-
*/
|
|
56
|
-
export function buildSchemaFromLayout(layout) {
|
|
57
|
-
return;
|
|
58
|
-
// let newSchema: any = { };
|
|
59
|
-
// const walkLayout = (layoutItems: any[], callback: Function): any[] => {
|
|
60
|
-
// let returnArray: any[] = [];
|
|
61
|
-
// for (let layoutItem of layoutItems) {
|
|
62
|
-
// const returnItem: any = callback(layoutItem);
|
|
63
|
-
// if (returnItem) { returnArray = returnArray.concat(callback(layoutItem)); }
|
|
64
|
-
// if (layoutItem.items) {
|
|
65
|
-
// returnArray = returnArray.concat(walkLayout(layoutItem.items, callback));
|
|
66
|
-
// }
|
|
67
|
-
// }
|
|
68
|
-
// return returnArray;
|
|
69
|
-
// };
|
|
70
|
-
// walkLayout(layout, layoutItem => {
|
|
71
|
-
// let itemKey: string;
|
|
72
|
-
// if (typeof layoutItem === 'string') {
|
|
73
|
-
// itemKey = layoutItem;
|
|
74
|
-
// } else if (layoutItem.key) {
|
|
75
|
-
// itemKey = layoutItem.key;
|
|
76
|
-
// }
|
|
77
|
-
// if (!itemKey) { return; }
|
|
78
|
-
// //
|
|
79
|
-
// });
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
/**
|
|
83
|
-
* 'buildSchemaFromData' function
|
|
84
|
-
*
|
|
85
|
-
* Build a JSON Schema from a data object
|
|
86
|
-
*
|
|
87
|
-
* // data - The data object
|
|
88
|
-
* // { boolean = false } requireAllFields - Require all fields?
|
|
89
|
-
* // { boolean = true } isRoot - is root
|
|
90
|
-
* // - The new JSON Schema
|
|
91
|
-
*/
|
|
92
|
-
export function buildSchemaFromData(
|
|
93
|
-
data, requireAllFields = false, isRoot = true
|
|
94
|
-
) {
|
|
95
|
-
const newSchema: any = {};
|
|
96
|
-
const getFieldType = (value: any): string => {
|
|
97
|
-
const fieldType = getType(value, 'strict');
|
|
98
|
-
return { integer: 'number', null: 'string' }[fieldType] || fieldType;
|
|
99
|
-
};
|
|
100
|
-
const buildSubSchema = (value) =>
|
|
101
|
-
buildSchemaFromData(value, requireAllFields, false);
|
|
102
|
-
if (isRoot) { newSchema.$schema = 'http://json-schema.org/draft-06/schema#'; }
|
|
103
|
-
newSchema.type = getFieldType(data);
|
|
104
|
-
if (newSchema.type === 'object') {
|
|
105
|
-
newSchema.properties = {};
|
|
106
|
-
if (requireAllFields) { newSchema.required = []; }
|
|
107
|
-
for (const key of Object.keys(data)) {
|
|
108
|
-
newSchema.properties[key] = buildSubSchema(data[key]);
|
|
109
|
-
if (requireAllFields) { newSchema.required.push(key); }
|
|
110
|
-
}
|
|
111
|
-
} else if (newSchema.type === 'array') {
|
|
112
|
-
newSchema.items = data.map(buildSubSchema);
|
|
113
|
-
// If all items are the same type, use an object for items instead of an array
|
|
114
|
-
if ((new Set(data.map(getFieldType))).size === 1) {
|
|
115
|
-
newSchema.items = newSchema.items.reduce((a, b) => ({ ...a, ...b }), {});
|
|
116
|
-
}
|
|
117
|
-
if (requireAllFields) { newSchema.minItems = 1; }
|
|
118
|
-
}
|
|
119
|
-
return newSchema;
|
|
120
|
-
}
|
|
121
|
-
|
|
122
|
-
/**
|
|
123
|
-
* 'getFromSchema' function
|
|
124
|
-
*
|
|
125
|
-
* Uses a JSON Pointer for a value within a data object to retrieve
|
|
126
|
-
* the schema for that value within schema for the data object.
|
|
127
|
-
*
|
|
128
|
-
* The optional third parameter can also be set to return something else:
|
|
129
|
-
* 'schema' (default): the schema for the value indicated by the data pointer
|
|
130
|
-
* 'parentSchema': the schema for the value's parent object or array
|
|
131
|
-
* 'schemaPointer': a pointer to the value's schema within the object's schema
|
|
132
|
-
* 'parentSchemaPointer': a pointer to the schema for the value's parent object or array
|
|
133
|
-
*
|
|
134
|
-
* // schema - The schema to get the sub-schema from
|
|
135
|
-
* // { Pointer } dataPointer - JSON Pointer (string or array)
|
|
136
|
-
* // { string = 'schema' } returnType - what to return?
|
|
137
|
-
* // - The located sub-schema
|
|
138
|
-
*/
|
|
139
|
-
export function getFromSchema(schema, dataPointer, returnType = 'schema') {
|
|
140
|
-
const dataPointerArray: any[] = JsonPointer.parse(dataPointer);
|
|
141
|
-
if (dataPointerArray === null) {
|
|
142
|
-
console.error(`getFromSchema error: Invalid JSON Pointer: ${dataPointer}`);
|
|
143
|
-
return null;
|
|
144
|
-
}
|
|
145
|
-
let subSchema = schema;
|
|
146
|
-
const schemaPointer = [];
|
|
147
|
-
const length = dataPointerArray.length;
|
|
148
|
-
if (returnType.slice(0, 6) === 'parent') { dataPointerArray.length--; }
|
|
149
|
-
for (let i = 0; i < length; ++i) {
|
|
150
|
-
const parentSchema = subSchema;
|
|
151
|
-
const key = dataPointerArray[i];
|
|
152
|
-
let subSchemaFound = false;
|
|
153
|
-
if (typeof subSchema !== 'object') {
|
|
154
|
-
console.error(`getFromSchema error: Unable to find "${key}" key in schema.`);
|
|
155
|
-
console.error(schema);
|
|
156
|
-
console.error(dataPointer);
|
|
157
|
-
return null;
|
|
158
|
-
}
|
|
159
|
-
if (subSchema.type === 'array' && (!isNaN(key) || key === '-')) {
|
|
160
|
-
if (hasOwn(subSchema, 'items')) {
|
|
161
|
-
if (isObject(subSchema.items)) {
|
|
162
|
-
subSchemaFound = true;
|
|
163
|
-
subSchema = subSchema.items;
|
|
164
|
-
schemaPointer.push('items');
|
|
165
|
-
} else if (isArray(subSchema.items)) {
|
|
166
|
-
if (!isNaN(key) && subSchema.items.length >= +key) {
|
|
167
|
-
subSchemaFound = true;
|
|
168
|
-
subSchema = subSchema.items[+key];
|
|
169
|
-
schemaPointer.push('items', key);
|
|
170
|
-
}
|
|
171
|
-
}
|
|
172
|
-
}
|
|
173
|
-
if (!subSchemaFound && isObject(subSchema.additionalItems)) {
|
|
174
|
-
subSchemaFound = true;
|
|
175
|
-
subSchema = subSchema.additionalItems;
|
|
176
|
-
schemaPointer.push('additionalItems');
|
|
177
|
-
} else if (subSchema.additionalItems !== false) {
|
|
178
|
-
subSchemaFound = true;
|
|
179
|
-
subSchema = { };
|
|
180
|
-
schemaPointer.push('additionalItems');
|
|
181
|
-
}
|
|
182
|
-
} else if (subSchema.type === 'object') {
|
|
183
|
-
if (isObject(subSchema.properties) && hasOwn(subSchema.properties, key)) {
|
|
184
|
-
subSchemaFound = true;
|
|
185
|
-
subSchema = subSchema.properties[key];
|
|
186
|
-
schemaPointer.push('properties', key);
|
|
187
|
-
} else if (isObject(subSchema.additionalProperties)) {
|
|
188
|
-
subSchemaFound = true;
|
|
189
|
-
subSchema = subSchema.additionalProperties;
|
|
190
|
-
schemaPointer.push('additionalProperties');
|
|
191
|
-
} else if (subSchema.additionalProperties !== false) {
|
|
192
|
-
subSchemaFound = true;
|
|
193
|
-
subSchema = { };
|
|
194
|
-
schemaPointer.push('additionalProperties');
|
|
195
|
-
}
|
|
196
|
-
}
|
|
197
|
-
if (!subSchemaFound) {
|
|
198
|
-
console.error(`getFromSchema error: Unable to find "${key}" item in schema.`);
|
|
199
|
-
console.error(schema);
|
|
200
|
-
console.error(dataPointer);
|
|
201
|
-
return;
|
|
202
|
-
}
|
|
203
|
-
}
|
|
204
|
-
return returnType.slice(-7) === 'Pointer' ? schemaPointer : subSchema;
|
|
205
|
-
}
|
|
206
|
-
|
|
207
|
-
/**
|
|
208
|
-
* 'removeRecursiveReferences' function
|
|
209
|
-
*
|
|
210
|
-
* Checks a JSON Pointer against a map of recursive references and returns
|
|
211
|
-
* a JSON Pointer to the shallowest equivalent location in the same object.
|
|
212
|
-
*
|
|
213
|
-
* Using this functions enables an object to be constructed with unlimited
|
|
214
|
-
* recursion, while maintaing a fixed set of metadata, such as field data types.
|
|
215
|
-
* The object can grow as large as it wants, and deeply recursed nodes can
|
|
216
|
-
* just refer to the metadata for their shallow equivalents, instead of having
|
|
217
|
-
* to add additional redundant metadata for each recursively added node.
|
|
218
|
-
*
|
|
219
|
-
* Example:
|
|
220
|
-
*
|
|
221
|
-
* pointer: '/stuff/and/more/and/more/and/more/and/more/stuff'
|
|
222
|
-
* recursiveRefMap: [['/stuff/and/more/and/more', '/stuff/and/more/']]
|
|
223
|
-
* returned: '/stuff/and/more/stuff'
|
|
224
|
-
*
|
|
225
|
-
* // { Pointer } pointer -
|
|
226
|
-
* // { Map<string, string> } recursiveRefMap -
|
|
227
|
-
* // { Map<string, number> = new Map() } arrayMap - optional
|
|
228
|
-
* // { string } -
|
|
229
|
-
*/
|
|
230
|
-
export function removeRecursiveReferences(
|
|
231
|
-
pointer, recursiveRefMap, arrayMap = new Map()
|
|
232
|
-
) {
|
|
233
|
-
if (!pointer) { return ''; }
|
|
234
|
-
let genericPointer =
|
|
235
|
-
JsonPointer.toGenericPointer(JsonPointer.compile(pointer), arrayMap);
|
|
236
|
-
if (genericPointer.indexOf('/') === -1) { return genericPointer; }
|
|
237
|
-
let possibleReferences = true;
|
|
238
|
-
while (possibleReferences) {
|
|
239
|
-
possibleReferences = false;
|
|
240
|
-
recursiveRefMap.forEach((toPointer, fromPointer) => {
|
|
241
|
-
if (JsonPointer.isSubPointer(toPointer, fromPointer)) {
|
|
242
|
-
while (JsonPointer.isSubPointer(fromPointer, genericPointer, true)) {
|
|
243
|
-
genericPointer = JsonPointer.toGenericPointer(
|
|
244
|
-
toPointer + genericPointer.slice(fromPointer.length), arrayMap
|
|
245
|
-
);
|
|
246
|
-
possibleReferences = true;
|
|
247
|
-
}
|
|
248
|
-
}
|
|
249
|
-
});
|
|
250
|
-
}
|
|
251
|
-
return genericPointer;
|
|
252
|
-
}
|
|
253
|
-
|
|
254
|
-
/**
|
|
255
|
-
* 'getInputType' function
|
|
256
|
-
*
|
|
257
|
-
* // schema
|
|
258
|
-
* // { any = null } layoutNode
|
|
259
|
-
* // { string }
|
|
260
|
-
*/
|
|
261
|
-
export function getInputType(schema, layoutNode: any = null) {
|
|
262
|
-
// x-schema-form = Angular Schema Form compatibility
|
|
263
|
-
// widget & component = React Jsonschema Form compatibility
|
|
264
|
-
const controlType = JsonPointer.getFirst([
|
|
265
|
-
[schema, '/x-schema-form/type'],
|
|
266
|
-
[schema, '/x-schema-form/widget/component'],
|
|
267
|
-
[schema, '/x-schema-form/widget'],
|
|
268
|
-
[schema, '/widget/component'],
|
|
269
|
-
[schema, '/widget']
|
|
270
|
-
]);
|
|
271
|
-
if (isString(controlType)) { return checkInlineType(controlType, schema, layoutNode); }
|
|
272
|
-
let schemaType = schema.type;
|
|
273
|
-
if (schemaType) {
|
|
274
|
-
if (isArray(schemaType)) { // If multiple types listed, use most inclusive type
|
|
275
|
-
schemaType =
|
|
276
|
-
inArray('object', schemaType) && hasOwn(schema, 'properties') ? 'object' :
|
|
277
|
-
inArray('array', schemaType) && hasOwn(schema, 'items') ? 'array' :
|
|
278
|
-
inArray('array', schemaType) && hasOwn(schema, 'additionalItems') ? 'array' :
|
|
279
|
-
inArray('string', schemaType) ? 'string' :
|
|
280
|
-
inArray('number', schemaType) ? 'number' :
|
|
281
|
-
inArray('integer', schemaType) ? 'integer' :
|
|
282
|
-
inArray('boolean', schemaType) ? 'boolean' : 'unknown';
|
|
283
|
-
}
|
|
284
|
-
if (schemaType === 'boolean') { return 'checkbox'; }
|
|
285
|
-
if (schemaType === 'object') {
|
|
286
|
-
if (hasOwn(schema, 'properties') || hasOwn(schema, 'additionalProperties')) {
|
|
287
|
-
return 'section';
|
|
288
|
-
}
|
|
289
|
-
// TODO: Figure out how to handle additionalProperties
|
|
290
|
-
if (hasOwn(schema, '$ref')) { return '$ref'; }
|
|
291
|
-
}
|
|
292
|
-
if (schemaType === 'array') {
|
|
293
|
-
const itemsObject = JsonPointer.getFirst([
|
|
294
|
-
[schema, '/items'],
|
|
295
|
-
[schema, '/additionalItems']
|
|
296
|
-
]) || {};
|
|
297
|
-
return hasOwn(itemsObject, 'enum') && schema.maxItems !== 1 ?
|
|
298
|
-
checkInlineType('checkboxes', schema, layoutNode) : 'array';
|
|
299
|
-
}
|
|
300
|
-
if (schemaType === 'null') { return 'none'; }
|
|
301
|
-
if (JsonPointer.has(layoutNode, '/options/titleMap') ||
|
|
302
|
-
hasOwn(schema, 'enum') || getTitleMapFromOneOf(schema, null, true)
|
|
303
|
-
) { return 'select'; }
|
|
304
|
-
if (schemaType === 'number' || schemaType === 'integer') {
|
|
305
|
-
return (schemaType === 'integer' || hasOwn(schema, 'multipleOf')) &&
|
|
306
|
-
hasOwn(schema, 'maximum') && hasOwn(schema, 'minimum') ? 'range' : schemaType;
|
|
307
|
-
}
|
|
308
|
-
if (schemaType === 'string') {
|
|
309
|
-
return {
|
|
310
|
-
'color': 'color',
|
|
311
|
-
'date': 'date',
|
|
312
|
-
'date-time': 'datetime-local',
|
|
313
|
-
'email': 'email',
|
|
314
|
-
'uri': 'url',
|
|
315
|
-
}[schema.format] || 'text';
|
|
316
|
-
}
|
|
317
|
-
}
|
|
318
|
-
if (hasOwn(schema, '$ref')) { return '$ref'; }
|
|
319
|
-
if (isArray(schema.oneOf) || isArray(schema.anyOf)) { return 'one-of'; }
|
|
320
|
-
console.error(`getInputType error: Unable to determine input type for ${schemaType}`);
|
|
321
|
-
console.error('schema', schema);
|
|
322
|
-
if (layoutNode) { console.error('layoutNode', layoutNode); }
|
|
323
|
-
return 'none';
|
|
324
|
-
}
|
|
325
|
-
|
|
326
|
-
/**
|
|
327
|
-
* 'checkInlineType' function
|
|
328
|
-
*
|
|
329
|
-
* Checks layout and schema nodes for 'inline: true', and converts
|
|
330
|
-
* 'radios' or 'checkboxes' to 'radios-inline' or 'checkboxes-inline'
|
|
331
|
-
*
|
|
332
|
-
* // { string } controlType -
|
|
333
|
-
* // schema -
|
|
334
|
-
* // { any = null } layoutNode -
|
|
335
|
-
* // { string }
|
|
336
|
-
*/
|
|
337
|
-
export function checkInlineType(controlType, schema, layoutNode: any = null) {
|
|
338
|
-
if (!isString(controlType) || (
|
|
339
|
-
controlType.slice(0, 8) !== 'checkbox' && controlType.slice(0, 5) !== 'radio'
|
|
340
|
-
)) {
|
|
341
|
-
return controlType;
|
|
342
|
-
}
|
|
343
|
-
if (
|
|
344
|
-
JsonPointer.getFirst([
|
|
345
|
-
[layoutNode, '/inline'],
|
|
346
|
-
[layoutNode, '/options/inline'],
|
|
347
|
-
[schema, '/inline'],
|
|
348
|
-
[schema, '/x-schema-form/inline'],
|
|
349
|
-
[schema, '/x-schema-form/options/inline'],
|
|
350
|
-
[schema, '/x-schema-form/widget/inline'],
|
|
351
|
-
[schema, '/x-schema-form/widget/component/inline'],
|
|
352
|
-
[schema, '/x-schema-form/widget/component/options/inline'],
|
|
353
|
-
[schema, '/widget/inline'],
|
|
354
|
-
[schema, '/widget/component/inline'],
|
|
355
|
-
[schema, '/widget/component/options/inline'],
|
|
356
|
-
]) === true
|
|
357
|
-
) {
|
|
358
|
-
return controlType.slice(0, 5) === 'radio' ?
|
|
359
|
-
'radios-inline' : 'checkboxes-inline';
|
|
360
|
-
} else {
|
|
361
|
-
return controlType;
|
|
362
|
-
}
|
|
363
|
-
}
|
|
364
|
-
|
|
365
|
-
/**
|
|
366
|
-
* 'isInputRequired' function
|
|
367
|
-
*
|
|
368
|
-
* Checks a JSON Schema to see if an item is required
|
|
369
|
-
*
|
|
370
|
-
* // schema - the schema to check
|
|
371
|
-
* // { string } schemaPointer - the pointer to the item to check
|
|
372
|
-
* // { boolean } - true if the item is required, false if not
|
|
373
|
-
*/
|
|
374
|
-
export function isInputRequired(schema, schemaPointer) {
|
|
375
|
-
if (!isObject(schema)) {
|
|
376
|
-
console.error('isInputRequired error: Input schema must be an object.');
|
|
377
|
-
return false;
|
|
378
|
-
}
|
|
379
|
-
const listPointerArray = JsonPointer.parse(schemaPointer);
|
|
380
|
-
if (isArray(listPointerArray)) {
|
|
381
|
-
if (!listPointerArray.length) { return schema.required === true; }
|
|
382
|
-
const keyName = listPointerArray.pop();
|
|
383
|
-
const nextToLastKey = listPointerArray[listPointerArray.length - 1];
|
|
384
|
-
if (['properties', 'additionalProperties', 'patternProperties', 'items', 'additionalItems']
|
|
385
|
-
.includes(nextToLastKey)
|
|
386
|
-
) {
|
|
387
|
-
listPointerArray.pop();
|
|
388
|
-
}
|
|
389
|
-
const parentSchema = JsonPointer.get(schema, listPointerArray) || {};
|
|
390
|
-
if (isArray(parentSchema.required)) {
|
|
391
|
-
return parentSchema.required.includes(keyName);
|
|
392
|
-
}
|
|
393
|
-
if (parentSchema.type === 'array') {
|
|
394
|
-
return hasOwn(parentSchema, 'minItems') &&
|
|
395
|
-
isNumber(keyName) &&
|
|
396
|
-
+parentSchema.minItems > +keyName;
|
|
397
|
-
}
|
|
398
|
-
}
|
|
399
|
-
return false;
|
|
400
|
-
}
|
|
401
|
-
|
|
402
|
-
/**
|
|
403
|
-
* 'updateInputOptions' function
|
|
404
|
-
*
|
|
405
|
-
* // layoutNode
|
|
406
|
-
* // schema
|
|
407
|
-
* // jsf
|
|
408
|
-
* // { void }
|
|
409
|
-
*/
|
|
410
|
-
export function updateInputOptions(layoutNode, schema, jsf) {
|
|
411
|
-
if (!isObject(layoutNode) || !isObject(layoutNode.options)) { return; }
|
|
412
|
-
|
|
413
|
-
// Set all option values in layoutNode.options
|
|
414
|
-
const newOptions: any = { };
|
|
415
|
-
const fixUiKeys = key => key.slice(0, 3).toLowerCase() === 'ui:' ? key.slice(3) : key;
|
|
416
|
-
mergeFilteredObject(newOptions, jsf.formOptions.defaultWidgetOptions, [], fixUiKeys);
|
|
417
|
-
[ [ JsonPointer.get(schema, '/ui:widget/options'), [] ],
|
|
418
|
-
[ JsonPointer.get(schema, '/ui:widget'), [] ],
|
|
419
|
-
[ schema, [
|
|
420
|
-
'additionalProperties', 'additionalItems', 'properties', 'items',
|
|
421
|
-
'required', 'type', 'x-schema-form', '$ref'
|
|
422
|
-
] ],
|
|
423
|
-
[ JsonPointer.get(schema, '/x-schema-form/options'), [] ],
|
|
424
|
-
[ JsonPointer.get(schema, '/x-schema-form'), ['items', 'options'] ],
|
|
425
|
-
[ layoutNode, [
|
|
426
|
-
'_id', '$ref', 'arrayItem', 'arrayItemType', 'dataPointer', 'dataType',
|
|
427
|
-
'items', 'key', 'name', 'options', 'recursiveReference', 'type', 'widget'
|
|
428
|
-
] ],
|
|
429
|
-
[ layoutNode.options, [] ],
|
|
430
|
-
].forEach(([ object, excludeKeys ]) =>
|
|
431
|
-
mergeFilteredObject(newOptions, object, excludeKeys, fixUiKeys)
|
|
432
|
-
);
|
|
433
|
-
if (!hasOwn(newOptions, 'titleMap')) {
|
|
434
|
-
let newTitleMap: any = null;
|
|
435
|
-
newTitleMap = getTitleMapFromOneOf(schema, newOptions.flatList);
|
|
436
|
-
if (newTitleMap) { newOptions.titleMap = newTitleMap; }
|
|
437
|
-
if (!hasOwn(newOptions, 'titleMap') && !hasOwn(newOptions, 'enum') && hasOwn(schema, 'items')) {
|
|
438
|
-
if (JsonPointer.has(schema, '/items/titleMap')) {
|
|
439
|
-
newOptions.titleMap = schema.items.titleMap;
|
|
440
|
-
} else if (JsonPointer.has(schema, '/items/enum')) {
|
|
441
|
-
newOptions.enum = schema.items.enum;
|
|
442
|
-
if (!hasOwn(newOptions, 'enumNames') && JsonPointer.has(schema, '/items/enumNames')) {
|
|
443
|
-
newOptions.enumNames = schema.items.enumNames;
|
|
444
|
-
}
|
|
445
|
-
} else if (JsonPointer.has(schema, '/items/oneOf')) {
|
|
446
|
-
newTitleMap = getTitleMapFromOneOf(schema.items, newOptions.flatList);
|
|
447
|
-
if (newTitleMap) { newOptions.titleMap = newTitleMap; }
|
|
448
|
-
}
|
|
449
|
-
}
|
|
450
|
-
}
|
|
451
|
-
|
|
452
|
-
// If schema type is integer, enforce by setting multipleOf = 1
|
|
453
|
-
if (schema.type === 'integer' && !hasValue(newOptions.multipleOf)) {
|
|
454
|
-
newOptions.multipleOf = 1;
|
|
455
|
-
}
|
|
456
|
-
|
|
457
|
-
// Copy any typeahead word lists to options.typeahead.source
|
|
458
|
-
if (JsonPointer.has(newOptions, '/autocomplete/source')) {
|
|
459
|
-
newOptions.typeahead = newOptions.autocomplete;
|
|
460
|
-
} else if (JsonPointer.has(newOptions, '/tagsinput/source')) {
|
|
461
|
-
newOptions.typeahead = newOptions.tagsinput;
|
|
462
|
-
} else if (JsonPointer.has(newOptions, '/tagsinput/typeahead/source')) {
|
|
463
|
-
newOptions.typeahead = newOptions.tagsinput.typeahead;
|
|
464
|
-
}
|
|
465
|
-
|
|
466
|
-
layoutNode.options = newOptions;
|
|
467
|
-
}
|
|
468
|
-
|
|
469
|
-
/**
|
|
470
|
-
* 'getTitleMapFromOneOf' function
|
|
471
|
-
*
|
|
472
|
-
* // { schema } schema
|
|
473
|
-
* // { boolean = null } flatList
|
|
474
|
-
* // { boolean = false } validateOnly
|
|
475
|
-
* // { validators }
|
|
476
|
-
*/
|
|
477
|
-
export function getTitleMapFromOneOf(
|
|
478
|
-
schema: any = {}, flatList: boolean = null, validateOnly = false
|
|
479
|
-
) {
|
|
480
|
-
let titleMap = null;
|
|
481
|
-
const oneOf = schema.oneOf || schema.anyOf || null;
|
|
482
|
-
if (isArray(oneOf) && oneOf.every(item => item.title)) {
|
|
483
|
-
if (oneOf.every(item => isArray(item.enum) && item.enum.length === 1)) {
|
|
484
|
-
if (validateOnly) { return true; }
|
|
485
|
-
titleMap = oneOf.map(item => ({ name: item.title, value: item.enum[0] }));
|
|
486
|
-
} else if (oneOf.every(item => item.const)) {
|
|
487
|
-
if (validateOnly) { return true; }
|
|
488
|
-
titleMap = oneOf.map(item => ({ name: item.title, value: item.const }));
|
|
489
|
-
}
|
|
490
|
-
|
|
491
|
-
// if flatList !== false and some items have colons, make grouped map
|
|
492
|
-
if (flatList !== false && (titleMap || [])
|
|
493
|
-
.filter(title => ((title || {}).name || '').indexOf(': ')).length > 1
|
|
494
|
-
) {
|
|
495
|
-
|
|
496
|
-
// Split name on first colon to create grouped map (name -> group: name)
|
|
497
|
-
const newTitleMap = titleMap.map(title => {
|
|
498
|
-
const [group, name] = title.name.split(/: (.+)/);
|
|
499
|
-
return group && name ? { ...title, group, name } : title;
|
|
500
|
-
});
|
|
501
|
-
|
|
502
|
-
// If flatList === true or at least one group has multiple items, use grouped map
|
|
503
|
-
if (flatList === true || newTitleMap.some((title, index) => index &&
|
|
504
|
-
hasOwn(title, 'group') && title.group === newTitleMap[index - 1].group
|
|
505
|
-
)) {
|
|
506
|
-
titleMap = newTitleMap;
|
|
507
|
-
}
|
|
508
|
-
}
|
|
509
|
-
}
|
|
510
|
-
return validateOnly ? false : titleMap;
|
|
511
|
-
}
|
|
512
|
-
|
|
513
|
-
/**
|
|
514
|
-
* 'getControlValidators' function
|
|
515
|
-
*
|
|
516
|
-
* // schema
|
|
517
|
-
* // { validators }
|
|
518
|
-
*/
|
|
519
|
-
export function getControlValidators(schema) {
|
|
520
|
-
if (!isObject(schema)) { return null; }
|
|
521
|
-
const validators: any = { };
|
|
522
|
-
if (hasOwn(schema, 'type')) {
|
|
523
|
-
switch (schema.type) {
|
|
524
|
-
case 'string':
|
|
525
|
-
forEach(['pattern', 'format', 'minLength', 'maxLength'], (prop) => {
|
|
526
|
-
if (hasOwn(schema, prop)) { validators[prop] = [schema[prop]]; }
|
|
527
|
-
});
|
|
528
|
-
break;
|
|
529
|
-
case 'number': case 'integer':
|
|
530
|
-
forEach(['Minimum', 'Maximum'], (ucLimit) => {
|
|
531
|
-
const eLimit = 'exclusive' + ucLimit;
|
|
532
|
-
const limit = ucLimit.toLowerCase();
|
|
533
|
-
if (hasOwn(schema, limit)) {
|
|
534
|
-
const exclusive = hasOwn(schema, eLimit) && schema[eLimit] === true;
|
|
535
|
-
validators[limit] = [schema[limit], exclusive];
|
|
536
|
-
}
|
|
537
|
-
});
|
|
538
|
-
forEach(['multipleOf', 'type'], (prop) => {
|
|
539
|
-
if (hasOwn(schema, prop)) { validators[prop] = [schema[prop]]; }
|
|
540
|
-
});
|
|
541
|
-
break;
|
|
542
|
-
case 'object':
|
|
543
|
-
forEach(['minProperties', 'maxProperties', 'dependencies'], (prop) => {
|
|
544
|
-
if (hasOwn(schema, prop)) { validators[prop] = [schema[prop]]; }
|
|
545
|
-
});
|
|
546
|
-
break;
|
|
547
|
-
case 'array':
|
|
548
|
-
forEach(['minItems', 'maxItems', 'uniqueItems'], (prop) => {
|
|
549
|
-
if (hasOwn(schema, prop)) { validators[prop] = [schema[prop]]; }
|
|
550
|
-
});
|
|
551
|
-
break;
|
|
552
|
-
}
|
|
553
|
-
}
|
|
554
|
-
if (hasOwn(schema, 'enum')) { validators.enum = [schema.enum]; }
|
|
555
|
-
return validators;
|
|
556
|
-
}
|
|
557
|
-
|
|
558
|
-
/**
|
|
559
|
-
* 'resolveSchemaReferences' function
|
|
560
|
-
*
|
|
561
|
-
* Find all $ref links in schema and save links and referenced schemas in
|
|
562
|
-
* schemaRefLibrary, schemaRecursiveRefMap, and dataRecursiveRefMap
|
|
563
|
-
*
|
|
564
|
-
* // schema
|
|
565
|
-
* // schemaRefLibrary
|
|
566
|
-
* // { Map<string, string> } schemaRecursiveRefMap
|
|
567
|
-
* // { Map<string, string> } dataRecursiveRefMap
|
|
568
|
-
* // { Map<string, number> } arrayMap
|
|
569
|
-
* //
|
|
570
|
-
*/
|
|
571
|
-
export function resolveSchemaReferences(
|
|
572
|
-
schema, schemaRefLibrary, schemaRecursiveRefMap, dataRecursiveRefMap, arrayMap
|
|
573
|
-
) {
|
|
574
|
-
if (!isObject(schema)) {
|
|
575
|
-
console.error('resolveSchemaReferences error: schema must be an object.');
|
|
576
|
-
return;
|
|
577
|
-
}
|
|
578
|
-
const refLinks = new Set<string>();
|
|
579
|
-
const refMapSet = new Set<string>();
|
|
580
|
-
const refMap = new Map<string, string>();
|
|
581
|
-
const recursiveRefMap = new Map<string, string>();
|
|
582
|
-
const refLibrary: any = {};
|
|
583
|
-
|
|
584
|
-
// Search schema for all $ref links, and build full refLibrary
|
|
585
|
-
JsonPointer.forEachDeep(schema, (subSchema, subSchemaPointer) => {
|
|
586
|
-
if (hasOwn(subSchema, '$ref') && isString(subSchema['$ref'])) {
|
|
587
|
-
const refPointer = JsonPointer.compile(subSchema['$ref']);
|
|
588
|
-
refLinks.add(refPointer);
|
|
589
|
-
refMapSet.add(subSchemaPointer + '~~' + refPointer);
|
|
590
|
-
refMap.set(subSchemaPointer, refPointer);
|
|
591
|
-
}
|
|
592
|
-
});
|
|
593
|
-
refLinks.forEach(ref => refLibrary[ref] = getSubSchema(schema, ref));
|
|
594
|
-
|
|
595
|
-
// Follow all ref links and save in refMapSet,
|
|
596
|
-
// to find any multi-link recursive refernces
|
|
597
|
-
let checkRefLinks = true;
|
|
598
|
-
while (checkRefLinks) {
|
|
599
|
-
checkRefLinks = false;
|
|
600
|
-
Array.from(refMap).forEach(([fromRef1, toRef1]) => Array.from(refMap)
|
|
601
|
-
.filter(([fromRef2, toRef2]) =>
|
|
602
|
-
JsonPointer.isSubPointer(toRef1, fromRef2, true) &&
|
|
603
|
-
!JsonPointer.isSubPointer(toRef2, toRef1, true) &&
|
|
604
|
-
!refMapSet.has(fromRef1 + fromRef2.slice(toRef1.length) + '~~' + toRef2)
|
|
605
|
-
)
|
|
606
|
-
.forEach(([fromRef2, toRef2]) => {
|
|
607
|
-
refMapSet.add(fromRef1 + fromRef2.slice(toRef1.length) + '~~' + toRef2);
|
|
608
|
-
checkRefLinks = true;
|
|
609
|
-
})
|
|
610
|
-
);
|
|
611
|
-
}
|
|
612
|
-
|
|
613
|
-
// Build full recursiveRefMap
|
|
614
|
-
// First pass - save all internally recursive refs from refMapSet
|
|
615
|
-
Array.from(refMapSet)
|
|
616
|
-
.map(refLink => refLink.split('~~'))
|
|
617
|
-
.filter(([fromRef, toRef]) => JsonPointer.isSubPointer(toRef, fromRef))
|
|
618
|
-
.forEach(([fromRef, toRef]) => recursiveRefMap.set(fromRef, toRef));
|
|
619
|
-
// Second pass - create recursive versions of any other refs that link to recursive refs
|
|
620
|
-
Array.from(refMap)
|
|
621
|
-
.filter(([fromRef1, toRef1]) => Array.from(recursiveRefMap.keys())
|
|
622
|
-
.every(fromRef2 => !JsonPointer.isSubPointer(fromRef1, fromRef2, true))
|
|
623
|
-
)
|
|
624
|
-
.forEach(([fromRef1, toRef1]) => Array.from(recursiveRefMap)
|
|
625
|
-
.filter(([fromRef2, toRef2]) =>
|
|
626
|
-
!recursiveRefMap.has(fromRef1 + fromRef2.slice(toRef1.length)) &&
|
|
627
|
-
JsonPointer.isSubPointer(toRef1, fromRef2, true) &&
|
|
628
|
-
!JsonPointer.isSubPointer(toRef1, fromRef1, true)
|
|
629
|
-
)
|
|
630
|
-
.forEach(([fromRef2, toRef2]) => recursiveRefMap.set(
|
|
631
|
-
fromRef1 + fromRef2.slice(toRef1.length),
|
|
632
|
-
fromRef1 + toRef2.slice(toRef1.length)
|
|
633
|
-
))
|
|
634
|
-
);
|
|
635
|
-
|
|
636
|
-
// Create compiled schema by replacing all non-recursive $ref links with
|
|
637
|
-
// thieir linked schemas and, where possible, combining schemas in allOf arrays.
|
|
638
|
-
let compiledSchema = { ...schema };
|
|
639
|
-
delete compiledSchema.definitions;
|
|
640
|
-
compiledSchema =
|
|
641
|
-
getSubSchema(compiledSchema, '', refLibrary, recursiveRefMap);
|
|
642
|
-
|
|
643
|
-
// Make sure all remaining schema $refs are recursive, and build final
|
|
644
|
-
// schemaRefLibrary, schemaRecursiveRefMap, dataRecursiveRefMap, & arrayMap
|
|
645
|
-
JsonPointer.forEachDeep(compiledSchema, (subSchema, subSchemaPointer) => {
|
|
646
|
-
if (isString(subSchema['$ref'])) {
|
|
647
|
-
let refPointer = JsonPointer.compile(subSchema['$ref']);
|
|
648
|
-
if (!JsonPointer.isSubPointer(refPointer, subSchemaPointer, true)) {
|
|
649
|
-
refPointer = removeRecursiveReferences(subSchemaPointer, recursiveRefMap);
|
|
650
|
-
JsonPointer.set(compiledSchema, subSchemaPointer, { $ref: `#${refPointer}` });
|
|
651
|
-
}
|
|
652
|
-
if (!hasOwn(schemaRefLibrary, 'refPointer')) {
|
|
653
|
-
schemaRefLibrary[refPointer] = !refPointer.length ? compiledSchema :
|
|
654
|
-
getSubSchema(compiledSchema, refPointer, schemaRefLibrary, recursiveRefMap);
|
|
655
|
-
}
|
|
656
|
-
if (!schemaRecursiveRefMap.has(subSchemaPointer)) {
|
|
657
|
-
schemaRecursiveRefMap.set(subSchemaPointer, refPointer);
|
|
658
|
-
}
|
|
659
|
-
const fromDataRef = JsonPointer.toDataPointer(subSchemaPointer, compiledSchema);
|
|
660
|
-
if (!dataRecursiveRefMap.has(fromDataRef)) {
|
|
661
|
-
const toDataRef = JsonPointer.toDataPointer(refPointer, compiledSchema);
|
|
662
|
-
dataRecursiveRefMap.set(fromDataRef, toDataRef);
|
|
663
|
-
}
|
|
664
|
-
}
|
|
665
|
-
if (subSchema.type === 'array' &&
|
|
666
|
-
(hasOwn(subSchema, 'items') || hasOwn(subSchema, 'additionalItems'))
|
|
667
|
-
) {
|
|
668
|
-
const dataPointer = JsonPointer.toDataPointer(subSchemaPointer, compiledSchema);
|
|
669
|
-
if (!arrayMap.has(dataPointer)) {
|
|
670
|
-
const tupleItems = isArray(subSchema.items) ? subSchema.items.length : 0;
|
|
671
|
-
arrayMap.set(dataPointer, tupleItems);
|
|
672
|
-
}
|
|
673
|
-
}
|
|
674
|
-
}, true);
|
|
675
|
-
return compiledSchema;
|
|
676
|
-
}
|
|
677
|
-
|
|
678
|
-
/**
|
|
679
|
-
* 'getSubSchema' function
|
|
680
|
-
*
|
|
681
|
-
* // schema
|
|
682
|
-
* // { Pointer } pointer
|
|
683
|
-
* // { object } schemaRefLibrary
|
|
684
|
-
* // { Map<string, string> } schemaRecursiveRefMap
|
|
685
|
-
* // { string[] = [] } usedPointers
|
|
686
|
-
* //
|
|
687
|
-
*/
|
|
688
|
-
export function getSubSchema(
|
|
689
|
-
schema, pointer, schemaRefLibrary = null,
|
|
690
|
-
schemaRecursiveRefMap: Map<string, string> = null, usedPointers: string[] = []
|
|
691
|
-
) {
|
|
692
|
-
if (!schemaRefLibrary || !schemaRecursiveRefMap) {
|
|
693
|
-
return JsonPointer.getCopy(schema, pointer);
|
|
694
|
-
}
|
|
695
|
-
if (typeof pointer !== 'string') { pointer = JsonPointer.compile(pointer); }
|
|
696
|
-
usedPointers = [ ...usedPointers, pointer ];
|
|
697
|
-
let newSchema: any = null;
|
|
698
|
-
if (pointer === '') {
|
|
699
|
-
newSchema = cloneDeep(schema);
|
|
700
|
-
} else {
|
|
701
|
-
const shortPointer = removeRecursiveReferences(pointer, schemaRecursiveRefMap);
|
|
702
|
-
if (shortPointer !== pointer) { usedPointers = [ ...usedPointers, shortPointer ]; }
|
|
703
|
-
newSchema = JsonPointer.getFirstCopy([
|
|
704
|
-
[schemaRefLibrary, [shortPointer]],
|
|
705
|
-
[schema, pointer],
|
|
706
|
-
[schema, shortPointer]
|
|
707
|
-
]);
|
|
708
|
-
}
|
|
709
|
-
return JsonPointer.forEachDeepCopy(newSchema, (subSchema, subPointer) => {
|
|
710
|
-
if (isObject(subSchema)) {
|
|
711
|
-
|
|
712
|
-
// Replace non-recursive $ref links with referenced schemas
|
|
713
|
-
if (isString(subSchema.$ref)) {
|
|
714
|
-
const refPointer = JsonPointer.compile(subSchema.$ref);
|
|
715
|
-
if (refPointer.length && usedPointers.every(ptr =>
|
|
716
|
-
!JsonPointer.isSubPointer(refPointer, ptr, true)
|
|
717
|
-
)) {
|
|
718
|
-
const refSchema = getSubSchema(
|
|
719
|
-
schema, refPointer, schemaRefLibrary, schemaRecursiveRefMap, usedPointers
|
|
720
|
-
);
|
|
721
|
-
if (Object.keys(subSchema).length === 1) {
|
|
722
|
-
return refSchema;
|
|
723
|
-
} else {
|
|
724
|
-
const extraKeys = { ...subSchema };
|
|
725
|
-
delete extraKeys.$ref;
|
|
726
|
-
return mergeSchemas(refSchema, extraKeys);
|
|
727
|
-
}
|
|
728
|
-
}
|
|
729
|
-
}
|
|
730
|
-
|
|
731
|
-
// TODO: Convert schemas with 'type' arrays to 'oneOf'
|
|
732
|
-
|
|
733
|
-
// Combine allOf subSchemas
|
|
734
|
-
if (isArray(subSchema.allOf)) { return combineAllOf(subSchema); }
|
|
735
|
-
|
|
736
|
-
// Fix incorrectly placed array object required lists
|
|
737
|
-
if (subSchema.type === 'array' && isArray(subSchema.required)) {
|
|
738
|
-
return fixRequiredArrayProperties(subSchema);
|
|
739
|
-
}
|
|
740
|
-
}
|
|
741
|
-
return subSchema;
|
|
742
|
-
}, true, <string>pointer);
|
|
743
|
-
}
|
|
744
|
-
|
|
745
|
-
/**
|
|
746
|
-
* 'combineAllOf' function
|
|
747
|
-
*
|
|
748
|
-
* Attempt to convert an allOf schema object into
|
|
749
|
-
* a non-allOf schema object with equivalent rules.
|
|
750
|
-
*
|
|
751
|
-
* // schema - allOf schema object
|
|
752
|
-
* // - converted schema object
|
|
753
|
-
*/
|
|
754
|
-
export function combineAllOf(schema) {
|
|
755
|
-
if (!isObject(schema) || !isArray(schema.allOf)) { return schema; }
|
|
756
|
-
let mergedSchema = mergeSchemas(...schema.allOf);
|
|
757
|
-
if (Object.keys(schema).length > 1) {
|
|
758
|
-
const extraKeys = { ...schema };
|
|
759
|
-
delete extraKeys.allOf;
|
|
760
|
-
mergedSchema = mergeSchemas(mergedSchema, extraKeys);
|
|
761
|
-
}
|
|
762
|
-
return mergedSchema;
|
|
763
|
-
}
|
|
764
|
-
|
|
765
|
-
/**
|
|
766
|
-
* 'fixRequiredArrayProperties' function
|
|
767
|
-
*
|
|
768
|
-
* Fixes an incorrectly placed required list inside an array schema, by moving
|
|
769
|
-
* it into items.properties or additionalItems.properties, where it belongs.
|
|
770
|
-
*
|
|
771
|
-
* // schema - allOf schema object
|
|
772
|
-
* // - converted schema object
|
|
773
|
-
*/
|
|
774
|
-
export function fixRequiredArrayProperties(schema) {
|
|
775
|
-
if (schema.type === 'array' && isArray(schema.required)) {
|
|
776
|
-
const itemsObject = hasOwn(schema.items, 'properties') ? 'items' :
|
|
777
|
-
hasOwn(schema.additionalItems, 'properties') ? 'additionalItems' : null;
|
|
778
|
-
if (itemsObject && !hasOwn(schema[itemsObject], 'required') && (
|
|
779
|
-
hasOwn(schema[itemsObject], 'additionalProperties') ||
|
|
780
|
-
schema.required.every(key => hasOwn(schema[itemsObject].properties, key))
|
|
781
|
-
)) {
|
|
782
|
-
schema = cloneDeep(schema);
|
|
783
|
-
schema[itemsObject].required = schema.required;
|
|
784
|
-
delete schema.required;
|
|
785
|
-
}
|
|
786
|
-
}
|
|
787
|
-
return schema;
|
|
788
|
-
}
|