diff --git a/mailing-list-api/mailing-lists.js b/mailing-list-api/mailing-lists.js deleted file mode 100644 index bc7c08a..0000000 --- a/mailing-list-api/mailing-lists.js +++ /dev/null @@ -1,8 +0,0 @@ -module.exports = { - staff: ["talea@techtonica.org", "michelle@techtonica.org"], - "cohort-h1-2020": [ - "ali@techtonica.org", - "humail@techtonica.org", - "khadar@techtonica.org", - ], -}; diff --git a/mailing-list-api/package.json b/mailing-list-api/package.json index 8b70aaa..29d0ef3 100644 --- a/mailing-list-api/package.json +++ b/mailing-list-api/package.json @@ -1,9 +1,11 @@ { "name": "@module-node/mailing-list-api", + "type": "module", "version": "1.0.0", "license": "CC-BY-SA-4.0", "description": "You must update this package", "scripts": { + "start": "nodemon src/server.js", "test": "jest" }, "repository": { @@ -15,6 +17,11 @@ }, "homepage": "https://github.com/CodeYourFuture/CYF-Coursework-Template#readme", "devDependencies": { - "jest": "^26.6.3" + "jest": "^26.6.3", + "nodemon": "^3.1.0" + }, + "dependencies": { + "dotenv": "^16.4.5", + "express": "^4.19.2" } } diff --git a/mailing-list-api/src/app.js b/mailing-list-api/src/app.js new file mode 100644 index 0000000..4121ba4 --- /dev/null +++ b/mailing-list-api/src/app.js @@ -0,0 +1,19 @@ +import express from "express"; +import cors from "cors"; +import readFileMiddleware from "./middlewares/readFileMiddleware.js"; +import { resolveFilePath } from "./utils/resolveFilePath.js"; +import allListRouter from "./routers/allListRoute.js"; + +const app = express(); + +app.use(express.json()); +app.use(cors()); + +app.get("/", (req, res) => { + res.send( + 'please go to \n/list ---> for all group names \n/list/:name ---> for mail list of a group of members\nyou can delete or update a group as well \n(please provide group name and members for update method as below)\n ---> {"name": "group-name","members": ["me@me.com", ...]}' + ); +}); +app.use("/lists", readFileMiddleware(resolveFilePath()), allListRouter); + +export { app }; diff --git a/mailing-list-api/src/data/mailing-lists.json b/mailing-list-api/src/data/mailing-lists.json new file mode 100644 index 0000000..c46e6c4 --- /dev/null +++ b/mailing-list-api/src/data/mailing-lists.json @@ -0,0 +1,17 @@ +{ + "staff": [ + "talea@techtonica.org", + "michelle@techtonica.org" + ], + "cohort-h1-2020": [ + "ali@techtonica.org", + "humail@techtonica.org", + "khadar@techtonica.org" + ], + "my-new-list": [ + "me@me.com.uk" + ], + "test1": [ + "me@me.com" + ] +} \ No newline at end of file diff --git a/mailing-list-api/src/middlewares/readFileMiddleware.js b/mailing-list-api/src/middlewares/readFileMiddleware.js new file mode 100644 index 0000000..1ab315a --- /dev/null +++ b/mailing-list-api/src/middlewares/readFileMiddleware.js @@ -0,0 +1,14 @@ +import fs from "fs"; + +const readFileMiddleware = (absoluteFilePath) => (req, res, next) => { + fs.readFile(absoluteFilePath, "utf8", (err, data) => { + if (err) { + return next(err); + } + const lists = new Map(Object.entries(JSON.parse(data))); + req.fileContent = lists; + next(); + }); +}; + +export default readFileMiddleware; diff --git a/mailing-list-api/src/routers/allListRoute.js b/mailing-list-api/src/routers/allListRoute.js new file mode 100644 index 0000000..13b69e6 --- /dev/null +++ b/mailing-list-api/src/routers/allListRoute.js @@ -0,0 +1,62 @@ +import { Router } from "express"; +import { resolveFilePath } from "../utils/resolveFilePath.js"; +import writeFile from "../utils/writeFile.js"; + +const router = Router(); + +router.get("/", (req, res) => { + const lists = req.fileContent; + const keys = Array.from(lists.keys()); + + if (lists.size === 0) { + res.status(200).json([]); + } + + res.status(200).json(keys); +}); + +router.get("/:name", (req, res) => { + const lists = req.fileContent; + const requestedName = req.params.name; + const requestedValue = lists.get(requestedName); + + if (!requestedValue) { + res.status(404).send("no such member"); + } + + res.status(200).json({ [requestedName]: requestedValue }); +}); + +router.delete("/:name", (req, res) => { + const lists = req.fileContent; + const deletingName = req.params.name; + const isDeleted = lists.delete(deletingName); + + if (!isDeleted) { + res.status(404).send("no such member"); + } + + const newObjectForWriting = Object.fromEntries(lists); + writeFile(newObjectForWriting, resolveFilePath()); + + res.status(200).send("member deleted"); +}); + +router.put("/:name", (req, res) => { + const lists = req.fileContent; + const newName = req.body.name; + const newMembers = req.body.members; + lists.set(newName, newMembers); + + if (!newName.trim() || !newMembers || !Array.isArray(newMembers) || !(newMembers.length > 0)) { + res.status(404).send("can not add member"); + } + + const newObjectForWriting = Object.fromEntries(lists); + + writeFile(newObjectForWriting, resolveFilePath()); + + res.status(200).send("new member added"); +}); + +export default router; diff --git a/mailing-list-api/src/server.js b/mailing-list-api/src/server.js new file mode 100644 index 0000000..370f4dd --- /dev/null +++ b/mailing-list-api/src/server.js @@ -0,0 +1,9 @@ +import { app } from "./app.js"; +import dotenv from "dotenv"; + +dotenv.config(); +const PORT = parseInt(process.env.PORT || "3000"); + +app.listen(PORT, () => { + console.log(`app is listening on port ${PORT}`); +}); diff --git a/mailing-list-api/src/utils/resolveFilePath.js b/mailing-list-api/src/utils/resolveFilePath.js new file mode 100644 index 0000000..68d99ef --- /dev/null +++ b/mailing-list-api/src/utils/resolveFilePath.js @@ -0,0 +1,8 @@ +import path from "path"; +import { fileURLToPath } from "url"; + +export const resolveFilePath = () => { + const relativeFilePath = "./data/mailing-lists.json"; + const __dirname = path.dirname(fileURLToPath(import.meta.url)); + return path.resolve(__dirname, "..", relativeFilePath); +}; diff --git a/mailing-list-api/src/utils/writeFile.js b/mailing-list-api/src/utils/writeFile.js new file mode 100644 index 0000000..f0c1f9f --- /dev/null +++ b/mailing-list-api/src/utils/writeFile.js @@ -0,0 +1,8 @@ +import fs from "fs"; + +const writeFile = (data, path) => { + const jsonData = JSON.stringify(data, null, 2); + fs.writeFileSync(path, jsonData); +}; + +export default writeFile; diff --git a/package-lock.json b/package-lock.json index a600e80..79a52bc 100644 --- a/package-lock.json +++ b/package-lock.json @@ -42,8 +42,13 @@ "name": "@module-node/mailing-list-api", "version": "1.0.0", "license": "CC-BY-SA-4.0", + "dependencies": { + "dotenv": "^16.4.5", + "express": "^4.19.2" + }, "devDependencies": { - "jest": "^26.6.3" + "jest": "^26.6.3", + "nodemon": "^3.1.0" } }, "node_modules/@ampproject/remapping": { @@ -1567,12 +1572,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", @@ -1580,7 +1585,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" }, @@ -1692,13 +1697,18 @@ } }, "node_modules/call-bind": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.5.tgz", - "integrity": "sha512-C3nQxfFZxFRVoJoGKKI8y3MOEo129NQ+FgQ08iye+Mk4zNZZGdjfs06bVTr+DBSlA66Q2VEcMki/cUCP4SercQ==", + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.7.tgz", + "integrity": "sha512-GHTSNSYICQ7scH7sZ+M2rFopRoLh8t2bLSW6BbgrtLsahOIB5iyAVJf9GjWK3cYTDaMj4XdBpM1cA6pIS0Kv2w==", "dependencies": { + "es-define-property": "^1.0.0", + "es-errors": "^1.3.0", "function-bind": "^1.1.2", - "get-intrinsic": "^1.2.1", - "set-function-length": "^1.1.1" + "get-intrinsic": "^1.2.4", + "set-function-length": "^1.2.1" + }, + "engines": { + "node": ">= 0.4" }, "funding": { "url": "https://github.com/sponsors/ljharb" @@ -1965,9 +1975,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" } @@ -2092,16 +2102,19 @@ } }, "node_modules/define-data-property": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.1.tgz", - "integrity": "sha512-E7uGkTzkk1d0ByLeSc6ZsFS79Axg+m1P/VsgYsxHgiuc3tFSj+MjMIwe90FC4lOAZzNBdY7kkO2P2wKdsQ1vgQ==", + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz", + "integrity": "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==", "dependencies": { - "get-intrinsic": "^1.2.1", - "gopd": "^1.0.1", - "has-property-descriptors": "^1.0.0" + "es-define-property": "^1.0.0", + "es-errors": "^1.3.0", + "gopd": "^1.0.1" }, "engines": { "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, "node_modules/define-property": { @@ -2183,6 +2196,17 @@ "node": ">=8" } }, + "node_modules/dotenv": { + "version": "16.4.5", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.4.5.tgz", + "integrity": "sha512-ZmdL2rui+eB2YwhsWzjInR8LldtZHGDoQ1ugH85ppHKwpUHL7j7rN0Ti9NCnGiQbhaZ11FpR+7ao1dNsmduNUg==", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://dotenvx.com" + } + }, "node_modules/ee-first": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", @@ -2252,6 +2276,25 @@ "is-arrayish": "^0.2.1" } }, + "node_modules/es-define-property": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.0.tgz", + "integrity": "sha512-jxayLKShrEqqzJ0eumQbVhTYQM27CfT1T35+gCgDFoL82JLsXqTJ76zv6A0YLOgEnLUMvLzsDsGIrl8NFpT2gQ==", + "dependencies": { + "get-intrinsic": "^1.2.4" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-errors": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", + "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", + "engines": { + "node": ">= 0.4" + } + }, "node_modules/escalade": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", @@ -2455,16 +2498,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", @@ -2742,15 +2785,19 @@ } }, "node_modules/get-intrinsic": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.2.tgz", - "integrity": "sha512-0gSo4ml/0j98Y3lngkFEot/zhiCeWsbYIlZ+uZOVgzLyLaUw7wxUL+nCTP0XJvJg1AXulJRI3UJi8GsbDuxdGA==", + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.4.tgz", + "integrity": "sha512-5uYhsJH8VJBTv7oslg4BznJYhDoRI6waYCxMmCdnTrcCrHA/fCFKoTFz2JKKE0HdDFUF7/oQuhzumXJK7paBRQ==", "dependencies": { + "es-errors": "^1.3.0", "function-bind": "^1.1.2", "has-proto": "^1.0.1", "has-symbols": "^1.0.3", "hasown": "^2.0.0" }, + "engines": { + "node": ">= 0.4" + }, "funding": { "url": "https://github.com/sponsors/ljharb" } @@ -2862,20 +2909,20 @@ } }, "node_modules/has-property-descriptors": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.1.tgz", - "integrity": "sha512-VsX8eaIewvas0xnvinAe9bw4WfIeODpGYikiWYLH+dma0Jw6KHYqWiWfhQlgOVK8D6PvjubK5Uc4P0iIhIcNVg==", + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz", + "integrity": "sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==", "dependencies": { - "get-intrinsic": "^1.2.2" + "es-define-property": "^1.0.0" }, "funding": { "url": "https://github.com/sponsors/ljharb" } }, "node_modules/has-proto": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.0.1.tgz", - "integrity": "sha512-7qE+iP+O+bgF9clE5+UoBFzE65mlBiVj3tKCrlNQ0Ogwm0BjpT/gK4SlLYDMybDh5I3TCTKnPPa0oMG7JDYrhg==", + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.0.3.tgz", + "integrity": "sha512-SJ1amZAJUiZS+PhsVLf5tGydlaVB8EdFpaSO4gmiUKUOxk8qzn5AIy4ZeJUmh22znIdk/uMAUT2pl3FxzVUH+Q==", "engines": { "node": ">= 0.4" }, @@ -4539,9 +4586,9 @@ "dev": true }, "node_modules/nodemon": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/nodemon/-/nodemon-3.0.2.tgz", - "integrity": "sha512-9qIN2LNTrEzpOPBaWHTm4Asy1LxXLSickZStAQ4IZe7zsoIpD/A7LWxhZV3t4Zu352uBcqVnRsDXSMR2Sc3lTA==", + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/nodemon/-/nodemon-3.1.0.tgz", + "integrity": "sha512-xqlktYlDMCepBJd43ZQhjWwMw2obW/JRvkrLxq5RCNcuDDX1DbcPT+qT1IlIIdf+DhnWs90JpTMe+Y5KxOchvA==", "dev": true, "dependencies": { "chokidar": "^3.5.2", @@ -5116,9 +5163,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", @@ -5730,14 +5777,16 @@ "dev": true }, "node_modules/set-function-length": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.1.1.tgz", - "integrity": "sha512-VoaqjbBJKiWtg4yRcKBQ7g7wnGnLV3M8oLvVWwOk2PdYY6PEFegR1vezXR0tw6fZGF9csVakIRjrJiy2veSBFQ==", + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.2.tgz", + "integrity": "sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==", "dependencies": { - "define-data-property": "^1.1.1", - "get-intrinsic": "^1.2.1", + "define-data-property": "^1.1.4", + "es-errors": "^1.3.0", + "function-bind": "^1.1.2", + "get-intrinsic": "^1.2.4", "gopd": "^1.0.1", - "has-property-descriptors": "^1.0.0" + "has-property-descriptors": "^1.0.2" }, "engines": { "node": ">= 0.4" @@ -5813,13 +5862,17 @@ "optional": true }, "node_modules/side-channel": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz", - "integrity": "sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==", + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.6.tgz", + "integrity": "sha512-fDW/EZ6Q9RiO8eFG8Hj+7u/oW+XrPTIChwCOM2+th2A6OblDtYYIpve9m+KvI9Z4C9qSEXlaGR6bTEYHReuglA==", "dependencies": { - "call-bind": "^1.0.0", - "get-intrinsic": "^1.0.2", - "object-inspect": "^1.9.0" + "call-bind": "^1.0.7", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.4", + "object-inspect": "^1.13.1" + }, + "engines": { + "node": ">= 0.4" }, "funding": { "url": "https://github.com/sponsors/ljharb"