From 9b4b9124b435df3c79145f6c328651c9284fff16 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rafael=20L=C3=A1szl=C3=B3?= <rlacko99@gmail.com> Date: Thu, 24 Dec 2020 19:44:44 +0100 Subject: [PATCH] Term middlewares --- src/middlewares/term/addTerm.ts | 40 ++++++ src/middlewares/term/deleteTerm.ts | 26 ++++ src/middlewares/term/filterMembersByState.ts | 39 ++++++ src/middlewares/term/getLatestTerm.ts | 24 ++++ src/middlewares/term/getMember.ts | 27 ++++ src/middlewares/term/getMemberState.ts | 23 ++++ src/middlewares/term/getMembers.ts | 23 ++++ src/middlewares/term/getTerm.ts | 26 ++++ src/middlewares/term/getTermsList.ts | 22 ++++ src/middlewares/term/responseMember.ts | 14 +++ src/middlewares/term/responseMemberState.ts | 14 +++ src/middlewares/term/responseMembersList.ts | 10 ++ src/middlewares/term/responseTerm.ts | 18 +++ src/middlewares/term/responseTermsList.ts | 15 +++ src/middlewares/user/getUsersList.ts | 2 +- src/models/TermSchema.ts | 30 +++-- src/routes/terms.ts | 123 +++++++++++++++++-- src/utils/declarations/response.d.ts | 7 +- 18 files changed, 459 insertions(+), 24 deletions(-) create mode 100644 src/middlewares/term/addTerm.ts create mode 100644 src/middlewares/term/deleteTerm.ts create mode 100644 src/middlewares/term/filterMembersByState.ts create mode 100644 src/middlewares/term/getLatestTerm.ts create mode 100644 src/middlewares/term/getMember.ts create mode 100644 src/middlewares/term/getMemberState.ts create mode 100644 src/middlewares/term/getMembers.ts create mode 100644 src/middlewares/term/getTerm.ts create mode 100644 src/middlewares/term/getTermsList.ts create mode 100644 src/middlewares/term/responseMember.ts create mode 100644 src/middlewares/term/responseMemberState.ts create mode 100644 src/middlewares/term/responseMembersList.ts create mode 100644 src/middlewares/term/responseTerm.ts create mode 100644 src/middlewares/term/responseTermsList.ts diff --git a/src/middlewares/term/addTerm.ts b/src/middlewares/term/addTerm.ts new file mode 100644 index 00000000..73449a4d --- /dev/null +++ b/src/middlewares/term/addTerm.ts @@ -0,0 +1,40 @@ +import { NextFunction, Request, Response } from "express"; + +import Term from "../../models/TermSchema"; +import { validateFields } from "../utils/validateFields"; + +const fields = [ + { name: "name", required: true }, + { name: "createDate", required: false }, + { name: "startDate", required: true }, + { name: "endDate", required: true }, +]; + +/** + * Add a new Term + */ +const addTerm = () => async ( + req: Request, + res: Response, + next: NextFunction +) => { + try { + const term = new Term(); + + // Validate and set fields from request body + validateFields({ fields, reqBody: req.body }); + fields.forEach((field) => { + const value = req.body[field.name]; + if (value) term.set(field.name, req.body[field.name]); + }); + term.createDate = new Date(); + await term.save(); + + res.data.newObjectId = term._id; + next(); + } catch (err) { + next(err); + } +}; + +export default addTerm; diff --git a/src/middlewares/term/deleteTerm.ts b/src/middlewares/term/deleteTerm.ts new file mode 100644 index 00000000..cf5c325a --- /dev/null +++ b/src/middlewares/term/deleteTerm.ts @@ -0,0 +1,26 @@ +import { NextFunction, Request, Response } from "express"; + +import File from "../../models/FileSchema"; +import Profile from "../../models/ProfileSchema"; +import Term from "../../models/TermSchema"; + +/** + * termId -> deletes the Term + */ +const deleteTerm = () => async ( + req: Request, + res: Response, + next: NextFunction +) => { + try { + const removedTerm = await Term.findByIdAndDelete(req.params.termId); + if (!removedTerm) + return res.status(404).json({ message: "Term not found!" }); + await File.findByIdAndDelete(removedTerm?.backgroundFile); + next(); + } catch (err) { + next(err); + } +}; + +export default deleteTerm; diff --git a/src/middlewares/term/filterMembersByState.ts b/src/middlewares/term/filterMembersByState.ts new file mode 100644 index 00000000..0fa32511 --- /dev/null +++ b/src/middlewares/term/filterMembersByState.ts @@ -0,0 +1,39 @@ +import { NextFunction, Request, Response } from "express"; +import Term, { MemberState } from "../../models/TermSchema"; + +import File from "../../models/FileSchema"; +import Profile from "../../models/ProfileSchema"; + +const convertState = (state: string) => { + switch (state) { + case "accepted": + return MemberState.Accepted; + case "applied": + return MemberState.Applied; + case "rejected": + return MemberState.Rejected; + } + return null; +}; + +/** + * filteredState, res.data.members -> filter Term members by state + */ +const filterMembersByState = () => async ( + req: Request, + res: Response, + next: NextFunction +) => { + try { + const state = convertState(req.params.filteredState); + if (state) + res.data.members = res.data.term?.members?.filter( + (member) => member.state == state + ); + next(); + } catch (err) { + next(err); + } +}; + +export default filterMembersByState; diff --git a/src/middlewares/term/getLatestTerm.ts b/src/middlewares/term/getLatestTerm.ts new file mode 100644 index 00000000..9cd890ed --- /dev/null +++ b/src/middlewares/term/getLatestTerm.ts @@ -0,0 +1,24 @@ +import { NextFunction, Request, Response } from "express"; + +import Profile from "../../models/ProfileSchema"; +import Term from "../../models/TermSchema"; + +const getLatestTerm = () => async ( + req: Request, + res: Response, + next: NextFunction +) => { + try { + res.data.term = await Term.findOne({}, {}, { sort: { createDate: -1 } }) + .lean() + .exec(); + if (!res.data.term) + return res.status(404).json({ message: "There are no Terms" }); + + next(); + } catch (err) { + next(err); + } +}; + +export default getLatestTerm; diff --git a/src/middlewares/term/getMember.ts b/src/middlewares/term/getMember.ts new file mode 100644 index 00000000..45c9882b --- /dev/null +++ b/src/middlewares/term/getMember.ts @@ -0,0 +1,27 @@ +import { NextFunction, Request, Response } from "express"; + +import File from "../../models/FileSchema"; +import Profile from "../../models/ProfileSchema"; +import Term from "../../models/TermSchema"; + +/** + * userId -> Found Term + */ +const getMember = () => async ( + req: Request, + res: Response, + next: NextFunction +) => { + try { + res.data.member = res.data.term?.members?.find( + (member) => member.user == req.params.userId + ); + if (!res.data.member) + return res.status(404).json({ message: "Member not found!" }); + next(); + } catch (err) { + next(err); + } +}; + +export default getMember; diff --git a/src/middlewares/term/getMemberState.ts b/src/middlewares/term/getMemberState.ts new file mode 100644 index 00000000..4054af99 --- /dev/null +++ b/src/middlewares/term/getMemberState.ts @@ -0,0 +1,23 @@ +import { NextFunction, Request, Response } from "express"; + +import File from "../../models/FileSchema"; +import Profile from "../../models/ProfileSchema"; +import Term from "../../models/TermSchema"; + +/** + * member -> memberState + */ +const getMemberState = () => async ( + req: Request, + res: Response, + next: NextFunction +) => { + try { + res.data.memberState = res.data.member!.state; + next(); + } catch (err) { + next(err); + } +}; + +export default getMemberState; diff --git a/src/middlewares/term/getMembers.ts b/src/middlewares/term/getMembers.ts new file mode 100644 index 00000000..23f5a9ce --- /dev/null +++ b/src/middlewares/term/getMembers.ts @@ -0,0 +1,23 @@ +import { NextFunction, Request, Response } from "express"; + +import File from "../../models/FileSchema"; +import Profile from "../../models/ProfileSchema"; +import Term from "../../models/TermSchema"; + +/** + * res.data.term.members -> res.data.members + */ +const getMembers = () => async ( + req: Request, + res: Response, + next: NextFunction +) => { + try { + res.data.members = res.data.term?.members; + next(); + } catch (err) { + next(err); + } +}; + +export default getMembers; diff --git a/src/middlewares/term/getTerm.ts b/src/middlewares/term/getTerm.ts new file mode 100644 index 00000000..2a9799b3 --- /dev/null +++ b/src/middlewares/term/getTerm.ts @@ -0,0 +1,26 @@ +import { NextFunction, Request, Response } from "express"; + +import Profile from "../../models/ProfileSchema"; +import Term from "../../models/TermSchema"; + +/** + * termId -> term + */ +const getTerm = () => async ( + req: Request, + res: Response, + next: NextFunction +) => { + try { + res.data.term = await Term.findById(req.params.termId).lean().exec(); + + if (!res.data.term) + return res.status(404).json({ message: "Term not found" }); + + next(); + } catch (err) { + next(err); + } +}; + +export default getTerm; diff --git a/src/middlewares/term/getTermsList.ts b/src/middlewares/term/getTermsList.ts new file mode 100644 index 00000000..e22b0637 --- /dev/null +++ b/src/middlewares/term/getTermsList.ts @@ -0,0 +1,22 @@ +import { NextFunction, Request, Response } from "express"; + +import Profile from "../../models/ProfileSchema"; +import Term from "../../models/TermSchema"; + +/** + * -> All Terms to res.data.terms + */ +const getTermsList = () => async ( + req: Request, + res: Response, + next: NextFunction +) => { + try { + res.data.terms = await Term.find().lean().exec(); + next(); + } catch (err) { + next(err); + } +}; + +export default getTermsList; diff --git a/src/middlewares/term/responseMember.ts b/src/middlewares/term/responseMember.ts new file mode 100644 index 00000000..e1348185 --- /dev/null +++ b/src/middlewares/term/responseMember.ts @@ -0,0 +1,14 @@ +import { NextFunction, Request, Response, response } from "express"; + +/** + * Return the found Member + */ +const responseMember = () => (req: Request, res: Response) => { + if (!res.data.member) { + res.status(404).json({ message: "Member not found!" }); + } else { + return res.json(res.data.member); + } +}; + +export default responseMember; diff --git a/src/middlewares/term/responseMemberState.ts b/src/middlewares/term/responseMemberState.ts new file mode 100644 index 00000000..295d9111 --- /dev/null +++ b/src/middlewares/term/responseMemberState.ts @@ -0,0 +1,14 @@ +import { NextFunction, Request, Response, response } from "express"; + +/** + * Return the found MemberState + */ +const responseMemberState = () => (req: Request, res: Response) => { + if (!res.data.memberState) { + res.status(404).json({ message: "MemberState not found!" }); + } else { + return res.json(res.data.memberState); + } +}; + +export default responseMemberState; diff --git a/src/middlewares/term/responseMembersList.ts b/src/middlewares/term/responseMembersList.ts new file mode 100644 index 00000000..3568bdf7 --- /dev/null +++ b/src/middlewares/term/responseMembersList.ts @@ -0,0 +1,10 @@ +import { NextFunction, Request, Response, response } from "express"; + +/** + * Return the found Members + */ +const responseMembersList = () => (req: Request, res: Response) => { + return res.json(res.data.members); +}; + +export default responseMembersList; diff --git a/src/middlewares/term/responseTerm.ts b/src/middlewares/term/responseTerm.ts new file mode 100644 index 00000000..3977d625 --- /dev/null +++ b/src/middlewares/term/responseTerm.ts @@ -0,0 +1,18 @@ +import { NextFunction, Request, Response, response } from "express"; + +/** + * Return the found Term + */ +const responseTerm = (sendMembers = true) => (req: Request, res: Response) => { + if (!res.data.term) { + res.status(404).json({ message: "Term not found!" }); + } else { + if (!sendMembers) { + const { members, ...kept } = res.data.term; + return res.json({ term: kept }); + } + return res.json(res.data.term); + } +}; + +export default responseTerm; diff --git a/src/middlewares/term/responseTermsList.ts b/src/middlewares/term/responseTermsList.ts new file mode 100644 index 00000000..c53c5207 --- /dev/null +++ b/src/middlewares/term/responseTermsList.ts @@ -0,0 +1,15 @@ +import { NextFunction, Request, Response, response } from "express"; + +/** + * Return the found Term + */ +const responseTermsList = (sendMembers = true) => ( + req: Request, + res: Response +) => { + if (!sendMembers) + return res.json(res.data.terms?.map(({ members, ...keep }) => keep)); + return res.json(res.data.terms); +}; + +export default responseTermsList; diff --git a/src/middlewares/user/getUsersList.ts b/src/middlewares/user/getUsersList.ts index cfff3060..a188f295 100644 --- a/src/middlewares/user/getUsersList.ts +++ b/src/middlewares/user/getUsersList.ts @@ -11,7 +11,7 @@ const getUsersList = () => async ( next: NextFunction ) => { try { - res.data.profiles = await Profile.find(); + res.data.profiles = await Profile.find().lean().exec(); next(); } catch (err) { next(err); diff --git a/src/models/TermSchema.ts b/src/models/TermSchema.ts index 5a1de32d..368f580c 100644 --- a/src/models/TermSchema.ts +++ b/src/models/TermSchema.ts @@ -6,35 +6,41 @@ export enum MemberState { Rejected = "REJECTED", } +export interface IMember { + user: string; + state: MemberState; + cardNumber: number; + cardReceiveDate?: Date; +} + export interface ITerm extends Document { backgroundFile?: string; name: string; + createDate?: Date; startDate: Date; endDate: Date; - members: { - user: string; - state: MemberState; - cardNumber: number; - cardReceiveDate?: Date; - }[]; + members: IMember[]; } const TermSchema = new Schema({ backgroundFile: { type: Schema.Types.ObjectId, ref: "File", required: false }, name: { type: String, required: true }, + createDate: { type: Date, required: true }, startDate: { type: Date, required: true }, endDate: { type: Date, required: true }, members: [ { - required: false, - type: { - user: { type: Schema.Types.ObjectId, ref: "Profile", required: true }, - state: Object.keys(MemberState).map( + user: { type: Schema.Types.ObjectId, ref: "Profile", required: true }, + state: { + type: String, + enum: Object.keys(MemberState).map( (k) => MemberState[k as keyof typeof MemberState] ), - cardNumber: { type: Number, required: true }, - cardReceiveDate: { type: Date, required: false }, + required: true, + default: MemberState.Applied, }, + cardNumber: { type: Number, required: true }, + cardReceiveDate: { type: Date, required: false }, }, ], }); diff --git a/src/routes/terms.ts b/src/routes/terms.ts index 980891c0..08d014eb 100644 --- a/src/routes/terms.ts +++ b/src/routes/terms.ts @@ -1,21 +1,124 @@ import { Application } from "express"; +import addTerm from "../middlewares/term/addTerm"; +import createdResponse from "../middlewares/utils/createdResponse"; +import deleteTerm from "../middlewares/term/deleteTerm"; import example from "../middlewares/example"; +import filterMembersByState from "../middlewares/term/filterMembersByState"; +import getLatestTerm from "../middlewares/term/getLatestTerm"; +import getMember from "../middlewares/term/getMember"; +import getMemberState from "../middlewares/term/getMemberState"; +import getMembers from "../middlewares/term/getMembers"; +import getTerm from "../middlewares/term/getTerm"; +import getTermsList from "../middlewares/term/getTermsList"; +import isRegistered from "../middlewares/auth/isRegistered"; +import isStaffOrAdmin from "../middlewares/auth/isStaffOrAdmin"; +import noContentResponse from "../middlewares/utils/noContentResponse"; +import responseMember from "../middlewares/term/responseMember"; +import responseMemberState from "../middlewares/term/responseMemberState"; +import responseMembersList from "../middlewares/term/responseMembersList"; +import responseTerm from "../middlewares/term/responseTerm"; +import responseTermsList from "../middlewares/term/responseTermsList"; +import setOwnUserId from "../middlewares/user/setOwnUserId"; const termsRoute = (prefix: string, app: Application): void => { - // Get all Terms - app.get(`${prefix}/`, example()); - // Get a Term - app.post(`${prefix}/term/:termId`, example()); + // Get all Terms without members + app.get( + `${prefix}/`, + isRegistered(), + getTermsList(), + responseTermsList(false) + ); + // Get all Terms with members + app.get( + `${prefix}/wmembers`, + isRegistered(), + isStaffOrAdmin(), + getTermsList(), + responseTermsList(true) + ); + // Create a Term + app.post( + `${prefix}/`, + isRegistered(), + isStaffOrAdmin(), + addTerm(), + createdResponse() + ); + // Get a Term without members + app.get( + `${prefix}/term/:termId`, + isRegistered(), + getTerm(), + responseTerm(false) + ); + // Get a Term with members + app.get( + `${prefix}/term/:termId/wmembers`, + isRegistered(), + isStaffOrAdmin(), + getTerm(), + responseTerm(true) + ); + // Delete a Term + app.delete( + `${prefix}/term/:termId`, + isRegistered(), + isStaffOrAdmin(), + deleteTerm(), + noContentResponse() + ); // Get members of a Term - app.get(`${prefix}/term/:termId/members`, example()); + app.get( + `${prefix}/term/:termId/members`, + isRegistered(), + isStaffOrAdmin(), + getTerm(), + getMembers(), + responseMembersList() + ); // Get filtered members of a Term (accepted/rejected/applied) - app.get(`${prefix}/term/:termId/members/:stateFilter`, example()); + app.get( + `${prefix}/term/:termId/members/:stateFilter`, + isRegistered(), + isStaffOrAdmin(), + getTerm(), + filterMembersByState(), + responseMembersList() + ); // Get own member state in a Term - app.get(`${prefix}/term/:termId/state/me`, example()); + app.get( + `${prefix}/term/:termId/state/me`, + isRegistered(), + setOwnUserId(), + getTerm(), + getMember(), + getMemberState(), + responseMemberState() + ); // Get a user's state in a Term - app.get(`${prefix}/term/:termId/state/user/:userId`, example()); - // Get the latest Term - app.get(`${prefix}/latest`, example()); + app.get( + `${prefix}/term/:termId/state/user/:userId`, + isRegistered(), + isStaffOrAdmin(), + getTerm(), + getMember(), + getMemberState(), + responseMemberState() + ); + // Get the latest Term without members + app.get( + `${prefix}/latest`, + isRegistered(), + getLatestTerm(), + responseTerm(false) + ); + // Get the latest Term with members + app.get( + `${prefix}/latest`, + isRegistered(), + getLatestTerm(), + responseTerm(true) + ); // Apply own user to the latest Term app.post(`${prefix}/apply/me/latest`, example()); // Apply a user to the latest Term diff --git a/src/utils/declarations/response.d.ts b/src/utils/declarations/response.d.ts index 878f90fe..1e9abe0d 100644 --- a/src/utils/declarations/response.d.ts +++ b/src/utils/declarations/response.d.ts @@ -2,7 +2,7 @@ import { INews } from "../../models/NewsSchema"; import { IProfile } from "../../models/ProfileSchema"; import { IFile } from "../../models/FileSchema"; import { IWarning } from "../../models/WarningSchema"; -import { ITerm } from "../../models/TermSchema"; +import { IMember, ITerm } from "../../models/TermSchema"; declare global { namespace Express { @@ -13,6 +13,11 @@ declare global { newObjectId?: string | null; value?: string | null; term?: Partial<ITerm> | null; + terms?: Partial<ITerm>[] | null; + member?: Partial<IMember> | null; + members?: Partial<IMember>[] | null; + memberState?: IMember["state"] | null; + news?: Partial<INews>[] | null; newsObject?: Partial<INews> | null; profiles?: Partial<IProfile>[] | null; -- GitLab