GithubHelp home page GithubHelp logo

apollographql / graphql-subscriptions Goto Github PK

View Code? Open in Web Editor NEW
1.6K 60.0 133.0 191 KB

:newspaper: A small module that implements GraphQL subscriptions for Node.js

License: MIT License

TypeScript 100.00%
graphql-subscriptions graphql real-time

graphql-subscriptions's Issues

Subscriptions fails with untraceable error

I've been trying to trace a subscription_failed error for hours without any success. The data sent to the client on failure is {"type":"subscription_fail","id":0,"payload":{"errors":[{"message":"Cannot read property 'forEach' of undefined"}]}}.

There is nothing logged to the console of the server. I've also checked the application code and there is no use of forEach so I suspect it's rooted in graphql-subscriptions or subscriptions-transport-ws. Both the client and server are using the latest releases of the Apollo stack.

Subscriptions were working flawlessly up until I resumed development today. I'm going to check out the master branch and see if it the error is still happening. I'll also attempt a clean install.

I couldn't find an issue similar to this one so I'm opening this in hope of some direction. Any help is much appreciated.

JWT auth + refresh token issue

I use JWT auth for authentication and no problems with apollo client authentication because http://dev.apollodata.com/core/network.html#networkInterfaceMiddleware even when token refreshes, but I have the issue with graphql subscriptions authentication. According to https://github.com/apollographql/graphql-subscriptions/blob/master/.designs/authorization.md and https://github.com/apollographql/tools-docs/blob/master/tools/graphql-subscriptions/authentication.md token sets once only (connectionParams: {token: 'some string'} and lazy: true) on initialisation SubscriptionClient and it works fine, but not when token refreshes.

Technologies/libs which I use in my project:

  • vue
  • vue-auth
  • vue-apollo
  • expressjs
  • graphql-subscriptions
  • subscriptions-transport-ws
  • graphql

Have to publish with subscription name in the object

I was following the tutorial in the README file. I noticed if I use

// publish to the channel
pubsub.publish('newCommentsChannel', {
  id: 123,
  content: 'Test',
  repoFullName: 'apollostack/GitHunt-API',
  posted_by: 'helfer',
});

I will get

{
  data: {
     commentAdded: null
   }
}

But if I publish with the subscription name in the object

// publish to the channel
pubsub.publish('newCommentsChannel', {
  commentAdded: {
    id: 123,
    content: 'Test',
    repoFullName: 'apollostack/GitHunt-API',
    posted_by: 'helfer',
  }
});

Then I will get the result as in the README.
Am I missing something from the document?

Using an external PubSub engine

Hello,

I would like to know if it is possible to use graphql-mqtt-subscriptions along with this package. More precisely, I'd like someone to explain how to wrap a pubsub with an AsyncIterator since the apollo client needs one for the subscription to be effective.

My final goal is to use a MQTT broker to handle subscriptions instead of the in-memory pubsub engine provided by your package.

Thank you for your help.

discussion: livequery integration

Let's discuss how a possible Meteor livequery integration could look like.

Advantages
Livequery integration would allow the full expressiveness of the mongo query syntax (limited by the minimongo limitations). Especially the ability to sort & limit queries seems interesting to me. This makes subscriptions possible like: give me updates on the top 3 voted posts. This would be hard to implement in a traditional pubsub system.

Disadvantages
Hard to extend to other DB's beyond mongo (but maybe postgres and rethink).
Only as scalable as livequery is (although no merge box required?)

Possible approach with the current system
The current system doesn't seem particularly well fit, but one could imagine something like:

const subscriptionManager = new SubscriptionManager({
  schema,
  pubsub,
  setupFunctions: {
    topComments: (options, args) => ({
      someName: {
        channelOptions: {
          cursor: Comments.find({reponame: args.repoFullName},{sort:{votes:-1},limit:3}),
          added: true,
          changed: true,
          removed: true
        }
      },
    }),
  },
});

The pubsub system could then add an observer on the cursor and trigger the changes the user indicated it wants to listen to (added, changed and/or `removed).

Problems with this approach
It's quite obvious that it's quite hacky, the current system doesn't seem well fit. E.g. the channel name (someName) has no meaning.

It's also not really a pubsub system. We're only subscribing, but can't publish.

Move `graphql` to be peer dependency instead of dependency

Unable to work with latest Node, now when adding graphql and graphql-subscriptions as dependencies - I get duplicate of graphql:

node_modules
     graphql
     graphql-subscriptions
           node_modules
                 graphql

Temporary workaround is to remove graphql dependency, and then NPM flatten the need for graphql and install it under the root node_modules.

Can not resolve field that is not part of the published object

What I am using:

react-native: 0.42.3
react-apollo: 1.0.0-rc.3
apollo-client: 1.0.0-rc.6
subscriptions-transport-ws: 0.5.5-alpha.0
graphql-subscriptions: ^0.3.1

I have the following schema:

type Episode {
  id: Int!
  number: String!
  title: String!
  cover: String!
}

type CastQueueItem {
  itemId: Int!
  episode: Episode!
}

type CastStatus {
  queueItems: [CastQueueItem]!
}

type Subscription {
  castStatus: CastStatus!
}

schema {
  query: Query # not included
  mutation: Mutation # not included
  subscription: Subscription
}

This is my Subscription to which I subscribe in my react-native application:

subscription castStatus {
  castStatus { 
    queueItems {
      itemId
      episode {
        id
        title
        cover
      }
    }
  }
}

Somewhere in my javascript I publish the CastStatus:

const castStatus = {
  queueItems: [
    { itemId: 1, episode: { id: 1, title: '1', },
    { itemId: 2, episode: { id: 2, title: '2', },
  ],
}
pubsub.publish(`castStatus`,  { castStatus })

The cover field has a resolver that exists on the schema I passed to the SubscriptionManager constructor.

I am expecting the cover field to be resolved trough my schema - but it seems not to work. I am getting no updates from the server as long as I the cover field is listed in my subscription. Also, I am not getting any error message :(

I wanted to ask if this behavior is on purpose and if so why SubscriptionManager does not resolve field via the schema.

pattern for getting initial subscription payload

I was wondering what a good pattern would be for receiving an initial subscription payload?

let me try to explain with the help of some contrived examples:

if we run the code from the docs ...

// hooking up subscription resolvers etc., simplified
const resolvers = {
  Subscription: {
    getReport: {
      subscribe: () => ....
// ...

// then run ...
pubsub.publish( 'somethingChanged', { getReport: { id: "1" }});

then the subscription query would not receive any payload:

subscription {
   getReport {
      id
   }
}

... unless we are triggering publish, e.g. through a Mutation.

function publish() {
   pubsub.publish( 'somethingChanged', { getReport: { id: "1" }});
}

const resolvers = {
  Mutation: {
    triggerReport: () => publish()

e.g. we could also make it work this way - just to have a (bad) example:

setInterval( () =>
   pubsub.publish( 'somethingChanged', { getReport: { id: "1" }})
), 1000 );

My understanding is that one would usually use a Mutation in order to trigger publish.
I was wondering how could one receive the initial payload without a trigger?

Unless subscriptions are only meant to be published through some sort of trigger?

Or would one get the initial payload through an ordinary graphql query?

query {
   getReport {
      id
   }
}

Thanks in advance for any advice!

Subscription must return Async Iterable when using withFilter

I was trying to use filtering explained in the official documentation but it fails with the "Subscription must return Async Iterable" error.

Here is my example for my Subscription resolver:

This doesn't work:

Subscription: {
    errorChanged: {
        subscribe: () => withFilter(
            () => pubsub.asyncIterator("errorChanged"),
            (payload, variables) => {
                return true;
            },
        ),
    },
}

This works:

Subscription: {
    errorChanged: {
       subscribe: () => pubsub.asyncIterator("errorChanged")
    },
}

Am I doing something wrong?

Using version: 0.4.4

[object Object] Result when doing pubsub.publish

The problem is when publishing a value it will return null on the subscribe method

export const subscriptions = model => ({
    [model]: {
        resolve(payload) {
            console.log({...payload[model]})
            return {...payload[model]}  
        },
        subscribe: withFilter(() => pubsub.asyncIterator(
            ['CREATED','UPDATED', 'DELETED']
            .map(mutation => `${model.toUpperCase()}_${mutation}`)),
            (payload,variables) => {
                return payload.id === variables.id
            })
    }
})

And publishing it like this

    [`update${model}`]: async(_, { id, ...args }, { models }, info) => {
        let result = await models[model].findById(id)
        await result.update(args)
        pubsub.publish(`${model.toUpperCase()}_UPDATED`, {
            [model]: args
        })
        
        return result
    },

this is what i get received from the Subscription resolve

[nodemon] starting `babel-node src`
Server started at 0.0.0.0:8080
express deprecated req.host: Use req.hostname instead src/server.js:97:88
{ name: 'testerssas',
  description: 'test',
  slug: 'testerssas',
  fee: 0 }
POST /graphql 200 59.101 ms - -

and getting this error

image

Noticed that [object Object] is an error

Publish events

Hi guys,

In file README, currently we have the demo code to publish events:

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

I tried to use the template above but in clients, I've got nothing. All fields are set to null. But if I modify it a little bit like this:

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

It works as expected. So is there any thing wrong with the documentation?

Schema not provided?

I have followed the Readme and tried to follow along with this Medium post and the GitHunt API example.

Unfortunately my pubsub doesn't seem to be publishing my updates. Interestingly when I try to subscribe directly through my node server I am getting the Error: Must provide schema, which I do not understand. Not sure if you can see anything wrong with this setup:

// subscribe.js
import { PubSub, SubscriptionManager } from 'graphql-subscriptions';
import schema from './schema';

const pubsub = new PubSub();
const subManger = new SubscriptionManager({
	schema,
	pubsub,
	setupFunctions: {
		todoAdded: (options, args) => ({
			todoAdded: (todo) => {
				return true;
			},
		}),
	},
});

subManger.subscribe({
	query: `subscription todos($completed:Boolean) {
				todoAdded(completed:$completed){
					id
					title
					completed
				}
			}`,
	variables: {
		completed: null
	},
	context: {},
	callback: (err, data) => console.log(data)
});

pubsub.publish('todoAdded', {
	id: 123,
	title: 'testing graphql subscriptions',
	completed: false
});

export { subManger, pubsub };
// schema.js
import Resolvers from './resolvers';
import Schema from './schema.graphql';
import { makeExecutableSchema } from 'graphql-tools';

const executableSchema = makeExecutableSchema({
	typeDefs: Schema,
	resolvers: Resolvers,
	allowUndefinedInResolve: true,
	resolverValidationOptions: {
		requireResolversForNonScalar: false
	},
	printErrors: process.env.NODE_ENV !== 'production',
});

export default executableSchema;
// schema.graphql
type Todo {
	id: ID
	createdAt: String
	updatedAt: String
	title: String
	completed: Boolean
}

type Query {
	todos(completed: Boolean): [Todo]
	todoAdded(completed: Boolean): Todo
}

type Mutation {
	addTodo(title: String!): Todo
	editTodo(id: Int!, title: String, completed: Boolean): Todo
	deleteTodo(id: Int!): String
}

type Subscription {
	todoAdded(completed: Boolean): Todo
}

schema {
	query: Query
	mutation: Mutation
	subscription: Subscription
}

Trouble subscribing to the publisher using async iterator

I have a graphql server and I'm attempting to trigger an event whenever the status of an entity is updated. I have a mutation for updating the entity, which uses graphql-subscriptions to publish the update for the subscriber to listen to. However, pubsub.subscribe(EVENT_NAME) picked up the change, but when I integrate that into the subscription resolver, it doesn't trigger the update.

I've redacted parts of the code not relevant to the issue for brevity.

My resolver:

Mutation: {
  updateStatus(_, { id, status, hash }) {
    return repository.updateStatus(id, status, hash);
  }
},
Subscription: {
  updatedStatus: {
    subscribe: () => pubsub.asyncIterator('updatedStatus'),
  }
},

My repository:

export const updateStatus = async function(id: String, status: String, hash: String) {
  try {
    const res = await fetch(`${baseURL}/article/${id}/status?publish_hash=${hash}`, {
      headers,
      method: 'PATCH',
      body: JSON.stringify({ status })
    });

    const article = await fetchSingleArticle(id, 'XML', headers.channel, false);

    await pubsub.publish('updatedStatus', article);
    return article;
  } catch (e) {
    console.log(e.message);
  }
}

My schema:

type Mutation {
  updateStatus(id: ID!, status: PublishingStatus = PUBLISHING, hash: String): Article
}

type Subscription {
  updatedStatus(id: ID!): Article
}

schema {
  query: Query
  mutation: Mutation
  subscription: Subscription
}

To test this, I'm just using graphiql, which is picking up the websocket connection fine.

I've attempted the publish using both article (as shown) and { updatedStatus: article }, neither seem to get picked up.

As mentioned previously pubsub.subscribe('updatedStatus') does pick up the change. So the issue seems to be related to the Subscription resolver.

I'm going off the following docs: http://dev.apollodata.com/tools/graphql-subscriptions/setup.html#setup

Authorizing subscriptions

I've got a foobarUpdated subscription, but I need to authorize the users who subscribe to it as well as validate the variable (if the given id is not a valid foobar id, subscription doesn't make sense).

Is there a way to deny/cancel a subscription? I'm using withFilter but that only works for filtering after the subscription has been initiated.

Merge all queries on each specific subscription

Hi,

Actually, if I'm not mistaken, for each subscription, when there is new data available the schema of each listener query is executed against the GraphQL Schema. Am I right ?

This seems rather inefficient.
I think it would be doable to merge all listeners query into one, execute it against the schema and then dispatch only the queried data for each listener.

What do you think ?

Cannot find name 'AsyncIterator' error in Typescript compilation process.

I have found a difficulty compiling my Typescript code and it turns out that inside many files in graphql-subscriptions and subscriptions-transport-ws, the AsyncIterator iterator type interface is treated as defined, but the Typescript compiler doesn't agree with that. here is the list of errors:

node_modules/graphql-subscriptions/dist/pubsub-engine.d.ts(5,52): error TS2304: Cannot find name 'AsyncIterator'.
node_modules/graphql-subscriptions/dist/pubsub.d.ts(12,52): error TS2304: Cannot find name 'AsyncIterator'.
node_modules/graphql-subscriptions/dist/with-filter.d.ts(2,94): error TS2304: Cannot find name 'AsyncIterator'.
node_modules/graphql-subscriptions/dist/with-filter.d.ts(3,58): error TS2304: Cannot find name 'AsyncIterator'.
node_modules/subscriptions-transport-ws/dist/server.d.ts(5,41): error TS2304: Cannot find name 'AsyncIterator'.
node_modules/subscriptions-transport-ws/dist/server.d.ts(29,58): error TS2304: Cannot find name 'AsyncIterator'.
node_modules/subscriptions-transport-ws/dist/server.d.ts(32,31): error TS2304: Cannot find name 'AsyncIterator'.

screenshot 22

I had found a temporary solution to bypass the errors by defining AsyncIterator type interface in every file involved in node_modules, here is the definition:

interface AsyncIterator<T> {
  next(value?: any): Promise<IteratorResult<T>>;
  return?(value?: any): Promise<IteratorResult<T>>;
  throw?(e?: any): Promise<IteratorResult<T>>;
}

screenshot 23

Subscription using Typescript - Property 'Subscription' is incompatible with index signature.

Hello,

I got this error when I tried to execute this code. Do you have any idea?

src/index.ts(40,49): error TS2345: Argument of type '{ typeDefs: string; resolvers: { Query: { addCell: (: any, { type }: { type: any; }) => Prom...' is not assignable to parameter of type 'IExecutableSchemaDefinition'.
Types of property 'resolvers' are incompatible.
Type '{ Query: { addCell: (
: any, { type }: { type: any; }) => Promise; }; Subscription: ...' is not assignable to type 'IResolvers'.
Property 'Subscription' is incompatible with index signature.
Type '{ newCell: { subscribe: (: any, { type }: { type: any; }) => AsyncIterator<{}>; }; }' is not assignable to type 'GraphQLScalarType | IResolverObject | (() => any)'.
Type '{ newCell: { subscribe: (
: any, { type }: { type: any; }) => AsyncIterator<{}>; }; }' is not assignable to type '() => any'.
Type '{ newCell: { subscribe: (_: any, { type }: { type: any; }) => AsyncIterator<{}>; }; }' provides no match for the signature '(): any'.

import { PubSub } from 'graphql-subscriptions';
const pubsub = new PubSub();

export const typeDefs = `

type Subscription {
  newCell: Cell
}

type Query {
  addCell(type: CellType!): Cell
}

schema {
  query: Query
  subscription: Subscription
}
`

export const resolvers = {
  Query: {
    addCell: async (_, { type }) => {
      let cell = new Cell();
      cell.type = type;
      pubsub.publish('newCell', { newCell: cell });
      return cell;
    },
  },

  Subscription: {
    newCell: {
      subscribe: () =>
        pubsub.asyncIterator('newCell')
    }
  }
};

Can't subscribe when field is of another type

I'm trying to create a Subscription to tell when a new post is created. My issue is that the type Post have an author: User! and I don't know how to pass the User to the Post on the mutation. I can pass the id, title, and description without issues but not the User.

Subscription:
https://github.com/KadoBOT/musical-memory/blob/master/src/components/Posts/index.js

Schema and Resolver:
https://github.com/KadoBOT/musical-memory/tree/master/server/schema

Can someone help me?

How to update all subscribed clients except the one making the changes

I tried to integrate graphql-subscriptions and it worked out pretty good, But it broke the app (which i can fix easily.. but i don't think that's the right decision)

Subscriptions sends the updates for everything i am subscribed to, But when i create a new item, The store is already updated from the mutation i just ran, but Subscription kicks in and runs the logic which updates the store again.. And i get the duplicate key warning.

I can and have fixed this by checking if a record already exists and then handling the update., But this means that the updates are still sent to all the subscribed clients who want to be notified of the changes.

Is there any way to send updates to all the subscribed clients, Except the one making the changes/operations (CRUD), without writing lot of codes?

Ofcourse i can use setupFunctions filter but if the same user has opened the app in different tabs, this will not work out.

What do you recommend, How should i move forward with this.

pass variables to pub sub?

I've hacked your code so it does

// 3. subscribe and keep the subscription id
            const subsPromise = this.pubsub.subscribe(triggerName, onMessage, channelOptions, options.variables);

rather than..

// 3. subscribe and keep the subscription id
            const subsPromise = this.pubsub.subscribe(triggerName, onMessage, channelOptions);

this means I can implement pubsub so it subscribes to an external event source (dont worry about details):

const valueSubscriptions = {};

class MyPubSub extends PubSub {

  subscribe(trigger, onMessage, channelOptions, variables){
    const ret = super.subscribe(trigger, onMessage);
    if (trigger === 'value') {
      ret.then(f => valueSubscriptions[f] = opcObserver(variables.id).subscribe(v => pubsub.publish('value', v)));
    }
    return ret;
  }
  unsubscribe(subid) {
    if (valueSubscriptions[subid]) {
      valueSubscriptions[subid].unsubscribe();
      delete valueSubscriptions[subid];
    }
    return super.unsubscribe(subid);
  }

}

would this make sense for you to incorporate?

Infinite loop after last unsubscribe in withFilter function.

I use withFilter to validate payload and got an infinite loop when there are no subscribers left (payload is resolved immediately with {value: undefined, done: true}).

const getNextPromise = () => {
        return asyncIterator
          .next()
          .then(payload => Promise.all([
            payload,
            Promise.resolve(filterFn(payload.value, args, context, info)).catch(() => false),
          ]))
          .then(([payload, filterResult]) => {
            if (filterResult === true) {
              return payload;
            }

            // Skip the current value and wait for the next one
            return getNextPromise();
          });
      };

This function recursively calls itself even if payload.done is true and it leads to infinite call. Adding check for payload.done before return getNextPromise(); fixes this problem or am I missing something?

Publish API should be removed (me thinks)

How one want to publish shouldn't matter to the SubscriptionManager, as the correct way to publish messages is through the onMessage callback.
I think the publish methods on PubSub and SubscriptionManager are confusing, as you can implement a correct (for now) server without them.

E.g. I implemented a PubSub based on Observables:

    pubsub: {
      subscribe(triggerName, onMessage, channelOptions) {
        return Promise.resolve(channelOptions.resolve$.subscribe(onMessage, console.error))
      },
      unsubscribe(subscription) {
        subscription.unsubscribe()
      }
    },

No need for publish (and I wouldn't know how to implement it if I wanted to).

It's fine for the default in-memory-event-based-pubsub-implementation to have a publish method, but it shouldn't be in the interface or wrapped in SubscriptionManager.

Thx.

Besides filter, add a property to format rootValue

You should have another property besides filter for trigger map which could clean the rootValue before executing on graphql and submitting to the client, a suggestion would be a promisable (!) format or even a resolve property.

On file pubsub.ts on line 197,

before execute

In my case this is important because I need to remove private values on an object type when the client authorized is not eligible to see it.

The following is an example of how I would be able to use it.

example usage

Async subscribe function

I want to validate the subscription params with an async function. If I return a promise from the subscription resolver on the server the client errors with Subscription must return Async Iterable.

Can I submit a PR to allow an async subscribe function? And where should I look to make those changes?

Dynamic channel names

newCommentsChannel: {
   filter: comment => comment.repository_name === args.repoFullName,
},

Hi, I was wondering about the snippet above, would it scale to a large number of subscriptions? Since each subscription is doing a filter check, that would be o(n). Alternatively, is there a way to declare that variable upfront, for example newComments_${postId} as the channel name. Currently, the setupFunctions method seems to be mapping each distinct channel. A way to use a pattern would be helpful. Would love to understand more about optimizing subscriptions. Cheers :)

Help: Easy subscriptions with rethinkdb changefeeds

Hello guys!

Sorry for the level on this. But I havnt found any better place to ask this, and i'm new to nodejs and graphql. I have been looking for an easy way to make use of rethinkdb changefeeds in my subscription, and just found out of this solution and was wondering what you guys think of it:

Subscription: {
    changefeedTest: {
      subscribe: () => {
        const feedUuid = uuid()
        r.table('people').filter({ maybeSomethingUniqueHereForEachUser }).changes().run().then((feed) => {
          feed.each((error, change) => {
            console.log('publishing data to user')
            pubsub.publish(feedUuid, { changefeedTest: change.new_val })
          })
        })

        return pubsub.asyncIterator(feedUuid)
      }
    }
  }

Any thoughts of this approach? With this code I skip the generic pubsub topics and create a unique one for each subscription query against the DB. This way I think i'm certain that each user will just get the data it should have since they will only get the unique topic.

Only problem I have right now, is that the changefeed keeps running after the user disconnects. So after a while testing I have X (one more each time) "publishing data to user" as soon as i open a new connection. I am thinking of saving a reference to the feed to an array, and close them in the onDisconnect hook on subscriptionserver. I would however either save them somehow on a session specific variable (i dont know how to do this in node), or save it in a big array grouped on session id.

feedsToDisconnect[uniqueSessionId].push(feed)

and then inside the onDisconnect:

feedsToDisconnect[uniqueSessionId].map((feed) => { feed.close() }) and kill of the group.

How to set context for subscriptions?

It seems that my resolver when responding to a subscription doesn't have access to the context variable, that are typically available with a non-subscription request. In my case I'm trying to push as many request that I can through a data loader.

Heres my resolver:

  resolve: (source, args, { loaders }) => {
    if (source.user.length === 0) {
      return {};
    }
    return loaders.users.load(source.user);
  },

Currently, I have something like this setup for standard graphql requests

  router.post('/graphql', graphqlKoa({
    schema,
    formatParams(params) {
      return params;
    },
    context: { loaders: {
      users: Loader(r.table('users'), 'id')
    } },
  }));

How might I set something like this with subscriptions?

Consider handling the errors thrown in setupFunction and make it promisable.

On file pubsub.ts on line 162, the setupFunction should be handling the errors thrown in the function. That is important because we can decline and authorize subscriptions directly in the setupFunction itself. And by having this in mind you should make the setupFunction promisable for security async operations, like the onSubscribe method.

should handle as promise

In my case this is important because I have different authorization methods, and one can be invalid depending on the subscription.

Here's an example of how I would be able to use it.

example usage

How to store all clients?

Hi, I have a task to display all connected clients on client side.
Ex:

Name1 (email)
Name2 (email)
...
NameN (email)

I know that I have to store some information on server and should use onConnect, but how?
With SocketIO I can use rooms, but how I can realize the same with graphql-subscriptions?

Add 3rd argument to PubSub subscribe - options

This little change will enable PubSub to access query itself.
Seems to be the necessary step if we want to implement things like subscribing to RethinkDB changefeeds which itself can trigger publish events.
Without this information we can only hardcode some changefeeds without arguments.

Just little change in SubscriptionManager, I am TS disabled, so no PR :-(

// 3. subscribe and keep the subscription id
            const subsPromise = this.pubsub.subscribe(triggerName, handler, options);
            subsPromise.then(id => this.subscriptions[externalSubscriptionId].push(id));

Subscription leak

SubscriptionManager::subscriptions maps is never cleaned up from subscription tracking and leaks their references.
I assume it should be cleaned up on unsubscribe ?

The terminology is confusing (at least for me)

I've struggled for a couple hours last night to get this but I couldn't.


Snippet 1

subscription($repoName: String!) {
  commentAdded(repoName: $repoName) { # <-- `commentAdded` is the subscription name
    id
    content
  }
}

Snippet 2

const subscriptionManager = new SubscriptionManager({
  schema,
  pubsub,
  setupFunctions: {
    commentAdded: (options, args) => ({
      commentAdded: {
        filter: comment => comment.repository_name === args.repoFullName,
      },
    }),
  },
});

Snippet 3

pubsub.publish('commentAdded', payload);

I have a couple questions

  1. In the 1st snippet commentAdded is the subscription name and in the 3rd snippet commentAdded is the event name. Are those the same thing?

  2. Why commentAdded appears twice in setupFunctions of the 2nd snippet? Wouldn't this be enough?

    const subscriptionManager = new SubscriptionManager({
      schema,
      pubsub,
      setupFunctions: {
        commentAdded: (options, args) =>
          payload => payload.repository_name === args.repoFullName,
      },
    });
  3. The example from the main readme of this repo confused me even more. The subscription name is commentAdded but the event name is newCommentsChannel. How is the event name constructed? Do you simply append Channel to the end of the operation name (newComments in this case)? Isn't the operation name optional?

    subscription newComments($repoName: String!){
      commentAdded(repoName: $repoName) { # <-- this is the subscription name
        id
        content
        createdBy {
          username
        }
      }
    }

Default GraphQL arguments are not passed into setupFunctions

Hello,

Versions used

  • master@341482dc982983f4904b7fee51e672820f376830

Setup

When defining setup functions like this :

  setupFunctions: {
    commentAdded: (options, args) => ({
      newCommentsChannel: {
        filter: comment => comment.repository_name === args.repoFullName,
      },
    }),
  },

I might be wrong here, since I'm not really familiar with graphql-subscriptions but I assume args represents the graphql arguments passed into the subscription query.

Considering this query :

subscription CommentAdded {
  commentAdded(feed: 1234) {
    ...
   }
}

args will be properly set to the expected value :

{ feed: 1234 }

However, when default arguments are defined in the schema like this :

type RootSubscription {
  commentAdded(
    feed: Int = 10
  ): Comment!
}

args will not be set to { feed: 10 } as one would expect.

Actual outcome

args is set to

{}

Expected outcome

args set to

{ feed: 10 }

Failing test

I forked and created a failing test case demonstrating the issue. See here : kouak@820101a

Let me know if you want me to submit a PR with this failing test case.

Warning: Resolve function missing

In Readme - Payload Manipulation writing You can also manipulate the published payload.

But if I not set resolve method. There is a warning show me Resolve function missing for "Subscription.commentAdded". To disable this warning check https://github.com/apollostack/graphql-tools/issues/131

To resolve this I must set a passthrough function.

    commentAdded: {
      resolve: root => root,
      subscribe: () => pubsub.asyncIterator('commentAdded')
    }

Is this a bug or change readme to You must manipulate the published payload, by adding resovle methods to your subscription

Need to see schema for example

Maybe I missed it, but it would be nice to see all the relevant pieces of the example, namely what the schema looks like.

Can't catch exception

Hi, I have warnings like
backend | [1] bk-app-dev | (node:2449) Warning: a promise was created in a handler at var/www/backend/node_modules/graphql-subscriptions/src/pubsub.ts:190:19 but was not returned from it, see http://goo.gl/rRqMUw, but I can't figure out where throwed error.

withFilter not working

Im using withFilter in my code

watchDevice: {
      resolve: (payload) => payload,
      subscribe: withFilter(() => pubsub.asyncIterator('updateDeviceLocation'), (payload, args) => {
        console.log('---------P', payload)
        console.log('---------A', args)
        return payload.id === args.id;
      }),
    },

but the filter function is not being called, any idea?

Memory leak: event handlers are not removed when a subscription ends

Versions used :

  • graphql-subscriptions 0.4.2
  • subscription-transport-ws 0.7.1

When a subscription is created, event handlers are created and registered through the underlying EventEmitter class.

However, these event handlers are never removed leading to a memory leak.

Now, I don't know whether the bug lies in this repo or in subscriptions-transport-ws as I'm not familiar enough with this codebase yet.

However, I managed to reproduce the issue in the Githunt-API example implementation.

Steps to reproduce

My only change is to console.log registered event handlers in the underlying EventEmitter object every 5 seconds.

  • Setup the project as described in the README
  • Start the server
  • Start a subscription in GraphiQL
  • Watch the event handler registered in console output
  • Close GraphiQL tab, reopen, and register a new subscription
  • Watch both event handlers in console output
  • Rince & repeat until OOM :)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.