Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow access to parent schema (and unlimited ancestors!) in test context #556

Merged
merged 12 commits into from
May 20, 2020

Conversation

akmjenkins
Copy link
Contributor

I made a terrible attempt at this PR earlier. This should do it.

This resolves #551 and adds the capability to help with #553.

@akmjenkins akmjenkins changed the title Allow access to parent schema in test context Allow access to parent schema (and unlimited ancestors!) in test context Jun 20, 2019
@akmjenkins
Copy link
Contributor Author

akmjenkins commented Jun 20, 2019

I've updated this PR, hope you don't mind. I know accessing multiple ancestors has been an issue people have been trying to solve around here and I thought that this PR could be easily expanded to solve it. Ideally, the existing parent property in the test context would be a function, but in the interest of not making a breaking change, I've added a from in the test context that returns the parent value, the parent schema and, if applicable, another from method which can be used to access unlimited ancestors like so:

  test: function() {
     // get the parent schema, value, and another from, if applicable
     const { schema, value, from } = this.from(); 

     // get an ancestor a long way back
     const wayBackAncestor = this.from().from().from().from()....
  }

This should make work regarding #201 a breeze.

src/object.js Outdated Show resolved Hide resolved
@akmjenkins
Copy link
Contributor Author

Thought I'd add a newer comment to override my older one. The most up to date proposal is for accessing parent(s) and parent schema(s) in a test context is to look like this:

  test() {
     const { value: parent, schema: parentSchema } = this.from[0];
     const { value: grandparent, schema: grandparentSchema } = this.from[1];
     ....
     const { value: gggggggrandparent, schema: gggggggrandparentSchema } = this.from[7]
  }

@BenevidesLecontes
Copy link

@jquense we need this desperately.

Copy link
Owner

@jquense jquense left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

sorry the delay folks, short on time!

src/object.js Outdated
@@ -143,6 +145,9 @@ inherits(ObjectSchema, MixedSchema, {
return value;
}

from = originalValue
? from
: [...from].splice(0, 1, { schema: this, value: originalValue });
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

splice returns the removed item not the array, is that what you want here?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No, it's not what I wanted to do, thanks for the catch. I've updated this.

src/object.js Outdated Show resolved Hide resolved
@jquense
Copy link
Owner

jquense commented Jul 2, 2019

what happens if you have an array inbetween?

@akmjenkins
Copy link
Contributor Author

akmjenkins commented Jul 3, 2019

what happens if you have an array inbetween?

I've added a modified the test case to showcase this, but in doing so I don't like what I've done. I'm still missing something in how refs are resolved. I made some comments in my test, @jquense hope you could offer me some suggestions?

test/mixed.js Outdated Show resolved Hide resolved
@yanzou
Copy link

yanzou commented May 15, 2020

@akmjenkins any update on this?
it would be a great help for me if this get merged!

it takes me 3+ misrable days googleing/check issues/the Yup source code and realize it is not possible to get values from ancestors...

test/mixed.js Outdated Show resolved Hide resolved
@jquense jquense merged commit db35920 into jquense:master May 20, 2020
@HansAarneLiblik
Copy link

HansAarneLiblik commented May 28, 2020

This could really use documentation and this is missing from @types/yup

@ghost
Copy link

ghost commented Jun 9, 2020

i need documenation for this

@geocine
Copy link

geocine commented Jun 10, 2020

For anyone wondering how this could be used this is how.

  const schema = Yup.object().shape({
    origin: Yup.object().shape({
      location: Yup.string().required(),
      port: Yup.string()
        .nullable()
        .test('customValidation', 'Error', function(val) {
          const { parent, from } = this;
          
          // Before we can only access the immediate parent values
          // of this property which is `origin` so we can get `parent.location`

          // But what if we want to get `destintation.location` , with parent, this is not possible
          // now we can, using, `from[1].value.destination.location` for getting the value 
        })
    }),
    destination: Yup.object().shape({
      location: Yup.string().required(),
      port: Yup.string().required()
    })
  });

From the context of origin.port above. If you need n levels up just use from[n] where n is the number of levels starting from 0

So getting back at the example above parent.location will be the same as from[0].value.location

Aside from values, this also introduced a way of getting the schema now you can get the schema at different levels using from[n].schema.

@akmjenkins
Copy link
Contributor Author

For anyone wondering how this could be used this is how.

  const schema = Yup.object().shape({
    origin: Yup.object().shape({
      location: Yup.string().required(),
      port: Yup.string()
        .nullable()
        .test('customValidation', 'Error', function(val) {
          const { parent, from } = this;
          
          // Before we can only access the immediate parent values
          // of this property which is `origin` so we can get `parent.location`

          // But what if we want to get `destintation.location` , with parent, this is not possible
          // now we can, using, `from[1].value.location` for getting the value 
        })
    }),
    destination: Yup.object().shape({
      location: Yup.string().required(),
      port: Yup.string().required()
    })
  });

From the context of origin.port above. If you need n levels up just use from[n] where n is the number of levels starting from 0

So getting back at the example above parent.location will be the same as from[0].value.location

Aside from values, this also introduced a way of getting the schema now you can get the schema at different levels using from[n].schema.

If you wanted to get destination.location you'd use from[1].value.destination.location.

Also, I was hoping this would make it relatively trivial to implement relative references, but using toposort to sort the graph has made things a little more complicated for me in this regard, hoping to get to it soon.

@geocine
Copy link

geocine commented Jun 10, 2020

If you wanted to get destination.location you'd use from[1].value.destination.location.

Oh yeah right I will correct that. Thanks for this @akmjenkins

@zelucena
Copy link

I have just come across this issue. I upgraded Yup in my project from version 0.28.1 to 0.29.1
const { parent, from } = this; const grandParent = from[1].value;
It works just fine.

@lucksp
Copy link

lucksp commented Jul 30, 2020

Are the typescript Types updated? I do not see anything with from declared, so I am having to do some nasty casting...

@adaladam
Copy link

adaladam commented Oct 5, 2020

awesome work guys, thank you!

@singlapradeep27
Copy link

will it work for when too?

@adjenks
Copy link

adjenks commented Feb 27, 2024

This still isn't in the docs.

@adjenks
Copy link

adjenks commented Feb 27, 2024

will it work for when too?

You can try stuffing the values object into the context parameter during validation, then accessing the values with the "$" context prefix. This worked for me, but it seems to have some limitations of its own.

@BenJackGill
Copy link

Why is this not documented? It's very useful!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Access parent schema in test