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

Automate subscriptions with links #42

Open
ujwal-setlur opened this issue Aug 2, 2018 · 4 comments
Open

Automate subscriptions with links #42

ujwal-setlur opened this issue Aug 2, 2018 · 4 comments
Labels
enhancement New feature or request

Comments

@ujwal-setlur
Copy link
Contributor

ujwal-setlur commented Aug 2, 2018

I have 2 collections: users and groups. I have 2 main link sfrom groups to users based on memberIds and adminIds fields. Essentially who are the members and who are the admins.

Here is my GraphQL schema with schema directives:

Group:

type Group @mongo(name: "groups")
{
  _id: ID!
  name: String
  description: String
  members: [User] @link(field: "memberIds")
  admins: [User] @link(field: "adminIds")
}

type ReactiveEventGroup {
  event: String,
  doc: Group
}

type Query {
  groups(_id: String): [Group]
}

type Subscription {
  groups: ReactiveEventGroup
}

Here is my User schema:

type Email
{
	address: String
	verified: Boolean
}

# our user type
type User @mongo(name: "users")
{
  _id: ID!
  emails: [Email]
  firstName: String
  lastName: String
  name: String
  company: String
  roles: [String]
  groups: [Group] @link(to: "members")
  admins: [Group] @link(to: "admins")
}

# our queries
type Query {
  users: [User]
  me: User
}

Here is how I do my subscription:

const GET_GROUPS = gql`
  query {
    groups {
      _id
      name
      description
      members {
      	_id
      	name
        company
      }
      admins {
      	_id
      }
    }
  }
`;

const SUBSCRIBE_GROUPS = gql`
  subscription {
    groups {
      event
      doc {
        _id
        name
        description
        members {
        	_id
        	name
          company
        }
        admins {
        	_id
        }
      }
    }
  }
`;

  render() {
    return (
      <ReactiveQuery
        query={GET_GROUPS}
        subscription={SUBSCRIBE_GROUPS}
      >
        {({data, loading, error}) => {
           if (loading) {
             return <Text>
                      Loading
                    </Text>;
           }
           if (error) {
             return <Text>
                      {error}
                    </Text>;
           }
           return <Groups
                    groups={data.groups}
                    onPress={this.viewGroup}
                    ref={(ref) => {
                           this.ref = ref;
                         }}
                    onSwipe={this.swipeButtons}
                  >
                  </Groups>;
         }}
      </ReactiveQuery>
      );
  }

Here are my query and subscription resolvers:

  Query: {
    groups(_, args, {db, userId}, ast) {

      // sanity checks
      if (!userId) {
        throw new Meteor.Error("authorization-error", "Action not authorized");
      }

      let filter = {
        memberIds: userId
      };
      if (args._id) {
        filter._id = args._id;
      }
      // use grapher to do the query instead of direct mongo query
      let allGroups = db.groups.astToQuery(ast, {
        $filters: filter
      }).fetch();
      return allGroups;
    }
  },
  Subscription: {
    groups: {
      resolve: payload => payload,
      subscribe(_, args, context) {
        console.log("Calling groups subscription; user = " + context.userId);

        // sanity checks
        if (!context.userId) {
          throw new Meteor.Error("authorization-error", "Action not authorized");
        }

        const observer = context.db.groups.find();

        return asyncIterator(observer);
      }
    }
  }

On initial load, all is well and good. I get a good list with all the data. However, when I create a new group, say with GraphQL playground, then I run into a problem. I do get the group created event, but I do not get all the information about the group, particularly the ones attached to links.

Here is the mutation that added the query:

mutation saveGroup {
  saveGroup(name: "Ujwal and Vineet", 
    members: ["WaHgrKPe6ePJJuE2o", "yf2AsaHQN36TqKtFc"], 
    admins: ["WaHgrKPe6ePJJuE2o"],
    description:"Ujwal is admin") {
  		success    
  }
}

Here is the update record I got:

{
  "_id": "DbvtEuZrwZYJpZh4x",
  "name": "Ujwal and Vineet",
  "description": "Ujwal is admin",
  "members": null,
  "admins": null,
  "__typename": "Group"
}

Notice that I get only _id, name, and description. I don't get members and admins fields. Both those fields have links on them pointing to User.

What am I doing wrong?

@ujwal-setlur
Copy link
Contributor Author

This is a bit of a critical issue for me, so any help will be greatly appreciated. Thanks!

@ujwal-setlur
Copy link
Contributor Author

OK, I think it is explained here:

https://github.com/cult-of-coders/apollo-live-server

The current limitation is that the reactive events dispatched by this publication are only at Notification level in the database, but what happens when we want to also send down the pipe relational info, such as, Task linked to Notification by taskId, how can we handle this ?

@ujwal-setlur
Copy link
Contributor Author

OK, I solved it by enhancing the resolver for the subscription:

  Subscription: {
    groups: {
      resolve: (payload, args, {db}) => {
        if (payload.event === Event.ADDED) {
          // do a direct grapher query to get the linked data
          let linkedDoc = db.groups.createQuery({
            $filters: {
              _id: payload.doc._id
            },
            _id: 1,
            name: 1,
            description: 1,
            members: {
              _id: 1,
              name: 1,
              company: 1
            },
            admins: {
              _id: 1,
              name: 1,
              company: 1
            }
          }).fetch();
          if (linkedDoc && linkedDoc.length) {
            // there should be only one record since we filtered on the added doc
            return ({
              doc: linkedDoc[0],
              event: payload.event
            });
          }
          // something went wrong, send back original payload
          return (payload);
        }
        return (payload);
      },
      subscribe(_, args, context) {
        console.log("Calling groups subscription; user = " + context.userId);

        // sanity checks
        if (!context.userId) {
          throw new Meteor.Error("authorization-error", "Action not authorized");
        }

        const observer = context.db.groups.find({
          memberIds: context.userId
        });

        return asyncIterator(observer);
      }
    }
  },
...

It would be nice to figure out what the associated query was in the initial ReactiveQuery subscription, so that we know exactly what date to send back.

But this works for now...

@theodorDiaconu
Copy link
Contributor

@ujwal-setlur indeed, this is currently a limitation, and even if we do this, grapher could export a functionality that does this essentially, however it's very problematic with links in which storage is on the other side, because you have to watch those documents too! So it kinda sucks, and we have to rely on something like publish-composite but for Apollo.

This would be a good feature request.

@theodorDiaconu theodorDiaconu changed the title Subscriptions on main links do not send info on fields that have links attached Automate subscriptions with links Aug 17, 2018
@theodorDiaconu theodorDiaconu added the enhancement New feature or request label Aug 17, 2018
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request
Projects
None yet
Development

No branches or pull requests

2 participants