Link mongoose schemas using ID's pushed to an array? - express

I'm trying to link 2 of my mongoose schemas using the ID's assigned by mongoose (_id)
I have a users schema and a servers schema, every server should have 1 user as the owner but every user can have many servers. So I'm saving the req.user.id(from passportJS) in the server schema and there is an array in users schema where I want to push the ID's for servers when they are created. I haven't quite made it to using populate yet because I don't understand how to take the ID from the server that is about to be created and push it to the users server array
Here is my servers schema:
var mongoose = require ('mongoose');
var timestamps = require('mongoose-times');
var Schema = mongoose.Schema;
var ServerSchema = new Schema({
name: String,
imgurl: String,
address: String,
port: String,
tags: Array,
votifier :{
enabled: Boolean,
address: String,
port: String,
pubKey: String
},
ownerID: {type: Schema.ObjectId, ref: 'User'},
upvotes: {type: Number, default: 0}
});
ServerSchema.plugin(timestamps);
module.exports = mongoose.model('Server', ServerSchema);
Here's my users schema:
var mongoose = require('mongoose');
var bcrypt = require('bcrypt-nodejs');
var timestamps = require('mongoose-times');
var Schema = mongoose.Schema;
var userSchema = Schema({
local :{
username: {type: String, required: true},
password: {type: String, required: true}
},
email: {type: String, required: true},
imgId: String,
servers: [{type: Schema.ObjectId, ref: 'Server'}]
});
userSchema.plugin(timestamps);
//methods
//gen hash
userSchema.methods.generateHash = function(password, next){
bcrypt.hash(password, bcrypt.genSaltSync(8), null, next);
console.log(password + ': Password has generated hash');
};
userSchema.methods.validPassword = function(candidatePassword, cb) {
bcrypt.compare(candidatePassword, this.local.password, function(err, isMatch){
if(err) return cb(err);
cb(null, isMatch);
});
};
module.exports = mongoose.model('User', userSchema);
How can I take the _ID of a server that is being created and push it to the array in the User schema which I will findByID using the OwnerID in server schema?
Any help would be appreciated, also if this is the complete wrong way to do this sort of thing please let me know! Thanks

You can create a new server like that:
newServer = new ServerSchema(...);
Then save it:
newServer.save(callback);
The callback should be define to do whatever you want to do with the actual saved result. Lets say that you have a function updateUser that receive userId and serverId and it pushes the serverId to the User of that Id.
Then your callback should be something like...
callback = function(serverSaveErr, serverSavedDoc) {
if (serverSaveErr) { handle errors ... }
else { updateUser(userId, serverSavedDoc._id }
}

Related

How to include a variable in a Mongoose chain query in Express.js?

I am new to the mongoose and Express and I am stuck with querying in Mongoose.
I need to get the users by filtering their NIC (key) and createdAt fields with greater than and less than keywords and for createdAt I need to compare it to a variable. The code is as follows.
const setData = asyncHandler(async (req, res) => {
const prevDate = new Date(Date.now() - 1000 * 86400).toISOString();
const nUser = new user({
Name: req.body.name,
Age: req.body.age,
NIC: req.body.NIC,
Sex: req.body.sex,
Telephone: req.body.tel,
Address: req.body.addr,
email: req.body.email,
Inquiry: req.body.inquiry,
Branch:req.body.branch,
askLoan:req.body.askLoan
})
// await nUser.save();
// console.log (nUser)
const userStored = await user.find({ NIC: nUser.NIC},**{createdAt: {$lte: prevDate}})**
console.log(userStored)
I changed the line for userStored like this:
const userStored = await user
.find({
NIC: nUser.NIC
})
.where({
createdAt: {
'$gte': prevDate
}
})
And it worked. Turns out using a comma to add queries was not optimal and also the single commas for the $lte or $gte.
This successfully returned results to the console.

How to use Mongoose Populate between 2 different Schema's

I have 2 schema's, Categories and Cards. Each Category has an array of cards, and I want to populate that array with values , but I am unsure how to go about this as the mongoose documentation is somewhat confusing to understand.
// Schemas in seperate files
// Category Schema
const mongoose = require("mongoose");
const Schema = mongoose.Schema;
const categorySchema = new Schema({
title: {
type: String,
trim: true,
max: 30,
},
cards: [{ type: Schema.Types.ObjectId, ref: "categoryCard" }],
});
module.exports = mongoose.model("category", categorySchema);
// Category Card Schema
const mongoose = require("mongoose");
const Schema = mongoose.Schema;
const categoryCardSchema = new Schema({
category: {
type: String,
trim: true,
},
name: {
type: String,
trim: true,
},
post: {
type: String,
required: true,
trim: true,
},
});
module.exports = mongoose.model("categoryCard", categoryCardSchema);
// Below is the express router file . I want users to be able to create cards for different categories , after the category is already created. It worked in postman, but it doesn't work on the front end for some reason.
router.route("/createCard").post((req, res) => {
const { title, name, post } = req.body;
newCard = new categoryCard({
category: title,
name,
post,
});
newCard.save();
category.findOne({ title }).exec((err, item) => {
if (!err) {
item.cards.push(newCard._id);
item.save();
res.send(item);
} else {
res.send(err);
}
});
});
You can Follow this code...
let categorys= await category.findOne({ title }).populate("cards")

Should a new Collection be created upon Model.create()

Am working with mongoose and have two models. The User model and the Service model, when a user logs in the method will findOne() user if one exists or create() a new user based on the what's passed in from req.body.
My Service Schema is like this:
const serviceSchema = new mongoose.Schema({
name: {
type: String,
default: 'contentEditor'
},
display: {
type: String,
default: 'Content Editor'
},
accessLevel: {
type: Number,
min: 0,
max: 4,
default: 4
}
});
My User Schema is a bit bigger, I've removed some of the field/value pairs but the part where I embed the Service Schema looks like this:
const userSchema = new mongoose.Schema(
{
email: {
type: String,
required: [true, 'Must have a email address'],
trim: true,
unique: true,
},
firstName: {
type: String,
},
lastName: {
type: String,
},
services: {
type: [serviceSchema],
ref: 'Services',
default: [serviceSchema],
},
},
);
When I hit the /api/v1/login endpoint a new user will be created with the Service document correctly but within the Mongoose database only a User collection exists. How do I make it so that both a Users collection and Services collection are created?
Edit: Below is the function that I create/find the user with when they login. When an existing User is found, by their email it will return that user if the user is not found then it will create a new one...
Both behaviours are as expected including adding the Services to the newly created User. What isn't expected is that only ONE collection is added to the DB.
const login = catchAsync(async ({ body: { email, password } }, res, next) => {
if (!email || !password) {
return next(new AppError('Please provide email and password', 400));
}
const { Success } = await webApi(email, password);
const mongoUser = await User.findOne({ email });
if (Success && mongoUser) {
return createSendtoken(mongoUser, 200, res);
}
if (Success && !mongoUser) {
const newUser = await User.create({ email });
return createSendtoken(newUser, 201, res);
}
return next(new AppError('User not found', 404));
});
Make sure you are making the serviceSchema a mongoose model.
const Services = mongoose.model('Service', serviceSchema)
You also have to save it using mongooses model.save() function

How do I create custom directives to protect my GraphQL API?

I want to use custom directives to protect my GraphQL API. More specifically, for specific GraphQL fields, I want to check if users have authorisation to query those fields when a request hits my GraphQL server.
The following links are articles that contain examples on achieving this objective.
Link 1: https://www.prisma.io/blog/graphql-directive-permissions-authorization-made-easy-54c076b5368e/
Link 2: https://codeburst.io/use-custom-directives-to-protect-your-graphql-apis-a78cbbe17355
However, both examples achieve this by first constructing their Schema using the GraphQL Schema Definition Language (below is a snippet from the repo for Link 2) that demonstrates how you can use custom directives to check if users have authorisation to query specific fields (such as "rating").
require('dotenv').config();
const express = require('express');
const graphqlHTTP = require('express-graphql');
const {
makeExecutableSchema
} = require('graphql-tools');
const {directiveResolvers} = require('./directives');
const {allProductsBySupplier, addProduct, product, suppliers} = require('./resolvers');
require('./auth');
const app = express();
const port = 8080;
const typeDefs = `
directive #isAuthenticated on QUERY | FIELD
directive #hasScope(scope: [String]) on QUERY | FIELD
type Product {
id: ID!
supplierId: ID!
sku: String
qty: Int
price: Int
parrot: String
rating: Int #hasScope(scope: ["read:rating"])
}
type Supplier {
id: ID!
name: String!
}
input ProductInput {
supplierId: ID!
sku: String!
qty: Int!
price: Int!
parrot: String!
rating: Int!
}
type Query {
allProductsBySupplier: [Product] #isAuthenticated
product: Product #isAuthenticated
suppliers: [Supplier]
}
type Mutation {
addProduct(input: ProductInput!): Product #hasScope(scope: ["add:product"])
}
`;
const resolvers = {
Query: {
allProductsBySupplier,
product,
suppliers
},
Mutation: {
addProduct
}
};
const schema = makeExecutableSchema({
typeDefs,
resolvers,
directiveResolvers
});
app.use(
'/graphql',
graphqlHTTP({
schema,
graphiql: true
})
);
app.listen(port);
console.log(`server running on localhost:${port}`);
I have constructed my API without using the GraphQL schema definition language as shown below. The following snippet has been extracted from the official graphql docs.
var express = require('express');
var graphqlHTTP = require('express-graphql');
var graphql = require('graphql');
// Maps id to User object
var fakeDatabase = {
'a': {
id: 'a',
name: 'alice',
},
'b': {
id: 'b',
name: 'bob',
},
};
// Define the User type
var userType = new graphql.GraphQLObjectType({
name: 'User',
fields: {
id: { type: graphql.GraphQLString },
name: { type: graphql.GraphQLString },
}
});
// Define the Query type
var queryType = new graphql.GraphQLObjectType({
name: 'Query',
fields: {
user: {
type: userType,
// `args` describes the arguments that the `user` query accepts
args: {
id: { type: graphql.GraphQLString }
},
resolve: function (_, {id}) {
return fakeDatabase[id];
}
}
}
});
var schema = new graphql.GraphQLSchema({query: queryType});
var app = express();
app.use('/graphql', graphqlHTTP({
schema: schema,
graphiql: true,
}));
app.listen(4000);
console.log('Running a GraphQL API server at localhost:4000/graphql');
How can I create custom directives to check if users have authorisation to query specific fields if I have constructed my Schema without using the GraphQL Schema Definition Language?

Mongoose - Promise then() not triggered on save

I'm using ExpressJS + Mongoose + TypeScript. I have created a schema as below
const schema: Schema = new Schema({
firstName: {
type: String,
required: true
},
lastName: {
type: String,
required: true
},
email: {
type: String,
required: true
},
gender: {
type: Boolean,
required: true
},
mobile: {
type: String,
required: false
},
password: {
type: String,
required: true
},
activationKey: {
type: String,
required: false
},
isActivated: {
type: Boolean,
required: true
},
imagePath: {
type: String,
required: false
},
});
I'm using the below code to save (insert) a new entry
MongoClient.connect('mongodb://MyUsername:MyPassword#ds135757.mlab.com:35777/my-db-us', (err, db) => {
if (err) {
console.log('mongoose error: ' + err);
} else {
console.log('mongoose db: ' + db);
const user = new User({
firstName: 'ee',
lastName: 'ee',
email: 'eee#fff.com',
gender: true,
mobile: '333',
password: '333',
isActivated: true
});
user.save().then((someValue) => {
console.log('saved');
}).catch((err) => {
console.log('not saved:' + err);
});
}
});
Console Messages
When correct data is sent. saved isn't printed
mongoose db: [object Object]
When incorrect data is sent
mongoose db: [object Object]
not saved:ValidationError: gender: Path `gender` is required.
When unable to connect to MongoDB if internet is disconnected
mongoose error: MongoError: failed to connect to server [ds135777.mlab.com:35777] on first connect [MongoError: connection 0 to ds135777.mlab.com:35777 timed out]
Module versions
"mongodb": "^2.2.34",
"#types/mongodb": "^3.0.5",
"mongoose": "^5.0.4",
"#types/mongoose": "^5.0.2",
Re-written example (Solution)
const mongoose = require('mongoose');
mongoose.connect('mongodb://MyUsername:MyPassword#ds135757.mlab.com:35777/my-db-us', function(err) {
if (err) {
console.log('err: ' + err);
} else {
console.log('connected');
const user = new User({
firstName: 'ee',
lastName: 'ee',
email: 'eee#fff.com',
gender: true,
mobile: '333',
password: '333',
isActivated: true
});
user.save().then((someValue) => {
console.log('saved');
}).catch((err) => {
console.log('not saved:' + err);
});
}
});
The messages printed are
connected
saved
I am guessing a funny problem with your code(Just guessing looking at your variable name convention ;-)).
You are saying you use mongoose but you connect using native MongoClient (again guessing based on variable name) you must be connecting using mongoose
const mongoose = require('mongoose');
then just replace your MongoClient with mongoose
then doesn't print anything as nothing is happening there and catch prints error as validation happens before connection
The reason is you are using native client for connecting and using mongoose for modelling which is not the correct way. Do connect to the Mongo DB URI using mongoose and save schema.
You are creating a standard MongoClient connection, this will not effect mongoose models. The connection that created the User model must be open for the various database actions to work. Assuming that the User model was created using the global mongoose object (e.g. mongoose.model('User', userSchema)) then you must call mongoose.connect() to activate the model's connection. If the User model was created via a non-global connection (e.g. mongoose.createConnection()) then you should ensure that the connection is in the open state.