Skip to content

Commit 15da456

Browse files
authored
fix(json-logic): apply custom validators when calculating the final schema (#238)
1 parent 56ca9d0 commit 15da456

File tree

5 files changed

+55
-8
lines changed

5 files changed

+55
-8
lines changed

src/form.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -284,7 +284,7 @@ export function createHeadlessForm(
284284
const updatedSchema = calculateFinalSchema({
285285
schema,
286286
values: initialValues,
287-
options: options.legacyOptions,
287+
options,
288288
})
289289

290290
const fields = buildFields({ schema: updatedSchema, originalSchema: schema, strictInputType })
@@ -301,7 +301,7 @@ export function createHeadlessForm(
301301
const updatedSchema = calculateFinalSchema({
302302
schema,
303303
values: value,
304-
options: options.legacyOptions,
304+
options,
305305
})
306306

307307
const result = validate(value, updatedSchema, options.legacyOptions)

src/mutations.ts

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import type { Field } from './field/type'
2+
import type { CreateHeadlessFormOptions } from './form'
23
import type { JsfObjectSchema, JsfSchema, JsonLogicContext, NonBooleanJsfSchema, ObjectValue, SchemaValue } from './types'
34
import type { LegacyOptions } from './validation/schema'
45
import { buildFieldSchema } from './field/schema'
@@ -24,18 +25,19 @@ export function calculateFinalSchema({
2425
}: {
2526
schema: JsfObjectSchema
2627
values: SchemaValue
27-
options?: LegacyOptions
28+
options?: CreateHeadlessFormOptions
2829
}): JsfObjectSchema {
2930
const jsonLogicContext = schema['x-jsf-logic'] ? getJsonLogicContextFromSchema(schema['x-jsf-logic'], values) : undefined
3031
const schemaCopy = safeDeepClone(schema)
32+
const { legacyOptions, customJsonLogicOps } = options
3133

32-
applySchemaRules(schemaCopy, values, options, jsonLogicContext)
34+
applySchemaRules(schemaCopy, values, legacyOptions, jsonLogicContext)
3335

3436
if (jsonLogicContext?.schema.computedValues) {
35-
applyComputedAttrsToSchema(schemaCopy, jsonLogicContext.schema.computedValues, values)
37+
applyComputedAttrsToSchema(schemaCopy, jsonLogicContext.schema.computedValues, values, customJsonLogicOps)
3638
// If we had computed values applied to the schema,
3739
// we need to re-apply the schema rules to update the fields
38-
applySchemaRules(schemaCopy, values, options, jsonLogicContext)
40+
applySchemaRules(schemaCopy, values, legacyOptions, jsonLogicContext)
3941
}
4042

4143
return schemaCopy

src/validation/json-logic.ts

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import type { RulesLogic } from 'json-logic-js'
22
import type { ValidationError, ValidationErrorPath } from '../errors'
3+
import type { CreateHeadlessFormOptions } from '../form'
34
import type { JsfObjectSchema, JsfSchema, JsonLogicContext, JsonLogicRules, JsonLogicSchema, NonBooleanJsfSchema, ObjectValue, SchemaValue } from '../types'
45
import jsonLogic from 'json-logic-js'
56

@@ -128,18 +129,23 @@ export function computePropertyValues(
128129
* @param schema - The schema to apply computed attributes to
129130
* @param computedValuesDefinition - The computed values to apply
130131
* @param values - The current form values
132+
* @param customJsonLogicOps - The custom JSON Logic operations to apply
131133
* @returns The schema with computed attributes applied
132134
*/
133-
export function applyComputedAttrsToSchema(schema: JsfObjectSchema, computedValuesDefinition: JsonLogicRules['computedValues'], values: SchemaValue): JsfObjectSchema {
135+
export function applyComputedAttrsToSchema(schema: JsfObjectSchema, computedValuesDefinition: JsonLogicRules['computedValues'], values: SchemaValue, customJsonLogicOps: CreateHeadlessFormOptions['customJsonLogicOps'] = {}): JsfObjectSchema {
134136
if (computedValuesDefinition) {
135137
const computedValues: Record<string, any> = {}
136138

139+
addCustomJsonLogicOperations(customJsonLogicOps)
140+
137141
Object.entries(computedValuesDefinition).forEach(([name, definition]) => {
138142
const computedValue = computePropertyValues(name, definition.rule, values)
139143
computedValues[name] = computedValue
140144
})
141145

142146
cycleThroughPropertiesAndApplyValues(schema, computedValues)
147+
148+
removeCustomJsonLogicOperations(customJsonLogicOps)
143149
}
144150

145151
return schema

test/validation/json-logic-v0.test.js

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ import {
1515
schemaWithComputedAttributeThatDoesntExist,
1616
schemaWithComputedAttributeThatDoesntExistDescription,
1717
schemaWithComputedAttributeThatDoesntExistTitle,
18+
schemaWithCustomComputedValueFunction,
1819
schemaWithCustomValidationFunction,
1920
schemaWithDeepVarThatDoesNotExist,
2021
schemaWithDeepVarThatDoesNotExistOnFieldset,
@@ -475,10 +476,18 @@ describe('jsonLogic: cross-values validations', () => {
475476

476477
it('validation on custom functions', () => {
477478
const actionThatWillThrow = () => {
478-
createHeadlessForm(schemaWithCustomValidationFunction, { strictInputType: false, customJsonLogicOps: { is_hello: 'not a funcion' } })
479+
createHeadlessForm(schemaWithCustomValidationFunction, { strictInputType: false, customJsonLogicOps: { is_hello: 'not a function' } })
479480
}
480481

481482
expect(actionThatWillThrow).toThrow('Custom JSON Logic operator \'is_hello\' must be a function, but received type \'string\'.')
482483
})
484+
485+
it('applies custom functions when initial values require them', () => {
486+
const actionThatWillThrow = () => {
487+
createHeadlessForm(schemaWithCustomComputedValueFunction, { strictInputType: false, customJsonLogicOps: { is_hello: a => a === 'hello world!' } })
488+
}
489+
490+
expect(actionThatWillThrow).not.toThrow()
491+
})
483492
})
484493
})

test/validation/json-logic.fixtures.js

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -763,3 +763,33 @@ export const schemaWithCustomValidationFunction = {
763763
},
764764
},
765765
}
766+
767+
export const schemaWithCustomComputedValueFunction = {
768+
'properties': {
769+
field_a: {
770+
type: 'string',
771+
},
772+
field_b: {
773+
'type': 'string',
774+
'title': 'Field with computed description',
775+
'x-jsf-logic-computedAttrs': {
776+
description: 'Computed value is {{custom_function_result}}',
777+
},
778+
},
779+
},
780+
'x-jsf-logic': {
781+
computedValues: {
782+
custom_function_result: {
783+
rule: {
784+
if: [
785+
{
786+
is_hello: { var: 'field_a' },
787+
},
788+
'value1',
789+
'value2',
790+
],
791+
},
792+
},
793+
},
794+
},
795+
}

0 commit comments

Comments
 (0)