busypeoples / spected

Validation library
MIT License
702 stars 32 forks source link

Its possible to tell if the whole structure has been validated without errors? #90

Open cloud-walker opened 7 years ago

cloud-walker commented 7 years ago

A function like allTheFieldOfTheNestedStructureAreTrue

I need to know if there is at least an error or not, to integrate spected with Formik

The formik hoc validate hook expect an empty object if the form has no errors, so I think to do something like

{
  validate: values => {
    const res = spected(validationSpecs, values)

    return isValid(res) ? ({}) : res
  }
}
busypeoples commented 7 years ago

Should work without too much work!

busypeoples commented 7 years ago

The isValid has to be implemented in user land, as spected can't know how your data structure looks like. i.e. A successful data structure might look like the following:

{ name: true, nr: true }

or like this i.e.:

{ name: null, nr: null}

This is due to the fact that spected also offers the low level validate function which enables oto define own success and fail callbacks, see also API Docs

Here is an example showing how isValid could be implemented for a regular spected call.

import spected from 'spected'

const data = {
  nr: 6,
  name: 'foobar'
}

const spec = {
  nr: [[ nr => nr > 2, 'Nr has to be greater than 2' ]],
  name: [[ name => name.length > 4, 'Name has to be minimum lenght of 5!' ]]
}

const r1 = spected(spec, data)

const isValid = input => Object.values(input).filter(x => x !== true).length === 0 

// or functional...
// const isValid = R.compose(R.isEmpty, R.filter(x => x !== true), R.values)

console.log(r1, isValid(r1))

Also check the demo: http://jsbin.com/liragopide/edit?js,console,output

Also check the demo using the low level validate: http://jsbin.com/nisuyadecu/1/edit?js,console,output

cloud-walker commented 7 years ago

Yes! I've already tought about that, btw my problem is that my form values structure is nested:

const values = {
  email: 'bruce.wayne@wayne.enterprise',
  posts: [
    {
      title: 'Learn functional programming the right way',
      body: 'lorem',
    },
    {
      title: 'How to become a better developer',
      body: 'lorem',
    },
  ],
}

So reading your source code, I think the best way to do it is an helper (?) that can take advantage of the same validationSpecs used on the normal spected function to return true / false

Like, I'm guessing here:

const values = {...}
const validationSpecs = {...}

isValid(validationSpecs, values) // true or false

The only problem is that I need now to run twice the algorithm, one for isValid and one for the actual validation output... mmmh!

busypeoples commented 7 years ago

Nested shouldn't be a problem either. Check isValid in the revalidation library. It works with nested structures. https://github.com/25th-floor/revalidation/blob/master/src/utils/isValid.js

This is how revalidation calculates the valid state after validating with spected. So I would pass the result returned from calling spected to the isValid function.

revalidation defines the success callback like this:

() => []

https://github.com/25th-floor/revalidation/blob/master/src/validate.js

So you might have to adapt the isValid function to check if true f.e.

busypeoples commented 7 years ago

If you need any help, let me know. I really like the idea of combing spected with Formik

cloud-walker commented 7 years ago

No I think its ok, the only concern may be performance because I need first to run spected to generate the validation results, and then run a isValid function over it (so re-visiting the structure) over the validation results to determine if there is an error or not.

I continue to suspect that the spected validate function itself should return an errors count or true or false if validation is passed or not, like:

spected(validationSpecs, values)
/*
  The function above returns {
    valid: true | false,
    schema: {
       here the normal validation object of the spected library
    }
  }

  or {
    count: 0 or n errors counted during reduce,
    schema: {
       here the normal validation object of the spected library
    }
  }
*/

I can't imagine a better solution but it feels ugly... and also could be a breaking change..

daniele-rapagnani commented 7 years ago

Hey, what about something like this:

const specialSpected = (spec, input) => {
  let valid = true;

  const failFunc = (f) => {
    if (valid) {
      valid = false;
    }

    return f;
  };

  const validation = validate(() => true, failFunc, spec, input);

  return {
    valid,
    validation
  };
}

In this way you have no performance hit because you don't have to traverse the object again. I don't know if a solution like this should be provided by spected itself, I can't think of an elegant way to integrate it in the current API other than providing another function, which sounds like unnecessary complexity.

(demo: http://jsbin.com/coqodotuyo/edit?js,console,output)

cloud-walker commented 7 years ago

I've sketched out a possible isValid function, here: http://jsbin.com/seworut/edit?js,output

For @daniele-rapagnani : I don't like to integrate the current idea either, but I still think it should be in the library, as its a common case, and I bet the most of the libraries out there will need it.

Cool trick BTW!

busypeoples commented 7 years ago

@cloud-walker @daniele-rapagnani Thanks for the very valuable input! I would rather avoid making isValid or valid a part of the API. I would rather like to see spected as a part of a pipeline. But I'll keep the issue open, maybe we can come up with a reasonable solution.

cloud-walker commented 7 years ago

Sure! Maybe we can add refined version of the @daniele-rapagnani recipe on the future FAQ?

ref: https://github.com/25th-floor/spected/issues/82

busypeoples commented 7 years ago

Yes, this would be really useful!