Skip to content

Commit a214abe

Browse files
author
Simone Erba
committed
Custom user defined validations
1 parent 365bdec commit a214abe

File tree

5 files changed

+57
-2
lines changed

5 files changed

+57
-2
lines changed

next/src/form.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import { getErrorMessage } from './errors/messages'
66
import { buildFieldSchema } from './field/schema'
77
import { calculateFinalSchema, updateFieldProperties } from './mutations'
88
import { validateSchema } from './validation/schema'
9+
import { registerCustomFunctionsToJsonLogic } from './validation/json-logic'
910

1011
export { ValidationOptions } from './validation/schema'
1112

@@ -255,6 +256,10 @@ export function createHeadlessForm(
255256
options: options.validationOptions,
256257
})
257258

259+
if (options.validationOptions?.customJsonLogicOps) {
260+
registerCustomFunctionsToJsonLogic(options.validationOptions.customJsonLogicOps)
261+
}
262+
258263
const fields = buildFields({ schema: updatedSchema, originalSchema: schema, strictInputType })
259264

260265
// TODO: check if we need this isError variable exposed

next/src/validation/json-logic.ts

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,17 @@
11
import type { RulesLogic } from 'json-logic-js'
22
import type { ValidationError, ValidationErrorPath } from '../errors'
33
import type { JsfObjectSchema, JsfSchema, JsonLogicContext, JsonLogicRules, JsonLogicSchema, NonBooleanJsfSchema, ObjectValue, SchemaValue } from '../types'
4-
import jsonLogic from 'json-logic-js'
4+
import jsonLogic from 'json-logic-js';
5+
6+
/**
7+
* Register user defined functions to be used in JSON Logic rules.
8+
* @param customJsonLogicOps An object containing custom JSON Logic operations to register
9+
*/
10+
export function registerCustomFunctionsToJsonLogic(customJsonLogicOps: Record<string, (...args: any[]) => any>) {
11+
for (const [name, func] of Object.entries(customJsonLogicOps)) {
12+
jsonLogic.add_operation(name, func)
13+
}
14+
}
515

616
/**
717
* Builds a json-logic context based on a schema and the current value

next/src/validation/schema.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,12 @@ export interface ValidationOptions {
2727
* @default false
2828
*/
2929
allowForbiddenValues?: boolean
30+
31+
/**
32+
* Custom jsonLogic operations to register (only applies once at setup)
33+
* Format: { [operationName]: (...args: any[]) => any }
34+
*/
35+
customJsonLogicOps?: Record<string, (...args: any[]) => any>
3036
}
3137

3238
/**

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

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
1-
import { createHeadlessForm } from '@/createHeadlessForm'
21
import { afterEach, beforeEach, describe, expect, it } from '@jest/globals'
2+
33
import { mockConsole, restoreConsoleAndEnsureItWasNotCalled } from '../test-utils'
4+
45
import {
56
badSchemaThatWillNotSetAForcedValue,
67
createSchemaWithRulesOnFieldA,
@@ -15,6 +16,7 @@ import {
1516
schemaWithComputedAttributeThatDoesntExist,
1617
schemaWithComputedAttributeThatDoesntExistDescription,
1718
schemaWithComputedAttributeThatDoesntExistTitle,
19+
schemaWithCustomValidationFunction,
1820
schemaWithDeepVarThatDoesNotExist,
1921
schemaWithDeepVarThatDoesNotExistOnFieldset,
2022
schemaWithInlinedRuleOnComputedAttributeThatReferencesUnknownVar,
@@ -30,6 +32,7 @@ import {
3032
schemaWithUnknownVariableInValidations,
3133
schemaWithValidationThatDoesNotExistOnProperty,
3234
} from './json-logic.fixtures'
35+
import { createHeadlessForm } from '@/createHeadlessForm'
3336

3437
beforeEach(mockConsole)
3538
afterEach(restoreConsoleAndEnsureItWasNotCalled)
@@ -446,4 +449,13 @@ describe('jsonLogic: cross-values validations', () => {
446449
expect(handleValidation({ field_a: 20, field_b: 41 }).formErrors).toEqual()
447450
})
448451
})
452+
453+
describe('custom operators', () => {
454+
it('custom function', () => {
455+
const { handleValidation } = createHeadlessForm(schemaWithCustomValidationFunction, { strictInputType: false, validationOptions: { customJsonLogicOps: { is_hello: (a, b) => a === 'hello' && b === 'w' } } })
456+
const { formErrors } = handleValidation({ field_a: 'hello', field_b: 'w' })
457+
expect(formErrors?.field_a).toEqual('Invalid hello world')
458+
expect(handleValidation({ field_a: 'hello', field_b: 'world' }).formErrors).toEqual(undefined)
459+
})
460+
})
449461
})

next/test/validation/json-logic.fixtures.js

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -744,3 +744,25 @@ export const schemaWithReduceAccumulator = {
744744
},
745745
},
746746
}
747+
748+
export const schemaWithCustomValidationFunction = {
749+
'properties': {
750+
field_a: {
751+
'type': 'string',
752+
'x-jsf-logic-validations': ['hello_world'],
753+
},
754+
field_b: {
755+
type: 'string',
756+
},
757+
},
758+
'x-jsf-logic': {
759+
validations: {
760+
hello_world: {
761+
errorMessage: 'Invalid hello world',
762+
rule: {
763+
'!': { is_hello: [{ var: 'field_a' }, { var: 'field_b' }] },
764+
},
765+
},
766+
},
767+
},
768+
}

0 commit comments

Comments
 (0)