Sequelize - Foreign keys are always null - javascript

Description
I'm starting to learn how to use Sequelize and I've run into an issue with foreign key relationships for one of my tables. I have three tables: Users, Projects, Times. Users and Projects have a Many to One relationship with Times.
Time.belongsTo(User)
Time.belongsTo(Project)
User.hasMany(Time)
Project.hasMany(Time)
After I create an entry in the Times table and then fetch it the resulting record always has Nulls for the two foreign keys.
db.Time.findAll().then(result => {
res.status(200).send(JSON.stringify(result))
})
Model
const {Sequelize, DataTypes} = require('sequelize');
const sequelize = new Sequelize('sqlite::memory:');
const User = sequelize.define('User', {
email: {
type: DataTypes.STRING,
allowNull: false,
unique: true
},
password: {
type: DataTypes.STRING,
allowNull: false
}
});
const Project = sequelize.define('Project', {
name: {
type: DataTypes.STRING,
allowNull: false
}
});
const Time = sequelize.define('Time', {
description: {
type: DataTypes.STRING,
allowNull: false
},
start: {
type: DataTypes.STRING,
allowNull: false
},
stop: {
type: DataTypes.STRING,
allowNull: false
}
});
console.log('Setting up Models')
Time.belongsTo(User)
Time.belongsTo(Project)
User.hasMany(Time)
Project.hasMany(Time)
const db = {
"sequelize": sequelize,
"User": User,
"Project": Project,
"Time": Time
}
exports.User = User;
exports.Project = Project;
exports.Time = Time;
exports.db = db;
Populating with Default Data
const {data} = require('./data')
const {db} = require('../data/models')
const dbHelper = {
connectDB: () => {
return new Promise((resolve, reject) => {
db.sequelize.authenticate().then(() => {
resolve(db)
}).catch((e) => {
reject(e);
})
})
},
hydrateDB: (db) => {
return new Promise((resolve, reject) => {
try {
db.sequelize.sync().then(() => {
hydrateUser(db).then(() => {
hydrateProject(db).then(() => {
hydrateTime(db).then(() => {
resolve()
})
})
})
})
} catch (e) {
reject(e)
}
})
}
}
const hydrateUser = (db) => {
return new Promise(resolve => {
data.user.forEach((datum) => {
db.User.create(datum).then(() => {
resolve()
})
})
})
}
const hydrateProject = (db) => {
return new Promise(resolve => {
data.project.forEach((datum) => {
db.Project.create(datum).then(() => {
resolve()
})
})
})
}
const hydrateTime = (db) => {
return new Promise(resolve => {
data.time.forEach((datum) => {
db.Time.create({
description: datum.description,
start: datum.start,
stop: datum.stop,
userId: 1,
projectId: 1
}).then(() => {
resolve()
})
})
})
}
exports.dbHelper = dbHelper;

You indicated fields in camel case here:
userId: 1,
projectId: 1
And actual fields created for Time are in pascal case as you showed in the screenshot.
So just correct names:
db.Time.create({
description: datum.description,
start: datum.start,
stop: datum.stop,
UserId: 1,
ProjectId: 1
}).then(() => {
resolve()
})

Related

Convert promises to async/await with Firebase queries

I have some firebase queries where I am using their recommended promises but I am running to a problem. I need the code to go through the steps in order but the last snippet of code is running before the prior code finishes. I would like to use async/await but I am not sure exactly how to do it.
here is my current code:
if (result.user.emailVerified) {
let ref = db.collection('users').where('email', '==', result.user.email)
ref.get().then(snapshot => {
if (snapshot.empty) {
let cid = null
let docID = null
let groupID = null
let pendingRef = db.collection('pendingusers')
pendingRef = pendingRef.where('email', '==', result.user.email)
pendingRef.get().then(snapshot => {
if (!snapshot.empty) {
snapshot.forEach(doc => {
cid = doc.data().cid
docID = doc.id
groupID = doc.data().groupID
})
}
}).then(() => {
db.collection('users').add({
url: null,
fullPath: null,
roles: ['member'],
uid: result.user.uid,
email: result.user.email,
emailVerified: result.user.emailVerified,
cid: cid,
active: null,
joined: Date.now()
}).then(() => {
// STEP 1
console.log('1. user has been added')
if (groupID !== null) {
db.collection('groups').doc(groupID).update({
members: firebase.firestore.FieldValue.arrayUnion(result.user.uid)
}).then(() => {
// STEP 2
console.log('2. group has been updated')
})
}
}).then(() => {
if (docID !== null) {
db.collection('pendingusers').doc(docID).delete()
.then(() => {
// STEP 3
console.log('3. removed from pending ', cid)
let churchRef = db.collection('churches')
churchRef = churchRef.where('cid', '==', cid)
churchRef.get().then((querySnapshot) => {
if (!querySnapshot.empty) {
querySnapshot.forEach((doc) => {
// STEP 4
console.log('4. data ', doc.data())
dispatch('AppData/resetNotifications', [], {
root: true
})
dispatch('AppData/resetUnits', [], {
root: true
})
dispatch('AppData/isRunning', false, {
root: true
})
dispatch('AppData/setChurchId', doc.data().cid, {
root: true
})
// set the church
dispatch('AppData/allChurches', [{
churchtitle: doc.data().churchtitle,
cid: doc.data().cid,
}], {
root: true
})
})
}
})
})
}
})
})
} else {
snapshot.forEach(doc => {
commit('updateUser', {
cid: doc.data().cid,
url: doc.data().url,
fullPath: doc.data().fullPath,
user_name: doc.data().user_name
})
dispatch('AppData/getCourse', {
cid: doc.data().cid,
userid: doc.data()
}, {
root: true
})
dispatch('AppData/getActiveCourse', {
cid: doc.data().cid,
user: doc.data()
}, {
root: true
})
})
}
}).then(() => {
// STEP 5
console.log('5. next step')
dispatch('AppData/getSections', {}, {
root: true
})
dispatch('AppData/getUnits', {}, {
root: true
})
dispatch('AppData/getMissions', {}, {
root: true
})
dispatch('AppData/getWeeks', {}, {
root: true
})
// get user's current roles
let ref = db.collection('users').where('email', '==', result.user.email)
ref.onSnapshot(snapshot => {
snapshot.docChanges().forEach(change => {
let docs = change.doc
commit('setRoles', docs.data().roles)
})
})
router.push('home')
})
} else {
// console.log('not verified')
commit('setLoginFeedback', {
code: 'no-verfied email',
message: 'You must first verifiy your email.'
});
}
I have commented the steps to help illustrate the flow. I would like the code to go from step 1,2,3,4,5 but currently it goes 5,1,2,3,4
I know async/await will solve this, I just need some guidance on converting the code from then() to async/await

Model.findById() is returning undefined

I trying to implement a favorite toggle where it saves the favorites in an array, I create a Schema and a router, the code you can see below the problem is when I try to test it on insomnia I'm getting undefined on my console.log(isFavorite). I don't know what could be wrong.
const userSchema = new Schema({
username: String,
email: String,
password: String,
favorites: [{ type: Schema.Types.ObjectId, ref: "Places" }],
},
{
timestamps: true,
});
// route
router.put("/favorites/:placeId", (req, res) => {
const userId = "5ebd13df31430045957db8c3";
User.findById(userId).then( (user) => {
const isFavorite = user.favorites.find( (favorite) => {
return favorite === req.params.placeId;
});
console.log(isFavorite);
console.log(req.params.placeId);
if (isFavorite) {
User.findOneAndUpdate(
{ _id: userId },
{
$pull: { favorites: req.params.placeId },
},
{
new: true,
})
.then((user) => res.json(user))
.catch((err) => res.status(400).json(err));
} else {
User.findOneAndUpdate(
{ _id: userId },
{
$push: { favorites: req.params.placeId },
},
{
new: true,
})
.then((user) => res.json(user))
.catch((err) => res.status(400).json(err));
}
});
});
this chunk is bad:
User.findById(userId).then((user) => {
const isFavorite = user.favorites.find((favorite) => {
return favorite === req.params.placeId;
});
instead must use populate():
let favorites = await User.findById(userId).populate('favorites');
and then filter favorites by placeId

How to use DataLoader with Mongoose

I'm trying to build the following use case of DataLoader together with Mongoose:
export const PurchaseOrderType = new GraphQLObjectType({
name: "PurchaseOrder",
description: "PurchaseOrder",
interfaces: () => [NodeInterface],
isTypeOf: value => value instanceof PurchaseOrderModel,
fields: () => ({
id: {
type: new GraphQLNonNull(GraphQLID),
resolve: obj => dbIdToNodeId(obj._id, "PurchaseOrder")
},
name: {
type: new GraphQLNonNull(GraphQLString)
},
customer: {
type: CustomerType,
resolve: (source, args, context) => {
return context.customerLoader.load(source.customer_id);
}
}
})
});
export default () => {
return graphqlHTTP((req, res, graphQLParams) => {
return {
schema: schema,
graphiql: true,
pretty: true,
context: {
customerLoader: customerGetByIdsLoader()
},
formatError: error => ({
message: error.message,
locations: error.locations,
stack: error.stack,
path: error.path
})
};
});
};
export const customerGetByIdsLoader = () =>
new DataLoader(ids => {
return customerGetByIds(ids);
});
export const customerGetByIds = async ids => {
let result = await Customer.find({ _id: { $in: ids }, deletedAt: null }).exec();
let rows = ids.map(id => {
let found = result.find(item => {
return item.id.equals(id);
});
return found ? found : null; << === found always undefined
});
return rows;
};
I'm facing the following problems when loading several PurchaseOrders:
A single customer_id is being called more than once in the ids parameter of the DataLoader. So an example id 5cee853eae92f6021f297f45 is being called on several requests to my loader, in successive calls. That suggests that the cache is not working properly.
My found variable when processing the read result is always being set to false, even comparing the right ids.
You can use findOne
export const customerGetByIds = async ids => {
let result = await Customer.find({ _id: { $in: ids }, deletedAt: null }).exec();
const rows = []
let promiseAll = ids.map(async (id) => {
let found = result.filter(item => item.id.toString() === id.toSring());
if(found) {
rows.push(found[0])
return found[0]
}
return null;
});
await Promise.all(promiseAll);
return rows;
};

Sequelize CLI complains about "Unexpected Identifier" when attempting to run db:seed:all

I am trying to set up a seeder for my Sequelize models. I have a single 1-many association. All of my syntax appears to be correct and passes through my linter (semistandard) successfully.
Whenever I run either version using sequelize db:seed:all, I receive an (extremely unhelpful) error that consists solely of the message "ERROR: Unexpected Identifier" without a filename, line number, or any stack trace.
I have tried using async/await as well as normal Promises.
Async/await version of my seeder (seed object arrays are shortened):
'use strict';
const uuid = require('uuid/v4');
const bcrypt = require('bcrypt');
const models = require('../models');
module.exports = {
up: async (queryInterface, Sequelize) => {
const passwordHash = await bcrypt.hash('password', 10);
await queryInterface.bulkInsert('Users', [
{
public_id: uuid(),
name: 'Snoop',
password_hash: passwordHash,
admin: true,
createdAt: new Date(),
updatedAt: new Date()
}
], {});
const user = await models.User.findOne({
where: { admin: true }
});
const result = await queryInterface.bulkInsert('Todos', [
{
text: 'Do a thing',
complete: true,
user_id: user.id,
createdAt: new Date(),
updatedAt: new Date()
}
], {});
return result;
},
down: async (queryInterface, Sequelize) => {
await queryInterface.bulkDelete('Todos', null, {});
await queryInterface.bulkDelete('Users', null, {});
}
};
Version using Promises:
'use strict';
const uuid = require('uuid/v4');
const bcrypt = require('bcrypt');
const { User } = require('../models');
function getSeedUsers (passwordHash) {
return [
{
public_id: uuid(),
name: 'Snoop',
password_hash: passwordHash,
admin: true,
createdAt: new Date(),
updatedAt: new Date()
}
];
}
function getSeedTodos (userId) {
return [
{
text: 'Do a thing',
complete: true,
user_id: userId,
createdAt: new Date(),
updatedAt: new Date()
}
];
}
module.exports = {
up: (queryInterface, Sequelize) => {
return new Promise((resolve, reject) => {
bcrypt.hash('password', 10)
.then(passwordHash => {
queryInterface.bulkInsert('Users', getSeedUsers(passwordHash), {})
.then(() => {
User.findOne({ where: { admin: true } })
.then(user => {
queryInterface.bulkInsert('Todos', getSeedTodos(user.id), {})
.then(result => resolve(result))
.catch(err => reject(err));
})
.catch(err => reject(err));
})
.catch(err => reject(err));
})
.catch(err => reject(err));
});
},
down: (queryInterface, Sequelize) => {
return Promise.all(
queryInterface.bulkDelete('Todos', null, {}),
queryInterface.bulkDelete('Users', null, {})
);
}
};
Expected: Successful database seeding
Actual: "ERROR: Unexpected Identifier"

Declaring multiple relayjs connections

I have to two GraphQLObjectType
const AType = new GraphQLObjectType({
name: 'A'
fields: () => ({
id: globalIdField('A', obj => obj._id),
Id: {
type: GraphQLID,
resolve: obj => obj._id
},
email: { type: GraphQLString },
name: { type: GraphQLString },
status: { type: GraphQLString },
description: { type: GraphQLString }
}),
interfaces: [nodeInterface]
});
and another Type BType, i am using AType inside the type of so t
const BType = new GraphQLObjectType({
name: 'BType',
fields: {
id: globalIdField('BType'),
ApplicantsDetails: {
type: AType,
resolve: obj => {
return obj.applicantsId;
}
},
respo_2: {
type: GraphQLString,
resolve: obj => "I have declared a ObjectType inside another"
}
},
interfaces: [nodeInterface]
});
and the main type from where i am returning a promise, so when i return the promise resolve(d), it should go to the BType and
const { connectionType: EmployerDashBoardConnectionType} =
connectionDefinitions({
name: 'EmployerDash',
nodeType: BType
});
above is the connection
EmployerDashBoardConnection: {
type: EmployerDashBoardConnectionType,
description: 'Employer DashBoard Details',
args: connectionArgsWithJobId,
resolve: (_, args, auth) => {
return new Promise((resolve, reject) => {
Dash(_, args, auth, function (err, d) {
if (err) {
reject(err);
} else {
resolve(d);
}
});
});
}
}
/* Introduce your new fields here */
}
the response from Dash() function call is
{
applicantsId:
{ _id: 5878548b51179817f48eb1f1,
email: '123#gmail.com',
password: '$2a$10$lDpfl7kL4i/8VPij8aypmeeiD1794g1afACUxca397LdlErMgWa.S',
__v: 0,
name: 'Alpaina',
status: 'Unemployed',
isJobSeeker: true,
to: 2017-01-13T04:16:11.755Z }
}
it only prints null
for using edges you need to pass an array to resolve function
resolve: (_, args, auth) => {
return new Promise((resolve, reject) => {
Dash(_, args, auth, function (err, d) {
if (err) {
reject(err);
} else {
// if d is object then
const a = [];
a.push(d);
resolve(connectionFromArray(a, args));
}
});
});
}
this code is to only solve your current problem
NOTE: while using relay connections you should always resolve a list [ ]
There are two problems in your code:
First problem:
The node type of EmployerDashBoardConnectionType is BType. So, the items returned in resolve() function of field EmployerDashBoardConnection should have the same properties - that's not the case in your code.
The structure of BType is:
{
id
ApplicantsDetails {
id,
Id,
email,
name,
status,
description,
}
respo_2,
}
whereas you are passing the following object, which totally does not match.
{
applicantsId: {
_id: 5878548b51179817f48eb1f1,
email: '123#gmail.com',
password: '$2a$10$lDpfl7kL4i/8VPij8aypmeeiD1794g1afACUxca397LdlErMgWa.S',
__v: 0,
name: 'Alpaina',
status: 'Unemployed',
isJobSeeker: true,
to: 2017-01-13T04:16:11.755Z
}
}
Second problem:
This is why you're getting null for edges. Your resolve() function:
resolve: (_, args, auth) => {
return new Promise((resolve, reject) => {
Dash(_, args, auth, function (err, d) {
if (err) {
reject(err);
} else {
resolve(d);
}
});
});
}
where you return nothing. You can use connectionFromArray from graphql-relay npm module:
resolve: (_, args, auth) => {
return new Promise((resolve, reject) => {
Dash(_, args, auth, function (err, d) {
if (err) {
reject(err);
} else {
resolve(connectionFromArray(d, args));
}
});
});
}
The value of d must be a list where each item should have top level properties id, ApplicantsDetails, respo_2.

Categories