Apollo 2.x:订阅解析器未触发?
Apollo 2.x: Subscription Resolver not Firing?
SO 上有很多 post 搜索 Apollo Subscription Resolver Never Activates?
,包括我来自 June 2017 的搜索。从那时起,我的订阅一直运行良好,持续了好几个月。但那是 Apollo 1.x,现在有了 Apollo 2.x,我遇到了类似的异常情况。
旧的 SO posts 似乎没有解决这个异常。在过去的几天里,我一直在仔细研究它们,并试图确保我正在做文档和文章所说的一切,但它还没有完全奏效。
为了完整起见,我提供了所有相关代码。
服务器设置
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;
})
}
}
}
突变解析器中的行 pubsub.publish...
运行,但订阅解析器从未激活。
我错过了什么?
更新
我必须修改订阅在我的客户端查询组件中的设置方式。当我发现更多时,我会更新这个post。
我成功了。下面是调用订阅解析器并处理来自它的响应的代码。
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);
SO 上有很多 post 搜索 Apollo Subscription Resolver Never Activates?
,包括我来自 June 2017 的搜索。从那时起,我的订阅一直运行良好,持续了好几个月。但那是 Apollo 1.x,现在有了 Apollo 2.x,我遇到了类似的异常情况。
旧的 SO posts 似乎没有解决这个异常。在过去的几天里,我一直在仔细研究它们,并试图确保我正在做文档和文章所说的一切,但它还没有完全奏效。
为了完整起见,我提供了所有相关代码。
服务器设置
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;
})
}
}
}
突变解析器中的行 pubsub.publish...
运行,但订阅解析器从未激活。
我错过了什么?
更新
我必须修改订阅在我的客户端查询组件中的设置方式。当我发现更多时,我会更新这个post。
我成功了。下面是调用订阅解析器并处理来自它的响应的代码。
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);