Description
Runtime
Node.js
Runtime version
v20.16.0
Module version
Last module version without issue
No response
Used with
Express
Any other relevant information
In addition to what's described below, as an experiment, I also tried match("all")
and it isn't working either, failing with alternatives.any
type, but if either a
or b
is commented out, it will correctly report this as an error with type
set as alternatives.all
. Oddly enough, match("any")
is not working with both alternatives present either.
It seems to me that match("one")
and match("any")
were implemented as if it's the same, although I didn't look at the code, and just inferred it from some of the discussions, like this one:
The functionality of alternatives().match()
is similar to oneOf
, anyOf
and allOf
in JSON Schema, so you can see how errors are reported. For example, for this oneOf
schema:
https://www.jsonschemavalidator.net/
{
"$schema": "https://json-schema.org/draft/2019-09/schema",
"$defs": {
"x": {"type": "string"},
"y": {"type": "string"}
},
"oneOf": [
{
"properties": {
"a": {"type": "number"},
"x": {"$ref": "#/$defs/x"},
"y": {"$ref": "#/$defs/y"}
}
},
{
"properties": {
"b": {"type": "string"},
"x": {"$ref": "#/$defs/x"},
"y": {"$ref": "#/$defs/y"}
}
}
]
}
, and this object:
{
"a": 123,
"b": "123",
"x": "x-value"
}
, the error is reported as:
JSON is valid against more than one schema from 'oneOf'. Valid schema indexes: 0, 1.
You can change the schema to allOf
or anyOf
and see the effects, such as allOf
will report the index of the failing schema and the individual fields (e.g. if a
is changed to "123"
), which would be quite useful in Joi.
What are you trying to achieve or the steps to reproduce?
I'm trying to validate a set of input values, some of which are mutually exclusive. For example, a user may identify themselves by an email address or a social media handle, but not both. In addition to the mutually exclusive fields, I also have common fields that should be present in both cases.
In this example, o
represents shared fields, a
is an email field and b
is a plain string field. I would like either a
or b
to be valid, but not both, while having the same rules for x
and y
.
let Joi = require("joi");
let o = Joi.object(
{
x: Joi.string().min(1).max(30).label("X"),
y: Joi.string().optional().min(1).max(60).label("Y"),
});
let x = Joi.alternatives()
.match("one")
.try(
Joi.object({a: Joi.string().email().label("A")}).concat(o),
Joi.object({b: Joi.string().label("B")}).concat(o)
)
.label("AB.XY");
console.dir(x.validate({
a: "[email protected]",
b: "b-value",
x: "x-value"
}), { depth: null });
What was the result you got?
The code above returns the following response, in which it identifies that neither a
nor b
is allowed, when the actual problem is that they both match, so for the lack of a better terminology they are both "allowed", when only one should be. It also seems incorrect that alternatives.any
is reported as type
, when the failing matching mode is alternatives.one
.
{
value: undefined,
error: [Error [ValidationError]: "AB.XY" does not match any of the allowed types] {
_original: { a: '[email protected]', b: 'b-value', x: 'x-value' },
details: [
{
message: '"AB.XY" does not match any of the allowed types',
path: [],
type: 'alternatives.any',
context: {
details: [
{
message: '"b" is not allowed',
details: [
{
message: '"b" is not allowed',
path: [ 'b' ],
type: 'object.unknown',
context: {
child: 'b',
label: 'b',
value: 'b-value',
key: 'b'
}
}
]
},
{
message: '"a" is not allowed',
details: [
{
message: '"a" is not allowed',
path: [ 'a' ],
type: 'object.unknown',
context: {
child: 'a',
label: 'a',
value: '[email protected]',
key: 'a'
}
}
]
}
],
label: 'AB.XY',
value: { a: '[email protected]', b: 'b-value', x: 'x-value' }
}
}
]
}
}
What result did you expect?
match("one")
is described that it should match only one schema, same as oneOf
in JSON Schema, so it should be reported that more than one schema matched for alternatives().match("one")
. Specifically, errors resulting from combining schemas and errors resulting from fields failing validation should be reported independently.
match("one")
does report an error, but the error is not reported as more than one schema matching, but rather as neither of the allowed schemas matched. This error does not reflect what actually was invalid and cannot be reported back to the user, so the calling code must detect the errors of combining schemas, discard them and report new ones, while trying to pluck things like labels from the reported errors. I also don't see a way to supply a custom error here, probably because of the concat
used for shared fields.