Can't insert even static size array into creation mutation into AWS GraphQL API in React Native - react-native

I have the the following schema in my schema.graphql folder (please note the relationship keywords):
type Blog #model {
id: ID!
name: String!
description: String
post: [Post] #hasMany
userId: String
username: String
}
type Post #model {
id: ID! #primaryKey
isPrivate: Boolean!
title: String!
description: String
comments: [Comment] #hasMany
userId: String
username: String
}
type Comment #model {
id: ID! #primaryKey
content: String
likes: [Like] #hasMany
userId: String
username: String
}
type Like #model {
id: ID! #primaryKey
isLiked: Boolean!
}
I'm performing creation, deletion and update mutations but somehow I cannot get them to work when I try to insert an array, be its size static or dynamic.
I'll give you an example:
THIS WORKS:
const postCreation = async () => {
try {
const user = await Auth.currentAuthenticatedUser();
const newPost = await API.graphql(
graphqlOperation(createPost, {
input: {
isPrivate: false,
title: "postTitle",
description: "postDescription",
userId: user.attributes.sub,
username: user.username,
},
}),
);
console.log(newPost);
}
catch(e) {
console.log(e.message);
}
}
But this DOES NOT:
const postCreation = async () => {
try {
const user = await Auth.currentAuthenticatedUser();
const newPost = await API.graphql(
graphqlOperation(createPost, {
input: {
isPrivate: false,
title: "postTitle",
description: "postDescription",
comments: [
{
content: "content1",
},
{
content:: "content2",
}
],
userId: user.attributes.sub,
username: user.username,
},
}),
);
console.log(newPost);
} catch(e){
console.log(e.message);
}
}
All I get from the catch block is "undefined"
What's the proper way of doing this?

Related

Link data between two types GraphQl (apollo,express) How do I match the data by the title?

I'm trying to connect two types Manga and Chapter. Manga having a array called Chapters.
type Manga {
_id: ID!
title: String!
alternative: String
description: String
chapter_number: Int
author: String
artist: String
genre: [String]
type: String
release_date: String
status: String
chapters: [Chapter]
}
type Chapter {
_id: ID!
manga: Manga
title: String!
chapter: Int
chapters: [String]
}
But when I try to do it in a resolver the Chapter has no data for me to link back to the Manga
const Manga = require("./models/Manga.model");
const Chapter = require("./models/Chapter.model");
const resolvers = {
Manga: {
chapters: async (parent) => {
return Chapter.find((chapter) => Chapter.title === parent.title);
}
},
I've tried console logging Chapters.title but it returns nothing. Am I doing something wrong or is there another way of doing it? I've tried searching online but after countless hours I'm stuck
Here's the rest of the code
const mongoose = require("mongoose");
const ChapterSchema = new mongoose.Schema({
title: {
type: String,
required: true
},
chapter: {
type: Number,
},
chapters: {
type: [String],
},
})
const Chapter = mongoose.model('Chapter', ChapterSchema);
module.exports = Chapter;
const mongoose = require("mongoose");
const MangaSchema = new mongoose.Schema({
title: {
type: String,
required: true
},
alternative: {
type: String,
},
description: {
type: String,
},
chapter_number: {
type: Number,
},
author: {
type: String,
},
artist: {
type: String,
},
genre: {
type: [String],
},
type: {
type: String,
},
release_date: {
type: String,
},
status: {
type: String,
},
description: {
type: String,
},
Chapters: {
type: [String],
},
})
const Manga = mongoose.model('Manga', MangaSchema);
module.exports = Manga;
const Manga = require("./models/Manga.model");
const Chapter = require("./models/Chapter.model");
const resolvers = {
Manga: {
chapters: async (parent) => {
return Chapter.find((chapter) => Chapter.title === parent.title);
}
},
Query: {
mangas: async () => {
return await Manga.find();
},
manga: async (parent, { id }, context, info) => {
return await Manga.findById(id);
},
chapters: async () => {
return await Chapter.find();
},
chapter: async (parent, { id }, context, info) => {
return await Chapter.findById(id);
},
},
Mutation: {
createManga: async (parent, args, context, info) => {
const {
title,
alternative,
description,
chapter_number,
author,
artist,
genre,
type,
release_date,
status,
} = args.manga;
const manga = new Manga({
title,
alternative,
description,
chapter_number,
author,
artist,
genre,
type,
release_date,
status,
});
await manga.save();
return manga;
},
deleteManga: async (parent, { id }, context, info) => {
await Manga.findByIdAndDelete(id);
return "Manga deleted";
},
updateManga: async (parent, args, context, info) => {
const { id } = args;
const {
title,
alternative,
description,
chapter_number,
author,
artist,
genre,
type,
release_date,
status,
} = args.manga;
const updates = {};
if (title !== undefined) {
updates.title = title;
}
if (alternative !== undefined) {
updates.alternative = alternative;
}
if (chapter_number !== undefined) {
updates.chapter_number = chapter_number;
}
if (author !== undefined) {
updates.author = author;
}
if (artist !== undefined) {
updates.artist = artist;
}
if (genre !== undefined) {
updates.genre = genre;
}
if (description !== undefined) {
updates.description = title;
}
if (type !== undefined) {
updates.type = type;
}
if (release_date !== undefined) {
updates.release_date = release_date;
}
if (status !== undefined) {
updates.status = status;
}
const manga = await Manga.findByIdAndUpdate(id, updates, { new: true });
return manga;
},
createChapter: async (parent, args, context, info) => {
const { title, chapter, chapters } = args.chapter;
const newchapter = new Chapter({ title, chapter, chapters });
await newchapter.save();
return newchapter;
},
deleteChapter: async (parent, { id }, context, info) => {
await Chapter.findByIdAndDelete(id);
return "Chapter deleted";
},
updateChapter: async (parent, args, context, info) => {
const { id } = args;
const { title, chapter, chapters } = args.chapter;
const updates = {};
if (title !== undefined) {
updates.title = title;
}
if (chapter !== undefined) {
updates.description = description;
}
if (chapters !== undefined) {
updates.chapters = chapters;
}
const updatechapter = await Chapter.findByIdAndUpdate(id, updates, {
new: true,
});
return updatechapter;
},
},
};
module.exports = resolvers;
const { gql } = require("apollo-server-express");
const typeDefs = gql`
type Manga {
_id: ID!
title: String!
alternative: String
description: String
chapter_number: Int
author: String
artist: String
genre: [String]
type: String
release_date: String
status: String
chapters: [Chapter]
}
type Chapter {
_id: ID!
manga: Manga
title: String!
chapter: Int
chapters: [String]
}
type GetChapters {
getAllChapters (title:String): [Chapter]
}
type Query {
manga(_id: ID!): Manga
mangas: [Manga]
chapter(_id: ID!): Chapter
chapters: [Chapter]
}
input mangaInput {
title: String
alternative: String
description: String
chapter_number: Int
author: String
artist: String
genre: [String]
type: String
release_date: String
status: String
}
input chapterInput {
title: String
chapter: Int
chapters: [String]
}
type Mutation {
createManga(manga: mangaInput): Manga
deleteManga(id: ID): String
updateManga(id: ID, manga: mangaInput): Manga
createChapter(chapter: chapterInput): Chapter
deleteChapter(id: ID): String
updateChapter(id: ID, chapter: chapterInput): Chapter
}
`;
module.exports = typeDefs;
const express = require('express')
const { ApolloServer} = require('apollo-server-express')
const typeDefs = require('./typeDefs')
const resolvers = require('./resolvers')
const mongoose = require('mongoose')
async function startServer () {
const app = express()
const apolloServer = new ApolloServer({
typeDefs,
resolvers,
})
await apolloServer.start()
apolloServer.applyMiddleware({app: app})
app.use((req,res) => {
res.send("hello from express apollo server")
})
await mongoose.connect("help pls", {
useUnifiedTopology: true,
useNewUrlParser: true,
});
console.log("Mongoose connected...")
app.listen(4000, () => console.log('running on port 4000'))
};
startServer();
Found out what was happening, most tutorials that I was watching were using a static db in their folder, however I was using mongodb(mongoose) which meant that i could not use the same way to access the data I needed but I found a website and this is the solution.
Manga: {
async chapters(parent) {
return await Chapter.find({title: parent.title})
}
},

Query result doesn't give the list of items, but just an empty array [ ] in aws-amplify

I try to make a query to see the list of users. My custom lambda function (which I attached as policy) is as follows:
const aws = require('aws-sdk');
const ddb = new aws.DynamoDB();
exports.handler = async (event, context) => {
if (!event.request.userAttributes.sub) {
console.log("Error: No user was written to DynamoDB")
context.done(null, event);
return;
}
// Save the user to DynamoDB
const date = new Date();
const params = {
Item: {
'id': { S: event.request.userAttributes.sub },
'__typename': { S: 'User' },
'username': { S: event.userName },
'email': { S: event.request.userAttributes.email },
'createdAt': { S: date.toISOString() },
'updatedAt': { S: date.toISOString() },
},
TableName: process.env.USERTABLE,
}
try {
await ddb.putItem(params).promise();
console.log("Success");
} catch (e) {
console.log("Error", e);
}
context.done(null, event);
}
And the schema model is as follows:
type User #model
#auth(rules: [
{ allow: public, operations: [read]}
{ allow: owner }
])
{
id: ID!
username: String!
email: String!
}
Although there is one user in the dynamoDb, when I make a query by:
query listUsers {
listUsers {
items {
email
id
username
}
}
}
the result is as follows:
{
"data": {
"listUsers": {
"items": []
}
}
}
As can be seen, the array of "items" is empty, the items are not shown, when I look at the console dynamoDb table, I see user items with id, username and email values. But it should give the results, where do I make mistake? Meanwhile I am new on aws-amplify.

mongoose code only works if I make 2 http requests

Main objective:
On call to route I either get a new or update existing CartItem object.
The Object amount and total are passed in the middleware so the object total gets recalculated.
The Schema seems to properly apply its middleware properly on the first request (based on the console logs).
However I only get an object with the updated total if I make another http request.
This is beyond my understanding and I would appreciate some assistance.
Schema:
const mongoose = require('mongoose');
const uniqueValidator = require('mongoose-unique-validator');
const Product = require('./Product');
const Cart = require('./Cart');
const refIsValid = require('../middleware/refIsValid');
const cartItemSchema = mongoose.Schema({
name: { type: String },
productRef: { type: mongoose.Schema.Types.ObjectId, ref: 'Product', required: true },
cartRef: { type: mongoose.Schema.Types.ObjectId, ref: 'Cart', required: true },
price: { type: Number, default: 0 },
imgUrl: { type: String },
amount: { type: Number, required: true },
total: { type: Number, default: 0 },
active: { type: Boolean, default: true },
uniqueName: { type: String, unique: true },
});
cartItemSchema.path('productRef').validate((value, respond) => {
return refIsValid(value, respond, Product);
}, 'Invalid product ref.');
cartItemSchema.path('cartRef').validate((value, respond) => {
return refIsValid(value, respond, Cart);
}, 'Invalid cart ref.');
cartItemSchema.path('price').get(function(num) {
return num.toFixed(2);
});
cartItemSchema.pre('save', async function(next) {
const refCart = await Cart.findById(this.cartRef).lean().exec();
const refProduct = await Product.findById(this.productRef).lean().exec();
const uniqueName = `${refProduct._id}_${refCart._id}`;
this.name = refProduct.name;
this.price = refProduct.price;
this.imgUrl = refProduct.imgUrl;
this.total = (this.price * this.amount).toFixed(2);
this.uniqueName = uniqueName;
next();
});
cartItemSchema.post('findOneAndUpdate', async function(result) {
console.log('TCL: result', result);
await result.save(function(err) {
if (err) {
console.error('ERROR!');
}
});
console.log('TCL: docToUpdate', result);
});
cartItemSchema.plugin(uniqueValidator);
module.exports = mongoose.model('cartItem', cartItemSchema);
controller:
static async updateOrCreate(req, res, next) {
try {
let { cartRef, productRef, amount } = req.body;
let options = { upsert: true, new: true, setDefaultsOnInsert: true };
// const uniqueName = `${productRef._id}_${cartRef._id}`;
const updateOrCreate = await CartItem.findOneAndUpdate(
{ cartRef: cartRef, productRef: productRef },
{ amount: amount },
options,
);
if (updateOrCreate) {
const result = await CartItem.findById(updateOrCreate._id);
console.log('TCL: CartItemController -> updateOrCreate -> result', result);
res.status(200).json({
isNew: false,
message: 'item updated',
productItem: result,
});
return;
}
} catch (error) {
error.statusCode = 500;
next(error);
}
}

Vuex - Normalizr doesn't work as expected

I am creating a simple chat app. I have three entities: rooms, messages and users.
I have a fake API that returns a response like this:
[{
id: 1,
name: 'room1',
avatar: 'some img url',
messages: [
{
id: 1,
text: 'some text',
user: {
id: 1,
username: 'Peter Peterson',
avatar: 'some img url'
}
]
}]
And my action looks like this:
getAllRooms({ commit }) {
commit(GET_ALL_ROOMS_REQUEST);
return FakeApi.getAllRooms()
.then(
rooms => {
const { entities } = normalize(rooms, room);
console.log(entities);
commit(GET_ALL_ROOMS_SUCCESS, {
rooms: entities.rooms, byId: rooms.map(room => room.id)
});
commit(GET_ALL_MESSAGES_SUCCESS, { messages: entities.messages });
commit(GET_ALL_USERS_SUCCESS, { users: entities.users });
},
err => commit(GET_ALL_ROOMS_ERROR)
)
}
And my mutations look like this:
[GET_ALL_ROOMS_REQUEST](state) {
state.loading = true;
},
[GET_ALL_ROOMS_SUCCESS](state, payload) {
state.rooms = payload.rooms;
state.byId = payload.byId;
state.loading = false;
},
[GET_ALL_ROOMS_ERROR]() {
state.error = true;
state.loading = false;
}
And my component calls the action like this:
{
mounted() {
this.getAllRooms();
}
}
These are my schema definitions:
const user = new schema.Entity('users');
const message = new schema.Entity('messages', {
user: user
});
const room = new schema.Entity('rooms', {
messages: [message]
})
when i check the response in then method after FakeApi.getAllRooms() every object is wrapped in some weird Observer, and I pass it like that to normalize and normalize returns some weird response.
What am I doing wrong?
The problem wasn't with vuejs, it was with the way I made the normalizr schemas. Because my response is an array at the root I should have had a new rooms array schema, like so:
const user = new schema.Entity('users');
const message = new schema.Entity('messages', {
user: user
});
const room = new schema.Entity('rooms', {
messages: [message]
});
const roomsSchema = [room];
And then use it like this: normalize(rooms, roomsSchema)

Relay mutation for React native returning 400 bad request?

I have been having SO much trouble trying to get a mutation to work.
Given this GraphQL Schema, can anyone PLEASE help me create a simple create User mutation? I don't understand what I am missing. I got it to a point where it throws a 400 error from the GraphQL server and it does not fire the resolve function.
var userType = new GraphQLObjectType({
name: 'User',
description: 'User creator',
fields: () => ({
id: {
type: new GraphQLNonNull(GraphQLString),
description: 'The id of the user.'
},
email: {
type: GraphQLString,
description: 'The email of the user.'
},
business: {
type: GraphQLString,
description:
'The name of the business of the user as the app refers to it.'
},
businessDisplayName: {
type: GraphQLString,
description: 'The name of the business of the user as they typed it in.'
},
trips: {
type: new GraphQLList(tripType),
description: 'The trips of the user, or an empty list if they have none.',
resolve: (user, params, source, fieldASTs) => {
var projections = infoToProjection(fieldASTs)
return Trip.find(
{
_id: {
// to make it easily testable
$in: user.trips.map(id => id.toString())
}
},
projections,
function(err, docs) {
return docs
}
)
}
}
})
})
var schema = new GraphQLSchema({
query: new GraphQLObjectType({
name: 'root',
fields: {
trips: {
type: new GraphQLList(tripType),
resolve: function() {
return Trip.find({})
}
},
users: {
type: new GraphQLList(userType),
resolve: function() {
return User.find({})
}
},
user: {
type: userType,
args: {
id: {
name: 'id',
type: new GraphQLNonNull(GraphQLString)
}
},
resolve: (root, { id }, source, fieldASTs) => {
return User.findOne(
{ _id: id },
infoToProjection(fieldASTs),
function(err, doc) {
return doc
}
)
}
},
trip: {
type: tripType,
args: {
id: {
name: 'id',
type: new GraphQLNonNull(GraphQLString)
}
},
resolve: (root, { id }, source, fieldASTs) => {
var projections = infoToProjection(fieldASTs)
return Trip.findOne({ _id: id }, projections, function(err, doc) {
return doc
})
}
}
}
}),
// mutation
mutation: new GraphQLObjectType({
name: 'Mutation',
fields: {
createUser: {
name: 'createUser',
type: userType,
args: {
input: { type: new GraphQLInputObjectType({
name: 'user',
fields: {
business: { type: GraphQLString },
email: { type: GraphQLString },
businessDisplayName: { type: GraphQLString }
}
})
}},
resolve: (parentValue, args) => {
let user = new User({ ...args.input })
user.save()
return user
}
}
})
})
export var getProjections = infoToProjection
export default schema
This works with GraphiQL using the following queries or mutations:
mutation {
createUser(input:{business:"business", email: "e#mai.l", businessDisplayName: "businessDN"}) {
id
email
business
businessDisplayName
}
}
fragment UserFragment on User {
id
business
businessDisplayName
trips{
title
}
}
{
hideya: user(id: "someid") {
...UserFragment
}
}
I finally fixed the problem. Tried to understand the source of the problem so I used a new NetworkLayer to enable appropriate logging and meaningful error messages. Then threw the an error when my mutation failed. The error message was : "Cannot query field clientMutationId". Looked that up and found that to be able to mutate objects you need to have that field on your GraphQL type. So I added it.
Lesson learned: I highly recommend using react-relay-network-layer.
More details:
Here is my code for it:
import {
RelayNetworkLayer,
urlMiddleware,
batchMiddleware,
} from 'react-relay-network-layer';
Relay.injectNetworkLayer(new RelayNetworkLayer([
batchMiddleware({
batchUrl: 'http://localhost:3000/graphql',
}),
urlMiddleware({
url: 'http://localhost:3000/graphql',
}),
]));
Note: This enables logging and by default it's a simple console.log.
Here is how I threw the error:
const params = {
email: email.toLowerCase(),
businessDisplayName: business,
business: business.toLowerCase()
}
var onSuccess = () => {
console.log('Mutation successful!')
}
var onFailure = transaction => {
var error = transaction.getError() || new Error('Mutation failed.')
console.error(error)
}
Relay.Store.commitUpdate(new FindOrCreateUser({ user: { ...params } }), { onFailure, onSuccess })
And of course you always need to clean your cache and restart your packager.