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

Subscriptions not working if using RenameRootFields #997

Closed
maxpain opened this issue Nov 6, 2018 · 11 comments
Closed

Subscriptions not working if using RenameRootFields #997

maxpain opened this issue Nov 6, 2018 · 11 comments
Labels

Comments

@maxpain
Copy link

maxpain commented Nov 6, 2018

Subscriptions not working if using RenameRootFields and schema stiching.
Example:

export default function makeHttpAndWsLink(uri, headers) {
	const httpLink = new HttpLink({ uri, fetch, headers })

	const wsLink = new WebSocketLink(
		new SubscriptionClient(uri, {
			reconnect: true,
			connectionParams: { headers }
		}, ws)
	)

	return split(isSubscription, wsLink, httpLink)
}

export default async function getSchema(): Promise<GraphQLSchema> {
	const link = makeHttpAndWsLink(HASURA_GRAPHQL_ENGINE_URL, { 'x-hasura-access-key': HASURA_ACCESS_KEY })
	const schema = await introspectSchema(link)
	const executableSchema = makeRemoteExecutableSchema({ schema, link })

	return transformSchema(executableSchema, [
		new RenameRootFields((operation, name, field) => 'hasura_' + name)
	])
}

GraphQL subscription:

subscription {
  hasura_games(limit: 1) {
    id
  }
}

An error:

{
  "errors": [
    {
      "message": "Cannot return null for non-nullable field subscription_root.hasura_games.",
      "locations": [
        {
          "line": 2,
          "column": 3
        }
      ],
      "path": [
        "hasura_games"
      ]
    }
  ]
}
@maxpain
Copy link
Author

maxpain commented Nov 10, 2018

Any help?

@maxpain
Copy link
Author

maxpain commented Nov 13, 2018

please...

@micha149
Copy link

Same Problem here… When the RenameRootFields transformer returns just the name as is, all works fine. So I guess it's related to the name and not the transformer logic.

This is my example code, which didn't work:

import { PubSub } from 'apollo-server';
import gql from 'graphql-tag';
import { makeExecutableSchema, RenameRootFields, transformSchema } from 'graphql-tools';

const pubsub = new PubSub();

const COUNTER_INCREMENT_TOPIC = 'counter_increment';
let counter = 0;

setInterval(() => {
    pubsub.publish(COUNTER_INCREMENT_TOPIC, { counterIncrement: { value: counter += 1 } });
}, 1000);

const resolvers = {
    Subscription: {
        counterIncrement: {
            subscribe: () => pubsub.asyncIterator(COUNTER_INCREMENT_TOPIC),
        },
    },
    Query: {
        currentCount: () => ({ value: counter }),
    },
};

const typeDefs = gql`
  type CounterValue {
    value: Int!
  }

  type Subscription {
    counterIncrement: CounterValue
  }

  type Query {
      currentCount: CounterValue
  }
`;

const schema = transformSchema(
    makeExecutableSchema({ typeDefs, resolvers }),
    [
        new RenameRootFields((_operation, name) => `prefix_${name}`),
        // new RenameRootFields((_operation, name) => name), // will work
    ],
);

@kommander
Copy link

kommander commented Dec 26, 2018

Make sure your AsyncIterator resolves to the renamed field, as no internals are taking care of that:

setInterval(() => {
    pubsub.publish(COUNTER_INCREMENT_TOPIC, { 
        prefix_counterIncrement: { value: counter += 1 } // << note the prefix_
    });
}, 1000);

EDIT: Curious as I am and with similar use cases, I recreated the example from @micha149 here https://github.com/kommander/graphql-tool-repro/tree/renamed-field-subscriptions. The transforms break the resolvers, returning null, about here https://github.com/apollographql/graphql-tools/blob/master/src/transforms/transformSchema.ts#L15-L28. So much magic.

ANOTHER EDIT: So the problem seems to be that the defaultMergedResolver at https://github.com/apollographql/graphql-tools/blob/master/src/stitching/defaultMergedResolver.ts#L14 cannot getResponseKeyFromInfo correctly, as it is renamed but the data comes in for the original name. It may be possible to try resolving it from the original type in the schema.

@kommander
Copy link

I tried to fix this in PR #1036

@yaacovCR
Copy link
Collaborator

My guess is that this more generally results from the fact that each subscription payload is mapped over the normal GraphQl execute function, with payload as the rootValue (https://github.com/graphql/graphql-js/blob/master/src/subscription/subscribe.js#L145-L146).

When schemas are merged, the mergedSchema receives a subscription payload from the target schema and passes it back, setting up another round of this resolution. For this reason, delegateToSchema must wrap the result with the subscriptionKey to allow for another round of resolution.

Currently, the wrapping is hard-coded with the subscriptionKey from the targetSchema, this should probably be transformed at this point to cover all edge cases, or the flow should be overall adjusted.

@yaacovCR
Copy link
Collaborator

Seems as easy as wrapping the payload with info.fieldName, added a test.

@apoggi-carecloud
Copy link

Is there an update on a fix for this?

@yaacovCR
Copy link
Collaborator

Fixed in graphql-tools-fork https://www.npmjs.com/package/graphql-tools-fork

@kamilkisiela
Copy link
Collaborator

We recently released an alpha version of GraphQL Tools (#1308) that should fix the issue.

Please update graphql-tools to next or run:

npx match-version graphql-tools next

Let us know if it solves the problem, we're counting for your feedback! :)

@yaacovCR yaacovCR mentioned this issue Mar 27, 2020
22 tasks
@yaacovCR
Copy link
Collaborator

Rolled into #1306

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

Successfully merging a pull request may close this issue.

6 participants