cashay icon indicating copy to clipboard operation
cashay copied to clipboard

Implement `NOT_NULL` and `INTERFACE` schema kinds

Open ksmth opened this issue 9 years ago • 12 comments
trafficstars

I'm currently evaluating a few alternatives to Relay and I wanted to see if I could drop cashay in and get a feel for it. Right now it doesn't handle NOT_NULL and INTERFACE kinds properly.

I'm talking about this function: https://github.com/mattkrick/cashay/blob/3a4bfbcdc2b17f497dd4626a9c8083f49c283898/src/normalize/denormalizeStore.js#L80

From the GraphQL docs about NON_NULL:

// http://graphql.org/docs/api-reference-type-system/#graphqlnonnull
A non-null is a kind of type marker, a wrapping type which points to
another type. Non-null types enforce that their values are never null
and can ensure an error is raised if this ever occurs during a
request. It is useful for fields which you can make a strong guarantee
on non-nullability, for example usually the id field of a database row
will never be null.

And here's the thing for INTERFACE:

When a field can return one of a heterogeneous set of types, a
Interface type is used to describe what types are possible, what fields
are in common across all types, as well as a function to determine 
which type is actually used when the field is resolved.

ksmth avatar May 27 '16 12:05 ksmth

the only time non-null is useful is to create variableDefinitions. Aside from that, non-null is an annoying shell we have to take off when walking the ast. https://github.com/mattkrick/cashay/blob/3a4bfbcdc2b17f497dd4626a9c8083f49c283898/src/normalize/denormalizeStore.js#L57

Interface info is irrelevant, since there's no good reason to know what fields are guaranteed, only what fields are provided. In the future, i'll remove this entirely from the client schema to make the payload even smaller.

I'm pretty confident cashay can handle just about anything you can throw at it, and there are tests to prove that it works with everything you mentioned: https://github.com/mattkrick/cashay/blob/3a4bfbcdc2b17f497dd4626a9c8083f49c283898/src/normalize/tests/denormalizeStore-tests.js

If you can find something that cannot be denormalized, please add a test for it here :smile:

mattkrick avatar May 27 '16 12:05 mattkrick

Hm, it doesn't denormalize NON_NULL properly as of now. In my particular case, the subState is a normalized string that should be visited using visitNormalizedString, but is not.

ksmth avatar May 27 '16 12:05 ksmth

Can you paste a snippet of your client schema?

mattkrick avatar May 27 '16 12:05 mattkrick

{
  "kind":"OBJECT",
  "name":"Me",
  "fields":{
    "account":{
      "name":"account",
      "type":{
        "kind":"NON_NULL",
        "ofType":{
          "kind":"INTERFACE",
          "name":"Account"
        }
      }
    },
    "clientEncryptionKey":{
      "name":"clientEncryptionKey",
      "type":{
        "kind":"NON_NULL",
        "ofType":{
          "kind":"SCALAR",
          "name":"String"
        }
      }
    }
  }
}

ksmth avatar May 27 '16 12:05 ksmth

account isn't deserialised properly

ksmth avatar May 27 '16 12:05 ksmth

i've never used an interface as a field before, usually i build an interface internally & then other fields use that: https://github.com/mattkrick/cashay/blob/master/src/tests/schema.js#L75

Could you help me understand why account needs to be an interface? Or come up with an example that i could use to make a test case?

mattkrick avatar May 27 '16 13:05 mattkrick

Right now I can't tell you why it needs to be an interface, as I'm not responsible for the API / schema, I'm only consuming it. I'm not sure when I'll have time to dig into creating a working schema for the test cases

ksmth avatar May 27 '16 13:05 ksmth

Talk to your back end dude, seems like account should be an object or maybe a union, but if theres a good use case for it I'll be glad to add it, I've just never come across one...

mattkrick avatar May 27 '16 14:05 mattkrick

We're using an Interface instead of a Union, since it makes querying for all common fields that are shared by all implementations easier. I have tried a few times now to come up with either failing tests or a working solution, but there's quite a few things that haven't been working (like updating the schema), but I'll create separate tickets for that.

ksmth avatar Jun 08 '16 20:06 ksmth

oh for sure, still very much in alpha as stuff is changing on the daily, but i think we (finally) got a good flow for schema creation as of this morning (yay!). The getState param in the mutationHandlers callback is still broken, but everything else should be working. I'm on a sprint for building a subscription method, but after that's done i should have some time to write some more proper unit tests.

WRT interfaces, i'd settle for a comparison of how you write the query for interface vs. how you'd have to write it for a union. For unions I'd write something like:

query {
  getObj {
    foo,
    bar,
    ... on TypeA {baz}
    ... on TypeB {faz}
  }
}

If it's easier to use an interface, I'd love to learn! Just cuz i can walk an AST doesn't mean i'm an expert by any means :smile:

mattkrick avatar Jun 08 '16 20:06 mattkrick

From what I understood is that you can't directly query the foo field in a Union, but you have to put it into each subtype. I'm not at the computer right now, but I'll verify this and report back. If querying doesn't look any different, I suppose there's not much reason to choose one over the other.

Shouldn't it be supported anyway if there's no advantage to use either Union or Interface? Solely because the spec allows it, I mean. Don't feel pushed, though, I'm merely experimenting with cashay at this point. ☺️

ksmth avatar Jun 08 '16 20:06 ksmth

Absolutely, if the spec allows it, cashay should support it, i just desperately need an example that fits into the test schema so I can write tests against it.

mattkrick avatar Jun 08 '16 22:06 mattkrick