Skip to content

Instantly share code, notes, and snippets.

@wtrocki
Last active July 23, 2020 15:53
Show Gist options
  • Save wtrocki/1434358599e7a78f1a15014ac8d2f3fa to your computer and use it in GitHub Desktop.
Save wtrocki/1434358599e7a78f1a15014ac8d2f3fa to your computer and use it in GitHub Desktop.

Simple case

const SOMETHING_CHANGED_TOPIC = 'something_changed';

export const resolvers = {
  Subscription: {
    somethingChanged: {
      subscribe: () => pubsub.asyncIterator(SOMETHING_CHANGED_TOPIC),
    },
  },
}

pubsub.publish(SOMETHING_CHANGED_TOPIC, { somethingChanged: { id: "123" }})

Simple case withFilter

See: https://github.com/apollographql/graphql-subscriptions

Using topics filtering

export const resolvers = {
  Subscription: {
    todoChanged(_parent, args, info): {
      subscribe: () => pubsub.asyncIterator(SOMETHING_CHANGED_TOPIC + "_" + hashFn(args.filter) || ""),
    },
  },
}

Publish:

pubsub.publish(hashFn(SOMETHING_CHANGED_TOPIC), { somethingChanged: { id: "123" }})

Ideally we need to have both approaches. hashFn is embeeded on compilation time which means we cannot handle this for cases like graphql-serve etc.

@machi1990
Copy link

Ideally we need to have both approaches. hashFn is embedded on compilation time which means we cannot handle this for cases like graphql-serve etc.

Since the subscription topic is set in the resolver, this would be hard to do without adding some configuration to the SchemaCRUDPlugin to cater for both use cases, right?

It seems to me like a potential security concern to pass the user ID as a subscription argument.

I guess a hashing function will take care of this and we could avoid situation where we'd use the raw "userId" or whatever as topics name.

Maybe this should come from the context instead, and the server can set this in the context with contextCreator.

export const resolvers = {
  Subscription: {
    todoChanged(_parent, args, context): {
      subscribe: () => pubsub.asyncIterator(SOMETHING_CHANGED_TOPIC + "_" + hashFn(context.auth.userId) || ""),
    },
  },
}

cc @machi1990

I am wondering what happens if we do not have the "auth" info i.e userId?

@craicoverflow
Copy link

I am wondering what happens if we do not have the "auth" info i.e userId?

You can have the same scenario when supplying userId as a filter argument. As example shows, it would default to "".

@machi1990
Copy link

machi1990 commented Jul 23, 2020

I am wondering what happens if we do not have the "auth" info i.e userId?

You can have the same scenario when supplying userId as a filter argument. As example shows, it would default to "".

Thanks for the response. I suppose defaulting to "", means this is the default topic and any subscription missing the "info needed for hashing" e.g userId, will be using his topic?

@craicoverflow
Copy link

object = deepSortKeys(object)
JSON.stringify(object).replace('"',"_").replace(",","").replace(":",""_")

What is object from this example, the filter argument coming from the subscription like {userId: {eq: "enda"}}? If that is the case, this will only work by using wildcards won't it? The create mutation resolver will publish the topic, and obviously this will not have the filter information available to it so it should use a wildcard in the topic path:

Mutation

const topic = 'CREATE_NOTE/filter/#';
pubSub.publish(topic, payload);

Subscription

const topic = `CREATE_NOTE/filter/${hashFn(filter)}`;
...
subscribe: () => pubsub.asyncIterator(topic)

need to set up a local OpenShift cluster to play with and learn about subscription topic wildcards.

@wtrocki
Copy link
Author

wtrocki commented Jul 23, 2020

I think that wildcard is defined only on server to define namespace for multiple topics.
When publishing we need to use explicit topic. This is major problem as we cannot rebuild it as you have mentioned.

The way I seen this done was always related to the user owning the object - then you publish to topic with his id.
This is very very specific to the auth implementation we have and it will be hard to have as generic provider.

As you have mentioned hashing will not work efficiently and in some cases will produce even worse results than in mem filtering.
I guess we can do inmemory filtering an call it a day + create follow up to extend our auth implementation to supply ownership subscriptiuons or similar concept.

@craicoverflow
Copy link

The way I seen this done was always related to the user owning the object - then you publish to topic with his id.

yep, saw it this way first after our conversation yesterday but wanted to get clarification on what object was.

This is very very specific to the auth implementation we have and it will be hard to have as generic provider.

Indeed.

I guess we can do inmemory filtering an call it a day + create follow up to extend our auth implementation to supply ownership subscriptiuons or similar concept.

👍

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