Since we now have the tests in place and failing, we can change our user and order controllers to start using MongoDB. First, let's change the user controller. The user controller content is as follows:
import { NextFunction, Request, Response } from 'express'
import * as halson from 'halson'
import { UserModel } from '../schemas/User'
import { formatOutput } from '../utility/orderApiUtility'
export let getUser = (req: Request, res: Response, next: NextFunction) => {
const username = req.params.username
UserModel.findOne({ username: username }, (err, user) => {
if (!user) {
return res.status(404).send()
}
user = user.toJSON()
user._id = user._id.toString()
user = halson(user).addLink('self', `/users/${user._id}`)
return formatOutput(res, user, 200, 'user')
})
}
export let addUser = (req: Request, res: Response, next: NextFunction) => {
const newUser = new UserModel(req.body)
newUser.save((error, user) => {
user = halson(user.toJSON()).addLink('self', `/users/${user._id}`)
return formatOutput(res, user, 201, 'user')
})
}
export let updateUser = (req: Request, res: Response, next: NextFunction) => {
const username = req.params.username
UserModel.findOne({ username: username }, (err, user) => {
if (!user) {
return res.status(404).send()
}
user.username = req.body.username || user.username
user.firstName = req.body.firstName || user.firstName
user.lastName = req.body.lastName || user.lastName
user.email = req.body.email || user.email
user.password = req.body.password || user.password
user.phone = req.body.phone || user.phone
user.userStatus = req.body.userStatus || user.userStatus
user.save(error => {
res.status(204).send()
})
})
}
export let removeUser = (req: Request, res: Response, next: NextFunction) => {
const username = req.params.username
UserModel.findOne({ username: username }, (err, user) => {
if (!user) {
return res.status(404).send()
}
user.remove(error => {
res.status(204).send()
})
})
}
Let's walk through this file:
- Notice that now, we import the schema:
import { UserModel } from '../schemas/User'
- Now, we are using the find method from mongoose to get the User document directly to MongoDB instead of getting the information from an array, as before. We are still using the username as a filter and getting it as a parameter:
export let getUser = (req: Request, res: Response, next: NextFunction) => {
const username = req.params.username
UserModel.findOne({ username: username }, (err, user) => {
if (!user) {
return res.status(404).send()
}
user = user.toJSON()
user._id = user._id.toString()
user = halson(user).addLink('self', `/users/${user._id}`)
return formatOutput(res, user, 200, 'user')
})
}
- The same idea applies to the addUser method, except we use the save method:
export let addUser = (req: Request, res: Response, next: NextFunction) => {
const newUser = new UserModel(req.body)
newUser.save((error, user) => {
user = halson(user.toJSON()).addLink('self', `/users/${user._id}`)
return formatOutput(res, user, 201, 'user')
})
}
- The updated User is a bit more complex, even though it is still fairly simple. The difference is that first, we search for the user and with its document, we do the changes and call the save method again:
export let updateUser = (req: Request, res: Response, next: NextFunction) => {
const username = req.params.username
UserModel.findOne({ username: username }, (err, user) => {
if (!user) {
return res.status(404).send()
}
user.username = req.body.username || user.username
user.firstName = req.body.firstName || user.firstName
user.lastName = req.body.lastName || user.lastName
user.email = req.body.email || user.email
user.password = req.body.password || user.password
user.phone = req.body.phone || user.phone
user.userStatus = req.body.userStatus || user.userStatus
user.save(error => {
res.status(204).send()
})
})
}
- The same idea applies to the remove operation. First, we search for the User, and then, with the user document, we call the remove method:
export let removeUser = (req: Request, res: Response, next: NextFunction) => {
const username = req.params.username
UserModel.findOne({ username: username }, (err, user) => {
if (!user) {
return res.status(404).send()
}
user.remove(error => {
res.status(204).send()
})
})
}
In general words, the order controller changed almost like the User controller such as the following file:
import { NextFunction, Request, Response } from 'express'
import * as halson from 'halson'
import * as _ from 'lodash'
import { OrderModel } from '../schemas/order'
import { UserModel } from '../schemas/User'
import { formatOutput } from '../utility/orderApiUtility'
export let getOrder = (req: Request, res: Response, next: NextFunction) => {
const id = req.params.id
OrderModel.findById(id, (err, order) => {
if (!order) {
return res.status(404).send()
}
order = halson(order.toJSON()).addLink('self', `/store/orders/${order.id}`)
return formatOutput(res, order, 200, 'order')
})
}
export let getAllOrders = (req: Request, res: Response, next: NextFunction) => {
const limit = Number(req.query.limit) || 0
const offset = Number(req.query.offset) || 0
OrderModel.find({}, null, { skip: offset, limit: limit }).then(orders => {
if (orders) {
orders = orders.map(order => {
return halson(order.toJSON())
.addLink('self', `/store/orders/${order.id}`)
.addLink('user', {
href: `/users/${order.userId}`,
})
})
}
return formatOutput(res, orders, 200, 'order')
})
}
export let addOrder = (req: Request, res: Response, next: NextFunction) => {
const userId = req.body.userId
UserModel.findById(userId, (err, user) => {
if (!user) {
return res.status(404).send()
}
const newOrder = new OrderModel(req.body)
newOrder.save((error, order) => {
order = halson(order.toJSON())
.addLink('self', `/store/orders/${order._id}`)
.addLink('user', {
href: `/users/${order.userId}`,
})
return formatOutput(res, order, 201, 'order')
})
})
}
export let removeOrder = (req: Request, res: Response, next: NextFunction) => {
const id = req.params.id
OrderModel.findById(id, (err, order) => {
if (!order) {
return res.status(404).send()
}
order.remove(error => {
res.status(204).send()
})
})
}
export let getInventory = (req: Request, res: Response, next: NextFunction) => {
const status = req.query.status
OrderModel.find({ status: status }, (err, orders) => {
orders = _.groupBy(orders, 'userId')
return formatOutput(res, orders, 200, 'inventory')
})
}
- There is a new import for the Order schema:
import { OrderModel } from '../schemas/order'
- Also to the User schema:
import { UserModel } from '../schemas/User'
- The get order uses the findById method from mongoose, using the order id as a parameter:
export let getOrder = (req: Request, res: Response, next: NextFunction) => {
const id = req.params.id
OrderModel.findById(id, (err, order) => {
if (!order) {
return res.status(404).send()
}
order = halson(order.toJSON()).addLink('self', `/store/orders/${order.id}`)
return formatOutput(res, order, 200, 'order')
})
}
- The getAllOrders method does the math for limit and offsets as mongoose parameter:
export let getAllOrders = (req: Request, res: Response, next: NextFunction) => {
const limit = Number(req.query.limit) || 0
const offset = Number(req.query.offset) || 0
OrderModel.find({}, null, { skip: offset, limit: limit }).then(orders => {
if (orders) {
orders = orders.map(order => {
return halson(order.toJSON())
.addLink('self', `/store/orders/${order.id}`)
.addLink('user', {
href: `/users/${order.userId}`,
})
})
}
return formatOutput(res, orders, 200, 'order')
})
}
- addOrder first gets the user, and then saves the order:
export let addOrder = (req: Request, res: Response, next: NextFunction) => {
const userId = req.body.userId
UserModel.findById(userId, (err, user) => {
if (!user) {
return res.status(404).send()
}
const newOrder = new OrderModel(req.body)
newOrder.save((error, order) => {
order = halson(order.toJSON())
.addLink('self', `/store/orders/${order._id}`)
.addLink('user', {
href: `/users/${order.userId}`,
})
return formatOutput(res, order, 201, 'order')
})
})
}
- remove. order retrieves the order before removing it:
export let removeOrder = (req: Request, res: Response, next: NextFunction) => {
const id = req.params.id
OrderModel.findById(id, (err, order) => {
if (!order) {
return res.status(404).send()
}
order.remove(error => {
res.status(204).send()
})
})
}
- The getInventory operation finds the orders and groups them all based on userId:
export let getInventory = (req: Request, res: Response, next: NextFunction) => {
const status = req.query.status
OrderModel.find({ status: status }, (err, orders) => {
orders = _.groupBy(orders, 'userId')
return formatOutput(res, orders, 200, 'inventory')
})
}
Finally, if we run the tests, they should pass:
$ npm run test
You will see the following output:
baseRoute
should respond with HTTP 200 status (109ms)
should respond with success message
userRoute
should respond with HTTP 404 status because there is no user (79ms)
should create a new user and retrieve it back (84ms)
should return the user created on the step before
should updated the user John
should return the user updated on the step before
should return 404 because the user does not exist
should remove an existent user
should return 404 when it is trying to remove an user because the user does not exist
userRoute
should respond with HTTP 404 status because there is no order
should create a new user for Order tests and retrieve it back
should create a new order and retrieve it back (56ms)
should return the order created on the step before
should return all orders so far
should not return orders because offset is higher than the size of the orders array
should return the inventory for all users
should remove an existing order
should return 404 when it is trying to remove an order because the order does not exist
19 passing (643ms)
The same applies to Stryker:
$ stryker run
The previous command will show the following output:
Stryker report after mongo changes