Apollo subscription issue - updateQuery not firing - reactjs

I have a functioning web socket created with Apollo's WebSocketLink interface. I managed to subscribe to an event using subscribeToMore and a message is pushed by the server (can see it in the network tab). Unfortunately updateQuery function is never triggered. I wonder whether it's the message structure that is incorrect (therefore a wrong server implementation) or is it something wrong in my client code.
For reference I added the message sent from server:
and here the graphql config for my component:
import { graphql } from "react-apollo/index";
import Insights from 'components/insights/Insights';
import gql from "graphql-tag";
import { withRouter } from "react-router-dom";
import get from 'lodash/get';
const query = gql`
query CampaignInsights($campaignId: ID) {
campaigns (id: $campaignId) {
edges {
node {
insights {
campaignPlanningInsight {
campaign
plannedTotals {
totalOptimizationRules
totalOfferGroups
totalOffers
}
liveTotals {
totalOptimizationRules
totalOfferGroups
totalOffers
}
}
}
}
}
}
}
`;
const insightsSubscription = gql`
subscription onInsightsUpdated($campaignId: ID) {
campaignPlanningInsightUpdated(id: $campaignId) {
id
plannedTotals {
totalOptimizationRules
totalOfferGroups
totalOffers
}
liveTotals {
totalOptimizationRules
totalOfferGroups
totalOffers
}
}
}
`;
const InsightsWithData = graphql(query, {
options: (props) => {
return {
variables: {
campaignId: props.match.params.campaignId
}
}
},
props: ({ data: { campaigns, subscribeToMore }, ownProps: { match }
}) => {
return {
insights: get(campaigns,
'edges[0].node.insights[0].campaignPlanningInsight', null),
subscribeToInsightsUpdate: () => {
return subscribeToMore({
document: insightsSubscription,
variables: {
campaignId: match.params.campaignId
},
updateQuery: (prev, { subscriptionData }) => {
debugger; // never gets here
if (!subscriptionData.data) {
return prev;
}
}
})
}
}
}
})(Insights);
export default withRouter(InsightsWithData);

I believe the issue might be the id of the graphql-ws websocket protocol.
That id needs to match the one sent by the frontend in the GQL_START message. Otherwise, the component won't re-render on a new message.
For more details, look into the subscription-transport-ws protocol

Related

Graphql subscription not getting client-side variables

I am trying to subscribe to a graphql-yoga subscription using apollo-client useSubscription hook
import React from 'react';
import {useSubscription,gql} from '#apollo/client';
const SUB_NEW_MSG = gql`
subscription SUB_NEW_MSG($chatRoom:ID!)
{
newMessage(chatRoom:$chatRoom)
}`;
function NewMsg(){
const { loading,error,data } = useSubscription(SUB_NEW_MSG,{
variables:{
chatRoom: "608851227be4796a8463607a",
},
});
if(loading) return <p>loading...</p>;
if(error) return <p>{error}</p>;
console.log(data);
return <h4>New Message:{data.newMessage}</h4>;
}
Network Status
But gives an error-
Error: Objects are not valid as a React child (found: Error: Variable "$chatRoom" of required type "ID!" was not provided.). If you meant to render a collection of children, use an array instead.
The schema at the backend is
type Subscription{
newMessage(chatRoom: ID!): String!
}
resolver is
const { pubsub } = require("../helper");
const { withFilter } = require("graphql-yoga");
module.exports = {
Subscription: {
newMessage: {
subscribe: withFilter(
() => pubsub.asyncIterator("newMessage"),
(payload, variables) => {
console.log(payload.query, variables, "HALO");
return payload.chatRoom === variables.chatRoom;
}
),
},
},
};
But when I pass the query like below,with no variables to useSubscription hook.Then it works
const SUB_NEW_MSG = gql`
subscription SUB_NEW_MSG
{
newMessage(chatRoom:"608851227be4796a8463607a")
}`;
What should I do?Are there any workarounds?
Change your query to this, and test it.
const SUB_NEW_MSG = gql`
subscription SUB_NEW_MSG($chatRoom: String!) {
newMessage(chatRoom: $chatRoom)
}
`;

GraphQL / Relay - optimisticResponse isn't propagating as I had hoped

In the mutation below I'm updating the avatarId for a person and I would like the new imageUrl to propagate everywhere it's needed, namely the <UserAvatar /> component which can be in a lot of components on the same page.
I know this new URL before ever hitting the UpdatePersonMutation so I'm not sure what I'm doing wrong.
Do I need to involve the store and / or use optimisticUpdater, and / or use subscriptions to get a new avatar image to show up everywhere instantly?
import { commitMutation } from 'react-relay';
import graphql from 'babel-plugin-relay/macro';
import modernEnvironment from '../Environment';
const mutation = graphql`
mutation UpdatePersonMutation($input: UpdatePersonInput!) {
updatePerson(input: $input) {
imageByAvatarId {
id
imageUrl // I know this before I ever reach this mutation
}
person {
id
avatarId
}
}
}
`;
const commit = (payload, callback) => {
const input = {
id: payload.id,
personPatch: {
avatarId: payload.avatarId,
},
};
return commitMutation(modernEnvironment, {
mutation,
variables: {
input,
},
optimisticResponse: {
updatePerson: {
imageByAvatarId: {
id: payload.imageId,
imageUrl: payload.imageUrl,
},
person: {
id: payload.id,
avatarId: payload.avatarId,
},
},
},
onCompleted: response => {
callback(response);
},
onError: error => {
console.log(error);
},
});
};
export default commit;

Apollo useSubscription doesn't work for new windows

This is my first time using useSub and I noticed that although my backend sends it's responses to the client (at least the console.log shows it is). The client using useSub doesn't do anything. I usually use subscribeToMore with query, but for this job I want to only get the most updated info. Is there a way to check if useSub connects correctly? Or is it broken in "#apollo/react-hooks": "^3.1.3"
Query
export const INCOMING_VIDEO_CHAT = gql`
subscription {
incomingVideoChat {
rn
p
}
}
`;
Client
const { data, loading } = useSubscription(INCOMING\_VIDEO\_CHAT, {
onSubscriptionData: ({ subscriptionData }) => {
console.log(subscriptionData);
}
});
Server:
module.exports = {
type: chatInfoType,
subscribe: () => pubsub.asyncIterator(INCOMING\_VIDEO\_CHAT),
async resolve(payload, { }, req) {
if (auth.isAuthenticated(req)) {
if (!payload) {
return;
}
const { userID, rn, p } = payload;
try {
if (req.id === userID) {
return { rn, p };
} else {
return;
}
} catch (e) {
throw new Error(e);
}
}
}
};
You can use Chrome Dev Tools to check if useSubscription connects correctly. In the Network tab of the Chrome DevTools, you should switch the filter to WS to see and debug your Apollo GraphQL subscription. You should read it.

Using ra-data-graphql with AppSync GraphQL API

I'm trying to use react-admin with AWS Amplify library and AWS AppSync SDK.
I can't wrap my head around how to use use ra-data-graphql/ra-data-graphql-simple with AWS AppSync API for querying/mutating data. Trying to do a very basic test with master/examples/demo from https://github.com/marmelab/react-admin/.
Any guidance will be appreciated.
Currently I'm trying to use dataProvider similar to below:
src/dataProvider/appsync.js:
import gql from 'graphql-tag';
import AWSAppSyncClient, { AUTH_TYPE } from 'aws-appsync';
import buildGraphQLProvider, { buildQuery } from 'ra-data-graphql-simple';
import { __schema as schema } from './schema';
const client = new AWSAppSyncClient({
url: "https://xxxx.appsync-api.us-east-1.amazonaws.com/graphql",
region: "us-east-1,
auth: {
type: AUTH_TYPE.AMAZON_COGNITO_USER_POOLS,
jwtToken: async () => (await Auth.currentSession()).getIdToken().getJwtToken(),
}
const myBuildQuery = introspection => (fetchType, resource, params) => {
const builtQuery = buildQuery(introspection)(fetchType, resource, params);
if (resource === 'listings' && fetchType === 'GET_LIST') {
return {
...builtQuery,
query: gql`
query getListings {
data: getListings {
items {
listingId
}
}
}`,
};
}
return builtQuery;
}
export default buildGraphQLProvider({ client: client, introspection: { schema }, buildQuery: myBuildQuery })
src/dataProvider/index.js:
export default type => {
switch (type) {
case 'graphql':
return import('./graphql').then(factory => factory.default());
case 'appsync':
return import('./appsync');
default:
return import('./rest').then(provider => provider.default);
}
};
src/App.js:
...
import dataProviderFactory from './dataProvider';
...
class App extends Component {
state = { dataProvider: null };
async componentDidMount() {
const dataProvider = await dataProviderFactory(
process.env.REACT_APP_DATA_PROVIDER
);
this.setState({ dataProvider });
}
...
src/dashboard/Dashboard.js:
...
fetchData() {
this.fetchListings();
}
async fetchListings() {
const { dataProvider } = this.props;
const { data: reviews } = await dataProvider(GET_LIST, 'listings');
console.log(listings)
}
...
Currently no data is returned from the API and the exception is thrown on await dataProvider(GET_LIST, 'listings'); saying call: [object Model] is not a function, however I see that buildGraphQLProvider promise was resolved succesfully to a function.
Can anyone suggest what I am doing wrong and what is the right way to approach the task?

Writing tests for React and Apollo

I'm trying to write a unit test from React with Apollo.
I found an example from https://dev-blog.apollodata.com/seamless-integration-for-graphql-and-react-6ffc0ad3fead
When trying that out I’m getting an error:
Error:
Uncaught (in react-apollo) Error: Network error: No more mocked responses for the query: query people {
allPeople(first: 1) {
people {
name
__typename
}
__typename
}
}
Test:
it('executes a query', (done) => {
const query = gql` query people { allPeople(first: 1) { people { name } } }`;
const data = { allPeople: { people: [ { name: 'Luke Skywalker' } ] } };
const networkInterface = mockNetworkInterface({ request: { query }, result: { data } });
const client = new ApolloClient({ networkInterface });
const withGraphQL = graphql(query);
class Container extends Component {
componentWillReceiveProps(props) {
expect(props.data.loading).to.be.false;
expect(props.data.allPeople).to.deep.equal(data.allPeople);
done();
}
render() {
return null;
}
};
const ContainerWithData = withGraphQL(Container);
mount(<ApolloProvider client={client}><ContainerWithData /></ApolloProvider>);
});
I know this is an old question, but maybe somebody gets here like I did :) These __typename fields were most likely your problem, the query ended up looking differently than the query you were passing to you mock interface. The error basically meant that it couldn't find any matching mocks for that query.
Anyway, here is a working example of this test, updated to work in Apollo 2.
it('executes a query', (done) => {
const query = gql` query people { allPeople(first: 1) { people { name } } }`;
const data = { allPeople: { people: [ { name: 'Luke Skywalker' } ] } };
const withGraphQL = graphql(query);
class Container extends React.Component {
componentWillReceiveProps(props) {
expect(props.data.loading).toBeFalsy();
expect(props.data.error).toBeUndefined();
expect(props.data.allPeople.people[0].name).toEqual(data.allPeople.people[0].name);
done();
}
render() {
return null;
}
};
const ContainerWithData = withGraphQL(Container);
mount(<MockedProvider removeTypename mocks={[ { request: { query }, result: { data } } ]}><ContainerWithData /></MockedProvider>);
});

Resources