Graphql mutation with nested types? - reactjs

I'am having troubles with graphql input types.
I'am using react with apollo
i have this working mutation:
addQuestion(
question: QuestionInput!
): Question!
with this type:
type QuestionInput {
name: String!
account_id: Int!
chatbot_id: Int!
question_variants: [String!]!
answer: String!
}
but the problem happens with this mutation:
updateBranch(
id: Int!
branch: BranchInput!
): Branch!
with this type:
type BranchInput {
lead_id: Int!
title: String!
visible_order: Int!
bot_photo: String!
responses: [ResponseInput!]!
bubbles: [BubbleInput!]!
}
and these nested types:
type ResponseInput {
branch_id: Int
owner: String!
message: String!
}
type BubbleInput {
branch_id: Int
name: String!
goto: String!
}
the mutation works on graphql playground:
mutation {
updateBranch(
id: 2
branch: {
lead_id: 1
title: "new title"
visible_order: 1
bot_photo: "photo"
responses: [
{ message: "message 1", owner: "owner1" }
{ message: "message 2", owner: "owner 2" }
]
bubbles: [{name: "bubble 1", goto: "link"}]
}
) {
id
title
}
}
When i execute the mutation in code like this :
const handleEditBranche = async (
lead_id: number,
title: string,
visible_order: number,
bot_photo: string,
responses: {}[],
bubbles: {}[],
id?: string
) => {
const newResponses = responses.map((response: any) => [
{ message: response.message, owner: response.owner },
]);
const newBubbles = bubbles.map((bubble: any) => [
{ name: bubble.name, goto: bubble.goto },
]);
console.log(id, title, visible_order, bot_photo, newResponses, newBubbles);
await updateBranche({
variables: {
id,
branch: {
title,
lead_id,
visible_order,
bot_photo,
responses: newResponses,
bubbles: newBubbles,
},
},
});
//setShowEdit({});
};
I get this error :
even though the data are correct:
I want to include those 2 type inside my mutation, but i don't know how.

The issue is with the way you are constructing the payload.
const newResponses = responses.map((response: any) =>
{ message: response.message, owner: response.owner },
);
const newBubbles = bubbles.map((bubble: any) =>
{ name: bubble.name, goto: bubble.goto },
);
console.log(id, title, visible_order, bot_photo, newResponses, newBubbles);
Removed the additional array for each element in newResponses and newBubbles

Related

Why doesn't updateQueryData from an RTK query update my state?Doesn't add object.Argument of type 'RowType' is not assignable parameter of type never

I am using an RTK request and want to overwrite the state with the result of the server's response.
I want to overwrite the original array with these results.
I am using updateQueryData function, I am using push method but it is not working.
enter image description here
I just started learning typescript. And my typing is not accurate
export interface RowListItemLevel1 {
children: RowListItemLevel2[] | [], //| RowType[],
id: number,
parent: number,
price: number,
quantity: number,
title: string,
type: string,
unit: string,
unitPrice: number,
}
export interface RowListItemLevel2 {
children: RowListItemLevel1[] | [],//| RowType[],
id: number,
parent: number,
price: number,
quantity: number,
title: string,
type: string,
unit: string,
unitPrice: number,
}
createRow: build.mutation<RowBaseResponse, RowAddRequest>({
query: ({ id:calculationId, ...body } ) =>{
return {
url: `/calculation/${calculationId}/row/add`,
method: 'POST',
body,
}},
async onQueryStarted({id:calculationId, ...patch }, { dispatch, queryFulfilled }) {
try {
const { data: createRow } = await queryFulfilled
dispatch(
calculationsApi.util.updateQueryData('getRowsList', {id:calculationId},(draft) => {
function changedCreateRow(arr:RowListItemLevel1[]){
arr.forEach((elem: RowListItemLevel1)=> {
if (elem.id === createRow.current.parent) {
elem.children.push(createRow.current)
}
return changedCreateRow(elem.children);
})
}
changedCreateRow(draft.data)
})
)
} catch {}
},
// invalidatesTags: ['CreateRow'],
}),
A friend from the team came to the rescue, and I fixed the typescript
export interface RowListItemLevel1 {
children: RowListItemLevel2[],
id: number,
parent: number,
price: number,
quantity: number,
title: string,
type: string,
unit: string,
unitPrice: number,
}
export interface RowListItemLevel2 {
children: RowListItemLevel1[],
id: number,
parent: number,
price: number,
quantity: number,
title: string,
type: string,
unit: string,
unitPrice: number,
}
&
elem.children.push({ ...createRow.current, children: [] })
now everything is working

Nested Mutation in graphql

I'm trying to implement GraphQL+MongoDb into a project and I'm stuck at nesting.
I have to do a meal history for an User. I've managed to create the User type+resolver+query but i'm stuck at getting a meal for that User.
I've got this typeDefs:
import { gql } from 'apollo-server';
export const typeDefs = gql`
type User {
id: Int!
CNP: String
firstName: String!
lastName: String!
email: String!
birthday: String
sex: String
Studii: String
Adresa: String
}
type Aliment {
id: String!
foodName: String!
}
type Foods {
aliment: Aliment!
quantity: Int!
}
type Meal {
id: String!
user: User!
currentmeal: [Foods!]!
date: String!
}
input AlimentInput {
id: String
foodName: String
}
input FoodsInput {
aliment: AlimentInput
quantity: String
}
input UserInput {
id: Int
CNP: String
firstName: String
lastName: String
email: String
birthday: String
sex: String
Studii: String
Adresa: String
}
input MealInput {
id: String
user: UserInput
currentmeal: [FoodsInput]
date: String
}
type Query {
getUsers: [User]
getUserInfo(id: Int): User
getMeals: [Meal]
}
type Mutation {
createMeal(
id: String!
user: UserInput
currentmeal: [FoodsInput]
date: String
): Meal
}
`;
and this resolver:
import { Users, Meals } from '../models/users.js';
export const resolvers = {
Query: {
getUsers: async () => {
console.log('1');
return Users.find({}).then((users) => {
console.log('2');
return users.map((user) => ({
id: user.id,
firstName: user.firstName,
lastName: user.lastName,
email: user.email,
}));
});
},
getUserInfo: async (parent, args) => {
console.log('b');
return Users.findOne({ id: args.id }).then(async (user) => {
return {
id: user.id,
firstName: user.firstName,
lastName: user.lastName,
email: user.email,
};
});
},
getMeals: async () => {
return Meals.find({}).then((meals) => {
return meals.map(async (meal) => {
console.log(meal);
return {
id: meal.id,
user: meal.user,
currentmeal: meal.currentmeal,
date: meal.date,
};
});
});
},
},
Mutation: {
createMeal: async (id, user, currentmeal, date) => {
return await Meals.create({
id: id,
user: user,
currentmeal: currentmeal,
date: date,
});
},
},
};
and these schemas:
import mongoose from 'mongoose';
const { Schema } = mongoose;
const { model } = mongoose;
const userSchema = new Schema({
id: { type: String, required: true, index: { unique: true } },
firstName: { type: String, required: true },
lastName: { type: String, required: true },
userName: { type: String, required: true },
email: { type: String, required: true },
jobTitle: { type: String, required: false },
});
const alimentSchema = new Schema({
id: { type: String, required: true, index: { unique: true } },
foodName: { type: String, required: true },
});
const foodsSchema = new Schema({
aliment: { type: Schema.Types.ObjectId, ref: 'users' },
quantity: { type: String },
});
const mealSchema = new Schema({
id: { type: String, required: true, index: { unique: true } },
user: { type: Schema.Types.ObjectId, ref: 'users' },
currentmeal: [foodsSchema],
date: { type: String },
});
export const Users = model('users', userSchema);
export const Aliments = model('aliments', alimentSchema);
export const Meals = model('meals', mealSchema);
When I use the getMeals query i want to get something like:
Meal{
id: 1
user:{
id: Int!
CNP: String
firstName: String!
lastName: String!
email: String!
birthday: String
sex: String
Studii: String
Adresa: String
}
currentmeal: [
{
aliment: {
id:1
foodName:Chicken breasts
}
quantity: 100
},
{
aliment: {
id:2
foodName:French Fries
}
quantity: 200
}
]
date: notimportant
}
I've been searching for several hours on nesting for GraphQL but found nothing useful.
I've managed to populate the database somehow and this is what i currently have:
import { MongoClient } from 'mongodb';
const populate = async () => {
const connstring ="";
const client = await MongoClient.connect(connstring, {
useNewUrlParser: true,
useUnifiedTopology: true,
});
const db = client.db('test');
let id = 0;
let meals = [];
for (id = 1; id < 90; id++) {
const user = await db.collection('users').findOne({ id: id });
const currentmeal = [];
let aliment1 = await db.collection('aliments').findOne({ id: id });
let aliment2 = await db.collection('aliments').findOne({ id: id });
currentmeal.push({
aliment: aliment1,
quantity: 200 - id,
});
currentmeal.push({
aliment: aliment2,
quantity: 200 - id,
});
const date = JSON.stringify(Date.now());
meals.push({
id,
user,
currentmeal,
date,
});
}
await db.collection('meals').insertMany(meals);
client.close();
};
populate();
this is how Users look:
this is how aliments look:
and this is how meals look:
I think that in the meals collection i should have user's _id instead of the user object, same for aliment in the currentmeal Array

MongoDB query to search for user based on multiple parameters

I have no idea how to deal with MongoDB, and I tried and failed.
The problem as following:
I have data gathered from URL. This data is: text, location, skills, type, hasJob, hasItem.
This data used to search for a user in database has matched values with these parameters.
for example, this is an URL: http://localhost:3000/profile/search/?type=exact&skills=Test,Programming&text=salam&location=uk
text=salam
type=exact
skills=[Test, Programming]
location=uk
The required query should do the following: if there is matched name and location and skills, the query should return the user that has these matched data. But, if there is a matched name and location and text without any skills matches, the query also should return the user.
So, the skills may be matched or not, in all cases, the query should return user if there is a matched name and location as text.
This is the search end-point that should does the work:
router.get('/search', (req, res) => {
if (!req.query || !req.query.type || (!req.query.text && !req.query.skills && !req.query.location)) {
return res.status(400).json({status: 'bad', error: 'Missing required fields.'});
}
if (req.query.type !== 'fuzzy' && req.query.type !== 'exact') {
return res.status(400).json({status: 'bad', error: 'Invalid type.'});
}
const user = req.user;
const exact = req.query.type === 'exact';
let result = [];
let searchBuilder = {};
let searchQuery = [];
let locationResult =[];
let textResult = [];
let skillsResuts = [];
let locationData=[];
let textData=[];
let skillsData= [];
let skills =[];
let item = {};
//1- Location
if (req.query.location) {
locationData = [
{'profile.address.city': { $regex: req.query.location }},
{'profile.address.country': { $regex: req.query.location }},
{'profile.address.state': { $regex: req.query.location }},
{'profile.address.zip': { $regex: req.query.location }}
]
searchBuilder['$or'] = locationData;
searchQuery.push({
'$or': locationData
})
}
//2- Text
if (req.query.text) {
textData = [
{ 'phone': req.query.text },
{'profile.fullName': { $regex: req.query.text }},
{'profile.email': { $regex: req.query.text }},
// {'profile.username': { $regex: req.query.text }}
]
searchBuilder['$or'] = textData;
searchQuery.push({
'$or': textData
})
}
//3- Skills
if (req.query.skills) {
let allSkills = req.query.skills.split(',')
skillsData = {
'profile.skills.custom.name': { '$in': allSkills }
}
skills = allSkills.map(function(v, i){return new RegExp(v, 'i')});
searchBuilder['profile.skills.custom.name'] = { '$in': skills }
item = {'profile.skills.custom.name': { '$in': skills }};
searchQuery.push({
'$or': [ skillsData ]
})
}
//4. hasJobs
if (req.query.hasJobs) {
searchBuilder['jobs.0'] = { $exists: true };
searchQuery.push({
'jobs.0': { '$exists': true }
})
}
//5.hasItems
if (req.query.hasItems) {
searchBuilder['items.0'] = { $exists: true };
searchQuery.push({
'items.0': { '$exists': true }
})
}
// Add the query here
db.User.find({}})
.populate('connections.user')
.populate('jobs')
.populate('items')
.populate('profile.skills.custom')
.select('-contacts')
.sort('connections.user.profile.fullName')
.then(users => {
async.each(users, (user, next) => {
if (user.id === req.user.id) {
return next();
}
const check = result.find(usr => {
return usr.id === user.id;
});
if (!check) {
result.push(user);
next();
}
else {
return next();
}
})
})
.then(() => {
res.json(result);
})
.catch(err => {
return res.status(400).json({status: 'bad', error: err});
});
});
and this is the schema
const UserSchema = new Schema({
phone: String,
shortPhone: String,
password: String,
profile: {
email: String,
fullName: String,
username: String,
address: {
city: {type: String, default: 'Unknown'},
state: String,
country: String,
zip: String,
},
photos: String,
summary: String,
resume: String,
resumeFile: String,
experiences: [{
title: String,
company: String,
location: String,
startDate: Date,
endDate: Date,
description: String
}],
educations: [{
school: String,
degree: String,
field: String,
startDate: Date,
endDate: Date,
description: String
}],
skills: {
standard: [{type: Schema.Types.ObjectId, ref: 'Skill'}],
custom: [{name: String}]
},
points: {type: Number, default: 0},
totalPoints: {type: Number, default: 0},
hidden: {type: Boolean, default: false},
public: {type: Boolean, default: true},
countryCode: String,
},
location: {type: [Number], index: '2d'},
items: [{type: Schema.Types.ObjectId, ref: 'Item'}],
blogs: [{type: Schema.Types.ObjectId, ref: 'Blog'}],
news: [{type: Schema.Types.ObjectId, ref: 'News'}],
conversations: [{type: Schema.Types.ObjectId, ref: 'Conversation'}],
viewBy: [{type: Schema.Types.ObjectId, ref: 'User'}],
level: {type: Number, default: 1},
verified: {type: Boolean, default: false},
notificationTokens: [{type: String}],
connections: [{
user: {type: Schema.Types.ObjectId, ref: 'User'},
relationship: {type: String, enum: ['requested', 'received', 'connected']},
isForced: {type: Boolean, default: false}
}],
declinedRequests: [String],
jobs: [{type: Schema.Types.ObjectId, ref: 'Job'}],
favoritedJobs: [{type: Schema.Types.ObjectId, ref: 'Job'}],
sharedJobs: [{type: Schema.Types.ObjectId, ref: 'Job'}],
viewedJobs: [{type: Schema.Types.ObjectId, ref: 'Job'}],
favoritedItems: [{type: Schema.Types.ObjectId, ref: 'Item'}],
sharedItems: [{type: Schema.Types.ObjectId, ref: 'Item'}],
viewedItems: [{type: Schema.Types.ObjectId, ref: 'Item'}],
actions: [{type: Schema.Types.ObjectId, ref: 'Action'}],
incentives: [{type: Schema.Types.ObjectId, ref: 'Incentive'}],
contacts: [String],
synced: {type: Boolean, default: false}
});
//UserSchema.plugin(autoPopulateAllFields);
exports.User = mongoose.model('User', UserSchema);

Save current User into field within array in Mongoose

Here is a relevant part of my Schema, where I'll make reservations to a "space":
var spaceSchema = new mongoose.Schema({
spaceName: String,
scheduledDates: [{
scheduledDates: String,
author: {
id: {
type: mongoose.Schema.Types.ObjectId,
ref: 'User'
},
username: String
}
}]
});
Author should be the current user that's logged in. Here is my route to update those fields:
router.put('/:space_id/schedule', function(req, res) {
Space.findByIdAndUpdate(req.params.space_id, {
'$push': { 'scheduledDates': req.body.space, 'author': req.user._id }
}, { "new": true, "upsert": true }, function(err, space) {
if (err) {
console.log(err);
} else {
console.log(req.body.space);
}
});
});
I can't access "author" correctly, because it's inside the array. What can I do to update this array, adding a new date and user to make the reservation?
Thank you
UPDATE
I tried to use "_id" instead of "id" in my property but got the same result. It seems like it's ignoring the "author" field, and only saving "scheduledDates"
So the schema was like this:
scheduledDates: [{
scheduledDates: String,
author: {
_id: {
type: mongoose.Schema.Types.ObjectId,
ref: 'User'
},
username: String
}
}]
And then in my route, I changed what I was 'pushing':
'$push': { 'scheduledDates': req.body.space, 'author._id': req.user._id }
UPDATED 2
Changed the way I was getting the object to push:
'$push': {
'scheduledDates': {
'scheduledDates': req.body.space,
'author': { _id: req.user._id, username: req.user.username }
}
}
Now I'm getting the following error:
message: 'Cast to string failed for value "{ scheduledDates: \'04/11/2017\' }" at path "scheduledDates"',
name: 'CastError',
stringValue: '"{ scheduledDates: \'04/11/2017\' }"',
kind: 'string',
value: [Object],
path: 'scheduledDates',
reason: undefined } } }

How to push to an array in mongoose

I have this code:
router.post('/setsuggestions', function(req, res, next){
if(!req.body.username || !req.body.challengessuggestions){
return res.status(400).json({message: challengessuggestions});
}
var query = { username: req.body.username };
/*User.findOneAndUpdate(query, { challengessuggestions: req.body.challengessuggestions }, callback = function(response){
res.json(response);
});*/
/*
User.findOneAndUpdate(
query,
{$push: {"challengessuggestions": {$oid: req.body.challengessuggestions}}},
callback = function(response) {
res.json(response);
}
);*/
User.findOneAndUpdate(
query,
{$push: {challengessuggestions: req.body.challengessuggestions}},
{safe: true, upsert: true},
function(err, model) {
res.json(err);
}
);
});
When I postman like this:
I get the following error:
{ "name": "MongoError", "message": "exception: The field
'challengessuggestions' must be an array but is of type OID in
document {_id: ObjectId('56263b910d1a2f1f0077ffae')}", "errmsg":
"exception: The field 'challengessuggestions' must be an array but is
of type OID in document {_id: ObjectId('56263b910d1a2f1f0077ffae')}",
"code": 16837, "ok": 0 }
This is the schema definition of AppUser:
var UserSchema = new mongoose.Schema({
username: { type: String, lowercase: true, unique: true },
firstname: { type: String},
lastname: { type: String},
difficulty: { type: String},
isstudent: { type: Boolean },
haschildren: { type: Boolean},
gender: { type: String },
email: { type: String, unique: true},
birthdate: String,
isdoingchallenges: { type: Boolean },
challengescompleted: [{ type: ObjectId, ref: 'Challenge' }],
currentchallenge: { type: ObjectId, ref: 'Challenge' },
challengessuggestions: [{ type: ObjectId, ref: 'Challenge' }],
hash: String,
salt: String
});
This is the schema definiton of challenge:
var Challengeschema = new mongoose.Schema({
name: { type: String, initial: true, required: true, index: true },
image: { type: Array },
difficulty: { type: String },
studentfriendly: { type: Boolean },
childfriendly: { type: Boolean },
description: { type: String }
});
I'm sending this in the function that calls the api:
Object {_id: "5631423f8c5ba50300f2b4f6", difficulty: "medium", name:
"Probeer 1 van onze recepten.", __v: 0, childfriendly: true…}
This gives me following error:
D:\Stijn\Documenten\EVA-project-Groep-6\Api\node_modules\mongoose\lib\schema\obj
ectid.js:134
throw new CastError('ObjectId', value, this.path);
^ Error
at MongooseError.CastError (D:\Stijn\Documenten\EVA-project-Groep-6\Api\node
_modules\mongoose\lib\error\cast.js:18:16)
at ObjectId.cast (D:\Stijn\Documenten\EVA-project-Groep-6\Api\node_modules\m
ongoose\lib\schema\objectid.js:134:13)
at Array.MongooseArray.mixin._cast (D:\Stijn\Documenten\EVA-project-Groep-6\
Api\node_modules\mongoose\lib\types\array.js:124:32)
at Array.MongooseArray.mixin._mapCast (D:\Stijn\Documenten\EVA-project-Groep
-6\Api\node_modules\mongoose\lib\types\array.js:295:17)
at Object.map (native)
at Array.MongooseArray.mixin.push (D:\Stijn\Documenten\EVA-project-Groep-6\A
pi\node_modules\mongoose\lib\types\array.js:308:25)
at Query. (D:\Stijn\Documenten\EVA-project-Groep-6\Api\routes\ind ex.js:144:44)
at D:\Stijn\Documenten\EVA-project-Groep-6\Api\node_modules\mongoose\node_mo
dules\kareem\index.js:177:19
at D:\Stijn\Documenten\EVA-project-Groep-6\Api\node_modules\mongoose\node_mo
dules\kareem\index.js:109:16
at doNTCallback0 (node.js:408:9)
at process._tickCallback (node.js:337:13) 29 Oct 22:05:38 - [nodemon] app crashed - waiting for file changes before starti ng...
How do I solve this?
Query the User user using findOne() first and use the first found document that's passed to the callback to save the embedded documents with:
router.post('/setsuggestions', function(req, res, next){
if(!req.body.username || !req.body.challengessuggestions){
return res.status(400).json({message: challengessuggestions});
}
var query = { username: req.body.username };
User.findOne(query, function (err, user){
if (err) //throw ...
if (user) {
if (user.challengessuggestions && user.challengessuggestions.length) {
user.challengessuggestions.push(req.body.challengessuggestions);
}
else {
user.challengessuggestions = [req.body.challengessuggestions];
}
// save changes
user.save(function (err) {
if (!err) {
// done ...
}
});
}
});
);

Resources