diff --git a/server/app.js b/server/app.js index dfe51f49..b1b22093 100644 --- a/server/app.js +++ b/server/app.js @@ -1,36 +1,238 @@ const express = require("express"); const morgan = require("morgan"); const cookieParser = require("cookie-parser"); +const cors = require("cors"); +const mongoose = require("mongoose"); const PORT = 5005; -// STATIC DATA -// Devs Team - Import the provided files with JSON data of students and cohorts here: -// ... +// MODELS +const Cohort = require("./models/Cohort.model"); +const Student = require("./models/Student.model"); - -// INITIALIZE EXPRESS APP - https://expressjs.com/en/4x/api.html#express +// INITIALIZE EXPRESS APP const app = express(); - // MIDDLEWARE -// Research Team - Set up CORS middleware here: -// ... app.use(express.json()); app.use(morgan("dev")); app.use(express.static("public")); app.use(express.urlencoded({ extended: false })); app.use(cookieParser()); +app.use(cors()); + +// MONGOOSE CONNECTION +mongoose + .connect("mongodb://127.0.0.1:27017/cohort-tools-api") + .then((x) => console.log(`Connected to Mongo! Database name: "${x.connections[0].name}"`)) + .catch((err) => console.error("Error connecting to MongoDB", err)); -// ROUTES - https://expressjs.com/en/starter/basic-routing.html -// Devs Team - Start working on the routes here: -// ... +// ========================================================= +// ROUTES +// ========================================================= + app.get("/docs", (req, res) => { res.sendFile(__dirname + "/views/docs.html"); }); +// --- COHORT ROUTES --- + +// GET /api/cohorts - Retrieve all cohorts +app.get("/api/cohorts", async (req, res, next) => { + try { + const cohorts = await Cohort.find(); + res.status(200).json(cohorts); + } catch (error) { + error.status = 500; + error.message = "Failed to retrieve cohorts"; + next(error); + } +}); + +// POST /api/cohorts - Create a new cohort +app.post("/api/cohorts", async (req, res, next) => { + try { + const newCohort = await Cohort.create(req.body); + res.status(201).json(newCohort); + } catch (error) { + error.status = 500; + error.message = "Failed to create cohorts"; + next(error); + } +}); + +// GET /api/cohorts/:cohortId - Retrieve a specific cohort by id +app.get("/api/cohorts/:cohortId", async (req, res, next) => { + try { + const cohort = await Cohort.findById(req.params.cohortId); + if (!cohort) { + const error = new Error("Cohort not found"); + error.status = 404; + return next(error); + + } + res.status(200).json(updatedCohort); + } catch (error) { + error.status = 500; + error.message = "Failed to retrieve cohort"; + next(error); + } + }); + +// PUT /api/cohorts/:cohortId - Update a specific cohort by id +app.put("/api/cohorts/:cohortId", async (req, res, next) => { + try { + const updatedCohort = await Cohort.findByIdAndUpdate(req.params.cohortId, req.body, { new: true }); + if (!updatedCohort) { + const error = new Error("Cohort not found"); + error.status = 404; + return next(error); + + } + res.status(200).json(updatedCohort); + } catch (error) { + error.status = 500; + error.message = "Failed to update cohort"; + next(error); + } +}); + +// DELETE /api/cohorts/:cohortId - Delete a specific cohort by id +app.delete("/api/cohorts/:cohortId", async (req, res, next) => { + try { + const deletedCohort = await Cohort.findByIdAndDelete(req.params.cohortId); + if (!deletedCohort) { + const error = new Error("Cohort not found"); + error.status = 404; + return next(error); + + } + res.status(204).send(); + } catch (error) { + error.status = 500; + error.messgae ="Failed to delete cohort"; + next(error); + + } +}); + + +// --- STUDENT ROUTES --- + +// GET /api/students - Retrieve all students +app.get("/api/students", async (req, res, next) => { + try { + const students = await Student.find().populate("cohort"); + res.status(200).json(students); + } catch (error) { + error.status = 500; + error.message = "Failed to retrieve students"; + next(error); + } +}); + +// POST /api/students - Create a new student +app.post("/api/students", async (req, res, next) => { + try { + const newStudent = await Student.create(req.body); + res.status(201).json(newStudent); + } catch (error) { + error.status = 500; + error.message = "Failed to create student"; + next(error); + } +}); + +// GET /api/students/cohort/:cohortId - Retrieve all students for a given cohort +app.get("/api/students/cohort/:cohortId", async (req, res, next) => { + try { + const students = await Student.find({ cohort: req.params.cohortId }).populate("cohort"); + res.status(200).json(students); + } catch (error) { + error.status = 500; + error.message = "Failed to retrieve students for the cohort"; + next(error); + } +}); + +// GET /api/students/:studentId - Retrieve a specific student by id +app.get("/api/students/:studentId", async (req, res, next) => { + try { + const student = await Student.findById(req.params.studentId).populate("cohort"); + if (!student) {const error = new Error ("Student not found"); + error.status = 404; + return next(error); + } + + res.status(200).json(student); + + } catch (error) { + error.status = 500; + error.message = "Failed to updatestudent"; + next(error); + } +}); + +// PUT /api/students/:studentId - Update a specific student by id +app.put("/api/students/:studentId", async (req, res, next) => { + try { + const updatedStudent = await Student.findByIdAndUpdate(req.params.studentId, req.body, { new: true }); + if (!updatedStudent) { + const error = new Error ("Student not found"); + error.status = 404; + return next(error); + } + + res.status(200).json(updatedStudent); + + } catch (error) { + error.status = 500; + error.message = "Failed to delete student"; + next(error); + } +}); + +// DELETE /api/students/:studentId - Delete a specific student by id +app.delete("/api/students/:studentId", async (req, res, next) => { + try { + const deletedStudent = await Student.findByIdAndDelete(req.params.studentId); + if (!deletedStudent) { + const error = new Error("Student not found"); + error.status = 404; + return next(error); + } + res.status(204).send(); + + } catch (error) { + error.status = 500; + error.message = "Failed to delete student"; + next(error); + } +}); + +// ========================================================= +// ERROR HANDLING +// ========================================================= + //404 + + app.use((req, res) => { + res.status(404).json ({ + message: "Route not found" + }); + + app.use((error, req, res, next) => { + console.error(error); + res.status(500).json({ message: "Internal server error" }); + }); + +}); + + + +// ========================================================= // START SERVER +// ========================================================= app.listen(PORT, () => { console.log(`Server listening on port ${PORT}`); }); \ No newline at end of file diff --git a/server/models/Cohort.model.js b/server/models/Cohort.model.js new file mode 100644 index 00000000..7d34e019 --- /dev/null +++ b/server/models/Cohort.model.js @@ -0,0 +1,18 @@ +const mongoose = require("mongoose"); +const Schema = mongoose.Schema; + +const cohortSchema = new Schema({ + cohortSlug: { type: String, required: true, unique: true }, + cohortName: { type: String, required: true }, + program: { type: String, enum: ["Web Dev", "UX/UI", "Data Analytics", "Cybersecurity"] }, + format: { type: String, enum: ["Full Time", "Part Time"] }, + campus: { type: String }, + startDate: { type: Date, default: Date.now }, + endDate: { type: Date }, + inProgress: { type: Boolean, default: false }, + programManager: { type: String, required: true }, + leadTeacher: { type: String, required: true }, + totalHours: { type: Number, default: 360 } +}); + +module.exports = mongoose.model("Cohort", cohortSchema); \ No newline at end of file diff --git a/server/models/Student.model.js b/server/models/Student.model.js new file mode 100644 index 00000000..c856dc66 --- /dev/null +++ b/server/models/Student.model.js @@ -0,0 +1,19 @@ +const mongoose = require("mongoose"); +const Schema = mongoose.Schema; + +const studentSchema = new Schema({ + firstName: { type: String, required: true }, + lastName: { type: String, required: true }, + email: { type: String, required: true, unique: true }, + phone: { type: String, required: true }, + linkedinUrl: { type: String, default: "" }, + languages: { type: [String], enum: ["English", "Spanish", "French", "German", "Portuguese", "Dutch", "Other"] }, + program: { type: String, enum: ["Web Dev", "UX/UI", "Data Analytics", "Cybersecurity"] }, + background: { type: String, default: "" }, + image: { type: String, default: "https://i.imgur.com/r8bo8u7.png" }, + // This establishes the relationship between a student and a cohort + cohort: { type: Schema.Types.ObjectId, ref: "Cohort" }, + projects: { type: Array } +}); + +module.exports = mongoose.model("Student", studentSchema); \ No newline at end of file diff --git a/server/package-lock.json b/server/package-lock.json index 23e2f78a..2f3fd24a 100644 --- a/server/package-lock.json +++ b/server/package-lock.json @@ -10,6 +10,7 @@ "license": "ISC", "dependencies": { "cookie-parser": "^1.4.6", + "cors": "^2.8.6", "express": "^4.18.2", "morgan": "^1.10.0", "nodemon": "^3.0.1" @@ -216,6 +217,23 @@ "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", "integrity": "sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==" }, + "node_modules/cors": { + "version": "2.8.6", + "resolved": "https://registry.npmjs.org/cors/-/cors-2.8.6.tgz", + "integrity": "sha512-tJtZBBHA6vjIAaF6EnIaq6laBBP9aq/Y3ouVJjEfoHbRBcHBAHYcMh/w8LDrk2PvIMMq8gmopa5D4V8RmbrxGw==", + "license": "MIT", + "dependencies": { + "object-assign": "^4", + "vary": "^1" + }, + "engines": { + "node": ">= 0.10" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, "node_modules/debug": { "version": "2.6.9", "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", @@ -700,6 +718,15 @@ "node": ">=0.10.0" } }, + "node_modules/object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/object-inspect": { "version": "1.12.3", "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.12.3.tgz", diff --git a/server/package.json b/server/package.json index 7aa0847c..a00539d1 100644 --- a/server/package.json +++ b/server/package.json @@ -4,8 +4,9 @@ "description": "ExpressJS API for the Cohort Tools app", "main": "app.js", "scripts": { - "dev": "nodemon app.js", - "test": "echo \"Error: no test specified\" && exit 1" + "start": "node app.js", + "dev": "nodemon app.js", + "test": "echo \"Error: no test specified\" && exit 1" }, "repository": { "type": "git", @@ -19,6 +20,7 @@ "homepage": "https://github.com/ironhack-labs/cohort-tools-project#readme", "dependencies": { "cookie-parser": "^1.4.6", + "cors": "^2.8.6", "express": "^4.18.2", "morgan": "^1.10.0", "nodemon": "^3.0.1"