Problem:When I try and send/store data in my database I get this error. Specifically, I am trying to create/save a classroom with student names.
Tech Used:
Prisma/Postgres connected to AWS RDS and Next.js, deployed on Vercel, etc.
Error Message
PrismaClientValidationError: Argument data.classrooms.upsert.0.create.students.connectOrCreate.0.create.school.connect of type schoolWhereUniqueInput needs at least one argument.
Argument data.classrooms.upsert.0.update.students.upsert.0.create.school.connect of type schoolWhereUniqueInput needs at least one argument.
at Document.validate (/var/task/node_modules/#prisma/client/runtime/index.js:29501:20)
at serializationFn (/var/task/node_modules/#prisma/client/runtime/index.js:33060:19)
at runInChildSpan (/var/task/node_modules/#prisma/client/runtime/index.js:22550:12)
at PrismaClient._executeRequest (/var/task/node_modules/#prisma/client/runtime/index.js:33067:31)
at async PrismaClient._request (/var/task/node_modules/#prisma/client/runtime/index.js:32994:16)
at async profile (/var/task/.next/server/pages/api/user/profile.js:175:27)
at async Object.apiResolver (/var/task/node_modules/next/dist/server/api-utils/node.js:366:9)
at async NextNodeServer.runApi (/var/task/node_modules/next/dist/server/next-server.js:481:9)
at async Object.fn (/var/task/node_modules/next/dist/server/next-server.js:735:37)
at async Router.execute (/var/task/node_modules/next/dist/server/router.js:247:36) {
clientVersion: '4.9.0'
}
DB Models with relationships: school (1 to many w/students); students (many to many with classrooms); teachers (one to many with students, many to many with classrooms)
Code/Prisma Query
export default async (req, res) => {
...
classroom.students.forEach((student) => {
const totalStudentPoints = student.rewardsRecieved.reduce(
(totalPoints, reward) => {
return totalPoints + reward.pointValue;
},
0
);
groups[student.group.name] += totalStudentPoints;
});
return { ...classroom, groupsTotalPoints: groups };
});
user.classrooms = newClassrooms;
res.json(user);
} else {
console.log("Could Not Find User");
res.status(401).json({
error: "Not authorized",
});
}
}
if (req.method === "PUT") {
const connectStudents = (shouldUpsert) => {
const students = req.body.students;
return students.map((student) => {
const UNSAFEHASH = md5(student.id);
const studentQuery: any = {
where: {
id: student.id,
},
create: {
id: student.id,
firstName: student.firstName,
lastName: student.lastName,
profilePicture: student.profilePicture,
userKey: UNSAFEHASH,
school: {
connect: {
id: req.body.schoolId,
},
},
group: {
connect: {
id: student.group.id,
},
},
},
};
if (shouldUpsert) {
studentQuery.update = {
firstName: student.firstName,
lastName: student.lastName,
profilePicture: student.profilePicture,
userKey: UNSAFEHASH,
group: {
connect: {
id: student.group.id,
},
},
};
}
return studentQuery;
});
};
try {
const user = await prisma.staff.update({
where: {
id: session.id,
},
data: {
firstName: req.body.firstName,
lastName: req.body.lastName,
classrooms: {
upsert: [
{
where: {
id: req.body.classId || "-1",
},
create: {
// id: req.body.classId,
name: req.body.className,
subject: req.body.classSubject,
students: {
connectOrCreate: connectStudents(false),
},
},
update: {
name: req.body.className,
subject: req.body.classSubject,
students: {
upsert: connectStudents(true),
},
},
},
],
},
},
});
Take a look at the PUT request and the prima.staff.update method more specifically. I was looking at the UPSERT I have there, but I can't figure out what's wrong.
Related
This might not be possible but is there a way to create an flexible amount of posts in prisma. For example, I have a user and I would like them to create be able to create any amount of posts at once, so it would be one post or three posts. Is this possible using Prisma?
Here is the query I'm using:
const user = await prisma.user.update({
where: {
id: 9,
},
data: {
posts: {
// This is where I would like to make the amount of posts being created on the frontend flexible
createMany: {
data: [{ title: 'My first post' }, { title: 'My second post' }],
},
},
},
})
Here you go an example how to do that:
const { PrismaClient } = require('#prisma/client')
const prisma = new PrismaClient()
const saveData = async () => {
const user = await prisma.user.create({
data: {
name: 'John Doe',
posts: {
createMany: {
data: [
{
title: 'First Post',
},
],
}
}
},
include: {
posts: true
}
})
console.log(JSON.stringify(user, null, 2));
await prisma.user.update({
where: {
id: user.id
},
data: {
posts: {
createMany: {
data: [
{
title: 'Second Post',
},
{
title: 'Third Post',
}
],
}
}
}
})
console.log(JSON.stringify(await prisma.user.findMany({ include: {posts: true} }), null, 2));
}
saveData()
And here you go the result
I wrote 2 resolvers (signup and login). They return a JWToken. In playground, I'm testing the functions and it won't let me make a mutation without specifying some subfields to display. They all are null and sometimes I get a "Cannot return null for non-nullable field users._id". I'm using express and mongodb.
const { UserTC, UserSchema } = require("../models/user");
const bcrypt = require('bcrypt')
const jsonwebtoken = require('jsonwebtoken')
const resolver = function () {};
resolver.me = UserTC.addResolver({
name: "me",
type: UserTC,
args: { record: UserTC.getInputType() },
resolve: async ({ source, args }) => {
if (!args.record) {
throw new Error('You are not authenticated!')
}
return await UserSchema.findById(args.record._id)
}
});
resolver.signup = UserTC.addResolver({
name: "signup",
type: UserTC,
args: { record: UserTC.getInputType() },
resolve: async ({ source, args }) => {
const user = await UserSchema.create({
firstName: args.record.firstName,
lastName: args.record.lastName,
email: args.record.email,
password: await bcrypt.hash(args.record.password, 10)
});
return jsonwebtoken.sign(
{ id: user._id, email: user.email },
process.env.JWT_SECRET,
{ expiresIn: '1y' }
)
},
});
resolver.login = UserTC.addResolver({
name: "login",
type: UserTC,
args: { record: UserTC.getInputType() },
resolve: async ({ source, args }) => {
const user = await UserSchema.findOne({email: args.record.email });
if (!user) {
throw new Error('Incorrect email')
}
const valid = await bcrypt.compare(args.record.password, user.password);
if (!valid) {
throw new Error('Incorrect password')
}
return jsonwebtoken.sign(
{ id: user._id, email: user.email },
process.env.JWT_SECRET,
{ expiresIn: '1y' }
)
},
});
module.exports = resolver;
Here are the queries:
mutation {
signup(
record: {
firstName: "Ben2"
lastName: "Dormant"
email: "Ben2.Dormant#gmail.com"
password: "qwerty"
}
)
}
mutation {
login(record: {
email: "nicolas.sursock#gmail.com",
password: "azerty" })
}
I founded a bug, try like this because you can not use await in reutrn
resolver.me = UserTC.addResolver({
name: "me",
type: UserTC,
args: { record: UserTC.getInputType() },
resolve: async ({ source, args }) => {
if (!args.record) {
throw new Error('You are not authenticated!')
}
let result = await UserSchema.findById(args.record._id)
return result
}
});
const message: Message = {
conversationId: conversationId,
content: this.message,
createdAt: id,
sender: this.senderId,
isSent: false,
id: id,
};
this.appsync.hc().then((client) => {
client
.mutate({
mutation: createMessage,
variables: message,
optimisticResponse: () => ({
createMessage: {
...message,
__typename: "Message",
},
}),
update: (proxy, { data: { createMessage: _message } }) => {
const options = {
query: getConversationMessages,
variables: {
conversationId: conversationId,
first: constants.messageFirst,
},
};
// error on below line
const data = proxy.readQuery(options);
const _tmp = unshiftMessage(data, _message);
proxy.writeQuery({ ...options, data: _tmp });
},
})
.then(({ data }) => {
console.log("mutation complete", data);
console.log("mutation complete", data);
})
.catch((err) => console.log("Error creating message", err));
});
Analytics.record("Chat MSG Sent");
}
ERROR
errorHandling.js:7 Error: Can't find field allMessageConnection({"conversationId":"XXXXXXXXXXXXXXXXXXXXXXXXXXX","first":100}) on object {
"me": {
"type": "id",
"generated": false,
"id": "User:XXXXXXXXXXXXXXXXXXX",
"typename": "User"
}
}.
I've been trying to get my Google Actions Smart Home (nodejs) working in AWS lambda. However it isn't working. Whenever I connect it on the Google Home app, I just get a message of "Couldn't update the setting...". I've already configured the API gateway correctly and set the Handler to "index.smarthome" as shown in the below image link. Why isn't it working, and how can I get my lambda google action smart home working?
Image Link
My firebase version is working though (modified from the washing machine example at https://codelabs.developers.google.com/codelabs/smarthome-washer/#2).
const functions = require('firebase-functions');
const {smarthome} = require('actions-on-google');
const app = smarthome();
app.onSync(body => {
return {
requestId: 'ff36a3cc-ec34-11e6-b1a0-64510650abcf',
payload: {
agentUserId: '123',
devices: [{
id: 'washer',
type: 'action.devices.types.WASHER',
traits: [
'action.devices.traits.OnOff',
'action.devices.traits.StartStop',
'action.devices.traits.RunCycle',
'action.devices.traits.Modes',
'action.devices.traits.Toggles',
],
name: {
defaultNames: ['My Washer'],
name: 'Washer',
nicknames: ['Washer']
},
deviceInfo: {
manufacturer: 'Acme Co',
model: 'acme-washer',
hwVersion: '1.0',
swVersion: '1.0.1'
},
attributes: {
pausable: true,
availableModes: [{
name: 'load',
name_values: [{
name_synonym: ['load'],
lang: 'en'
}],
settings: [{
setting_name: 'small',
setting_values: [{
setting_synonym: ['small'],
lang: 'en'
}]
}, {
setting_name: 'large',
setting_values: [{
setting_synonym: ['large'],
lang: 'en'
}]
}],
ordered: true
}],
availableToggles: [{
name: 'Turbo',
name_values: [{
name_synonym: ['turbo'],
lang: 'en'
}]
}]
}
}]
}
};
});
app.onExecute((body) => {
const {requestId} = body;
const payload = {
commands: [{
ids: [],
status: 'SUCCESS',
states: {
online: true,
},
}],
};
for (const input of body.inputs) {
for (const command of input.payload.commands) {
for (const device of command.devices) {
const deviceId = device.id;
payload.commands[0].ids.push(deviceId);
for (const execution of command.execution) {
const execCommand = execution.command;
const {params} = execution;
switch (execCommand) {
case 'action.devices.commands.OnOff':
payload.commands[0].states.on = params.on;
break;
case 'action.devices.commands.StartStop':
payload.commands[0].states.isRunning = params.start;
break;
case 'action.devices.commands.PauseUnpause':
payload.commands[0].states.isPaused = params.pause;
break;
case 'action.devices.commands.SetModes':
break;
case 'action.devices.commands.SetToggles':
break;
}
}
}
}
}
return {
requestId: requestId,
payload: payload,
};
});
exports.smarthome = functions.https.onRequest(app);
And here is the code that I used in my AWS lambda function. I referenced https://github.com/actions-on-google/actions-on-google-nodejs & creating dialogflow v2 project with serverless to make it lambda compatible. The main difference between the lambda and firebase versions is the "exports.smarthome" code.
const {smarthome} = require('actions-on-google');
const app = smarthome();
app.onSync(body => {
return {
requestId: 'ff36a3cc-ec34-11e6-b1a0-64510650abcf',
payload: {
agentUserId: '123',
devices: [{
id: 'washer',
type: 'action.devices.types.WASHER',
traits: [
'action.devices.traits.OnOff',
'action.devices.traits.StartStop',
'action.devices.traits.RunCycle',
'action.devices.traits.Modes',
'action.devices.traits.Toggles',
],
name: {
defaultNames: ['My Washer'],
name: 'Washer',
nicknames: ['Washer']
},
deviceInfo: {
manufacturer: 'Acme Co',
model: 'acme-washer',
hwVersion: '1.0',
swVersion: '1.0.1'
},
attributes: {
pausable: true,
availableModes: [{
name: 'load',
name_values: [{
name_synonym: ['load'],
lang: 'en'
}],
settings: [{
setting_name: 'small',
setting_values: [{
setting_synonym: ['small'],
lang: 'en'
}]
}, {
setting_name: 'large',
setting_values: [{
setting_synonym: ['large'],
lang: 'en'
}]
}],
ordered: true
}],
availableToggles: [{
name: 'Turbo',
name_values: [{
name_synonym: ['turbo'],
lang: 'en'
}]
}]
}
}]
}
};
});
app.onExecute((body) => {
const {requestId} = body;
const payload = {
commands: [{
ids: [],
status: 'SUCCESS',
states: {
online: true,
},
}],
};
for (const input of body.inputs) {
for (const command of input.payload.commands) {
for (const device of command.devices) {
const deviceId = device.id;
payload.commands[0].ids.push(deviceId);
for (const execution of command.execution) {
const execCommand = execution.command;
const {params} = execution;
switch (execCommand) {
case 'action.devices.commands.OnOff':
payload.commands[0].states.on = params.on;
break;
case 'action.devices.commands.StartStop':
payload.commands[0].states.isRunning = params.start;
break;
case 'action.devices.commands.PauseUnpause':
payload.commands[0].states.isPaused = params.pause;
break;
case 'action.devices.commands.SetModes':
break;
case 'action.devices.commands.SetToggles':
break;
}
}
}
}
}
return {
requestId: requestId,
payload: payload,
};
});
exports.smarthome = function(event, context, callback) {
app.handler(event, {})
.then((res) => {
if (res.status != 200) {
callback(null, {
"fulfillmentText": `I got status code: ${res.status}`
});
} else {
callback(null, res.body);
}
}).catch((e) => {
callback(null, {
"fulfillmentText": `There was an error\n${e}`
});
});
};
Check your AWS CloudWatch logs and see what happens when the lambda is called. You can print to stdout in your lambda and have it show up in these logs.
Along with your Cloudwatch logs, you could also have a look at your Stackdriver logs.
I fail to receive information from my postgres db when trying to connect with graphql.
I receive the following response:
{
"errors": [
{
"message": "password authentication failed for user \"admin\"",
"locations": [
{
"line": 2,
"column": 3
}
],
"path": [
"account"
]
}
],
"data": {
"account": null
}
}
I honestly don't know where to define the user and pass.
const express = require('express');
const expressGraphQL = require('express-graphql');
const schema = require('./schema');
const app = express();
app.use('/graphql', expressGraphQL({
schema,
graphiql: true
}))
app.listen(4000, () => {
console.log('Listening...')
})
and this is my schema file
const graphql = require('graphql');
const connectionString = 'myURI';
const pgp = require('pg-promise')();
const db = {}
db.conn = pgp(connectionString);
const {
GraphQLObjectType,
GraphQLID,
GraphQLString,
GraphQLBoolean,
GraphQLList,
GraphQLSchema
} = graphql;
const AccountType = new GraphQLObjectType({
name: 'account',
fields: () => ({
id: { type: GraphQLID },
name: { type: GraphQLString },
busines_name: { type: GraphQLString },
email: {
type: new GraphQLList(EmailType),
resolve(parentValue, args) {
const query = `SELECT * FROM "emails" WHERE
account=${parentValue.id}`;
return db.conn.many(query)
.then(data => {
return data;
})
.catch(err => {
return 'The error is', err;
});
}
}
})
})
const EmailType = new GraphQLObjectType({
name: 'Email',
fields: {
id: { type: GraphQLID },
email: { type: GraphQLString },
primary: { type: GraphQLBoolean }
}
})
const RootQuery = new GraphQLObjectType({
name: 'RootQueryType',
fields: {
account: {
type: AccountType,
args: { id: { type: GraphQLID } },
resolve(parentValue, args) {
const query = `SELECT * FROM "account" WHERE id=${args.id}`;
return db.conn.one(query)
.then(data => {
return data;
})
.catch(err => {
return 'The error is', err;
});
}
},
emails: {
type: EmailType,
args: { id: { type: GraphQLID } },
resolve(parentValue, args) {
const query = `SELECT * FROM "emails" WHERE id=${args.id}`;
return db.conn.one(query)
.then(data => {
return data;
})
.catch(err => {
return 'The error is', err;
});
}
}
}
})
module.exports = new GraphQLSchema({
query: RootQuery
})
I would like to know where to define the user and the password for the db of what i'm doing wrong besides that.
const connectionString = 'myURI';
It should be enough if your connection string includes the username and password. Is your DB connection string of the form postgres://username:password#server:5432 ?
See https://www.postgresql.org/docs/10/libpq-connect.html#LIBPQ-CONNSTRING