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

More Comprehensive Directive Support #7

Open
dupski opened this issue Jun 10, 2018 · 8 comments
Open

More Comprehensive Directive Support #7

dupski opened this issue Jun 10, 2018 · 8 comments

Comments

@dupski
Copy link
Collaborator

dupski commented Jun 10, 2018

There is a need to support Directives, currently just the @client directive for apollo, however I would like us to implement this in a generic way to allow for possible use of @skip(if: ...), etc. if needed.

Relavant GraphQL Spec:
http://facebook.github.io/graphql/October2016/#sec-Directives-Are-In-Valid-Locations
Medium article:
https://medium.com/front-end-developers/graphql-directives-3dec6106c384

Initial Requirements:

  • Directives can be added to any node in the tree
  • Multiple directives can be added for a single node

Future Requirements

  • Directives can have arguments
@dupski
Copy link
Collaborator Author

dupski commented Jun 10, 2018

Possible way to implement:

Looking at the usage of @client from https://github.com/apollographql/apollo-link-state

How about something like this:

const query = {
  mutation: {
    updateNetworkStatus: {
      __args: {
        isConnected: true
      },
      __directives: {
        client: true
      }
    }
  }
};

This would then result in:

mutation {
    updateNetworkStatus(isConnected: true) @client
}

this should work for sub-nodes too, e.g.:

const query = {
  Post: {
    id: true,
    name: {
      __directives: {
        some_directive: true
      }
    }
  }
};

This would then result in:

query {
    Post {
      id
      name @some_directive
    }
}

It would be cool to support directive arguments too, e.g.:

const query = {
  Post: {
    id: true,
    name: {
      __directives: {
        myDirective: {
          directiveArg: 20
        }
      }
    }
  }
};

@joeflack4
Copy link
Contributor

joeflack4 commented Jun 11, 2018

Adding this support sounds useful. However, I'm wondering about support for the particular use case I have right now.

I have this object:

const everyDayHealthFocuses = {
    diet: {
        id: 'diet',
        options: {
            'calorie-count': {
                category: 'Diet',
                icon: 'fa fa-question-circle',
                id: 'calorie-count',
                selected: false,
                text: 'Calorie Count'
            },
            mood: {
                category: 'Diet',
                icon: 'fa fa-question-circle',
                id: 'mood',
                selected: false,
                text: 'Mood'
            },
            weight: {
                category: 'Diet',
                icon: 'fa fa-question-circle',
                id: 'weight',
                selected: false,
                text: 'Weight'
            },
        },
        title: 'Diet'
    },
};

I want a single function that I can call which will transform this object into a graphql query string, with the addition of the directive which instructs that this should be querying my local cache, e.g. @client:

query {
  diet @client {
    id
    options {
      'calorieCount' {
        category
        icon
        id
        selected
        text
      }
      mood {
        category
        icon
        id
        selected
        text
      }
      weight {
        category
        icon
        id
        selected
        text
      }
    }
    title
  }
}

As background, this object is an actual object being used in a React component in one of my applications.

As long as I can get this functionality, I'm happy.

I think that what you proposed can be in intermediate step between these two, e.g.:

const query = {
    diet: {
        __directives: {
          '@client': true
        }
        id: true,
        options: {
            'calorie-count': {
                category: true,
                icon: true,
                id: true,
                selected: true,
                text: true,
            },
            mood: {
                category: true,
                icon: true,
                id: true,
                selected: true,
                text: true,
            },
            weight: {
                category: true,
                icon: true,
                id: true,
                selected: true,
                text: true
            },
        },
        title: true
    },
};

Does something like this seem agreeable? I was going for an approach similar to this, but it seemed like more complex / longer code than simple string manipulation. But I think the string manipulation approach I was going for might not be quite as effective for a wider number of use cases.

Also I'm trying to think about the approach you're trying to go for with "__directives" and "new WithDirectives()". I assume these are just ideas and you would be leaving the approach for how to finish adding the functionality to me? I've used ES6 classes for react, but never otherwise to combine with the "new" keyword to create an object in this instance. I'm not sure how I would implement this. You mentioned later that you think the "__directives" approach would also work; I would also prefer that approach.

Before the string manipulation method, I was simply looking for the last key in my object, and then simply replacing its value with {apolloTarget: {'@client': true}}, "apolloTarget" just being arbitrarily chosen; I could name it something else of course. I didn't finish that approach completely, though.

On another aside, I'm still new with Apollo and was actually not building my query correctly before. I thought that I was supposed to add @client at the end of my query, as with mutations. But I think with queries, it looks like, if I'm correct, the only place I'd add @client would be between key names in the root of the query, and the next opening curly brace, e.g.:

query {
  key1 <could add a directive here> {
    ...
  }
  key2 <could also add a directive here> {
    ...
  }
  ...
}

On an aside, I wonder what the API for this functionality should be... Right now I have it as:

import { objToApolloQuery } from 'json-to-graphql-query';
...
objToApolloQuery(obj, target);
// ex: objToApolloQuery({a: {b: 'c'}}, 'client')

Since in my use case, my entire object will reside in the local cache, maybe export a dedicated function just for that? Ex: objToApolloLocalCacheQuery({a: b: 'c'}})

Idk, let me know if you also have a preference as to how I should expose this functionality to users.

@dupski
Copy link
Collaborator Author

dupski commented Jun 11, 2018

Nice Joe

Yep your example above using __directives is exactly what I was thinking. I don't think we'll need the WithDirectives thing so please disregard that! :)

I now also don't think we should have a specific objToApolloQuery method in the library because Apollo is not doing anything non-standard.

All you will need in your own code is a method that does something like:

// get your "data" object, then...
data.diet['__directives`] = { client: true };
const query = jsonToGraphqlQuery(data, { keysToStrip: ['__typename'] });

@joeflack4
Copy link
Contributor

joeflack4 commented Jun 13, 2018

Hey Russell,
Today I did a refactor of the code in PR #6, which includes support for directives, starting with arg-less ones, e.g. Apollo's "@client".

@joeflack4
Copy link
Contributor

  • Added support for queries that have arguments.
  • Added capability for nodes to have args and directives simultaneously.

@dupski dupski changed the title Support for Directives More Comprehensive Directive Support Jul 2, 2018
@dupski
Copy link
Collaborator Author

dupski commented Jul 2, 2018

Merged and released Joe's PR, but there are still a few open items:

  • We need to add support for multiple directives on one node
  • There are a few TODOs in the code and tests that need sorting
  • It would be good to support arguments for directives as per the graphQL spec.

PRs welcome as always! :)

@vkolgi
Copy link
Owner

vkolgi commented Dec 20, 2021

Multiple directives on one node is kind of supported through this PR (#33) and merged.

@joeflack4
Copy link
Contributor

Thanks!

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

No branches or pull requests

3 participants