diff --git a/package-lock.json b/package-lock.json index 3fb12fd..fe39c38 100644 --- a/package-lock.json +++ b/package-lock.json @@ -10,7 +10,7 @@ "license": "ISC", "dependencies": { "cors": "^2.8.5", - "express": "^4.18.2", + "express": "^4.19.2", "morgan": "^1.10.0", "nodemon": "^3.0.1" }, @@ -1410,12 +1410,12 @@ } }, "node_modules/body-parser": { - "version": "1.20.1", - "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.1.tgz", - "integrity": "sha512-jWi7abTbYwajOytWCQc37VulmWiRae5RyTpaCyDcS5/lMdtwSz5lOpDE67srw/HYe35f1z3fDQw+3txg7gNtWw==", + "version": "1.20.2", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.2.tgz", + "integrity": "sha512-ml9pReCu3M61kGlqoTm2umSXTlRTuGTx0bfYj+uIUKKYycG5NtSbeetV3faSU6R7ajOPw0g/J1PvK4qNy7s5bA==", "dependencies": { "bytes": "3.1.2", - "content-type": "~1.0.4", + "content-type": "~1.0.5", "debug": "2.6.9", "depd": "2.0.0", "destroy": "1.2.0", @@ -1423,7 +1423,7 @@ "iconv-lite": "0.4.24", "on-finished": "2.4.1", "qs": "6.11.0", - "raw-body": "2.5.1", + "raw-body": "2.5.2", "type-is": "~1.6.18", "unpipe": "1.0.0" }, @@ -1730,9 +1730,9 @@ "dev": true }, "node_modules/cookie": { - "version": "0.5.0", - "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.5.0.tgz", - "integrity": "sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw==", + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.6.0.tgz", + "integrity": "sha512-U71cyTamuh1CRNCfpGY6to28lxvNwPG4Guz/EVjgf3Jmzv0vlDp1atT9eS5dDjMYHucpHbWns6Lwf3BKz6svdw==", "engines": { "node": ">= 0.6" } @@ -2032,16 +2032,16 @@ } }, "node_modules/express": { - "version": "4.18.2", - "resolved": "https://registry.npmjs.org/express/-/express-4.18.2.tgz", - "integrity": "sha512-5/PsL6iGPdfQ/lKM1UuielYgv3BUoJfz1aUwU9vHZ+J7gyvwdQXFEBIEIaxeGf0GIcreATNyBExtalisDbuMqQ==", + "version": "4.19.2", + "resolved": "https://registry.npmjs.org/express/-/express-4.19.2.tgz", + "integrity": "sha512-5T6nhjsT+EOMzuck8JjBHARTHfMht0POzlA60WV2pMD3gyXw2LZnZ+ueGdNxG+0calOJcWKbpFcuzLZ91YWq9Q==", "dependencies": { "accepts": "~1.3.8", "array-flatten": "1.1.1", - "body-parser": "1.20.1", + "body-parser": "1.20.2", "content-disposition": "0.5.4", "content-type": "~1.0.4", - "cookie": "0.5.0", + "cookie": "0.6.0", "cookie-signature": "1.0.6", "debug": "2.6.9", "depd": "2.0.0", @@ -4013,9 +4013,9 @@ } }, "node_modules/raw-body": { - "version": "2.5.1", - "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.1.tgz", - "integrity": "sha512-qqJBtEyVgS0ZmPGdCFPWJ3FreoqvG4MVQln/kCgF7Olq95IbOp0/BWyMwbdtn4VTvkM8Y7khCQ2Xgk/tcrCXig==", + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.2.tgz", + "integrity": "sha512-8zGqypfENjCIqGhgXToC8aB2r7YrBX+AQAfIPs/Mlk+BtPTztOvTS01NRW/3Eh60J+a48lt8qsCzirQ6loCVfA==", "dependencies": { "bytes": "3.1.2", "http-errors": "2.0.0", diff --git a/package.json b/package.json index ec6510a..d8aab1f 100644 --- a/package.json +++ b/package.json @@ -20,7 +20,7 @@ "homepage": "https://github.com/boolean-uk/api-address-book#readme", "dependencies": { "cors": "^2.8.5", - "express": "^4.18.2", + "express": "^4.19.2", "morgan": "^1.10.0", "nodemon": "^3.0.1" }, diff --git a/request-response-cycle.png b/request-response-cycle.png new file mode 100644 index 0000000..d91f3f0 Binary files /dev/null and b/request-response-cycle.png differ diff --git a/src/controllers/contacts/contactControllers.js b/src/controllers/contacts/contactControllers.js new file mode 100644 index 0000000..851f73e --- /dev/null +++ b/src/controllers/contacts/contactControllers.js @@ -0,0 +1,127 @@ +const contacts = require("../../../data/contacts.js") +const meetings = require("../../../data/meetings.js") +const { + verifyContactBody, + findContactById, + findContactMeetings, + deleteContactMeetings, + removeContact, + foundContactId, + replaceUpdatedContact, + verifyResultFound, + verifyMeetingLength, + verifyMeetingBody, + pushData +} = require("../../domain/contacts/contactRepository.js") + +const getAllContacts = (req, res) => { + res.json({ + contacts: contacts, + }) +} + +const createContact = (req, res) => { + const contact = req.body + + verifyContactBody(contact, res) + + const contactId = { id: contacts.length + 1, ...contact } + + pushData(contacts, contactId) + + res.status(201).json({ + contact: contactId, + }) +} + +const getContactById = (req, res) => { + const id = Number(req.params.id) + const result = findContactById(contacts, id) + + verifyResultFound(result, res, id) + + res.json({ + contact: result, + }) +} + +const deleteContact = (req, res) => { + const id = Number(req.params.id) + const result = findContactById(contacts, id) + const contactMeetings = findContactMeetings(meetings, result) + + verifyResultFound(result, res, id) + + deleteContactMeetings(contactMeetings, meetings, result) + + removeContact(contacts, result) + + res.json({ + contact: result, + }) +} + +const updateContact = (req, res) => { + const contact = req.body + const id = Number(req.params.id) + const result = foundContactId(contacts, id) + + verifyResultFound(result, res, id) + + verifyContactBody(contact, res) + + const contactId = { id: result.id, ...contact } + + replaceUpdatedContact(contacts, result, contactId) + + res.json({ + contact: contactId, + }) +} + +const getContactMeetings = (req, res) => { + const id = Number(req.params.id) + const contact = foundContactId(contacts, id) + + verifyResultFound(contact, res, id) + + const meeting = findContactMeetings(meetings, contact) + + verifyMeetingLength(meeting, res, contact) + + res.json({ + meetings: meeting, + }) +} + +const createContactMeeting = (req, res) => { + const id = Number(req.params.id) + const contact = foundContactId(contacts, id) + const meeting = req.body + + verifyResultFound(contact, res, id) + + verifyMeetingBody(meeting, res) + + const meetingIDs = { + ...meeting, + contactId: contact.id, + id: meetings.length + 1, + } + + pushData(meetings, meetingIDs) + + res.status(201).json({ + meeting: meetingIDs, + }) +} + +module.exports = { + getAllContacts, + createContact, + getContactById, + deleteContact, + updateContact, + getContactMeetings, + createContactMeeting +} diff --git a/src/controllers/meetings/meetingControllers.js b/src/controllers/meetings/meetingControllers.js new file mode 100644 index 0000000..a7e99ca --- /dev/null +++ b/src/controllers/meetings/meetingControllers.js @@ -0,0 +1,67 @@ +const meetings = require("../../../data/meetings.js") +const { verifyMeetingBody } = require("../../domain/contacts/contactRepository.js") +const { + foundMeetings, + verifyMeetingFound, + removeMeeting, + replaceUpdatedMeeting +} = require("../../domain/meetings/meetingRepository.js") + +const getAllMeetings = (req, res) => { + res.json({ + meetings: meetings, + }) +} + +const getMeetingById = (req, res) => { + const id = Number(req.params.id) + const result = foundMeetings(meetings, id) + + verifyMeetingFound(result, res, id) + + res.json({ + meeting: result, + }) +} + +const deleteMeeting = (req, res) => { + const id = Number(req.params.id) + const result = foundMeetings(meetings, id) + + verifyMeetingFound(result, res, id) + + removeMeeting(meetings, result) + + res.json({ + meeting: result, + }) +} + +const updateMeeting = (req, res) => { + const id = Number(req.params.id) + const result = foundMeetings(meetings, id) + const meeting = req.body + + verifyMeetingFound(result, res, id) + + verifyMeetingBody(meeting, res) + + const meetingIDs = { + ...meeting, + contactId: result.contactId, + id: result.id, + } + + replaceUpdatedMeeting(meetings, result, meetingIDs) + + res.json({ + meeting: meetingIDs, + }) +} + +module.exports = { + getAllMeetings, + getMeetingById, + deleteMeeting, + updateMeeting +} diff --git a/src/domain/contacts/contactRepository.js b/src/domain/contacts/contactRepository.js new file mode 100644 index 0000000..403f061 --- /dev/null +++ b/src/domain/contacts/contactRepository.js @@ -0,0 +1,119 @@ +const contactObj = [ + "firstName", + "lastName", + "street", + "city", + "type", + "email", + "linkedin", + "twitter" +] +const meetingsObj = ["name"] + +const verifyContactBody = (contact, res) => { + for (const prop of contactObj) { + if (contact[prop] === undefined) { + return res.status(400).send({ + message: `${prop} is missing.`, + }) + } + } + + for (const prop in contact) { + if (typeof contact[prop] !== "string") { + return res.status(400).send({ + message: `${prop} needs to be a string.`, + }) + } + + if (!contactObj.includes(prop)) { + return res.status(400).send({ + message: `${prop} isn't a contact property.`, + }) + } + } +} + +const pushData = (where, what) => { + return where.push(what) +} + +const findContactById = (contacts, id) => { + return contacts.find((contact) => contact.id === id) +} + +const findContactMeetings = (meetings, result) => { + return meetings.filter((m) => m.contactId === result.id) +} + +const deleteContactMeetings = (contactMeetings, meetings) => { + return contactMeetings.forEach((meeting) => { + meetings.splice(meetings.indexOf(meeting), 1) + }) +} + +const removeContact = (contacts, result) => { + return contacts.splice(contacts.indexOf(result), 1) +} + +const foundContactId = (contacts, id) => { + return contacts.find((c) => c.id === id) +} + +const replaceUpdatedContact = (contacts, result, contactId) => { + return contacts.splice(contacts.indexOf(result), 1, contactId) +} + +const verifyResultFound = (result, res, id) => { + if (!result) { + return res.status(404).send({ + message: `contact with id of ${id} not found.`, + }) + } +} + +const verifyMeetingLength = (meeting, res, contact) => { + if (meeting.length === 0) { + return res.status(404).send({ + message: `${contact.firstName} ${contact.lastName} didn't have any meeting.`, + }) + } +} + +const verifyMeetingBody = (meeting, res) => { + for (const prop of meetingsObj) { + if (meeting[prop] === undefined) { + return res.status(404).json({ + message: `${prop} is missing`, + }) + } + } + + for (const prop in meeting) { + if (typeof meeting[prop] !== "string") { + return res.status(404).json({ + message: `${prop} needs to be a string`, + }) + } + + if (!meetingsObj.includes(prop)) { + return res.status(404).json({ + message: `${prop} isn't a meeting property`, + }) + } + } +} + +module.exports = { + verifyContactBody, + pushData, + findContactById, + findContactMeetings, + deleteContactMeetings, + removeContact, + foundContactId, + replaceUpdatedContact, + verifyResultFound, + verifyMeetingLength, + verifyMeetingBody +} diff --git a/src/domain/meetings/meetingRepository.js b/src/domain/meetings/meetingRepository.js new file mode 100644 index 0000000..e932f21 --- /dev/null +++ b/src/domain/meetings/meetingRepository.js @@ -0,0 +1,26 @@ +const foundMeetings = (meetings, id) => { + return meetings.find((meeting) => meeting.id === id) +} + +const verifyMeetingFound = (result, res, id) => { + if (!result) { + return res.status(404).send({ + message: `meeting with id of ${id} not found.`, + }) + } +} + +const removeMeeting = (meetings, result) => { + return meetings.splice(meetings.indexOf(result), 1) +} + +const replaceUpdatedMeeting = (meetings, result, meetingIDs) => { + return meetings.splice(meetings.indexOf(result), 1, meetingIDs) +} + +module.exports = { + foundMeetings, + verifyMeetingFound, + removeMeeting, + replaceUpdatedMeeting +} diff --git a/src/index.js b/src/index.js index 8c18618..620a89a 100644 --- a/src/index.js +++ b/src/index.js @@ -1,6 +1,6 @@ -const app = require('./server.js') +const app = require("./server.js") const port = 3030 app.listen(port, () => { - console.log(`Server is running on http://localhost:${port}/`) + console.log(`Server is running on http://localhost:${port}/`) }) diff --git a/src/routes/contacts/contactRouter.js b/src/routes/contacts/contactRouter.js new file mode 100644 index 0000000..7a3077b --- /dev/null +++ b/src/routes/contacts/contactRouter.js @@ -0,0 +1,28 @@ +const express = require("express") +const router = express.Router() + +const { + getAllContacts, + createContact, + getContactById, + deleteContact, + updateContact, + getContactMeetings, + createContactMeeting +} = require("../../controllers/contacts/contactControllers") + +router.get("/", getAllContacts) + +router.post("/", createContact) + +router.get("/:id", getContactById) + +router.delete("/:id", deleteContact) + +router.put("/:id", updateContact) + +router.get("/:id/meetings", getContactMeetings) + +router.post("/:id/meetings", createContactMeeting) + +module.exports = router diff --git a/src/routes/meetings/meetingRouter.js b/src/routes/meetings/meetingRouter.js new file mode 100644 index 0000000..b1e7681 --- /dev/null +++ b/src/routes/meetings/meetingRouter.js @@ -0,0 +1,19 @@ +const express = require("express") +const router = express.Router() + +const { + getAllMeetings, + getMeetingById, + deleteMeeting, + updateMeeting +} = require("../../controllers/meetings/meetingControllers") + +router.get("/", getAllMeetings) + +router.get("/:id", getMeetingById) + +router.delete("/:id", deleteMeeting) + +router.put("/:id", updateMeeting) + +module.exports = router diff --git a/src/server.js b/src/server.js index 30f0ce3..3714a40 100644 --- a/src/server.js +++ b/src/server.js @@ -3,11 +3,14 @@ const morgan = require("morgan") const cors = require("cors") const app = express() +const contactRouter = require("../src/routes/contacts/contactRouter.js") +const meetingRouter = require("../src/routes/meetings/meetingRouter.js") + app.use(morgan("dev")) app.use(cors()) app.use(express.json()) -// write your app code here - +app.use("/contacts", contactRouter) +app.use("/meetings", meetingRouter) module.exports = app