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

Support for fragment interpolation in Apollo #29

Closed
jnwng opened this issue Nov 23, 2016 · 9 comments · Fixed by #33
Closed

Support for fragment interpolation in Apollo #29

jnwng opened this issue Nov 23, 2016 · 9 comments · Fixed by #33

Comments

@jnwng
Copy link
Contributor

jnwng commented Nov 23, 2016

Now that we have support for fragment interpolation via graphql-tag (link to relevant deprecation notice in graphql-fragments), we can now do the following in Apollo:

const fragment = gql`
  fragment Foo on SomeResource {
    someField
    ...SomeOtherFragment
  }
  ${SomeOtherFragment}

However, using this syntax gets caught in eslint-plugin-graphql:
image, because previously Apollo recommended against interpolation.

Presumably this is okay to re-enable, but we'd want to still guard against other kinds of interpolation. Perhaps we can allow interpolation if it occurs outside any fragment/query/mutation declarations?

@stubailo
Copy link
Contributor

Perhaps we can allow interpolation if it occurs outside any fragment/query/mutation declarations?

I think that's a good call!

@jnwng
Copy link
Contributor Author

jnwng commented Nov 23, 2016

i dove into this last night, and had some trouble figuring out a good solution to this - wanted to share some context in case you all might have ideas on how to address.

in particular, at the time that we're going through our TaggedTemplateLiteral to replace fragment interpolation, we haven't yet "parsed" the document into GraphQL, so we don't know the "structure" of the particular element we're checking. basically, we dont know whether or not the template interpolation is occurring within a query / mutation / fragment structure (yet).

in the relay and lokka clients, fragment definitions themselves are not interpolated, just the "names" of those fragments (i.e., via ...fragmentName). in apollo, we're interpolating the actual definition. so in this case, we could skip the fragment expression replacement altogether, and we'd resolve the issue, which means:

gql`query { someField } ${someFragment}` 

would get parsed as

gql`query { someField }`

of course, this would not guard against the bad case, which is interpolation within the query / mutation / fragment structures themselves (which is pretty important). we'd need to get to the graphql "parse" step though to be able to do that (i think).

couple of solutions that i wanted to spitball:

  1. interpolate a "fake" fragment definition
    that would turn this:
gql`query { someField } ${someFragment}` 

into

gql`query { someField } fragment someFragment on SomeResource { someField }`

unfortunately, this will not validate against a provided remote schema, but would get us a little farther in that graphql itself would lint against the "interpolation in invalid structures" case.

  1. in apollo, skip the expression replace and define an apollo-only parse step
    this would happen before the graphql parse step, and would deal with checking the valid and invalid interpolation cases before stripping the interpolation entirely and getting the document checked by graphql

  2. don't parse anything, guess the structure of the document
    obviously, the weakest solution - count the brackets in the TaggedTemplateLiteral and if this interpolation occurs when there's an open bracket in the document, throw the "invalid interpolation" error, otherwise, you're at the top-level and you're good to go (in apollo).

any thoughts on alternatives?

@tmeasday
Copy link

@jnwng I'm not really up to speed with how this package works but in your solution 1., why could we not just insert the actual definition from the fragment? That's how graphql-tag works.

@jnwng
Copy link
Contributor Author

jnwng commented Nov 25, 2016

@tmeasday to provide you some context, what we're evaluating here is the actual javascript AST - not actually executing the code. so at this particular time, we don't actually have the runtime "fragment" definition referenced by the variable being interpolated, just the knowledge that there is a variable being interpolated. here's a visualization of said AST: http://astexplorer.net/#/2UxKgxp2pf (you can click on the template literals and see the corresponding AST on the right).

make sure to select the babel-eslint option like this:
image, which will make sure the AST representation is what we're evaluating.

basically, what we're doing is evaluating the javascript AST first, making some graphql-related substitutions to said AST, and subsequently evaluating the GraphQL AST (which is going to parse the actual GraphQL document). its the first part that's giving me some trouble, since in the javascript AST we don't actually know what the structure of the query is going to look like.

@jnwng
Copy link
Contributor Author

jnwng commented Dec 5, 2016

ping @stubailo, i'm sure you had way too many github notifications to follow up on after your vacation :)

@stubailo
Copy link
Contributor

stubailo commented Dec 6, 2016

of course, this would not guard against the bad case, which is interpolation within the query / mutation / fragment structures themselves (which is pretty important). we'd need to get to the graphql "parse" step though to be able to do that (i think).

Wouldn't a janky solution of "no interpolation allowed except outside all curly braces" work to identify this case? We already have to do some sketchy stuff for relay interpolation like generate a fake fragment name so it doesn't seem that much worse :P

@jnwng
Copy link
Contributor Author

jnwng commented Dec 6, 2016

deleted my previous comment because i only realized what you meant - i can combine a couple of these solutions to get to the right place. we'll first 1) remove the fragment from the interpolation step, then 2) make sure its outside of the curly braces to negate the bad case i mentioned above (so, combining the first and third solutions). thanks for the insight!

i'll take this if that's okay!

@stubailo
Copy link
Contributor

stubailo commented Dec 6, 2016

Yeah that would be awesome!

@msmfsd
Copy link

msmfsd commented Jan 30, 2017

Thanks @jnwng for this fix!

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 a pull request may close this issue.

4 participants