Renamed to winners. Winners gets controller setup.
Rewrote everything that happened in history to better take advantage of monogdb instead of doing everything in js. Our endpoints become: - /winners - getAll w/ includeWines and sort query params. - /winners/latest - latest winners grouped w/ includeWines query params. - /winners/by-date - all winners grouped by date w/ includeWines and sort. - /winners/by-date/:date - get winners per epoch or string date. - /winners/by-name/:name - get winner by name parameter w/ sort for wins direction.
This commit is contained in:
@@ -1,92 +0,0 @@
|
||||
const path = require("path");
|
||||
const historyRepository = require(path.join(__dirname, "../history"));
|
||||
|
||||
const all = (req, res) => {
|
||||
return historyRepository
|
||||
.all()
|
||||
.then(lotteries =>
|
||||
res.send({
|
||||
lotteries: lotteries,
|
||||
success: true
|
||||
})
|
||||
)
|
||||
.catch(error => {
|
||||
const { statusCode, message } = error;
|
||||
|
||||
return res.status(statusCode || 500).send({
|
||||
success: false,
|
||||
message: message || "Unable to fetch history."
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
const latest = (req, res) => {
|
||||
return historyRepository
|
||||
.latest()
|
||||
.then(lottery =>
|
||||
res.send({
|
||||
lottery: lottery,
|
||||
success: true
|
||||
})
|
||||
)
|
||||
.catch(error => {
|
||||
const { statusCode, message } = error;
|
||||
|
||||
return res.status(statusCode || 500).send({
|
||||
success: false,
|
||||
message: message || "Unable to fetch latest history."
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
const byDate = (req, res) => {
|
||||
let { date } = req.params;
|
||||
date = new Date(new Date(parseInt(date)).setHours(0, 0, 0, 0)).getTime();
|
||||
|
||||
return historyRepository
|
||||
.byEpochDate(date)
|
||||
.then(winners =>
|
||||
res.send({
|
||||
date: date,
|
||||
winners: winners,
|
||||
success: true
|
||||
})
|
||||
)
|
||||
.catch(error => {
|
||||
const { statusCode, message } = error;
|
||||
|
||||
return res.status(statusCode || 500).send({
|
||||
success: false,
|
||||
message: message || "Unable to fetch history for date."
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
const byName = (req, res) => {
|
||||
const { name } = req.params;
|
||||
|
||||
return historyRepository
|
||||
.byName(name)
|
||||
.then(lotteries =>
|
||||
res.send({
|
||||
name: name,
|
||||
lotteries: lotteries,
|
||||
success: true
|
||||
})
|
||||
)
|
||||
.catch(error => {
|
||||
const { statusCode, message } = error;
|
||||
|
||||
return res.status(statusCode || 500).send({
|
||||
success: false,
|
||||
message: message || "Unable to fetch history for name."
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
module.exports = {
|
||||
all,
|
||||
latest,
|
||||
byDate,
|
||||
byName
|
||||
};
|
||||
165
api/controllers/winnerController.js
Normal file
165
api/controllers/winnerController.js
Normal file
@@ -0,0 +1,165 @@
|
||||
const path = require("path");
|
||||
const winnerRepository = require(path.join(__dirname, "../winner"));
|
||||
|
||||
const sortOptions = ["desc", "asc"];
|
||||
const includeWinesOptions = ["true", "false"];
|
||||
|
||||
const all = (req, res) => {
|
||||
let { sort, includeWines } = req.query;
|
||||
|
||||
if (sort !== undefined && !sortOptions.includes(sort)) {
|
||||
return res.status(400).send({
|
||||
message: `Sort option must be: '${sortOptions.join(", ")}'`,
|
||||
success: false
|
||||
});
|
||||
}
|
||||
|
||||
if (includeWines !== undefined && !includeWinesOptions.includes(includeWines)) {
|
||||
return res.status(400).send({
|
||||
message: `includeWines option must be: '${includeWinesOptions.join(", ")}'`,
|
||||
success: false
|
||||
});
|
||||
}
|
||||
|
||||
return winnerRepository
|
||||
.all(includeWines == "true")
|
||||
.then(winners =>
|
||||
res.send({
|
||||
winners: sort !== "asc" ? winners : winners.reverse(),
|
||||
success: true
|
||||
})
|
||||
)
|
||||
.catch(error => {
|
||||
const { statusCode, message } = error;
|
||||
|
||||
return res.status(statusCode || 500).send({
|
||||
success: false,
|
||||
message: message || "Unable to fetch winners."
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
const byDate = (req, res) => {
|
||||
let { date } = req.params;
|
||||
|
||||
const regexDate = new RegExp("^\\d{4}-\\d{2}-\\d{2}$");
|
||||
if (!isNaN(date)) {
|
||||
date = new Date(new Date(parseInt(date * 1000)).setHours(0, 0, 0, 0));
|
||||
} else if (regexDate.test(date)) {
|
||||
date = new Date(date);
|
||||
} else if (date !== undefined) {
|
||||
return res.status(400).send({
|
||||
message: "Invalid date parameter, allowed epoch seconds or YYYY-MM-DD.",
|
||||
success: false
|
||||
});
|
||||
}
|
||||
|
||||
return winnerRepository
|
||||
.byDate(date)
|
||||
.then(winners =>
|
||||
res.send({
|
||||
date: date,
|
||||
winners: winners,
|
||||
success: true
|
||||
})
|
||||
)
|
||||
.catch(error => {
|
||||
const { statusCode, message } = error;
|
||||
|
||||
return res.status(statusCode || 500).send({
|
||||
success: false,
|
||||
message: message || "Unable to fetch winner by date."
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
const groupedByDate = (req, res) => {
|
||||
let { sort, includeWines } = req.query;
|
||||
|
||||
if (sort !== undefined && !sortOptions.includes(sort)) {
|
||||
return res.status(400).send({
|
||||
message: `Sort option must be: '${sortOptions.join(", ")}'`,
|
||||
success: false
|
||||
});
|
||||
}
|
||||
|
||||
if (includeWines !== undefined && !includeWinesOptions.includes(includeWines)) {
|
||||
return res.status(400).send({
|
||||
message: `includeWines option must be: '${includeWinesOptions.join(", ")}'`,
|
||||
success: false
|
||||
});
|
||||
}
|
||||
|
||||
return winnerRepository
|
||||
.groupedByDate(includeWines, sort)
|
||||
.then(lotteries =>
|
||||
res.send({
|
||||
lotteries: lotteries,
|
||||
success: true
|
||||
})
|
||||
)
|
||||
.catch(error => {
|
||||
const { statusCode, message } = error;
|
||||
|
||||
return res.status(statusCode || 500).send({
|
||||
success: false,
|
||||
message: message || "Unable to fetch winner by date."
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
const latest = (req, res) => {
|
||||
return winnerRepository
|
||||
.latest()
|
||||
.then(winners =>
|
||||
res.send({
|
||||
...winners,
|
||||
success: true
|
||||
})
|
||||
)
|
||||
.catch(error => {
|
||||
const { statusCode, message } = error;
|
||||
|
||||
return res.status(statusCode || 500).send({
|
||||
success: false,
|
||||
message: message || "Unable to fetch winner by date."
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
const byName = (req, res) => {
|
||||
const { name } = req.params;
|
||||
const { sort } = req.query;
|
||||
|
||||
if (sort !== undefined && !sortOptions.includes(sort)) {
|
||||
return res.status(400).send({
|
||||
message: `Sort option must be: '${sortOptions.join(", ")}'`,
|
||||
success: false
|
||||
});
|
||||
}
|
||||
|
||||
return winnerRepository
|
||||
.byName(name, sort)
|
||||
.then(winner =>
|
||||
res.send({
|
||||
winner: winner,
|
||||
success: true
|
||||
})
|
||||
)
|
||||
.catch(error => {
|
||||
const { statusCode, message } = error;
|
||||
|
||||
return res.status(statusCode || 500).send({
|
||||
success: false,
|
||||
message: message || "Unable to fetch winner by name."
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
module.exports = {
|
||||
all,
|
||||
byDate,
|
||||
groupedByDate,
|
||||
latest,
|
||||
byName
|
||||
};
|
||||
117
api/history.js
117
api/history.js
@@ -1,117 +0,0 @@
|
||||
const path = require("path");
|
||||
|
||||
const Highscore = require(path.join(__dirname, "/schemas/Highscore"));
|
||||
const Wine = require(path.join(__dirname, "/schemas/Wine"));
|
||||
|
||||
class HistoryByDateNotFound extends Error {
|
||||
constructor(message = "History for given date not found.") {
|
||||
super(message);
|
||||
this.name = "HistoryByDateNotFound";
|
||||
this.statusCode = 404;
|
||||
}
|
||||
}
|
||||
|
||||
class HistoryForUserNotFound extends Error {
|
||||
constructor(message = "History for given user not found.") {
|
||||
super(message);
|
||||
this.name = "HistoryForUserNotFound";
|
||||
this.statusCode = 404;
|
||||
}
|
||||
}
|
||||
|
||||
// Utils
|
||||
const epochToDateString = date => new Date(parseInt(date)).toDateString();
|
||||
|
||||
const sortNewestFirst = lotteries => {
|
||||
return lotteries.sort((a, b) => (parseInt(a.date) < parseInt(b.date) ? 1 : -1));
|
||||
};
|
||||
|
||||
const groupHighscoreByDate = async (highscore = undefined) => {
|
||||
if (highscore == undefined) highscore = await Highscore.find();
|
||||
|
||||
const highscoreByDate = [];
|
||||
|
||||
highscore.forEach(person => {
|
||||
person.wins.map(win => {
|
||||
const epochDate = new Date(win.date).setHours(0, 0, 0, 0);
|
||||
const winnerObject = {
|
||||
name: person.name,
|
||||
color: win.color,
|
||||
wine: win.wine,
|
||||
date: epochDate
|
||||
};
|
||||
|
||||
const existingDateIndex = highscoreByDate.findIndex(el => el.date == epochDate);
|
||||
if (existingDateIndex > -1) highscoreByDate[existingDateIndex].winners.push(winnerObject);
|
||||
else
|
||||
highscoreByDate.push({
|
||||
date: epochDate,
|
||||
winners: [winnerObject]
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
return sortNewestFirst(highscoreByDate);
|
||||
};
|
||||
|
||||
const resolveWineReferences = (highscoreObject, key) => {
|
||||
const listWithWines = highscoreObject[key];
|
||||
|
||||
return Promise.all(
|
||||
listWithWines.map(element =>
|
||||
Wine.findById(element.wine).then(wine => {
|
||||
element.wine = wine;
|
||||
return element;
|
||||
})
|
||||
)
|
||||
).then(resolvedListWithWines => {
|
||||
highscoreObject[key] = resolvedListWithWines;
|
||||
return highscoreObject;
|
||||
});
|
||||
};
|
||||
// end utils
|
||||
|
||||
// Routes
|
||||
const all = () => {
|
||||
return Highscore.find().then(highscore => groupHighscoreByDate(highscore));
|
||||
};
|
||||
|
||||
const latest = () => {
|
||||
return groupHighscoreByDate()
|
||||
.then(lotteries => lotteries.shift()) // first element in list
|
||||
.then(latestLottery => resolveWineReferences(latestLottery, "winners"));
|
||||
};
|
||||
|
||||
const byEpochDate = date => {
|
||||
return groupHighscoreByDate()
|
||||
.then(lotteries => {
|
||||
const lottery = lotteries.filter(lottery => lottery.date == date);
|
||||
if (lottery.length > 0) {
|
||||
return lottery[0];
|
||||
} else {
|
||||
throw new HistoryByDateNotFound();
|
||||
}
|
||||
})
|
||||
.then(lottery => resolveWineReferences(lottery, "winners"))
|
||||
.then(lottery => lottery.winners);
|
||||
};
|
||||
|
||||
const byName = name => {
|
||||
return Highscore.find({ name })
|
||||
.then(highscore => {
|
||||
if (highscore.length > 0) {
|
||||
return highscore[0];
|
||||
} else {
|
||||
throw new HistoryForUserNotFound();
|
||||
}
|
||||
})
|
||||
.then(highscore => resolveWineReferences(highscore, "wins"))
|
||||
.then(highscore => sortNewestFirst(highscore.wins));
|
||||
};
|
||||
|
||||
module.exports = {
|
||||
all,
|
||||
latest,
|
||||
byEpochDate,
|
||||
byName
|
||||
};
|
||||
191
api/winner.js
Normal file
191
api/winner.js
Normal file
@@ -0,0 +1,191 @@
|
||||
const path = require("path");
|
||||
|
||||
const Winner = require(path.join(__dirname, "/schemas/Highscore"));
|
||||
const Wine = require(path.join(__dirname, "/schemas/Wine"));
|
||||
|
||||
class HistoryByDateNotFound extends Error {
|
||||
constructor(message = "History for given date not found.") {
|
||||
super(message);
|
||||
this.name = "HistoryByDateNotFound";
|
||||
this.statusCode = 404;
|
||||
}
|
||||
}
|
||||
|
||||
class HistoryForUserNotFound extends Error {
|
||||
constructor(message = "History for given user not found.") {
|
||||
super(message);
|
||||
this.name = "HistoryForUserNotFound";
|
||||
this.statusCode = 404;
|
||||
}
|
||||
}
|
||||
|
||||
const all = (includeWines = false) => {
|
||||
if (includeWines === false) {
|
||||
return Winner.find().sort("-wins.date");
|
||||
} else {
|
||||
return Winner.find()
|
||||
.sort("-wins.date")
|
||||
.populate("wins.wine");
|
||||
}
|
||||
};
|
||||
|
||||
const byDate = date => {
|
||||
const startQueryDate = new Date(date.setHours(0, 0, 0, 0));
|
||||
const endQueryDate = new Date(date.setHours(24, 59, 59, 99));
|
||||
const query = [
|
||||
{
|
||||
$match: {
|
||||
"wins.date": {
|
||||
$gte: startQueryDate,
|
||||
$lte: endQueryDate
|
||||
}
|
||||
}
|
||||
},
|
||||
{ $unwind: "$wins" },
|
||||
{
|
||||
$match: {
|
||||
"wins.date": {
|
||||
$gte: startQueryDate,
|
||||
$lte: endQueryDate
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
$lookup: {
|
||||
from: "wines",
|
||||
localField: "wins.wine",
|
||||
foreignField: "_id",
|
||||
as: "wins.wine"
|
||||
}
|
||||
},
|
||||
{ $unwind: "$wins.wine" },
|
||||
{
|
||||
$project: {
|
||||
name: "$name",
|
||||
date: "$wins.date",
|
||||
color: "$wins.color",
|
||||
wine: "$wins.wine"
|
||||
}
|
||||
}
|
||||
];
|
||||
|
||||
return Winner.aggregate(query).then(winners => {
|
||||
if (winners.length == 0) {
|
||||
throw new HistoryByDateNotFound();
|
||||
}
|
||||
return winners;
|
||||
});
|
||||
};
|
||||
|
||||
const byName = (name, sort = "desc") => {
|
||||
const populateOptions = { sort: "date" };
|
||||
|
||||
return Winner.findOne({ name }, ["name", "wins"])
|
||||
.sort("-wins.date")
|
||||
.populate("wins.wine")
|
||||
.then(winner => {
|
||||
if (winner) {
|
||||
winner.wins = sort !== "asc" ? winner.wins.reverse() : winner.wins;
|
||||
return winner;
|
||||
} else {
|
||||
throw new HistoryForUserNotFound();
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
const latest = () => {
|
||||
const query = [
|
||||
{
|
||||
$unwind: "$wins"
|
||||
},
|
||||
{
|
||||
$lookup: {
|
||||
from: "wines",
|
||||
localField: "wins.wine",
|
||||
foreignField: "_id",
|
||||
as: "wins.wine"
|
||||
}
|
||||
},
|
||||
{
|
||||
$group: {
|
||||
_id: "$wins.date",
|
||||
winners: {
|
||||
$push: {
|
||||
_id: "$_id",
|
||||
name: "$name",
|
||||
color: "$wins.color",
|
||||
wine: "$wins.wine"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
$project: {
|
||||
date: "$_id",
|
||||
winners: "$winners"
|
||||
}
|
||||
},
|
||||
{
|
||||
$sort: {
|
||||
_id: -1
|
||||
}
|
||||
},
|
||||
{
|
||||
$limit: 1
|
||||
}
|
||||
];
|
||||
|
||||
return Winner.aggregate(query).then(winners => winners[0]);
|
||||
};
|
||||
|
||||
const groupedByDate = (includeWines = false, sort = "desc") => {
|
||||
const query = [
|
||||
{
|
||||
$unwind: "$wins"
|
||||
},
|
||||
{
|
||||
$group: {
|
||||
_id: "$wins.date",
|
||||
winners: {
|
||||
$push: {
|
||||
_id: "$_id",
|
||||
name: "$name",
|
||||
color: "$wins.color",
|
||||
wine: "$wins.wine"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
$project: {
|
||||
date: "$_id",
|
||||
winners: "$winners"
|
||||
}
|
||||
},
|
||||
{
|
||||
$sort: {
|
||||
_id: -1
|
||||
}
|
||||
}
|
||||
];
|
||||
|
||||
if (includeWines) {
|
||||
query.splice(1, 0, {
|
||||
$lookup: {
|
||||
from: "wines",
|
||||
localField: "wins.wine",
|
||||
foreignField: "_id",
|
||||
as: "wins.wine"
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
return Winner.aggregate(query).then(lotteries => (sort != "asc" ? lotteries : lotteries.reverse()));
|
||||
};
|
||||
|
||||
module.exports = {
|
||||
all,
|
||||
byDate,
|
||||
latest,
|
||||
groupedByDate
|
||||
};
|
||||
Reference in New Issue
Block a user