adonisjs / validator

Schema based validator for AdonisJS
MIT License
116 stars 39 forks source link

[V5] Custom validation rule return undefined for validator.helpers.getFieldValue #116

Closed rodrigorz closed 3 years ago

rodrigorz commented 3 years ago

Package version

"@adonisjs/auth": "^8.0.4" "@adonisjs/core": "^5.1.8" "@adonisjs/lucid": "^14.1.0" "@adonisjs/mail": "^7.1.1" "@adonisjs/repl": "^3.1.2" "@adonisjs/view": "^6.0.2"

Node.js and npm version

node: v14.16.0 npm: 6.14.11

Sample Code (to reproduce the issue)

I have this custom validation rule to set field as required only if the selected category have the period = 'semiannual'. (worked on v5 preview version)

validator.rule(
  'requiredIfCategorySemiannual',
  async (
    value,
    [{ categoryField }]: [RequiredIfCategorySemiannualOptions],
    { root, tip, pointer, arrayExpressionPointer, errorReporter }
  ) => {
    // this should return the "category_id" value present in "root", but i got undefined
    const categoryId = validator.helpers.getFieldValue(
      categoryField,
      root,
      tip
    );

    // see the log result below
    console.log({
      categoryField,
      categoryId,
      root,
      tip,
    });

    const category = await Category.find(categoryId); // the error happens here, because categoryId is undefined

    if (category && category.period === Category.PERIOD_SEMIANNUAL && !value) {
      errorReporter.report(
        pointer,
        'requiredIfCategorySemiannual',
        undefined,
        arrayExpressionPointer
      );
    }
  },
  () => ({
    allowUndefineds: true,
    async: true,
  })
);

Result of the console.log & error:

{
  categoryField: 'category_id', # field that i am looking for
  categoryId: undefined, # should be the value of helpers.getFieldValue
  root: {
    voucher: { semester: '1', campus: 'Anhembi', course: 'Engenharia Mecânica' },
    birth_date: '20/09/2002',
    document: '22841363031',
    name: 'João da Silva',
    partner_service_id: 23,
    category_id: 1,  # here is the field/value i want
    person_subscription_id: 2,
    params: {}
  },
  tip: { semester: '1', campus: 'Anhembi', course: 'Engenharia Mecânica' },
  value: '1'
}
[1623445573524] ERROR    (api/74 on 4e98c73e0aab): "find" expects a value. Received undefined

The field "root.category_id" exists, but the function validator.helpers.getFieldValue returns undefined. Do I need to change the way i am getting the value of "category_id" to validate the "semester" field?

BONUS (a sample repo to reproduce the issue)

thetutlage commented 3 years ago

The reason you get undefined is, because the value exists inside the root and not the tip. To help further I want to see how you are using this rule

rodrigorz commented 3 years ago

I thought if it didn't exist in tip, it would get the value of root.

  // validator schema
  public schema = schema.create({
    person_subscription_id: schema.number([
      rules.unsigned(),
      rules.exists({
        table: PersonSubscription.table,
        column: 'id',
      }),
    ]),
    category_id: schema.number([
      rules.unsigned(),
      rules.exists({
        table: Category.table,
        column: 'id',
      }),
    ]),
    partner_service_id: schema.number([
      rules.unsigned(),
      rules.exists({
        table: PartnerService.table,
        column: 'id',
      }),
    ]),
    name: schema.string({ trim: true }),
    document: schema.string({ trim: true }),
    birth_date: schema.date({ format: 'dd/MM/yyyy' }),
    voucher: schema.object().members({
      course: schema.string({ trim: true }),
      campus: schema.string({ trim: true }),
      year: schema.string({ trim: true }),
      semester: schema.string.optional({ trim: true }, [
        rules.requiredIfCategorySemiannual({ categoryField: 'category_id' }),
      ]),
    }),
  });

The semester is inside voucher object, the categoryField value needs to be different? Or is better work with refs, to pass the Id number to my custom rule?

thetutlage commented 3 years ago

Nope, it is about meeting the expectation of the user defining the schema. If you look at the trimmed down version of the schema

voucher: schema.object().members({
  semester: schema.string.optional({}, [
    rules.requiredIfCategorySemiannual({ categoryField: 'category_id' }),
  ])
})

It means, the semester field relies on the sibling category_id and not the category_id from anywhere.

If you want to target the category id one level up, then it has to be prefixed with /. Meaning search in the root of the object.

rules.requiredIfCategorySemiannual({ categoryField: '/category_id' }),

Again, it is not about the technical limitations, but more to do with intent

rodrigorz commented 3 years ago

Thanks, problem solved.

Just one more question, if category_id is inside of another object i need to pass the full path?

rules.requiredIfCategorySemiannual({ categoryField: '/anotherObject/category_id' }),
thetutlage commented 3 years ago

Yup. Closing, since it is not an issue