Moved everything not lottery out.
Moved all logic related to lotteryAttendees, lotteryWinners and prelotteryWines into each their repository and controller. Now only holds draw, archive and get archived lotteries.
This commit is contained in:
171
api/controllers/lotteryController.js
Normal file
171
api/controllers/lotteryController.js
Normal file
@@ -0,0 +1,171 @@
|
||||
const path = require("path");
|
||||
const lotteryRepository = require(path.join(__dirname, "../lottery"));
|
||||
|
||||
const drawWinner = (req, res) => {
|
||||
return lotteryRepository
|
||||
.drawWinner()
|
||||
.then(({ winner, color, winners }) => {
|
||||
var io = req.app.get("socketio");
|
||||
io.emit("winner", {
|
||||
color: color,
|
||||
name: winner.name,
|
||||
winner_count: winners.length + 1
|
||||
});
|
||||
|
||||
return { winner, color, winners };
|
||||
})
|
||||
.then(({ winner, color, winners }) =>
|
||||
res.send({
|
||||
color: color,
|
||||
winner: winner,
|
||||
success: true
|
||||
})
|
||||
)
|
||||
.catch(error => {
|
||||
const { statusCode, message } = error;
|
||||
|
||||
return res.status(statusCode || 500).send({
|
||||
message: message || "Unexpected error occured while drawing winner.",
|
||||
success: false
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
const archiveLottery = (req, res) => {
|
||||
const { lottery } = req.body;
|
||||
if (lottery == undefined || !lottery instanceof Object) {
|
||||
return res.status(400).send({
|
||||
message: "Missing lottery object.",
|
||||
success: false
|
||||
});
|
||||
}
|
||||
|
||||
let { stolen, date, raffles, wines } = lottery;
|
||||
stolen = stolen !== undefined ? stolen : 0; // default = 0
|
||||
date = date !== undefined ? date : new Date(); // default
|
||||
|
||||
const validDateFormat = new RegExp("d{4}-d{2}-d{2}");
|
||||
if (!validDateFormat.test(date) || isNaN(date)) {
|
||||
return res.status(400).send({
|
||||
message: "Date must be defined as 'yyyy-mm-dd'.",
|
||||
success: false
|
||||
});
|
||||
} else {
|
||||
date = Date.parse(date, "yyyy-MM-dd");
|
||||
}
|
||||
|
||||
return verifyLotteryPayload(raffles, stolen, wines)
|
||||
.then(_ => lottery.archive(date, raffles, stolen, wines))
|
||||
.catch(error => {
|
||||
const { statusCode, message } = error;
|
||||
|
||||
return res.status(statusCode || 500).send({
|
||||
message: message || "Unexpected error occured while submitting lottery.",
|
||||
success: false
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
const lotteryByDate = (req, res) => {
|
||||
const { epoch } = req.params;
|
||||
|
||||
if (!/^\d+$/.test(epoch)) {
|
||||
return res.status(400).send({
|
||||
message: "Last parameter must be epoch (in seconds).",
|
||||
success: false
|
||||
});
|
||||
}
|
||||
const date = new Date(Number(epoch) * 1000);
|
||||
|
||||
return lotteryRepository
|
||||
.lotteryByDate(date)
|
||||
.then(lottery =>
|
||||
res.send({
|
||||
lottery,
|
||||
message: `Lottery for date: ${dateToDateString(date)}/${epoch}.`,
|
||||
success: true
|
||||
})
|
||||
)
|
||||
.catch(error => {
|
||||
const { statusCode, message } = error;
|
||||
|
||||
return res.status(statusCode || 500).send({
|
||||
message: message || "Unexpected error occured while fetching lottery by date.",
|
||||
success: false
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
const allLotteries = (req, res) => {
|
||||
const isAdmin = req.isAuthenticated() || true;
|
||||
|
||||
return lotteryRepository
|
||||
.allLotteries(isAdmin)
|
||||
.then(lotteries =>
|
||||
res.send({
|
||||
lotteries,
|
||||
message: "All lotteries.",
|
||||
success: true
|
||||
})
|
||||
)
|
||||
.catch(error => {
|
||||
const { statusCode, message } = error;
|
||||
|
||||
return res.status(statusCode || 500).send({
|
||||
message: message || "Unexpected error occured while fetching all lotteries.",
|
||||
success: false
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
function verifyLotteryPayload(raffles, stolen, wines) {
|
||||
return new Promise((resolve, reject) => {
|
||||
if (raffles == undefined || !raffles instanceof Array) {
|
||||
reject({
|
||||
message: "Raffles must be array.",
|
||||
status: 400
|
||||
});
|
||||
}
|
||||
|
||||
const requiredColors = [raffles["red"], raffles["blue"], raffles["green"], raffles["yellow"]];
|
||||
const correctColorsTypes = requiredColors.filter(color => typeof color === "number");
|
||||
if (requiredColors.length !== correctColorsTypes.length) {
|
||||
reject({
|
||||
message:
|
||||
"Incorrect or missing raffle colors, required type Number for keys: 'blue', 'red', 'green' & 'yellow'.",
|
||||
status: 400
|
||||
});
|
||||
}
|
||||
|
||||
if (stolen == undefined || (isNaN(stolen) && stolen >= 0)) {
|
||||
reject({
|
||||
message: "Number of stolen raffles must be positive integer or 0.",
|
||||
status: 400
|
||||
});
|
||||
}
|
||||
|
||||
if (wines == undefined || !wines instanceof Array) {
|
||||
reject({
|
||||
message: "Wines must be array.",
|
||||
status: 400
|
||||
});
|
||||
}
|
||||
|
||||
resolve();
|
||||
});
|
||||
}
|
||||
|
||||
function dateToDateString(date) {
|
||||
const ye = new Intl.DateTimeFormat("en", { year: "numeric" }).format(date);
|
||||
const mo = new Intl.DateTimeFormat("en", { month: "2-digit" }).format(date);
|
||||
const da = new Intl.DateTimeFormat("en", { day: "2-digit" }).format(date);
|
||||
|
||||
return `${ye}-${mo}-${da}`;
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
drawWinner,
|
||||
archiveLottery,
|
||||
lotteryByDate,
|
||||
allLotteries
|
||||
};
|
||||
296
api/lottery.js
296
api/lottery.js
@@ -1,245 +1,83 @@
|
||||
const path = require("path");
|
||||
const crypto = require("crypto");
|
||||
|
||||
const Attendee = require(path.join(__dirname, "/schemas/Attendee"));
|
||||
const PreLotteryWine = require(path.join(__dirname, "/schemas/PreLotteryWine"));
|
||||
const VirtualWinner = require(path.join(__dirname, "/schemas/VirtualWinner"));
|
||||
const Lottery = require(path.join(__dirname, "/schemas/Purchase"));
|
||||
|
||||
const crypto = require("crypto");
|
||||
const Message = require(path.join(__dirname, "/message"));
|
||||
const {
|
||||
WinnerNotFound,
|
||||
NoMoreAttendeesToWin,
|
||||
CouldNotFindNewWinnerAfterNTries,
|
||||
LotteryByDateNotFound
|
||||
} = require(path.join(__dirname, "/vinlottisErrors"));
|
||||
|
||||
class UserNotFound extends Error {
|
||||
constructor(message = "User not found.") {
|
||||
super(message);
|
||||
this.name = "UserNotFound";
|
||||
this.statusCode = 404;
|
||||
}
|
||||
const archive = (date, raffles, stolen, wines) => {
|
||||
const { blue, red, yellow, green } = raffles;
|
||||
const bought = blue + red + yellow + green;
|
||||
date = date.setHours(0, 0, 0, 0);
|
||||
|
||||
// TODO log missing user
|
||||
}
|
||||
|
||||
class WineNotFound extends Error {
|
||||
constructor(message = "Wine not found.") {
|
||||
super(message);
|
||||
this.name = "WineNotFound";
|
||||
this.statusCode = 404;
|
||||
}
|
||||
|
||||
// TODO log missing user
|
||||
}
|
||||
|
||||
class WinnerNotFound extends Error {
|
||||
constructor(message = "Winner not found.") {
|
||||
super(message);
|
||||
this.name = "WinnerNotFound";
|
||||
this.statusCode = 404;
|
||||
}
|
||||
|
||||
// TODO log missing user
|
||||
}
|
||||
|
||||
class NoMoreAttendeesToWinError extends Error {
|
||||
constructor(message = "No more attendees left to drawn from.") {
|
||||
super(message);
|
||||
this.name = "NoMoreAttendeesToWinError";
|
||||
this.statusCode = 404;
|
||||
}
|
||||
}
|
||||
|
||||
class CouldNotFindNewWinnerAfterNTriesError extends Error {
|
||||
constructor(tries) {
|
||||
let message = `Could not a new winner after ${tries} tries.`;
|
||||
super(message);
|
||||
this.name = "CouldNotFindNewWinnerAfterNTriesError";
|
||||
this.statusCode = 404;
|
||||
}
|
||||
}
|
||||
|
||||
const redactAttendeeInfoMapper = attendee => {
|
||||
return {
|
||||
name: attendee.name,
|
||||
raffles: attendee.red + attendee.blue + attendee.yellow + attendee.green,
|
||||
red: attendee.red,
|
||||
blue: attendee.blue,
|
||||
green: attendee.green,
|
||||
yellow: attendee.yellow
|
||||
};
|
||||
};
|
||||
|
||||
const redactWinnerInfoMapper = winner => {
|
||||
return {
|
||||
name: winner.name,
|
||||
color: winner.color
|
||||
};
|
||||
};
|
||||
|
||||
const allAttendees = (isAdmin = false) => {
|
||||
if (!isAdmin) {
|
||||
return Attendee.find().then(attendees => attendees.map(redactAttendeeInfoMapper));
|
||||
} else {
|
||||
return Attendee.find();
|
||||
}
|
||||
};
|
||||
|
||||
const addAttendee = attendee => {
|
||||
const { name, red, blue, green, yellow, phoneNumber } = attendee;
|
||||
|
||||
let newAttendee = new Attendee({
|
||||
name,
|
||||
red,
|
||||
blue,
|
||||
green,
|
||||
yellow,
|
||||
phoneNumber,
|
||||
winner: false
|
||||
});
|
||||
|
||||
return newAttendee.save();
|
||||
};
|
||||
|
||||
const updateAttendeeById = (id, updateModel) => {
|
||||
return Attendee.findOne({ _id: id }).then(attendee => {
|
||||
if (attendee == null) {
|
||||
throw new UserNotFound();
|
||||
}
|
||||
|
||||
const updatedAttendee = {
|
||||
name: updateModel.name != null ? updateModel.name : attendee.name,
|
||||
green: updateModel.green != null ? updateModel.green : attendee.green,
|
||||
red: updateModel.red != null ? updateModel.red : attendee.red,
|
||||
blue: updateModel.blue != null ? updateModel.blue : attendee.blue,
|
||||
yellow: updateModel.yellow != null ? updateModel.yellow : attendee.yellow,
|
||||
phoneNumber: updateModel.phoneNumber != null ? updateModel.phoneNumber : attendee.phoneNumber,
|
||||
winner: updateModel.winner != null ? updateModel.winner : attendee.winner
|
||||
};
|
||||
|
||||
return Attendee.updateOne({ _id: id }, updatedAttendee).then(_ => updatedAttendee);
|
||||
});
|
||||
};
|
||||
|
||||
const deleteAttendeeById = id => {
|
||||
return Attendee.findOne({ _id: id }).then(attendee => {
|
||||
if (attendee == null) {
|
||||
throw new UserNotFound();
|
||||
}
|
||||
|
||||
return Attendee.deleteOne({ _id: id }).then(_ => attendee);
|
||||
});
|
||||
};
|
||||
|
||||
const deleteAttendees = () => {
|
||||
return Attendee.deleteMany();
|
||||
};
|
||||
|
||||
const allWines = () => {
|
||||
return PreLotteryWine.find();
|
||||
};
|
||||
|
||||
const addWines = wines => {
|
||||
const prelotteryWines = wines.map(wine => {
|
||||
let newPrelotteryWine = new PreLotteryWine({
|
||||
name: wine.name,
|
||||
vivinoLink: wine.vivinoLink,
|
||||
rating: wine.rating,
|
||||
image: wine.image,
|
||||
price: wine.price,
|
||||
country: wine.country,
|
||||
id: wine.id
|
||||
});
|
||||
|
||||
return newPrelotteryWine.save();
|
||||
});
|
||||
|
||||
return Promise.all(prelotteryWines);
|
||||
};
|
||||
|
||||
const updateWineById = (id, updateModel) => {
|
||||
return PreLotteryWine.findOne({ _id: id }).then(wine => {
|
||||
if (wine == null) {
|
||||
throw new WineNotFound();
|
||||
}
|
||||
|
||||
const updatedWine = {
|
||||
name: updateModel.name != null ? updateModel.name : wine.name,
|
||||
vivinoLink: updateModel.vivinoLink != null ? updateModel.vivinoLink : wine.vivinoLink,
|
||||
rating: updateModel.rating != null ? updateModel.rating : wine.rating,
|
||||
image: updateModel.image != null ? updateModel.image : wine.image,
|
||||
price: updateModel.price != null ? updateModel.price : wine.price,
|
||||
country: updateModel.country != null ? updateModel.country : wine.country,
|
||||
id: updateModel.id != null ? updateModel.id : wine.id
|
||||
};
|
||||
|
||||
return PreLotteryWine.updateOne({ _id: id }, updatedWine).then(_ => updatedWine);
|
||||
});
|
||||
};
|
||||
|
||||
const deleteWineById = id => {
|
||||
return PreLotteryWine.findOne({ _id: id }).then(wine => {
|
||||
if (wine == null) {
|
||||
throw new WineNotFound();
|
||||
}
|
||||
|
||||
return PreLotteryWine.deleteOne({ _id: id }).then(_ => wine);
|
||||
});
|
||||
};
|
||||
|
||||
const deleteWines = () => {
|
||||
return PreLotteryWine.deleteMany();
|
||||
};
|
||||
|
||||
const addWinners = winners => {
|
||||
return Promise.all(
|
||||
winners.map(winner => {
|
||||
let newWinnerElement = new VirtualWinner({
|
||||
name: winner.name,
|
||||
color: winner.color,
|
||||
timestamp_drawn: new Date().getTime()
|
||||
});
|
||||
|
||||
return newWinnerElement.save();
|
||||
})
|
||||
return Lottery.findOneAndUpdate(
|
||||
{ date },
|
||||
{
|
||||
date,
|
||||
blue,
|
||||
red,
|
||||
yellow,
|
||||
green,
|
||||
bought,
|
||||
stolen,
|
||||
wines
|
||||
},
|
||||
{ upsert: true }
|
||||
);
|
||||
};
|
||||
|
||||
const allWinners = (isAdmin = false) => {
|
||||
if (!isAdmin) {
|
||||
return VirtualWinner.find().then(winners => winners.map(redactWinnerInfoMapper));
|
||||
} else {
|
||||
return VirtualWinner.find();
|
||||
}
|
||||
};
|
||||
const lotteryByDate = date => {
|
||||
const startOfDay = new Date(date.setHours(0, 0, 0, 0));
|
||||
const endOfDay = new Date(date.setHours(24, 59, 59, 99));
|
||||
|
||||
const winnerById = (id, isAdmin = false) => {
|
||||
return VirtualWinner.findOne({ _id: id }).then(winner => {
|
||||
if (winner == null) {
|
||||
throw new WinnerNotFound();
|
||||
const query = [
|
||||
{
|
||||
$match: {
|
||||
date: {
|
||||
$gte: startOfDay,
|
||||
$lte: endOfDay
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
$lookup: {
|
||||
from: "wines",
|
||||
localField: "wines",
|
||||
foreignField: "_id",
|
||||
as: "wines"
|
||||
}
|
||||
}
|
||||
];
|
||||
|
||||
if (!isAdmin) {
|
||||
return redactWinnerInfoMapper(winner);
|
||||
} else {
|
||||
return winner;
|
||||
const aggregateLottery = Lottery.aggregate(query);
|
||||
return aggregateLottery.project("-_id -__v").then(lotteries => {
|
||||
if (lotteries.length == 0) {
|
||||
throw new LotteryByDateNotFound(date);
|
||||
}
|
||||
return lotteries[0];
|
||||
});
|
||||
};
|
||||
|
||||
const deleteWinnerById = id => {
|
||||
return VirtualWinner.findOne({ _id: id }).then(winner => {
|
||||
if (winner == null) {
|
||||
throw new WinnerNotFound();
|
||||
}
|
||||
|
||||
return VirtualWinner.deleteOne({ _id: id }).then(_ => winner);
|
||||
});
|
||||
};
|
||||
|
||||
const deleteWinners = () => {
|
||||
return VirtualWinner.deleteMany();
|
||||
const allLotteries = () => {
|
||||
return Lottery.find()
|
||||
.select("-_id -__v")
|
||||
.populate("wines");
|
||||
};
|
||||
|
||||
const drawWinner = async () => {
|
||||
let allContestants = await Attendee.find({ winner: false });
|
||||
|
||||
if (allContestants.length == 0) {
|
||||
throw new NoMoreAttendeesToWinError();
|
||||
throw new NoMoreAttendeesToWin();
|
||||
}
|
||||
|
||||
let raffleColors = [];
|
||||
@@ -278,7 +116,7 @@ const drawWinner = async () => {
|
||||
tries++;
|
||||
}
|
||||
if (contestantsToChooseFrom == undefined) {
|
||||
throw new CouldNotFindNewWinnerAfterNTriesError(maxTries);
|
||||
throw new CouldNotFindNewWinnerAfterNTries(maxTries);
|
||||
}
|
||||
|
||||
let attendeeListDemocratic = [];
|
||||
@@ -360,20 +198,8 @@ function shuffle(array) {
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
allAttendees,
|
||||
addAttendee,
|
||||
updateAttendeeById,
|
||||
deleteAttendeeById,
|
||||
deleteAttendees,
|
||||
allWines,
|
||||
addWines,
|
||||
updateWineById,
|
||||
deleteWineById,
|
||||
deleteWines,
|
||||
addWinners,
|
||||
allWinners,
|
||||
winnerById,
|
||||
deleteWinnerById,
|
||||
deleteWinners,
|
||||
drawWinner
|
||||
drawWinner,
|
||||
archive,
|
||||
lotteryByDate,
|
||||
allLotteries
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user