What is updateQuery supposed to do in a client subscription - props are not updating - react-apollo

Using an apollo client with a graphql subscription, I'm following along in the example but I don't see the updateQuery from the subscription event updating my properties. Does the return value from the updateQuery supposed to be handled automatically - or am I supposed to persist it to the store myself?
componentWillReceiveProps(newProps) {
if (!newProps.data.loading && !this.subscription) {
this.subscription = newProps.data.subscribeToMore({
document: getMessages,
variables: { id: 'foo'},
updateQuery: (previousState, {subscriptionData}) => {
const newEntry = subscriptionData.data.createMessage;
return {
Conversation: {
messages: [
...previousState.Conversation.messages,
{
...newEntry
}
]
}
}
},
onError: (err) => console.error(err),
})
}
}

Related

Lambda unable to read DynamoDB Table when called from another DynamoDB table as a trigger

I have two DynamoDB tables: Order and Client.
When an Order is created, a Lambda function is triggered and first seeks to read the Client associated with the Order. However, the Lambda function fails to do so and does not report any error.
The Lambda function is:
var AWS = require('aws-sdk');
AWS.config.update({region: process.env.REGION})
docClient = new AWS.DynamoDB.DocumentClient();
exports.handler = async (event) => {
await event.Records.forEach( async (record) => {
const { eventName, dynamodb } = record
if(eventName==="INSERT") {
console.log('A new order has been created')
const client_id = dynamodb.NewImage.client_id.S
let params = {
TableName: process.env.API_REALAPI_CLIENTTABLE_NAME,
Key: {
"id": client_id
}
}
console.log(params)
try {
const result = await docClient.get(params).promise()
console.log(result)
} catch(err) { console.log(err); throw new Error(err); }
}
})
}
When I run this, it gets to console.log(params) but then doesn't show any result nor any error.
My function was configured with the amplify cli and it appears to have read permissions to the Client table:
"Statement": [
{
"Action": [
"dynamodb:Put*",
"dynamodb:Create*",
"dynamodb:BatchWriteItem",
"dynamodb:Get*",
"dynamodb:BatchGetItem",
"dynamodb:List*",
"dynamodb:Describe*",
"dynamodb:Scan",
"dynamodb:Query",
"dynamodb:Update*",
"dynamodb:RestoreTable*",
"dynamodb:Delete*"
],
"Resource": [
"arn:aws:dynamodb:us-east-1:123456123456:table/Client-abcdefg-dev",
"arn:aws:dynamodb:us-east-1:123456123456:table/Client-abcdefg-dev/index/*"
],
"Effect": "Allow"
},
{
Am I missing something?
Thanks!
The issue is that you are using await in a forEach loop which doesn't work. Your handler is existing before the Db call is done which is why there is no response. You will want to do something more like this:
exports.handler = async (event) => {
const jobs = event.Records.map((record) => {
const { eventName, dynamodb } = record
if (eventName === 'INSERT') {
console.log('A new order has been created')
const client_id = dynamodb.NewImage.client_id.S
let params = {
TableName: process.env.API_REALAPI_CLIENTTABLE_NAME,
Key: {
id: client_id
}
}
console.log(params)
return docClient.get(params).promise()
}
})
try {
const results = await Promise.all(jobs)
} catch (err) {
console.log(err)
throw new Error(err)
}
}

GraphQL Subscriptions return an empty (null) response [duplicate]

I have the following GRAPHQL subscription:
Schema.graphql
type Subscription {
booking: SubscriptionData
}
type SubscriptionData {
booking: Booking!
action: String
}
And this is the resolver subsrciption file
Resolver/Subscription.js
const Subscription = {
booking: {
subscribe(parent, args, { pubsub }, info) {
return pubsub.asyncIterator("booking");
}
}
};
export default Subscription;
Then I have the following code on the Mutation in question
pubsub.publish("booking", { booking: { booking }, action: "test" });
I have the follow subscription file in front end (React)
const getAllBookings = gql`
query {
bookings {
time
durationMin
payed
selected
activity {
name
}
}
}
`;
const getAllBookingsInitial = {
query: gql`
query {
bookings {
time
durationMin
payed
selected
activity {
name
}
}
}
`
};
class AllBookings extends Component {
state = { allBookings: [] }
componentWillMount() {
console.log('componentWillMount inside AllBookings.js')
client.query(getAllBookingsInitial).then(res => this.setState({ allBookings: res.data.bookings })).catch(err => console.log("an error occurred: ", err));
}
componentDidMount() {
console.log(this.props.getAllBookingsQuery)
this.createBookingsSubscription = this.props.getAllBookingsQuery.subscribeToMore(
{
document: gql`
subscription {
booking {
booking {
time
durationMin
payed
selected
activity {
name
}
}
action
}
}
`,
updateQuery: async (prevState, { subscriptionData }) => {
console.log('subscriptionData', subscriptionData)
const newBooking = subscriptionData.data.booking.booking;
const newState = [...this.state.allBookings, newBooking]
this.setState((prevState) => ({ allBookings: [...prevState.allBookings, newBooking] }))
this.props.setAllBookings(newState);
}
},
err => console.error(err)
);
}
render() {
return null;
}
}
export default graphql(getAllBookings, { name: "getAllBookingsQuery" })(
AllBookings
);
And I get the following response:
data: {
booking: {booking: {...} action: null}}
I get that I am probably setting up the subscription wrong somehow but I don't see the issue.
Based on your schema, the desired data returned should look like this:
{
"booking": {
"booking": {
...
},
"action": "test"
}
}
The first booking is the field on Subscription, while the second booking is the field on SubscriptionData. The object you pass to publish should have this same shape (i.e. it should always include the root-level subscription field).
pubsub.publish('booking', {
booking: {
booking,
action: 'test',
},
})

GraphQL query ( Apollo client link state ) returns empty object {}

The Query
const GET_MEMBER = gql`
query getMembers {
getMembers #client {
firstname
lastname
__typename
}
}
`
export { GET_MEMBER }
The Resolver:
export default {
resolvers: {
Query: {
getMembers: async (_, variables, { cache }) => {
try {
const res = await apiClient.get('/contacts')
return { ...res.data, __typename: 'Member' }
} catch (e) {
throw e
}
},
},
apiClient is an instance of axios
React App:
<Query query={GET_MEMBER}>
{({ loading, error, data }) => {....
I am getting this warning
and the Query in my React App returns {}
Where should I start to debeg it?

graphql-subscriptions withFilter returns undefined; subscriptions without variables work ok

I'm trying to get my head around graphql-subscriptions and withFilter. Subscriptions without variables work as intended, but if I try to use withFilter, I only get 'Subscription field must return Async Iterable. Received: undefined' error when I try to run the subscription.
Am I doing something wrong with setting up withFilter, are the some incompatibilities with packages I'm using or am I completely missing something obvious here? All queries and mutations work properly, so the basic set up should be fine.
My set up is similar to this (all code snippets are in https://gist.github.com/aqmattil/41e10e7c9f30b8ea964cecdc61c58f20
Package.json
// package.json
"dependencies": {
"apollo-server-express": "^2.0.0-beta.2",
"body-parser": "^1.18.3",
"express": "^4.16.3",
"graphql": "^0.13.2",
"graphql-subscriptions": "^0.5.8",
"subscriptions-transport-ws": "^0.9.11"
}
Mutations
// mutations.js
const mutation = new GraphQLObjectType({
name: 'mutation',
fields: {
addSite: {
type: SiteType,
description: "Create a new Site",
args: {
name: { type: new GraphQLNonNull(GraphQLString) },
location: { type: GraphQLString },
company: { type: GraphQLString }
},
async resolve(parentValue, { name, location, company }) {
const site = await new Site({ name, location, company }).save()
const siteid = site._id;
console.log("addSite resolve", siteid, name, location, company );
pubsub.publish('siteAdded', { 'siteAdded': site } );
return site;
}
}
}
});
module.exports = mutation;
Subscriptions
// subscriptions.js
const graphql = require('graphql');
const {
GraphQLObjectType,
GraphQLString
} = graphql;
const { withFilter } = require('graphql-subscriptions');
const SiteType = require('./site_type');
const pubsub = require('./pubsub_helper');
const Subscriptions = new GraphQLObjectType({
name: 'subscription',
fields: () => ({
/*
// this code works, commented out to test withfilter
siteAdded: {
type: SiteType,
resolve(payload) {
return payload.siteAdded;
},
subscribe() {
return pubsub.asyncIterator('siteAdded');
}
},
*/
// test withFilter
siteAdded: {
type: SiteType,
args: {
name: { type: GraphQLString }
},
resolve(payload) {
return payload.siteAdded;
},
subscribe() {
// this returns undefined
withFilter(
() => {
console.log("in subscribe withfilter");
return pubsub.asyncIterator('siteAdded');
}
),
(payload, variables) => {
console.log("payload, variables", payload, variables);
return true;
}
}
}
})
});
module.exports = Subscriptions;
I'm using graphiql to run the queries,
// this is used to add a site
mutation {
addSite(name:"test name", location: "somewhere") {
id
}
}
// simple subscription - this works as inteded, and new sites are shown
subscription {
siteAdded {
name
location
company {
id
}
}
}
// using query variables --> returns "Subscription
// field must return Async Iterable. Received: undefined"
subscription {
siteAdded(name: "test name") {
name
location
company {
id
}
}
}

Error TypeError: Cannot read property 'dispatch' of undefined at app.js:12012

Hi I've been trying to learn vuejs and vuex while trying to get response from an api call with vuex concept I got the following error.Please help.
This error occurred
Error TypeError: Cannot read property 'dispatch' of undefined
at app.js:12012
loginAction.js
export const getUsersList = function (store) {
let url = '/Apis/allUsers';
Vue.http.get(url).then((response) => {
store.dispatch('GET_USER_RES', response.data);
if (response.status == 200) {
}
}).catch((response) => {
console.log('Error', response)
})
}
loginStore.js
const state = {
userResponse: []
}
const mutations = {
GET_USER_RES (state, userResponse) {
state.userResponse = userResponse;
}
}
export default {
state, mutations
}
login.vue
import {getUsersList} from './loginAction';
export default {
created () {
try{
getUsersList();
}catch(e){
console.log(e);
}
},
vuex: {
getters: {
getUsersList: state => state.userResponse
},
actions: {
getUsersList
}
}
}
</ script>
If you call the actions manually (like in your try/catch) they'll not get the store context as the first argument. You could use getUsersList(this.store) I think, but instead I would use dispatch to reach all your actions. (I edited just a little bit to get a minimal running example, but I think you get the point!)
new Vue({
render: h => h(App),
created() {
this.$store.dispatch('getUsersList');
},
store: new Vuex.Store({
getters: {
getUsersList: state => state.userResponse
},
actions: {
getUsersList
}
})
}).$mount("#app");
Also, use commit to reach the mutations instead of dispatch. ie:
export const getUsersList = function ({commit}) {
let url = '/Apis/allUsers';
Vue.http.get(url).then((response) => {
commit('GET_USER_RES', response.data); // because GET_USER_RES is a mutation
...

Resources