Apollo 2.x: Resolver подписки не работает?

На SO есть много сообщений о поиске на Apollo Subscription Resolver Never Activates?, в том числе одно от меня от Июнь 2017 г.. С тех пор у меня есть подписки, которые хорошо работают в течение многих месяцев. Но это был Apollo 1.x, а теперь с Apollo 2.x у меня похожая аномалия.

Старые сообщения SO, похоже, не решают эту аномалию. Я провел последние пару дней, просматривая их все и пытаясь убедиться, что делаю все, что сказано в документах и ​​статьях, но это еще не совсем работает.

Для полноты картины я привожу весь соответствующий код.

НАСТРОЙКА СЕРВЕРА

import { createApolloServer } from "meteor/apollo";
import { makeExecutableSchema } from "graphql-tools";
import merge from "lodash/merge";
import cors from 'cors';

import GoalsSchema from "../../api/goals/Goal.graphql";
import GoalsResolvers from "../../api/goals/resolvers";
import ResolutionsSchema from "../../api/resolutions/Resolutions.graphql";
import ResolutionsResolvers from "../../api/resolutions/resolvers";
import UsersSchema from "../../api/users/User.graphql";
import UsersResolvers from "../../api/users/resolvers";
import { createServer } from 'http';
import { SubscriptionServer } from 'subscriptions-transport-ws';
import { execute, subscribe } from 'graphql';

const typeDefs = [GoalsSchema, ResolutionsSchema, UsersSchema];

//must change this line to get changes in .graphql files recognized. afdkk

const resolvers = merge(GoalsResolvers, ResolutionsResolvers, UsersResolvers);

const schema = makeExecutableSchema({
    typeDefs,
    resolvers
});

createApolloServer({ schema });

const WS_PORT = 3200;

// Create WebSocket listener server
// https://www.apollographql.com/docs/graphql-subscriptions/express.html
const websocketServer = createServer((request, response) => {
    response.writeHead(404);
    response.end();
});

// Bind it to port and start listening
websocketServer.listen(WS_PORT, () => console.info(
    `Websocket Server is now running on ws://localhost:${WS_PORT}`
));

const subscriptionServer = SubscriptionServer.create(
    {
        schema,
        execute,
        subscribe,
    },
    {
        server: websocketServer,
        path: '/subscriptions',
    },
);

НАСТРОЙКА КЛИЕНТА

import React from "react";
import {Meteor} from "meteor/meteor";
import {render} from "react-dom";
import {ApolloProvider} from "react-apollo";
import {ApolloLink, from} from "apollo-link";
import {ApolloClient} from "apollo-client";
import {HttpLink} from "apollo-link-http";
import {InMemoryCache} from "apollo-cache-inmemory";
import {onError} from 'apollo-link-error';
import {split} from 'apollo-link';
import {WebSocketLink} from 'apollo-link-ws';
import {getMainDefinition} from 'apollo-utilities';
import {toIdValue} from 'apollo-utilities';

import App from "../../ui/App";

// Create an http link:
const httpLink = new HttpLink({
    uri: Meteor.absoluteUrl("graphql"),
    credentials: 'same-origin'
})

// Create a WebSocket link:
const wsLink = new WebSocketLink({
    uri: `ws://localhost:3200/subscriptions`,
    options: {
        reconnect: true
    }
});

// using the ability to split links, you can send data to each link
// depending on what kind of operation is being sent
const splitLink = split(
    // split based on operation type
    ({query}) => {
        const {kind, operation} = getMainDefinition(query);
        return kind === 'OperationDefinition' && operation === 'subscription';
    },
    wsLink,
    httpLink,
);

const authLink = new ApolloLink((operation, forward) => {
    const token = Accounts._storedLoginToken();
    operation.setContext(() => ({
        headers: {
            "meteor-login-token": token
        }
    }));
    return forward(operation);
});


const client = new ApolloClient({
    link: ApolloLink.from([
        onError(({graphQLErrors, networkError}) => {
            if (graphQLErrors)
                graphQLErrors.map(({message, locations, path}) =>
                    console.info(
                        `[GraphQL error]: Message: ${message}, Location: ${locations}, Path: ${path}`,
                    ),
                );
            if (networkError) console.info(`[Network error]: ${networkError}`);
        }),
        authLink,
        splitLink,
     ]),
    cache: new InMemoryCache({})
});

const ApolloApp = () => (
    <ApolloProvider client = {client}>
        <App/>
    </ApolloProvider>
);

Meteor.startup(() => {
    render(<ApolloApp/>, document.getElementById("app"));
});

ТИПЫ

type Resolution {
  _id: String!
  name: String!
  goals: [Goal]
  completed: Boolean
}

type Query {
  resolutions: [Resolution]
  getResolutionViaId(resolutionId: String!): Resolution
}

type Mutation {
  createResolution(name: String!): Resolution
}

type Subscription {
  resolutionWasAdded(userId: String!): Resolution
}

ЗАПРОСЫ

let CREATE_RESOLUTION = gql`
    mutation createResolution($name: String!) {
      createResolution(name: $name) {
        __typename
        _id
        name
        ...resolutionGoals
        completed
      }
    }
    ${resolutionQueryFragments.resolutionGoals}
`;


const RESOLUTION_SUBSCRIBE = gql`
          subscription resolutionWasAdded($userId: String!){
              resolutionWasAdded(userId: $userId){
                __typename
                _id
                name
                ...resolutionGoals
                completed
              }
            } 
            ${resolutionQueryFragments.resolutionGoals}
    `;

РЕЗОЛВЕР

    Mutation: {
        createResolution(obj, args, {userId}) {
            let name = args.name;
            if (userId) {
                return Promise.resolve()
                    .then(() => {
                        const resolutionId = Resolutions.insert({
                            name,
                            userId
                        });
                        return resolutionId;
                    })
                    .then(resolutionId => {
                        const resAdded = Resolutions.findOne(resolutionId);
                        return resAdded;
                    })
                    .then(resolutionWasAdded => {
                        pubsub.publish('resolutionWasAdded', {resolutionWasAdded: args})

                        return resolutionWasAdded;
                    })
                    .catch((err) => {
                        console.info(err);
                    });
            }
            throw new Error("Unauthorized");
        }
    },

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

Строка pubsub.publish... в преобразователе мутаций выполняется, но преобразователь подписки никогда не активируется.

Что мне не хватает?

ОБНОВИТЬ

Мне нужно изменить настройку подписки в моем компоненте клиентского запроса. Я обновлю этот пост позже, когда узнаю больше.

Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
Что такое Apollo Client и зачем он нужен?
Что такое Apollo Client и зачем он нужен?
Apollo Client - это полнофункциональный клиент GraphQL для JavaScript-приложений, который упрощает получение, управление и обновление данных в...
0
0
776
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

У меня это работает. Вот код, который вызывает преобразователь подписки и обрабатывает ответ от него.

import React, {Component} from "react";
import gql from "graphql-tag";
import {graphql} from "react-apollo";
import {Mutation} from "react-apollo";
import {withApollo} from "react-apollo";
import {GET_RESOLUTIONS_FOR_MUTATION_COMPONENT, CREATE_RESOLUTION} from '../../imports/api/resolutions/queries';
import {isDuplicateObject} from "../api/resolutions/queries";

const ResolutionForm = () => {
    let input;
    let state = {
        error: null
    };

    return (
        <Mutation
            mutation = {CREATE_RESOLUTION}
            update = {(cache, {data: {createResolution}}) => {
                const {resolutions} = cache.readQuery({query: GET_RESOLUTIONS_FOR_MUTATION_COMPONENT});
                if (!isDuplicateObject(createResolution, resolutions)) {
                    cache.writeQuery({
                        query: GET_RESOLUTIONS_FOR_MUTATION_COMPONENT,
                        data: {resolutions: resolutions.concat([createResolution])}
                    });
                }
            }}
        >
            {(createResolution, {data}) => (
                <div>
                    <form
                        onSubmit = {e => {
                            e.preventDefault();
                            createResolution({
                                variables: {
                                    name: input.value
                                },
                                optimisticResponse: {
                                    __typename: "Mutation",
                                    createResolution: {
                                        __typename: "Resolution",
                                        completed: false,
                                        goals: [],
                                        _id: "012345",
                                        name: input.value
                                    }
                                }
                            });
                            input.value = "";
                        }}
                    >
                        <input
                            ref = {node => {
                                input = node;
                            }}
                            placeholder = "Enter a Resolution"
                        />
                        <button type = "submit">Submit</button>
                    </form>
                </div>
            )}
        </Mutation>
    );
};

export default withApollo(ResolutionForm);

Другие вопросы по теме