Apollo 2.x:订阅解析器未触发?

Posted

技术标签:

【中文标题】Apollo 2.x:订阅解析器未触发?【英文标题】:Apollo 2.x: Subscription Resolver not Firing? 【发布时间】:2018-09-30 10:05:36 【问题描述】:

在 SO 上有很多帖子都可以搜索 Apollo Subscription Resolver Never Activates?,其中包括来自 June 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.log(
    `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.log(
                        `[GraphQL error]: Message: $message, Location: $locations, Path: $path`,
                    ),
                );
            if (networkError) console.log(`[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.log(err);
                    );
            
            throw new Error("Unauthorized");
        
    ,

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

Mutation 解析器中的pubsub.publish... 行运行,但订阅解析器从未激活。

我错过了什么?

更新

我必须修改在我的客户端查询组件中设置订阅的方式。当我发现更多时,我会更新这篇文章。

【问题讨论】:

【参考方案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);

【讨论】:

以上是关于Apollo 2.x:订阅解析器未触发?的主要内容,如果未能解决你的问题,请参考以下文章

Express cookie 解析器未在生产中创建 cookie

Apollo 订阅解析器永远不会激活?

Apollo:订阅与查询解析器之间的区别?

Apollo GraphQL - 未触发解析器

Apollo 客户端解析器仅触发一次

每次更改多次触发 GraphQl 订阅解析器