Giter Club home page Giter Club logo

Comments (28)

ruiaraujo012 avatar ruiaraujo012 commented on May 31, 2024 1

No, but that's an interesting idea!

I think so, in my current implementation, this is important to select the duplicated option, and with the code that I've shared here, it works.
image

With my current code pipeline actions accept a dataset as the first argument and return a dataset as a result. This will allow you to easily write your own actions and add as many issues as you want. I plan to publish a first draft next week.

Those are great news, I'm looking forward to testing it.

from valibot.

ruiaraujo012 avatar ruiaraujo012 commented on May 31, 2024 1

Although it's not recommended, I'll let v.custom with throw as it is working as I need, I'll come here again when the rewrite is done to update it. And perhaps close the issue then with a solution.

In summary, I would like to have:

  • An option to validate every element of the array and add the issue to the index of the occurrence (in this example, otherNationalities.2.
  • An option to add issues globally to the schema based on field relations:
const Schema = v.object(
  {
     mainNationality: v.string([v.minLength(1, 'required.nationality')]),
     otherNationalities: v.array(v.string([v.minLength(1, 'required.otherNationalities')]), [
        v.minLength(1, 'required.otherNationalities'),
        (custom valiration to check duplicated)
     ])
    (...other fields)
  },
  [
    v.<someMethod>((inputs) => {
      // If some element in otherNationalities is equal to the mainNationality, 
      // add an issue to the otherNationalities item index
    }),
  ],
);

from valibot.

ruiaraujo012 avatar ruiaraujo012 commented on May 31, 2024 1

Oh, that's what I need, I thought that the custom was deprecated. Thanks. I'll play a bit with the current state of the lib.

Please update this issue when this action gets done.

from valibot.

fabian-hiller avatar fabian-hiller commented on May 31, 2024 1

You are right. I will probably add it today or tomorrow. I will ping you then.

from valibot.

fabian-hiller avatar fabian-hiller commented on May 31, 2024 1

I am working on it and will probably add it later today.

from valibot.

ruiaraujo012 avatar ruiaraujo012 commented on May 31, 2024 1

Thanks, I'll play with it again.

from valibot.

fabian-hiller avatar fabian-hiller commented on May 31, 2024 1

Feel free to create a new issue (or PR) to add an items action in the next weeks.

from valibot.

ruiaraujo012 avatar ruiaraujo012 commented on May 31, 2024 1

Is this issue resolved? I will close it for now, but will reopen it if not.

I'll check it ASAP and let you know.

Feel free to create a new issue (or PR) to add an items action in the next weeks.

I'll open one issue latter today.

from valibot.

ruiaraujo012 avatar ruiaraujo012 commented on May 31, 2024 1

I agree, let's do the following, I'll close this issue and open a new one with a recap of this one.

from valibot.

fabian-hiller avatar fabian-hiller commented on May 31, 2024

Don't throw inside of custom. Instead always return a boolean: https://valibot.dev/api/custom/

You can forward issues with this method: https://valibot.dev/api/forward/

from valibot.

ruiaraujo012 avatar ruiaraujo012 commented on May 31, 2024

Hum, I saw some issues where people were throwing inside custom.

Imagine this scenario, how can I achieve the same result without throwing in custom?

codOutrasNacionalidades: v.array(v.string([v.minLength(1, 'required.otherNationalities')]), [
    v.minLength(1, 'required.otherNationalities'),
    v.custom((input) => {
      const duplicates = input.filter((item, index) => input.indexOf(item) !== index);

      const issues: v.SchemaIssues = [] as unknown as v.SchemaIssues;

      if (duplicates.length > 0) {
        input.forEach((val, index) => {
          if (duplicates.includes(val)) {
            issues.push(
              createValibotIssue({
                fieldPath: `pessoa.codOutrasNacionalidades.${index}`,
                input: { 'pessoa.codOutrasNacionalidades': input },
                message: 'validation.uniqueNationalities',
                reason: 'array',
              }),
            );
          }
        });
      }

      if (issues.length > 0) {
        throw new v.ValiError(issues);
      }

      return true;
    }),
  ]),
export const createValibotIssue = ({
  message,
  reason,
  input,
  fieldPath,
}: Pick<SchemaIssue, 'message' | 'reason'> & { input: Record<string, unknown>; fieldPath: string }): SchemaIssue => ({
  context: '',
  expected: null,
  input: input[fieldPath],
  message,
  path: [
    {
      input,
      key: fieldPath,
      origin: 'value',
      type: 'object',
      value: input[fieldPath],
    },
  ],
  reason,
  received: '',
});

from valibot.

fabian-hiller avatar fabian-hiller commented on May 31, 2024

I would return false instead of throwing and error and add the error message as the second parameter to custom. But currently there is no way to return multiple issues, but I am in the process of rewriting Valibot and it will be easier then.

from valibot.

fabian-hiller avatar fabian-hiller commented on May 31, 2024

Also we have a some and every validation action specifically for arrays. You could use every to check for duplicate values.

from valibot.

ruiaraujo012 avatar ruiaraujo012 commented on May 31, 2024

I would return false instead of throwing and error and add the error message as the second parameter to custom. But currently there is no way to return multiple issues, but I am in the process of rewriting Valibot and it will be easier then.

I've seen the discussion, I'm waiting for that :).

Also we have a some and every validation action specifically for arrays. You could use every to check for duplicate values.

I didn't try it yet, but do every put the error at the index of the element?

from valibot.

fabian-hiller avatar fabian-hiller commented on May 31, 2024

I didn't try it yet, but do every put the error at the index of the element?

No, but that's an interesting idea!

I've seen the discussion, I'm waiting for that

With my current code pipeline actions accept a dataset as the first argument and return a dataset as a result. This will allow you to easily write your own actions and add as many issues as you want. I plan to publish a first draft next week.

from valibot.

fabian-hiller avatar fabian-hiller commented on May 31, 2024

I plan to publish a blog post in two or three weeks about the results and investigations of the new pipe function implementation as well as the rewrite of the library. For now, I expect the bundle size to be smaller, the performance to be faster and the types to be more accurate.

from valibot.

ruiaraujo012 avatar ruiaraujo012 commented on May 31, 2024

Hi @fabian-hiller I saw your comment on the rewrite PR, I was thinking about testing the new implementation of pipe in this context, do you have any suggestion on where I can start? Or there are no way to do this yet?

from valibot.

fabian-hiller avatar fabian-hiller commented on May 31, 2024

It is possible, but you have to write your own action. The concept is very simple. Here is an example where I have called the action "items":

import {
  _addIssue,
  type BaseIssue,
  type BaseValidation,
  type ErrorMessage,
} from 'valibot';

/**
 * Items issue type.
 */
export interface ItemsIssue<TInput extends unknown[]>
  extends BaseIssue<TInput> {
  /**
   * The issue kind.
   */
  readonly kind: 'validation';
  /**
   * The issue type.
   */
  readonly type: 'items';
  /**
   * The expected input.
   */
  readonly expected: null;
  /**
   * The validation function.
   */
  readonly requirement: (input: TInput[number]) => boolean;
}

/**
 * Items action type.
 */
export interface ItemsAction<
  TInput extends unknown[],
  TMessage extends ErrorMessage<ItemsIssue<TInput>> | undefined,
> extends BaseValidation<TInput, TInput, ItemsIssue<TInput>> {
  /**
   * The action type.
   */
  readonly type: 'items';
  /**
   * The action reference.
   */
  readonly reference: typeof items;
  /**
   * The expected property.
   */
  readonly expects: null;
  /**
   * The validation function.
   */
  readonly requirement: (input: TInput[number]) => boolean;
  /**
   * The error message.
   */
  readonly message: TMessage;
}

/**
 * Creates an items validation action.
 *
 * @param requirement The validation function.
 *
 * @returns An items action.
 */
export function items<TInput extends unknown[]>(
  requirement: (input: TInput[number]) => boolean
): ItemsAction<TInput, undefined>;

/**
 * Creates an items validation action.
 *
 * @param requirement The validation function.
 * @param message The error message.
 *
 * @returns An items action.
 */
export function items<
  TInput extends unknown[],
  const TMessage extends ErrorMessage<ItemsIssue<TInput>> | undefined,
>(
  requirement: (input: TInput[number]) => boolean,
  message: TMessage
): ItemsAction<TInput, TMessage>;

export function items(
  requirement: (input: unknown) => boolean,
  message?: ErrorMessage<ItemsIssue<unknown[]>>
): ItemsAction<unknown[], ErrorMessage<ItemsIssue<unknown[]>> | undefined> {
  return {
    kind: 'validation',
    type: 'items',
    reference: items,
    async: false,
    expects: null,
    requirement,
    message,
    _run(dataset, config) {
      if (dataset.typed) {
        for (let index = 0; index < dataset.value.length; index++) {
          const item = dataset.value[index];
          if (!this.requirement(item)) {
            _addIssue(this, 'item', dataset, config, {
              input: item,
              path: [
                {
                  type: 'array',
                  origin: 'value',
                  input: dataset.value,
                  key: index,
                  value: item,
                },
              ],
            });
          }
        }
      }
      return dataset;
    },
  };
}

Now you can write the following code:

import * as v from 'valibot';
import { items } from './your-path';

const Schema = v.pipe(
  v.array(v.string()),
  items((item) => item.startsWith('foo'), 'Your message')
);

from valibot.

ruiaraujo012 avatar ruiaraujo012 commented on May 31, 2024

Thanks. I'll try that, do you plan on implementing it in valibot?

from valibot.

fabian-hiller avatar fabian-hiller commented on May 31, 2024

Yes, I will consider it. Do you have some good alternatives names for the action?

from valibot.

ruiaraujo012 avatar ruiaraujo012 commented on May 31, 2024

The only one that I could think of is iterate.

from valibot.

ruiaraujo012 avatar ruiaraujo012 commented on May 31, 2024

I was thinking, it would be nice if the callback had 2 or 3 arguments (item, index, items) => boolean.

With that we could create validations based on other items, for example, identify duplicated values.

items((item, index, items) => items.filter((v)=> v===item).length > 1, 'Duplicated')

Let me know what you think.

Another requirement I can think that would be great is an action to validate properties based on other properties. Let me give you an example:

{
    "primaryEmail": "[email protected]",
    "otherEmails": ["[email protected]", "[email protected]"]
}

To validate that the primaryEmail is not present in otherEmails the items action doesn't work, I need some actions that allow me to add issues globally.

I hope I didn't confuse you.

from valibot.

fabian-hiller avatar fabian-hiller commented on May 31, 2024

The only one that I could think of is iterate.

Thanks! Will think about it! Both names could work.

I was thinking, it would be nice if the callback had 2 or 3 arguments (item, index, items) => boolean.

I agree!

To validate that the primaryEmail is not present in...

This requires a custom validation in the pipeline of the object definition with a forward to the appropriate field. Note: We renamed custom to check with the rewrite.

from valibot.

ruiaraujo012 avatar ruiaraujo012 commented on May 31, 2024

@fabian-hiller I was playing around and found that the forward action is missing.

from valibot.

fabian-hiller avatar fabian-hiller commented on May 31, 2024

Done

from valibot.

fabian-hiller avatar fabian-hiller commented on May 31, 2024

Is this issue resolved? I will close it for now, but will reopen it if not.

from valibot.

ruiaraujo012 avatar ruiaraujo012 commented on May 31, 2024

I'm not able to put a schema to work with this requirement, can you help here?

{
    "primaryEmail": "[email protected]",
    "otherEmails": ["[email protected]", "[email protected]"]
}

To validate that the primaryEmail is not present in otherEmails the items action doesn't work, I need some actions that allow me to add issues globally.

The forward action doesn't work because I cannot put the key and index of the duplicated email, in this case otherEmails.1.

One possible solution that came to my mind is a method that allow us to add issues to the list of issues already created by valibot validations. Something like:

v.someMethod((inputs, ctx) => {
// ...(my custom validation with ctx.addIssue())...
return ctx.issues // Or other type of return
}) 

from valibot.

fabian-hiller avatar fabian-hiller commented on May 31, 2024

Maybe we should add an advanced refine method (similar to Zod's superRefine) that allows you to directly interact with the input and output dataset of the method and not only with the raw value.

from valibot.

Related Issues (20)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.