@nolwenture/formik-jsonschema-form 0.22.4 → 0.22.5
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.
|
@@ -159,6 +159,77 @@ const ifThenElseExampleSchema = {
|
|
|
159
159
|
},
|
|
160
160
|
],
|
|
161
161
|
};
|
|
162
|
+
const allOfExample = {
|
|
163
|
+
type: "object",
|
|
164
|
+
properties: {
|
|
165
|
+
customerType: { type: "string" },
|
|
166
|
+
},
|
|
167
|
+
allOf: [
|
|
168
|
+
{
|
|
169
|
+
if: { properties: { customerType: { const: "BUSINESS" } } },
|
|
170
|
+
then: {
|
|
171
|
+
properties: {
|
|
172
|
+
businessName: { type: "string", title: "Company name" },
|
|
173
|
+
code: { type: "string", title: "Code" },
|
|
174
|
+
notRequired: { type: "string", title: "Not required" },
|
|
175
|
+
address: { $ref: "#/definitions/address" },
|
|
176
|
+
},
|
|
177
|
+
required: ["businessName", "code", "address"],
|
|
178
|
+
},
|
|
179
|
+
},
|
|
180
|
+
{
|
|
181
|
+
if: { properties: { customerType: { const: "INDIVIDUAL" } } },
|
|
182
|
+
then: {
|
|
183
|
+
properties: {
|
|
184
|
+
firstName: { type: "string", title: "First name" },
|
|
185
|
+
code: { type: "number", title: "Code" },
|
|
186
|
+
address: { $ref: "#/definitions/address" },
|
|
187
|
+
},
|
|
188
|
+
required: ["firstName", "code", "address"],
|
|
189
|
+
},
|
|
190
|
+
},
|
|
191
|
+
{
|
|
192
|
+
if: { properties: { customerType: { const: "UNKNOWN" } } },
|
|
193
|
+
then: {
|
|
194
|
+
properties: {
|
|
195
|
+
newCustomer: { type: "boolean", title: "New customer" },
|
|
196
|
+
code: { type: "boolean", title: "Code" },
|
|
197
|
+
extraField: { type: "number", title: "Extra field" },
|
|
198
|
+
address: { $ref: "#/definitions/address" },
|
|
199
|
+
},
|
|
200
|
+
required: ["newCustomer", "code", "address"],
|
|
201
|
+
},
|
|
202
|
+
},
|
|
203
|
+
],
|
|
204
|
+
definitions: {
|
|
205
|
+
address: {
|
|
206
|
+
type: "object",
|
|
207
|
+
required: ["streetName", "city", "country"],
|
|
208
|
+
properties: {
|
|
209
|
+
streetName: {
|
|
210
|
+
type: "string",
|
|
211
|
+
title: "Street name",
|
|
212
|
+
},
|
|
213
|
+
city: {
|
|
214
|
+
type: "string",
|
|
215
|
+
title: "Town/City",
|
|
216
|
+
},
|
|
217
|
+
postcode: {
|
|
218
|
+
type: "string",
|
|
219
|
+
title: "Postcode",
|
|
220
|
+
},
|
|
221
|
+
state: {
|
|
222
|
+
type: "string",
|
|
223
|
+
title: "State/Province/Region",
|
|
224
|
+
},
|
|
225
|
+
country: {
|
|
226
|
+
type: "string",
|
|
227
|
+
title: "Country",
|
|
228
|
+
},
|
|
229
|
+
},
|
|
230
|
+
},
|
|
231
|
+
},
|
|
232
|
+
};
|
|
162
233
|
const nestedRequiredConditionalExampleSchema = {
|
|
163
234
|
type: "object",
|
|
164
235
|
properties: {
|
|
@@ -189,6 +260,17 @@ const nestedRequiredConditionalExampleSchema = {
|
|
|
189
260
|
],
|
|
190
261
|
};
|
|
191
262
|
describe("Create Validation Schema", () => {
|
|
263
|
+
function listErrors(schema, data) {
|
|
264
|
+
var _a;
|
|
265
|
+
const yupSchema = createValidationSchema(schema);
|
|
266
|
+
try {
|
|
267
|
+
yupSchema.validateSync(data, { abortEarly: false });
|
|
268
|
+
}
|
|
269
|
+
catch (err) {
|
|
270
|
+
const error = err;
|
|
271
|
+
return (_a = error === null || error === void 0 ? void 0 : error.inner) === null || _a === void 0 ? void 0 : _a.map((aa) => aa.path);
|
|
272
|
+
}
|
|
273
|
+
}
|
|
192
274
|
it("creates yup validationSchema based on given JSON schema", () => __awaiter(void 0, void 0, void 0, function* () {
|
|
193
275
|
const result = createValidationSchema(exampleSchema);
|
|
194
276
|
const expected = result.isValidSync({
|
|
@@ -356,6 +438,76 @@ describe("Create Validation Schema", () => {
|
|
|
356
438
|
expect(firstTrueInvalid3).toEqual(false);
|
|
357
439
|
expect(firstTrueValid).toEqual(true);
|
|
358
440
|
});
|
|
441
|
+
it("generates validation for if-then", () => __awaiter(void 0, void 0, void 0, function* () {
|
|
442
|
+
const faultyBusiness = listErrors(allOfExample, {
|
|
443
|
+
customerType: "BUSINESS",
|
|
444
|
+
});
|
|
445
|
+
expect(faultyBusiness).toEqual(expect.arrayContaining([
|
|
446
|
+
"address.country",
|
|
447
|
+
"address.city",
|
|
448
|
+
"address.streetName",
|
|
449
|
+
"businessName",
|
|
450
|
+
"code",
|
|
451
|
+
]));
|
|
452
|
+
const business = listErrors(allOfExample, {
|
|
453
|
+
customerType: "BUSINESS",
|
|
454
|
+
businessName: ".",
|
|
455
|
+
code: ".",
|
|
456
|
+
address: {
|
|
457
|
+
streetName: ".",
|
|
458
|
+
city: ".",
|
|
459
|
+
country: ".",
|
|
460
|
+
},
|
|
461
|
+
});
|
|
462
|
+
expect(business).toEqual(undefined);
|
|
463
|
+
const faultyIndividual = listErrors(allOfExample, {
|
|
464
|
+
customerType: "INDIVIDUAL",
|
|
465
|
+
});
|
|
466
|
+
expect(faultyIndividual).toEqual(expect.arrayContaining([
|
|
467
|
+
"address.country",
|
|
468
|
+
"address.city",
|
|
469
|
+
"address.streetName",
|
|
470
|
+
"firstName",
|
|
471
|
+
"code",
|
|
472
|
+
]));
|
|
473
|
+
const individual = listErrors(allOfExample, {
|
|
474
|
+
customerType: "INDIVIDUAL",
|
|
475
|
+
firstName: ".",
|
|
476
|
+
code: 123,
|
|
477
|
+
address: {
|
|
478
|
+
streetName: ".",
|
|
479
|
+
city: ".",
|
|
480
|
+
country: ".",
|
|
481
|
+
},
|
|
482
|
+
});
|
|
483
|
+
expect(individual).toEqual(undefined);
|
|
484
|
+
const faultyUnknown = listErrors(allOfExample, {
|
|
485
|
+
customerType: "UNKNOWN",
|
|
486
|
+
});
|
|
487
|
+
expect(faultyUnknown).toEqual(expect.arrayContaining([
|
|
488
|
+
"address.country",
|
|
489
|
+
"address.city",
|
|
490
|
+
"address.streetName",
|
|
491
|
+
"newCustomer",
|
|
492
|
+
"code",
|
|
493
|
+
]));
|
|
494
|
+
const unknown = listErrors(allOfExample, {
|
|
495
|
+
customerType: "UNKNOWN",
|
|
496
|
+
newCustomer: true,
|
|
497
|
+
code: false,
|
|
498
|
+
address: {
|
|
499
|
+
streetName: ".",
|
|
500
|
+
city: ".",
|
|
501
|
+
country: ".",
|
|
502
|
+
},
|
|
503
|
+
});
|
|
504
|
+
expect(unknown).toEqual(undefined);
|
|
505
|
+
const yupSchema = createValidationSchema(allOfExample);
|
|
506
|
+
// Makes sure the context resets between function calls.
|
|
507
|
+
// eslint-disable-next-line
|
|
508
|
+
// @ts-expect-error
|
|
509
|
+
expect(yupSchema.fields.code.deps.length).toEqual(3);
|
|
510
|
+
}));
|
|
359
511
|
it("generates validation for conditional objects with nested validations", () => {
|
|
360
512
|
const result = createValidationSchema(nestedRequiredConditionalExampleSchema);
|
|
361
513
|
const isValidWhenConditionNotNet = result.isValidSync({
|
|
@@ -4,5 +4,9 @@ type Condition = {
|
|
|
4
4
|
dependentKeys: string[];
|
|
5
5
|
conditionFn: (...value: any[]) => boolean;
|
|
6
6
|
};
|
|
7
|
-
|
|
7
|
+
type ConditionsContext = {
|
|
8
|
+
condition: Condition;
|
|
9
|
+
yupSchema: Yup.Schema<any, any, any, "">;
|
|
10
|
+
};
|
|
11
|
+
export declare function createValidationSchema(schema: Schema, definitions?: Schema, conditionsContext?: Record<string, ConditionsContext[]>, condition?: Condition): Yup.ObjectSchema<{}, Yup.AnyObject, {}, "">;
|
|
8
12
|
export {};
|
|
@@ -9,9 +9,9 @@ var __rest = (this && this.__rest) || function (s, e) {
|
|
|
9
9
|
}
|
|
10
10
|
return t;
|
|
11
11
|
};
|
|
12
|
-
import { solveReferences } from "./solveReferences";
|
|
13
12
|
import * as Yup from "yup";
|
|
14
13
|
import { resolveOneOfValue } from "./resolveSchema";
|
|
14
|
+
import { solveReferences } from "./solveReferences";
|
|
15
15
|
function getConditionalValidations(schema) {
|
|
16
16
|
const { dependencies, allOf } = schema;
|
|
17
17
|
let conditionalValidations = [];
|
|
@@ -115,7 +115,7 @@ function getYupType(type) {
|
|
|
115
115
|
throw new Error("Unsupported schema type");
|
|
116
116
|
}
|
|
117
117
|
}
|
|
118
|
-
export function createValidationSchema(schema, definitions = {}, condition) {
|
|
118
|
+
export function createValidationSchema(schema, definitions = {}, conditionsContext = {}, condition) {
|
|
119
119
|
var _a;
|
|
120
120
|
if (!(schema === null || schema === void 0 ? void 0 : schema.properties)) {
|
|
121
121
|
return Yup.object().shape({});
|
|
@@ -123,10 +123,11 @@ export function createValidationSchema(schema, definitions = {}, condition) {
|
|
|
123
123
|
const schemaDefinitions = Object.assign(Object.assign({}, definitions), ((_a = schema.definitions) !== null && _a !== void 0 ? _a : {}));
|
|
124
124
|
const requiredFields = schema.required;
|
|
125
125
|
function getValue(schema, key) {
|
|
126
|
+
var _a;
|
|
126
127
|
let type = schema.type;
|
|
127
128
|
let yupSchema;
|
|
128
129
|
if (schema.properties) {
|
|
129
|
-
yupSchema = createValidationSchema(schema, schemaDefinitions);
|
|
130
|
+
yupSchema = createValidationSchema(schema, schemaDefinitions, conditionsContext);
|
|
130
131
|
type = type !== null && type !== void 0 ? type : "object";
|
|
131
132
|
}
|
|
132
133
|
else {
|
|
@@ -144,12 +145,29 @@ export function createValidationSchema(schema, definitions = {}, condition) {
|
|
|
144
145
|
yupSchema = yupSchema["required"]("Required");
|
|
145
146
|
}
|
|
146
147
|
}
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
148
|
+
if (!condition) {
|
|
149
|
+
return yupSchema;
|
|
150
|
+
}
|
|
151
|
+
const existing = (_a = conditionsContext[key]) !== null && _a !== void 0 ? _a : [];
|
|
152
|
+
conditionsContext[key] = [...existing, { yupSchema, condition }];
|
|
153
|
+
// TODO: The latest given type for duplicate field is used.
|
|
154
|
+
// This might become a problem at some point, but testing has not
|
|
155
|
+
// shown any problems so far.
|
|
156
|
+
const yupType = getYupType(type).when(condition.dependentKeys, {
|
|
157
|
+
is: condition.conditionFn,
|
|
158
|
+
then: () => yupSchema,
|
|
159
|
+
});
|
|
160
|
+
// If there are existing conditions for the field with same name
|
|
161
|
+
// for example definition or duplicate field, add those clauses in.
|
|
162
|
+
// Otherwise the validation would be overwritten by the latest one
|
|
163
|
+
// breaking the earlier validations.
|
|
164
|
+
return existing.reduce((result, context) => {
|
|
165
|
+
const { condition: ctxCondition, yupSchema: ctxYupSchema } = context;
|
|
166
|
+
return result.when(ctxCondition.dependentKeys, {
|
|
167
|
+
is: ctxCondition.conditionFn,
|
|
168
|
+
then: () => ctxYupSchema,
|
|
169
|
+
});
|
|
170
|
+
}, yupType);
|
|
153
171
|
}
|
|
154
172
|
const baseValidationSchema = Object.keys(schema.properties).reduce((validationSchema, key) => {
|
|
155
173
|
var _a;
|
|
@@ -159,6 +177,12 @@ export function createValidationSchema(schema, definitions = {}, condition) {
|
|
|
159
177
|
}, Yup.object().shape({}));
|
|
160
178
|
return getConditionalValidations(schema).reduce((validationSchema, conditionalValidation) => {
|
|
161
179
|
const solvedSchemas = solveReferences(conditionalValidation.schema, schemaDefinitions, {}, {});
|
|
162
|
-
|
|
180
|
+
/*
|
|
181
|
+
"Only schemas of the same type can be concatenated. concat is not
|
|
182
|
+
a "merge" function in the sense that all settings from the provided schema,
|
|
183
|
+
override ones in the base, including type, presence and nullability."
|
|
184
|
+
Source: https://github.com/jquense/yup?tab=readme-ov-file#schemaconcatschema-schema-schema
|
|
185
|
+
*/
|
|
186
|
+
return validationSchema.concat(createValidationSchema(solvedSchemas.schema, schemaDefinitions, conditionsContext, conditionalValidation.condition));
|
|
163
187
|
}, baseValidationSchema);
|
|
164
188
|
}
|