Repository: mcollina/fastify-gql
Pull request #256 (2020-08-15 17:29:02)

feat: add asyncIterator & fix pubsub serialize/deserialize
I added asyncIterator to the subscription context so that we can process multiple topics at once.

And I added json serialize/deserialize to the pubsub sending/receiving process. If we don't do this, a problem occurs when we perform a field-based permission check in middleware such as [graphql-shield](https://github.com/maticzav/graphql-shield).

I am not 100% sure that this PR will not be a problem. It would be good to apply after judging whether there is a problem with asynchronous processing or performance. @mcollina 

gtolarc: First of all, it was wrong to think that the problem was caused by middleware.
In my case, the "Maximum call stack size exceeded" error occurs in the following part.
https://github.com/mcollina/mqemitter-redis/blob/master/mqemitter-redis.js#L162

These are the issues related to this issue. When encoding circular structures, msgpack-lite has a problem.
https://github.com/kawanet/msgpack-lite/issues/63
https://github.com/kawanet/msgpack-lite/issues/74

I experienced an error when publish a mongoose model object, which is an object like this:
```
CoreMongooseArray [
        'transaction',
        [Symbol(mongoose#Array#_atomics)]: {},
        [Symbol(mongoose#Array#_path)]: 'scopes',
        [Symbol(mongoose#Array#_schema)]: [SchemaArray],
        [Symbol(mongoose#Array#_parent)]: [Circular] ]
```

As in the answer to the above issue, we should remove the circular structure by JSON.stringify.
I don't know if this is a good solution, but it works anyway. @mcollina 
gtolarc: https://github.com/davidyaha/graphql-redis-subscriptions/blob/master/src/redis-pubsub.ts#L78
Other pubsub implementations are also serializing json.


Patch:
diff --git a/README.md b/README.md
index b59b34b..8759d8a 100644
--- a/README.md
+++ b/README.md
@@ -350,6 +350,8 @@ const resolvers = {
   },
   Subscription: {
     notificationAdded: {
+      // You can also subscribe to multiple topics at once using an array like this:
+      //  pubsub.subscribe(['TOPIC1', 'TOPIC2'])
       subscribe: async (root, args, { pubsub }) => await pubsub.subscribe('NOTIFICATION_ADDED')
     }
   }
diff --git a/lib/subscriber.js b/lib/subscriber.js
index 60154e2..64048f5 100644
--- a/lib/subscriber.js
+++ b/lib/subscriber.js
@@ -45,8 +45,11 @@ class SubscriptionContext {
     })
   }
 
-  subscribe (topic) {
-    return this.pubsub.subscribe(topic, this.queue).then(() => this.queue)
+  subscribe (topics) {
+    if (typeof topics === 'string') {
+      return this.pubsub.subscribe(topics, this.queue).then(() => this.queue)
+    }
+    return Promise.all(topics.map((topic) => this.pubsub.subscribe(topic, this.queue))).then(() => this.queue)
   }
 
   publish (event) {
diff --git a/package.json b/package.json
index 8a990ae..bcd2a8e 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
 {
   "name": "fastify-gql",
-  "version": "5.3.3",
+  "version": "5.3.4",
   "description": "Fastify GraphQL adapter",
   "main": "index.js",
   "types": "index.d.ts",


Test patch:
diff --git a/test/subscriber.js b/test/subscriber.js
index baa0e88..e48edca 100644
--- a/test/subscriber.js
+++ b/test/subscriber.js
@@ -81,3 +81,24 @@ test('subscription context publish event returns a promise reject on error', asy
     t.deepEqual(error, e)
   }
 })
+
+test('subscription context can handle multiple topics', t => {
+  t.plan(2)
+
+  const pubsub = new PubSub(mq())
+  const sc = new SubscriptionContext({ pubsub })
+
+  sc.subscribe(['TOPIC1', 'TOPIC2'])
+  sc.publish({
+    topic: 'TOPIC1',
+    payload: 1
+  }).then(() => {
+    t.pass()
+  })
+  sc.publish({
+    topic: 'TOPIC2',
+    payload: 2
+  }).then(() => {
+    t.pass()
+  })
+})