hapijs / hapijs/joi

How to use the value of a sibling field as the label of another field?

Open
#2,654 1 comment 0 reactions 0 assignees View on GitHub
Dominant language
JavaScript
Stars
21.2k
Forks
1.5k
Avg merge
4h 57m
Merged PRs (30d)
14

Description

#### Support plan

* *is this issue currently blocking your project?* (yes/no): no
* *is this issue affecting a production system?* (yes/no): no

#### Context

* *node version*: 14.17.3
* *module version*: 17.4.2
* *environment* (e.g. node, browser, native): node
* *used with* (e.g. hapi application, another framework, standalone, ...): express, typescript
* *any other relevant information*:

#### How can we help?

I have a process on my API that accepts arbitrary inputs from an Angular application to set values for a report which is generated. I'm trying to validate the input based on other parameters being passed in (such as the field data type) which works great, however when a value fails the validation the error messages are too vague / technical such as `"params[2].field_value" must be a valid date`.

Here is an example input that I'm passing in, along with the validation code I'm using: (note the invalid date on the 3rd entry in the array on "End Date", in the "field_value" property)

```json
{
"params": [
{
"field_seq": 1,
"field_label": "Cust ID",
"field_type": "varchar",
"field_required": "required",
"field_value": "1CALJAM"
},
{
"field_seq": 2,
"field_label": "Start Date",
"field_type": "date",
"field_required": "required",
"field_value": "08/01/2021"
},
{
"field_seq": 3,
"field_label": "End Date",
"field_type": "date",
"field_required": "required",
"field_value": "08/3120212"
},
{
"field_seq": 4,
"field_label": "BOL",
"field_type": "varchar",
"field_required": "required",
"field_value": "Y"
}
]
}
```

```typescript
function validate(req: Request) {
const paramSchema = Joi.object({
field_seq: Joi.number().positive().precision(0).max(100).required(),
field_label: Joi.string()
.trim()
.max(255)
.required(),
field_type: Joi.string().trim().max(12).required(),
field_required: Joi.string().trim().max(12).required(),
field_value: Joi.when('field_required', { is: Joi.string().regex(/^required$/), then: Joi.required() })
.when('field_type', { is: Joi.string().regex(/^varchar$/), then: Joi.string().trim().max(4000) })
.when('field_type', { is: Joi.string().regex(/^number$/), then: Joi.number() })
.when('field_type', {
is: Joi.string().regex(/^date$/),
then: Joi.date(),
}),
});

const schema = Joi.object({
sessionId: Joi.string().trim().max(255).required(),
reportName: Joi.string().trim().max(255).required(),
params: Joi.array().required().min(0).items(paramSchema),
});

return schema.validate(req.body, { debug: true }).error;
}
```

I've tried using the label() setting and this does allow me to change the label for `field_value` to something else, but so far only a static string has worked successfully.

I made an attempt to use function calls on `custom()` to set a field value and later get it, but the order that the functions are called results in this not working. Here is an example of that validation code:

```typescript
function validate(req: Request) {
let fieldLabel: string;

function setFieldLabel(label: string) {
console.log('setFieldLabel:', label);
fieldLabel = label;
}

function getFieldLabel(): string {
console.log('getFieldLabel:', fieldLabel);
return fieldLabel ?? 'empty';
}

const paramSchema = Joi.object({
field_seq: Joi.number().positive().precision(0).max(100).required(),
field_label: Joi.string()
.trim()
.max(255)
.required()
.custom((value) => {
setFieldLabel(value);
}),
field_type: Joi.string().trim().max(12).required(),
field_required: Joi.string().trim().max(12).required(),
field_value: Joi.when('field_required', { is: Joi.string().regex(/^required$/), then: Joi.required() })
.when('field_type', { is: Joi.string().regex(/^varchar$/), then: Joi.string().label(getFieldLabel()).trim().max(4000) })
.when('field_type', { is: Joi.string().regex(/^number$/), then: Joi.number().label(getFieldLabel()) })
.when('field_type', {
is: Joi.string().regex(/^date$/),
then: Joi.date().label(getFieldLabel()),
}),
});

const schema = Joi.object({
sessionId: Joi.string().trim().max(255).required(),
reportName: Joi.string().trim().max(255).required(),
params: Joi.array().required().min(0).items(paramSchema),
});

return schema.validate(req.body, { debug: true }).error;
}
```

Ideally, I would like to just replace `"params[2].field_value" must be a valid date` with (in this specific example) `"End Date" must be a valid date`, with the label value being pulled from the value of `field_label` in the same object.

Is something like this even possible?

Contributor guide

Open the contributing guide

Research direction

Start with the reported paramSchema and Joi's label(), custom(), and when() behavior. Check the existing Joi validation and error-message tests or documentation for sibling-field context; done means establishing whether a field label can be derived from the same object and documenting or specifying the supported behavior.

Written by the indexing model from the issue text.

Assessment

Tech stack
javascript, typescript
Domain
api, backend, testing-qa
Issue type
Feature
Difficulty
5/5
Estimated time
Over a week
Activity status
Stale
Clarity
Mostly clear
Newbie friendliness
25/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.