From 9131065e51b9087a4f13afa1a83f0b1e639fcdc7 Mon Sep 17 00:00:00 2001 From: Kara Barleta Date: Sat, 7 Mar 2020 16:51:51 -0500 Subject: [PATCH 1/3] Add GET, POST, PUT, DELETE endpoints --- db/groups.data.json | 1 + package-lock.json | 41 +++++++++++++----- src/api/index.js | 3 ++ src/index.js | 20 ++++++++- src/middleware/error-handler.js | 4 +- src/routes/groups/groups.controller.js | 60 ++++++++++++++++++++++++++ src/routes/groups/groups.router.js | 16 +++++++ 7 files changed, 132 insertions(+), 13 deletions(-) create mode 100644 db/groups.data.json create mode 100644 src/routes/groups/groups.controller.js create mode 100644 src/routes/groups/groups.router.js diff --git a/db/groups.data.json b/db/groups.data.json new file mode 100644 index 0000000..b19c35d --- /dev/null +++ b/db/groups.data.json @@ -0,0 +1 @@ +{"groups":[{"id":1,"name":"Kemmer, Osinski and O'Keefe","description":"A group that cares about Table","location":"East Serenity, Anguilla"},{"id":2,"name":"Jast LLC","description":"A group that cares about Soap","location":"Zboncakton, Antarctica (the territory South of 60 deg S)"},{"id":3,"name":"Wunsch Inc","description":"A group that cares about Chips","location":"Fredside, Somalia"},{"id":4,"name":"Rohan - Kirlin","description":"A group that cares about Table","location":"Langview, Grenada"},{"id":5,"name":"Klein, Beahan and Skiles","description":"A group that cares about Car","location":"East Kareemside, India"},{"id":6,"name":"Powlowski, Grimes and Hermiston","description":"A group that cares about Table","location":"North Albinstad, British Indian Ocean Territory (Chagos Archipelago)"},{"id":7,"name":"Sporer - Wiegand","description":"A group that cares about Bacon","location":"Thielland, Georgia"},{"id":8,"name":"Berge Group","description":"A group that cares about Chicken","location":"New Aliviaborough, Finland"},{"id":9,"name":"Kautzer - Stiedemann","description":"A group that cares about Car","location":"Eliasborough, Nicaragua"},{"id":10,"name":"O'Connell - Leffler","description":"A group that cares about Car","location":"Volkmanfurt, Mauritania"},{"id":11,"name":"Cummerata Group","description":"A group that cares about Bacon","location":"Wunschfurt, Andorra"},{"id":12,"name":"Emmerich, Gottlieb and Pagac","description":"A group that cares about Pizza","location":"Aleenmouth, Saint Barthelemy"},{"id":13,"name":"Haag - Mitchell","description":"A group that cares about Chair","location":"Lake Gust, South Georgia and the South Sandwich Islands"},{"id":14,"name":"Barton - Aufderhar","description":"A group that cares about Mouse","location":"Lake Gailshire, Bahamas"},{"id":15,"name":"Wyman - Mueller","description":"A group that cares about Cheese","location":"Port Kaci, Netherlands Antilles"},{"id":16,"name":"McLaughlin, Champlin and VonRueden","description":"A group that cares about Hat","location":"Binsview, Papua New Guinea"},{"id":17,"name":"Bruen and Sons","description":"A group that cares about Sausages","location":"Howeside, Moldova"},{"id":18,"name":"Koch Group","description":"A group that cares about Pizza","location":"Lempifort, Northern Mariana Islands"},{"id":19,"name":"Gaylord - Weissnat","description":"A group that cares about Bacon","location":"West Torranceview, Guatemala"},{"id":20,"name":"Reichert Group","description":"A group that cares about Salad","location":"West Jacey, Luxembourg"},{"id":21,"name":"Ondricka, Weber and Corkery","description":"A group that cares about Chicken","location":"South Devantetown, Russian Federation"},{"id":22,"name":"Kihn, Pfannerstill and Stamm","description":"A group that cares about Pants","location":"Bartolettiport, Virgin Islands, British"},{"id":23,"name":"Kris - Auer","description":"A group that cares about Shoes","location":"Johnsmouth, Niger"},{"id":24,"name":"Wintheiser - Windler","description":"A group that cares about Pizza","location":"Swaniawskitown, Gambia"},{"id":25,"name":"Bergnaum - Mertz","description":"A group that cares about Fish","location":"New Maximo, Rwanda"},{"id":26,"name":"Nader - Metz","description":"A group that cares about Keyboard","location":"Sandrineville, Mayotte"},{"id":27,"name":"Schroeder, Mills and Breitenberg","description":"A group that cares about Soap","location":"North Lewchester, Colombia"},{"id":28,"name":"Schulist - Davis","description":"A group that cares about Gloves","location":"Adamsstad, Mexico"},{"id":29,"name":"Reichel - Little","description":"A group that cares about Salad","location":"North Eleonore, Uganda"},{"id":30,"name":"Green, Heathcote and Bogan","description":"A group that cares about Ball","location":"Schadenfort, Slovakia (Slovak Republic)"},{"id":31,"name":"Stehr and Sons","description":"A group that cares about Hat","location":"West Jamilstad, Martinique"},{"id":32,"name":"Hegmann Inc","description":"A group that cares about Hat","location":"Port Maggie, Cocos (Keeling) Islands"},{"id":33,"name":"Stoltenberg - Hackett","description":"A group that cares about Chips","location":"Titoburgh, United Kingdom"},{"id":34,"name":"Lind - Collier","description":"A group that cares about Pizza","location":"Lake Domenicaborough, Heard Island and McDonald Islands"},{"id":35,"name":"Breitenberg, Paucek and Schuppe","description":"A group that cares about Table","location":"Jenkinsfort, Niue"},{"id":36,"name":"Quigley - Pacocha","description":"A group that cares about Bike","location":"Daughertystad, Costa Rica"},{"id":37,"name":"Kessler Group","description":"A group that cares about Soap","location":"Jazlynshire, Nauru"},{"id":38,"name":"Morissette, Greenfelder and Steuber","description":"A group that cares about Ball","location":"New Bertbury, Montenegro"},{"id":39,"name":"Gibson Group","description":"A group that cares about Computer","location":"South Beverlyview, Holy See (Vatican City State)"},{"id":40,"name":"Schaefer, Buckridge and Hayes","description":"A group that cares about Chips","location":"Port Jaredton, Saint Martin"},{"id":41,"name":"Auer and Sons","description":"A group that cares about Salad","location":"South Dante, Liechtenstein"},{"id":42,"name":"Reilly - Lubowitz","description":"A group that cares about Shirt","location":"East Jackeline, Iran"},{"id":43,"name":"Gerlach, Langosh and Ankunding","description":"A group that cares about Sausages","location":"Russelland, Kiribati"},{"id":44,"name":"Friesen, Windler and Roob","description":"A group that cares about Keyboard","location":"East Edenstad, Gambia"},{"id":45,"name":"Jerde, Romaguera and Donnelly","description":"A group that cares about Towels","location":"South Johanville, Madagascar"},{"id":46,"name":"Satterfield, Murphy and Kilback","description":"A group that cares about Chicken","location":"Rippinshire, Guinea"},{"id":47,"name":"Pouros and Sons","description":"A group that cares about Bacon","location":"Patview, Cameroon"},{"id":48,"name":"Marvin LLC","description":"A group that cares about Mouse","location":"New Grady, Guadeloupe"},{"id":49,"name":"Blick, Goldner and Batz","description":"A group that cares about Shirt","location":"Ferryfort, Peru"},{"id":50,"name":"Turner LLC","description":"A group that cares about Shoes","location":"Joanabury, Russian Federation"},{"id":51,"name":"Eichmann LLC","description":"A group that cares about Towels","location":"New Kendall, Norway"},{"id":52,"name":"Pollich Inc","description":"A group that cares about Tuna","location":"Lindaland, Libyan Arab Jamahiriya"},{"id":53,"name":"Hoeger Group","description":"A group that cares about Tuna","location":"Lake Wilsonmouth, Samoa"},{"id":54,"name":"Dietrich Inc","description":"A group that cares about Hat","location":"New Terrill, Brunei Darussalam"},{"id":55,"name":"Corwin, DuBuque and Schmitt","description":"A group that cares about Gloves","location":"Johannahaven, Mauritania"},{"id":56,"name":"Sporer, Leuschke and Gutkowski","description":"A group that cares about Pants","location":"Concepcionshire, Portugal"},{"id":57,"name":"Wuckert Inc","description":"A group that cares about Bike","location":"East Bert, Tokelau"},{"id":58,"name":"Jacobson Group","description":"A group that cares about Car","location":"Jarrodside, Zambia"},{"id":59,"name":"Jerde Group","description":"A group that cares about Chair","location":"Kreigerfurt, Nigeria"},{"id":60,"name":"Schimmel - Blick","description":"A group that cares about Shoes","location":"North Mathiasbury, Saudi Arabia"},{"id":61,"name":"Mertz LLC","description":"A group that cares about Bacon","location":"Lake Keyon, Finland"},{"id":62,"name":"Hayes Group","description":"A group that cares about Table","location":"New Koby, Niger"},{"id":63,"name":"Altenwerth - Hamill","description":"A group that cares about Chips","location":"New Laura, Syrian Arab Republic"},{"id":64,"name":"Sauer - Windler","description":"A group that cares about Mouse","location":"Lake Kadinville, Tanzania"},{"id":65,"name":"Hegmann LLC","description":"A group that cares about Chips","location":"North Ivory, Virgin Islands, British"},{"id":66,"name":"Klein LLC","description":"A group that cares about Shoes","location":"West Newtonborough, Gabon"},{"id":67,"name":"Aufderhar, Reilly and Jacobi","description":"A group that cares about Towels","location":"Margretstad, Dominican Republic"},{"id":68,"name":"Crona - Oberbrunner","description":"A group that cares about Chicken","location":"West Alexanderfort, Israel"},{"id":69,"name":"Corkery - Reilly","description":"A group that cares about Bacon","location":"North Rosemarytown, Belize"},{"id":70,"name":"Waelchi and Sons","description":"A group that cares about Ball","location":"Laronhaven, Liberia"},{"id":71,"name":"Barrows Inc","description":"A group that cares about Bacon","location":"Pfannerstillfurt, Morocco"},{"id":72,"name":"Koepp, Ziemann and Turner","description":"A group that cares about Chair","location":"North Eliane, French Southern Territories"},{"id":73,"name":"Simonis, Sanford and Murray","description":"A group that cares about Table","location":"East Brisa, Madagascar"},{"id":74,"name":"Franecki, Stokes and Langworth","description":"A group that cares about Hat","location":"Doviehaven, Saint Pierre and Miquelon"},{"id":75,"name":"Goodwin - Dickens","description":"A group that cares about Ball","location":"Torpport, Vietnam"},{"id":76,"name":"Fisher, Nienow and Osinski","description":"A group that cares about Shoes","location":"Port Abby, Holy See (Vatican City State)"},{"id":77,"name":"Senger - Wiza","description":"A group that cares about Shoes","location":"South Willa, Benin"},{"id":78,"name":"Beier, West and Emard","description":"A group that cares about Cheese","location":"Feiltown, Georgia"},{"id":79,"name":"Hand Group","description":"A group that cares about Bacon","location":"Leoneberg, El Salvador"},{"id":80,"name":"Reilly - VonRueden","description":"A group that cares about Car","location":"Willardport, Aruba"},{"id":81,"name":"Ritchie - Steuber","description":"A group that cares about Shoes","location":"East Donavonbury, Gambia"},{"id":82,"name":"Robel, Kuhlman and Hand","description":"A group that cares about Salad","location":"Krystelberg, Ethiopia"},{"id":83,"name":"Schmitt, Harris and Pacocha","description":"A group that cares about Fish","location":"Kautzerfort, Ghana"},{"id":84,"name":"Hand Group","description":"A group that cares about Chicken","location":"North Lynnborough, Japan"},{"id":85,"name":"Wilkinson LLC","description":"A group that cares about Gloves","location":"Port Araceli, Greece"},{"id":86,"name":"Toy Group","description":"A group that cares about Bike","location":"Daltonmouth, Albania"},{"id":87,"name":"Greenfelder, McDermott and Ledner","description":"A group that cares about Shoes","location":"New Deja, Myanmar"},{"id":88,"name":"Becker Inc","description":"A group that cares about Computer","location":"South Zechariahshire, Azerbaijan"},{"id":89,"name":"Koch - Herzog","description":"A group that cares about Table","location":"Krishaven, Guatemala"},{"id":90,"name":"Hilll Group","description":"A group that cares about Tuna","location":"Lake Jeremie, Cape Verde"},{"id":91,"name":"Oberbrunner Inc","description":"A group that cares about Pizza","location":"Lucindafurt, Anguilla"},{"id":92,"name":"Littel, Schmeler and Schneider","description":"A group that cares about Computer","location":"Lake Karen, Chad"},{"id":93,"name":"Heidenreich and Sons","description":"A group that cares about Chair","location":"East Jodychester, Indonesia"},{"id":94,"name":"Simonis - Wolff","description":"A group that cares about Pants","location":"Port Shyannebury, Ireland"},{"id":95,"name":"Dickens and Sons","description":"A group that cares about Soap","location":"South Eleanora, India"},{"id":96,"name":"Labadie, Kunde and O'Connell","description":"A group that cares about Towels","location":"East Samantafurt, Albania"},{"id":97,"name":"Mueller, Ortiz and Ward","description":"A group that cares about Salad","location":"O'Connerhaven, Tanzania"},{"id":98,"name":"Dietrich Inc","description":"A group that cares about Salad","location":"McCulloughport, British Indian Ocean Territory (Chagos Archipelago)"},{"id":99,"name":"Lindgren and Sons","description":"A group that cares about Shoes","location":"Heathtown, Libyan Arab Jamahiriya"}]} \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index d3c5cd7..ab8b81e 100644 --- a/package-lock.json +++ b/package-lock.json @@ -2809,7 +2809,8 @@ "ansi-regex": { "version": "2.1.1", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "aproba": { "version": "1.2.0", @@ -2830,12 +2831,14 @@ "balanced-match": { "version": "1.0.0", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "brace-expansion": { "version": "1.1.11", "bundled": true, "dev": true, + "optional": true, "requires": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" @@ -2850,17 +2853,20 @@ "code-point-at": { "version": "1.1.0", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "concat-map": { "version": "0.0.1", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "console-control-strings": { "version": "1.1.0", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "core-util-is": { "version": "1.0.2", @@ -2977,7 +2983,8 @@ "inherits": { "version": "2.0.3", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "ini": { "version": "1.3.5", @@ -2989,6 +2996,7 @@ "version": "1.0.0", "bundled": true, "dev": true, + "optional": true, "requires": { "number-is-nan": "^1.0.0" } @@ -3003,6 +3011,7 @@ "version": "3.0.4", "bundled": true, "dev": true, + "optional": true, "requires": { "brace-expansion": "^1.1.7" } @@ -3010,12 +3019,14 @@ "minimist": { "version": "0.0.8", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "minipass": { "version": "2.3.5", "bundled": true, "dev": true, + "optional": true, "requires": { "safe-buffer": "^5.1.2", "yallist": "^3.0.0" @@ -3034,6 +3045,7 @@ "version": "0.5.1", "bundled": true, "dev": true, + "optional": true, "requires": { "minimist": "0.0.8" } @@ -3114,7 +3126,8 @@ "number-is-nan": { "version": "1.0.1", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "object-assign": { "version": "4.1.1", @@ -3126,6 +3139,7 @@ "version": "1.4.0", "bundled": true, "dev": true, + "optional": true, "requires": { "wrappy": "1" } @@ -3211,7 +3225,8 @@ "safe-buffer": { "version": "5.1.2", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "safer-buffer": { "version": "2.1.2", @@ -3247,6 +3262,7 @@ "version": "1.0.2", "bundled": true, "dev": true, + "optional": true, "requires": { "code-point-at": "^1.0.0", "is-fullwidth-code-point": "^1.0.0", @@ -3266,6 +3282,7 @@ "version": "3.0.1", "bundled": true, "dev": true, + "optional": true, "requires": { "ansi-regex": "^2.0.0" } @@ -3309,12 +3326,14 @@ "wrappy": { "version": "1.0.2", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "yallist": { "version": "3.0.3", "bundled": true, - "dev": true + "dev": true, + "optional": true } } }, diff --git a/src/api/index.js b/src/api/index.js index a146839..f5f9262 100644 --- a/src/api/index.js +++ b/src/api/index.js @@ -1,8 +1,11 @@ const express = require("express"); const { healthRouter } = require('../routes/health/health.router') +const { groupsRouter } = require('../routes/groups/groups.router') const router = express.Router(); +// Top-level resource routes router.use("/health", healthRouter); +router.use("/groups", groupsRouter); module.exports = router; diff --git a/src/index.js b/src/index.js index 2bc7574..de5b541 100644 --- a/src/index.js +++ b/src/index.js @@ -1,5 +1,6 @@ const express = require("express"); const morgan = require("morgan"); +const bodyParser = require("body-parser"); const router = require("./api"); const { logger } = require("./utils/logger"); @@ -13,11 +14,28 @@ const port = 3000; logger.info("🤖 Initializing middleware"); +app.use(bodyParser.json()); app.use(morgan("tiny", { stream: logger.stream })); +// Top-level router - makes it easier to version apis (seperation of concerns) app.use("/", router); -app.use(errorHandler); +app.use(errorHandler); // app-level middleware +// this needs to be the very last middleware that needs to be defined +// (doesn't crash whole app, when running to an error) + // Serve the application at the given port app.listen(port, () => { logger.info(`🎧 Listening at http://localhost:${port}/`); }); + +// TASKS: +// 1. GET community groups +// 2. POST +// - prolly want the id and req.body as a response back +// 3. VAlIDATION Middleware ~ just to one controller (other than ): +// - is done on the request body +// - validation chain api +// - validation +// - res.send() +// - you create more custom validator +// 4. ERROR HANDLING \ No newline at end of file diff --git a/src/middleware/error-handler.js b/src/middleware/error-handler.js index ed54462..c092ec5 100644 --- a/src/middleware/error-handler.js +++ b/src/middleware/error-handler.js @@ -1,5 +1,7 @@ -const logger = require('../utils/logger'); +const { logger } = require('../utils/logger'); +// We can tell this is a error middleware because this has 4 arguements +// the first being an error const errorHandler = ( err, req, diff --git a/src/routes/groups/groups.controller.js b/src/routes/groups/groups.controller.js new file mode 100644 index 0000000..44a14c0 --- /dev/null +++ b/src/routes/groups/groups.controller.js @@ -0,0 +1,60 @@ +const groupsData = require('./../../../db/groups.data.json') +const fs = require("fs"); +const { promisify } = require("util"); + +const writeFile = promisify(fs.writeFile); + +const listGroup = (req, res) => { + return res.json(groupsData); +}; + +const postGroup = async (req, res) => { + const id = groupsData.groups.length + 1; + const newgroupsData = { + groups: [...groupsData.groups, { id, ...req.body }] + }; + await writeFile("db/groups.data.json", JSON.stringify(newgroupsData)); + res.status(201); + return res.json({ + ...req.body + }); +}; + +const updateGroup = async (req, res) => { + const groupsArray = groupsData.groups + const updatedGroupsArray = groupsArray.map((group) => { + if (group.id === parseInt(req.params.groupId, 10)) { + return {...group, ...req.body} + } + return {...group} + }) + const newgroupsData = { + groups: [...updatedGroupsArray] + }; + await writeFile("db/groups.data.json", JSON.stringify(newgroupsData)); + res.status(200); + return res.json({ + ...req.body + }); +}; + +const deleteGroup = async (req, res) => { + const groupsArray = groupsData.groups + const updatedGroupsArray = groupsArray.filter((group) => group.id !== parseInt(req.params.groupId, 10)) + const newgroupsData = { + groups: [...updatedGroupsArray] + }; + await writeFile("db/groups.data.json", JSON.stringify(newgroupsData)); + res.status(200); + return res.json({ + ...req.body + }); +}; + + +module.exports = { + listGroup, + postGroup, + updateGroup, + deleteGroup +} diff --git a/src/routes/groups/groups.router.js b/src/routes/groups/groups.router.js new file mode 100644 index 0000000..ad93fb6 --- /dev/null +++ b/src/routes/groups/groups.router.js @@ -0,0 +1,16 @@ +const express = require('express'); + +const { listGroup, postGroup, updateGroup, deleteGroup } = require("./groups.controller"); + +const router = express.Router(); + +router.get("", listGroup); +router.post("", postGroup); +router.put("/:groupId", updateGroup); +router.delete("/:groupId", deleteGroup); + +module.exports = { + groupsRouter: router +} + +// For middleware, we need to use the .next() function to move to the next set of middleware From 52342da0c8153a83a4d9579779613401acf66cfc Mon Sep 17 00:00:00 2001 From: Kara Barleta Date: Sat, 7 Mar 2020 17:41:29 -0500 Subject: [PATCH 2/3] Add body and param validators --- db/groups.data.json | 2 +- package-lock.json | 14 ++++++++++++++ package.json | 1 + src/middleware/error-handler.js | 8 +++++++- src/middleware/validate-body.js | 18 ++++++++++++++++++ src/routes/groups/groups.controller.js | 2 +- src/routes/groups/groups.router.js | 21 ++++++++++++++++----- 7 files changed, 58 insertions(+), 8 deletions(-) create mode 100644 src/middleware/validate-body.js diff --git a/db/groups.data.json b/db/groups.data.json index b19c35d..a3616da 100644 --- a/db/groups.data.json +++ b/db/groups.data.json @@ -1 +1 @@ -{"groups":[{"id":1,"name":"Kemmer, Osinski and O'Keefe","description":"A group that cares about Table","location":"East Serenity, Anguilla"},{"id":2,"name":"Jast LLC","description":"A group that cares about Soap","location":"Zboncakton, Antarctica (the territory South of 60 deg S)"},{"id":3,"name":"Wunsch Inc","description":"A group that cares about Chips","location":"Fredside, Somalia"},{"id":4,"name":"Rohan - Kirlin","description":"A group that cares about Table","location":"Langview, Grenada"},{"id":5,"name":"Klein, Beahan and Skiles","description":"A group that cares about Car","location":"East Kareemside, India"},{"id":6,"name":"Powlowski, Grimes and Hermiston","description":"A group that cares about Table","location":"North Albinstad, British Indian Ocean Territory (Chagos Archipelago)"},{"id":7,"name":"Sporer - Wiegand","description":"A group that cares about Bacon","location":"Thielland, Georgia"},{"id":8,"name":"Berge Group","description":"A group that cares about Chicken","location":"New Aliviaborough, Finland"},{"id":9,"name":"Kautzer - Stiedemann","description":"A group that cares about Car","location":"Eliasborough, Nicaragua"},{"id":10,"name":"O'Connell - Leffler","description":"A group that cares about Car","location":"Volkmanfurt, Mauritania"},{"id":11,"name":"Cummerata Group","description":"A group that cares about Bacon","location":"Wunschfurt, Andorra"},{"id":12,"name":"Emmerich, Gottlieb and Pagac","description":"A group that cares about Pizza","location":"Aleenmouth, Saint Barthelemy"},{"id":13,"name":"Haag - Mitchell","description":"A group that cares about Chair","location":"Lake Gust, South Georgia and the South Sandwich Islands"},{"id":14,"name":"Barton - Aufderhar","description":"A group that cares about Mouse","location":"Lake Gailshire, Bahamas"},{"id":15,"name":"Wyman - Mueller","description":"A group that cares about Cheese","location":"Port Kaci, Netherlands Antilles"},{"id":16,"name":"McLaughlin, Champlin and VonRueden","description":"A group that cares about Hat","location":"Binsview, Papua New Guinea"},{"id":17,"name":"Bruen and Sons","description":"A group that cares about Sausages","location":"Howeside, Moldova"},{"id":18,"name":"Koch Group","description":"A group that cares about Pizza","location":"Lempifort, Northern Mariana Islands"},{"id":19,"name":"Gaylord - Weissnat","description":"A group that cares about Bacon","location":"West Torranceview, Guatemala"},{"id":20,"name":"Reichert Group","description":"A group that cares about Salad","location":"West Jacey, Luxembourg"},{"id":21,"name":"Ondricka, Weber and Corkery","description":"A group that cares about Chicken","location":"South Devantetown, Russian Federation"},{"id":22,"name":"Kihn, Pfannerstill and Stamm","description":"A group that cares about Pants","location":"Bartolettiport, Virgin Islands, British"},{"id":23,"name":"Kris - Auer","description":"A group that cares about Shoes","location":"Johnsmouth, Niger"},{"id":24,"name":"Wintheiser - Windler","description":"A group that cares about Pizza","location":"Swaniawskitown, Gambia"},{"id":25,"name":"Bergnaum - Mertz","description":"A group that cares about Fish","location":"New Maximo, Rwanda"},{"id":26,"name":"Nader - Metz","description":"A group that cares about Keyboard","location":"Sandrineville, Mayotte"},{"id":27,"name":"Schroeder, Mills and Breitenberg","description":"A group that cares about Soap","location":"North Lewchester, Colombia"},{"id":28,"name":"Schulist - Davis","description":"A group that cares about Gloves","location":"Adamsstad, Mexico"},{"id":29,"name":"Reichel - Little","description":"A group that cares about Salad","location":"North Eleonore, Uganda"},{"id":30,"name":"Green, Heathcote and Bogan","description":"A group that cares about Ball","location":"Schadenfort, Slovakia (Slovak Republic)"},{"id":31,"name":"Stehr and Sons","description":"A group that cares about Hat","location":"West Jamilstad, Martinique"},{"id":32,"name":"Hegmann Inc","description":"A group that cares about Hat","location":"Port Maggie, Cocos (Keeling) Islands"},{"id":33,"name":"Stoltenberg - Hackett","description":"A group that cares about Chips","location":"Titoburgh, United Kingdom"},{"id":34,"name":"Lind - Collier","description":"A group that cares about Pizza","location":"Lake Domenicaborough, Heard Island and McDonald Islands"},{"id":35,"name":"Breitenberg, Paucek and Schuppe","description":"A group that cares about Table","location":"Jenkinsfort, Niue"},{"id":36,"name":"Quigley - Pacocha","description":"A group that cares about Bike","location":"Daughertystad, Costa Rica"},{"id":37,"name":"Kessler Group","description":"A group that cares about Soap","location":"Jazlynshire, Nauru"},{"id":38,"name":"Morissette, Greenfelder and Steuber","description":"A group that cares about Ball","location":"New Bertbury, Montenegro"},{"id":39,"name":"Gibson Group","description":"A group that cares about Computer","location":"South Beverlyview, Holy See (Vatican City State)"},{"id":40,"name":"Schaefer, Buckridge and Hayes","description":"A group that cares about Chips","location":"Port Jaredton, Saint Martin"},{"id":41,"name":"Auer and Sons","description":"A group that cares about Salad","location":"South Dante, Liechtenstein"},{"id":42,"name":"Reilly - Lubowitz","description":"A group that cares about Shirt","location":"East Jackeline, Iran"},{"id":43,"name":"Gerlach, Langosh and Ankunding","description":"A group that cares about Sausages","location":"Russelland, Kiribati"},{"id":44,"name":"Friesen, Windler and Roob","description":"A group that cares about Keyboard","location":"East Edenstad, Gambia"},{"id":45,"name":"Jerde, Romaguera and Donnelly","description":"A group that cares about Towels","location":"South Johanville, Madagascar"},{"id":46,"name":"Satterfield, Murphy and Kilback","description":"A group that cares about Chicken","location":"Rippinshire, Guinea"},{"id":47,"name":"Pouros and Sons","description":"A group that cares about Bacon","location":"Patview, Cameroon"},{"id":48,"name":"Marvin LLC","description":"A group that cares about Mouse","location":"New Grady, Guadeloupe"},{"id":49,"name":"Blick, Goldner and Batz","description":"A group that cares about Shirt","location":"Ferryfort, Peru"},{"id":50,"name":"Turner LLC","description":"A group that cares about Shoes","location":"Joanabury, Russian Federation"},{"id":51,"name":"Eichmann LLC","description":"A group that cares about Towels","location":"New Kendall, Norway"},{"id":52,"name":"Pollich Inc","description":"A group that cares about Tuna","location":"Lindaland, Libyan Arab Jamahiriya"},{"id":53,"name":"Hoeger Group","description":"A group that cares about Tuna","location":"Lake Wilsonmouth, Samoa"},{"id":54,"name":"Dietrich Inc","description":"A group that cares about Hat","location":"New Terrill, Brunei Darussalam"},{"id":55,"name":"Corwin, DuBuque and Schmitt","description":"A group that cares about Gloves","location":"Johannahaven, Mauritania"},{"id":56,"name":"Sporer, Leuschke and Gutkowski","description":"A group that cares about Pants","location":"Concepcionshire, Portugal"},{"id":57,"name":"Wuckert Inc","description":"A group that cares about Bike","location":"East Bert, Tokelau"},{"id":58,"name":"Jacobson Group","description":"A group that cares about Car","location":"Jarrodside, Zambia"},{"id":59,"name":"Jerde Group","description":"A group that cares about Chair","location":"Kreigerfurt, Nigeria"},{"id":60,"name":"Schimmel - Blick","description":"A group that cares about Shoes","location":"North Mathiasbury, Saudi Arabia"},{"id":61,"name":"Mertz LLC","description":"A group that cares about Bacon","location":"Lake Keyon, Finland"},{"id":62,"name":"Hayes Group","description":"A group that cares about Table","location":"New Koby, Niger"},{"id":63,"name":"Altenwerth - Hamill","description":"A group that cares about Chips","location":"New Laura, Syrian Arab Republic"},{"id":64,"name":"Sauer - Windler","description":"A group that cares about Mouse","location":"Lake Kadinville, Tanzania"},{"id":65,"name":"Hegmann LLC","description":"A group that cares about Chips","location":"North Ivory, Virgin Islands, British"},{"id":66,"name":"Klein LLC","description":"A group that cares about Shoes","location":"West Newtonborough, Gabon"},{"id":67,"name":"Aufderhar, Reilly and Jacobi","description":"A group that cares about Towels","location":"Margretstad, Dominican Republic"},{"id":68,"name":"Crona - Oberbrunner","description":"A group that cares about Chicken","location":"West Alexanderfort, Israel"},{"id":69,"name":"Corkery - Reilly","description":"A group that cares about Bacon","location":"North Rosemarytown, Belize"},{"id":70,"name":"Waelchi and Sons","description":"A group that cares about Ball","location":"Laronhaven, Liberia"},{"id":71,"name":"Barrows Inc","description":"A group that cares about Bacon","location":"Pfannerstillfurt, Morocco"},{"id":72,"name":"Koepp, Ziemann and Turner","description":"A group that cares about Chair","location":"North Eliane, French Southern Territories"},{"id":73,"name":"Simonis, Sanford and Murray","description":"A group that cares about Table","location":"East Brisa, Madagascar"},{"id":74,"name":"Franecki, Stokes and Langworth","description":"A group that cares about Hat","location":"Doviehaven, Saint Pierre and Miquelon"},{"id":75,"name":"Goodwin - Dickens","description":"A group that cares about Ball","location":"Torpport, Vietnam"},{"id":76,"name":"Fisher, Nienow and Osinski","description":"A group that cares about Shoes","location":"Port Abby, Holy See (Vatican City State)"},{"id":77,"name":"Senger - Wiza","description":"A group that cares about Shoes","location":"South Willa, Benin"},{"id":78,"name":"Beier, West and Emard","description":"A group that cares about Cheese","location":"Feiltown, Georgia"},{"id":79,"name":"Hand Group","description":"A group that cares about Bacon","location":"Leoneberg, El Salvador"},{"id":80,"name":"Reilly - VonRueden","description":"A group that cares about Car","location":"Willardport, Aruba"},{"id":81,"name":"Ritchie - Steuber","description":"A group that cares about Shoes","location":"East Donavonbury, Gambia"},{"id":82,"name":"Robel, Kuhlman and Hand","description":"A group that cares about Salad","location":"Krystelberg, Ethiopia"},{"id":83,"name":"Schmitt, Harris and Pacocha","description":"A group that cares about Fish","location":"Kautzerfort, Ghana"},{"id":84,"name":"Hand Group","description":"A group that cares about Chicken","location":"North Lynnborough, Japan"},{"id":85,"name":"Wilkinson LLC","description":"A group that cares about Gloves","location":"Port Araceli, Greece"},{"id":86,"name":"Toy Group","description":"A group that cares about Bike","location":"Daltonmouth, Albania"},{"id":87,"name":"Greenfelder, McDermott and Ledner","description":"A group that cares about Shoes","location":"New Deja, Myanmar"},{"id":88,"name":"Becker Inc","description":"A group that cares about Computer","location":"South Zechariahshire, Azerbaijan"},{"id":89,"name":"Koch - Herzog","description":"A group that cares about Table","location":"Krishaven, Guatemala"},{"id":90,"name":"Hilll Group","description":"A group that cares about Tuna","location":"Lake Jeremie, Cape Verde"},{"id":91,"name":"Oberbrunner Inc","description":"A group that cares about Pizza","location":"Lucindafurt, Anguilla"},{"id":92,"name":"Littel, Schmeler and Schneider","description":"A group that cares about Computer","location":"Lake Karen, Chad"},{"id":93,"name":"Heidenreich and Sons","description":"A group that cares about Chair","location":"East Jodychester, Indonesia"},{"id":94,"name":"Simonis - Wolff","description":"A group that cares about Pants","location":"Port Shyannebury, Ireland"},{"id":95,"name":"Dickens and Sons","description":"A group that cares about Soap","location":"South Eleanora, India"},{"id":96,"name":"Labadie, Kunde and O'Connell","description":"A group that cares about Towels","location":"East Samantafurt, Albania"},{"id":97,"name":"Mueller, Ortiz and Ward","description":"A group that cares about Salad","location":"O'Connerhaven, Tanzania"},{"id":98,"name":"Dietrich Inc","description":"A group that cares about Salad","location":"McCulloughport, British Indian Ocean Territory (Chagos Archipelago)"},{"id":99,"name":"Lindgren and Sons","description":"A group that cares about Shoes","location":"Heathtown, Libyan Arab Jamahiriya"}]} \ No newline at end of file +{"groups":[{"id":1,"name":"Kemmer, Osinski and O'Keefe","description":"A group that cares about Table","location":"East Serenity, Anguilla"},{"id":2,"name":"Jast LLC","description":"A group that cares about Soap","location":"Zboncakton, Antarctica (the territory South of 60 deg S)","test":"TESTING"},{"id":3,"name":"Wunsch Inc","description":"A group that cares about Chips","location":"Fredside, Somalia"},{"id":4,"name":"Rohan - Kirlin","description":"A group that cares about Table","location":"Langview, Grenada"},{"id":5,"name":"Klein, Beahan and Skiles","description":"A group that cares about Car","location":"East Kareemside, India"},{"id":6,"name":"Powlowski, Grimes and Hermiston","description":"A group that cares about Table","location":"North Albinstad, British Indian Ocean Territory (Chagos Archipelago)"},{"id":7,"name":"Sporer - Wiegand","description":"A group that cares about Bacon","location":"Thielland, Georgia"},{"id":8,"name":"Berge Group","description":"A group that cares about Chicken","location":"New Aliviaborough, Finland"},{"id":9,"name":"Kautzer - Stiedemann","description":"A group that cares about Car","location":"Eliasborough, Nicaragua"},{"id":10,"name":"O'Connell - Leffler","description":"A group that cares about Car","location":"Volkmanfurt, Mauritania"},{"id":11,"name":"Cummerata Group","description":"A group that cares about Bacon","location":"Wunschfurt, Andorra"},{"id":12,"name":"Emmerich, Gottlieb and Pagac","description":"A group that cares about Pizza","location":"Aleenmouth, Saint Barthelemy"},{"id":13,"name":"Haag - Mitchell","description":"A group that cares about Chair","location":"Lake Gust, South Georgia and the South Sandwich Islands"},{"id":14,"name":"Barton - Aufderhar","description":"A group that cares about Mouse","location":"Lake Gailshire, Bahamas"},{"id":15,"name":"Wyman - Mueller","description":"A group that cares about Cheese","location":"Port Kaci, Netherlands Antilles"},{"id":16,"name":"McLaughlin, Champlin and VonRueden","description":"A group that cares about Hat","location":"Binsview, Papua New Guinea"},{"id":17,"name":"Bruen and Sons","description":"A group that cares about Sausages","location":"Howeside, Moldova"},{"id":18,"name":"Koch Group","description":"A group that cares about Pizza","location":"Lempifort, Northern Mariana Islands"},{"id":19,"name":"Gaylord - Weissnat","description":"A group that cares about Bacon","location":"West Torranceview, Guatemala"},{"id":20,"name":"Reichert Group","description":"A group that cares about Salad","location":"West Jacey, Luxembourg"},{"id":21,"name":"Ondricka, Weber and Corkery","description":"A group that cares about Chicken","location":"South Devantetown, Russian Federation"},{"id":22,"name":"Kihn, Pfannerstill and Stamm","description":"A group that cares about Pants","location":"Bartolettiport, Virgin Islands, British"},{"id":23,"name":"Kris - Auer","description":"A group that cares about Shoes","location":"Johnsmouth, Niger"},{"id":24,"name":"Wintheiser - Windler","description":"A group that cares about Pizza","location":"Swaniawskitown, Gambia"},{"id":25,"name":"Bergnaum - Mertz","description":"A group that cares about Fish","location":"New Maximo, Rwanda"},{"id":26,"name":"Nader - Metz","description":"A group that cares about Keyboard","location":"Sandrineville, Mayotte"},{"id":27,"name":"Schroeder, Mills and Breitenberg","description":"A group that cares about Soap","location":"North Lewchester, Colombia"},{"id":28,"name":"Schulist - Davis","description":"A group that cares about Gloves","location":"Adamsstad, Mexico"},{"id":29,"name":"Reichel - Little","description":"A group that cares about Salad","location":"North Eleonore, Uganda"},{"id":30,"name":"Green, Heathcote and Bogan","description":"A group that cares about Ball","location":"Schadenfort, Slovakia (Slovak Republic)"},{"id":31,"name":"Stehr and Sons","description":"A group that cares about Hat","location":"West Jamilstad, Martinique"},{"id":32,"name":"Hegmann Inc","description":"A group that cares about Hat","location":"Port Maggie, Cocos (Keeling) Islands"},{"id":33,"name":"Stoltenberg - Hackett","description":"A group that cares about Chips","location":"Titoburgh, United Kingdom"},{"id":34,"name":"Lind - Collier","description":"A group that cares about Pizza","location":"Lake Domenicaborough, Heard Island and McDonald Islands"},{"id":35,"name":"Breitenberg, Paucek and Schuppe","description":"A group that cares about Table","location":"Jenkinsfort, Niue"},{"id":36,"name":"Quigley - Pacocha","description":"A group that cares about Bike","location":"Daughertystad, Costa Rica"},{"id":37,"name":"Kessler Group","description":"A group that cares about Soap","location":"Jazlynshire, Nauru"},{"id":38,"name":"Morissette, Greenfelder and Steuber","description":"A group that cares about Ball","location":"New Bertbury, Montenegro"},{"id":39,"name":"Gibson Group","description":"A group that cares about Computer","location":"South Beverlyview, Holy See (Vatican City State)"},{"id":40,"name":"Schaefer, Buckridge and Hayes","description":"A group that cares about Chips","location":"Port Jaredton, Saint Martin"},{"id":41,"name":"Auer and Sons","description":"A group that cares about Salad","location":"South Dante, Liechtenstein"},{"id":42,"name":"Reilly - Lubowitz","description":"A group that cares about Shirt","location":"East Jackeline, Iran"},{"id":43,"name":"Gerlach, Langosh and Ankunding","description":"A group that cares about Sausages","location":"Russelland, Kiribati"},{"id":44,"name":"Friesen, Windler and Roob","description":"A group that cares about Keyboard","location":"East Edenstad, Gambia"},{"id":45,"name":"Jerde, Romaguera and Donnelly","description":"A group that cares about Towels","location":"South Johanville, Madagascar"},{"id":46,"name":"Satterfield, Murphy and Kilback","description":"A group that cares about Chicken","location":"Rippinshire, Guinea"},{"id":47,"name":"Pouros and Sons","description":"A group that cares about Bacon","location":"Patview, Cameroon"},{"id":48,"name":"Marvin LLC","description":"A group that cares about Mouse","location":"New Grady, Guadeloupe"},{"id":49,"name":"Blick, Goldner and Batz","description":"A group that cares about Shirt","location":"Ferryfort, Peru"},{"id":50,"name":"Turner LLC","description":"A group that cares about Shoes","location":"Joanabury, Russian Federation"},{"id":51,"name":"Eichmann LLC","description":"A group that cares about Towels","location":"New Kendall, Norway"},{"id":52,"name":"Pollich Inc","description":"A group that cares about Tuna","location":"Lindaland, Libyan Arab Jamahiriya"},{"id":53,"name":"Hoeger Group","description":"A group that cares about Tuna","location":"Lake Wilsonmouth, Samoa"},{"id":54,"name":"Dietrich Inc","description":"A group that cares about Hat","location":"New Terrill, Brunei Darussalam"},{"id":55,"name":"Corwin, DuBuque and Schmitt","description":"A group that cares about Gloves","location":"Johannahaven, Mauritania"},{"id":56,"name":"Sporer, Leuschke and Gutkowski","description":"A group that cares about Pants","location":"Concepcionshire, Portugal"},{"id":57,"name":"Wuckert Inc","description":"A group that cares about Bike","location":"East Bert, Tokelau"},{"id":58,"name":"Jacobson Group","description":"A group that cares about Car","location":"Jarrodside, Zambia"},{"id":59,"name":"Jerde Group","description":"A group that cares about Chair","location":"Kreigerfurt, Nigeria"},{"id":60,"name":"Schimmel - Blick","description":"A group that cares about Shoes","location":"North Mathiasbury, Saudi Arabia"},{"id":61,"name":"Mertz LLC","description":"A group that cares about Bacon","location":"Lake Keyon, Finland"},{"id":62,"name":"Hayes Group","description":"A group that cares about Table","location":"New Koby, Niger"},{"id":63,"name":"Altenwerth - Hamill","description":"A group that cares about Chips","location":"New Laura, Syrian Arab Republic"},{"id":64,"name":"Sauer - Windler","description":"A group that cares about Mouse","location":"Lake Kadinville, Tanzania"},{"id":65,"name":"Hegmann LLC","description":"A group that cares about Chips","location":"North Ivory, Virgin Islands, British"},{"id":66,"name":"Klein LLC","description":"A group that cares about Shoes","location":"West Newtonborough, Gabon"},{"id":67,"name":"Aufderhar, Reilly and Jacobi","description":"A group that cares about Towels","location":"Margretstad, Dominican Republic"},{"id":68,"name":"Crona - Oberbrunner","description":"A group that cares about Chicken","location":"West Alexanderfort, Israel"},{"id":69,"name":"Corkery - Reilly","description":"A group that cares about Bacon","location":"North Rosemarytown, Belize"},{"id":70,"name":"Waelchi and Sons","description":"A group that cares about Ball","location":"Laronhaven, Liberia"},{"id":71,"name":"Barrows Inc","description":"A group that cares about Bacon","location":"Pfannerstillfurt, Morocco"},{"id":72,"name":"Koepp, Ziemann and Turner","description":"A group that cares about Chair","location":"North Eliane, French Southern Territories"},{"id":73,"name":"Simonis, Sanford and Murray","description":"A group that cares about Table","location":"East Brisa, Madagascar"},{"id":74,"name":"Franecki, Stokes and Langworth","description":"A group that cares about Hat","location":"Doviehaven, Saint Pierre and Miquelon"},{"id":75,"name":"Goodwin - Dickens","description":"A group that cares about Ball","location":"Torpport, Vietnam"},{"id":76,"name":"Fisher, Nienow and Osinski","description":"A group that cares about Shoes","location":"Port Abby, Holy See (Vatican City State)"},{"id":77,"name":"Senger - Wiza","description":"A group that cares about Shoes","location":"South Willa, Benin"},{"id":78,"name":"Beier, West and Emard","description":"A group that cares about Cheese","location":"Feiltown, Georgia"},{"id":79,"name":"Hand Group","description":"A group that cares about Bacon","location":"Leoneberg, El Salvador"},{"id":80,"name":"Reilly - VonRueden","description":"A group that cares about Car","location":"Willardport, Aruba"},{"id":81,"name":"Ritchie - Steuber","description":"A group that cares about Shoes","location":"East Donavonbury, Gambia"},{"id":82,"name":"Robel, Kuhlman and Hand","description":"A group that cares about Salad","location":"Krystelberg, Ethiopia"},{"id":83,"name":"Schmitt, Harris and Pacocha","description":"A group that cares about Fish","location":"Kautzerfort, Ghana"},{"id":84,"name":"Hand Group","description":"A group that cares about Chicken","location":"North Lynnborough, Japan"},{"id":85,"name":"Wilkinson LLC","description":"A group that cares about Gloves","location":"Port Araceli, Greece"},{"id":86,"name":"Toy Group","description":"A group that cares about Bike","location":"Daltonmouth, Albania"},{"id":87,"name":"Greenfelder, McDermott and Ledner","description":"A group that cares about Shoes","location":"New Deja, Myanmar"},{"id":88,"name":"Becker Inc","description":"A group that cares about Computer","location":"South Zechariahshire, Azerbaijan"},{"id":89,"name":"Koch - Herzog","description":"A group that cares about Table","location":"Krishaven, Guatemala"},{"id":90,"name":"Hilll Group","description":"A group that cares about Tuna","location":"Lake Jeremie, Cape Verde"},{"id":91,"name":"Oberbrunner Inc","description":"A group that cares about Pizza","location":"Lucindafurt, Anguilla"},{"id":92,"name":"Littel, Schmeler and Schneider","description":"A group that cares about Computer","location":"Lake Karen, Chad"},{"id":93,"name":"Heidenreich and Sons","description":"A group that cares about Chair","location":"East Jodychester, Indonesia"},{"id":94,"name":"Simonis - Wolff","description":"A group that cares about Pants","location":"Port Shyannebury, Ireland"},{"id":95,"name":"Dickens and Sons","description":"A group that cares about Soap","location":"South Eleanora, India"},{"id":96,"name":"Labadie, Kunde and O'Connell","description":"A group that cares about Towels","location":"East Samantafurt, Albania"},{"id":97,"name":"Mueller, Ortiz and Ward","description":"A group that cares about Salad","location":"O'Connerhaven, Tanzania"},{"id":98,"name":"Dietrich Inc","description":"A group that cares about Salad","location":"McCulloughport, British Indian Ocean Territory (Chagos Archipelago)"},{"id":99,"name":"Lindgren and Sons","description":"A group that cares about Shoes","location":"Heathtown, Libyan Arab Jamahiriya"},{"id":0,"test":"TESTING"},{"id":556,"test":"TESTING"},{"id":5555,"test":"TESTING"}]} \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index ab8b81e..32828a0 100644 --- a/package-lock.json +++ b/package-lock.json @@ -2556,6 +2556,15 @@ "vary": "~1.1.2" } }, + "express-validator": { + "version": "6.4.0", + "resolved": "https://registry.npmjs.org/express-validator/-/express-validator-6.4.0.tgz", + "integrity": "sha512-Fs+x0yDOSiUV+o5jIRloMyBxqpSzJiMM8KQW1IRVv2l49F6ATU0F9uPa+3K6vXNlLlhUjauv2FCGLFPMaNr24w==", + "requires": { + "lodash": "^4.17.15", + "validator": "^12.1.0" + } + }, "extend": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", @@ -7986,6 +7995,11 @@ } } }, + "validator": { + "version": "12.2.0", + "resolved": "https://registry.npmjs.org/validator/-/validator-12.2.0.tgz", + "integrity": "sha512-jJfE/DW6tIK1Ek8nCfNFqt8Wb3nzMoAbocBF6/Icgg1ZFSBpObdnwVY2jQj6qUqzhx5jc71fpvBWyLGO7Xl+nQ==" + }, "vary": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", diff --git a/package.json b/package.json index a806b98..185c817 100644 --- a/package.json +++ b/package.json @@ -15,6 +15,7 @@ }, "dependencies": { "express": "^4.16.3", + "express-validator": "^6.4.0", "morgan": "^1.9.0", "pg": "^7.10.0", "winston": "^3.0.0" diff --git a/src/middleware/error-handler.js b/src/middleware/error-handler.js index c092ec5..d0354e9 100644 --- a/src/middleware/error-handler.js +++ b/src/middleware/error-handler.js @@ -1,5 +1,10 @@ const { logger } = require('../utils/logger'); +const ERROR_CODES = { + UNPROCESSABLE_ENTITY: 422, + INTERNAL_SERVER_ERROR: 500 +}; + // We can tell this is a error middleware because this has 4 arguements // the first being an error const errorHandler = ( @@ -14,5 +19,6 @@ const errorHandler = ( }; module.exports = { - errorHandler + errorHandler, + ERROR_CODES } diff --git a/src/middleware/validate-body.js b/src/middleware/validate-body.js new file mode 100644 index 0000000..f725b63 --- /dev/null +++ b/src/middleware/validate-body.js @@ -0,0 +1,18 @@ +const { validationResult } = require("express-validator"); + +const validateBody = (req, res, next) => { + const errors = validationResult(req); + if (!errors.isEmpty()) { + throw new Error( + JSON.stringify({ + status: "UNPROCESSABLE_ENTITY", + message: errors.array() + }) + ); + } + next(); +}; + +module.exports = { + validateBody +}; \ No newline at end of file diff --git a/src/routes/groups/groups.controller.js b/src/routes/groups/groups.controller.js index 44a14c0..b1fffcc 100644 --- a/src/routes/groups/groups.controller.js +++ b/src/routes/groups/groups.controller.js @@ -11,7 +11,7 @@ const listGroup = (req, res) => { const postGroup = async (req, res) => { const id = groupsData.groups.length + 1; const newgroupsData = { - groups: [...groupsData.groups, { id, ...req.body }] + groups: [...groupsData.groups, { id: parseInt(id, 10), ...req.body }] }; await writeFile("db/groups.data.json", JSON.stringify(newgroupsData)); res.status(201); diff --git a/src/routes/groups/groups.router.js b/src/routes/groups/groups.router.js index ad93fb6..a585bab 100644 --- a/src/routes/groups/groups.router.js +++ b/src/routes/groups/groups.router.js @@ -1,16 +1,27 @@ const express = require('express'); - const { listGroup, postGroup, updateGroup, deleteGroup } = require("./groups.controller"); +const { check, param } = require('express-validator'); +const { validateBody } = require('../../middleware/validate-body'); const router = express.Router(); router.get("", listGroup); -router.post("", postGroup); -router.put("/:groupId", updateGroup); -router.delete("/:groupId", deleteGroup); +router.post("", [ + check("id") + .not() + .isEmpty() + .withMessage('must have id field'), + check("id") + .not() + .isString() + .withMessage('must have id as type integer'), +], validateBody, postGroup); +// For middleware, we need to use the .next() function to move to the next set of middleware +// the validator methods from the express-library below already have a .next() function embeded under the hood +router.put("/:groupId", [param('groupId').isInt().withMessage('must contain a number')], validateBody, updateGroup); +router.delete("/:groupId", [param('groupId').isInt().withMessage('must contain a number')], validateBody, deleteGroup); module.exports = { groupsRouter: router } -// For middleware, we need to use the .next() function to move to the next set of middleware From b910312e4612bb957d13e7281fb3b4af7ee7ec1d Mon Sep 17 00:00:00 2001 From: Kara Barleta Date: Sat, 7 Mar 2020 19:54:01 -0500 Subject: [PATCH 3/3] aadd swagger documentation --- package-lock.json | 159 ++++++++++++++++++++++++----- package.json | 2 + src/index.js | 4 + src/routes/groups/groups.router.js | 62 ++++++++++- swaggerDocs.js | 27 +++++ 5 files changed, 230 insertions(+), 24 deletions(-) create mode 100644 swaggerDocs.js diff --git a/package-lock.json b/package-lock.json index 32828a0..2dd2b82 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1237,7 +1237,6 @@ "version": "1.0.10", "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", - "dev": true, "requires": { "sprintf-js": "~1.0.2" } @@ -1445,8 +1444,7 @@ "balanced-match": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", - "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=", - "dev": true + "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=" }, "base": { "version": "0.11.2", @@ -1574,7 +1572,6 @@ "version": "1.1.11", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "dev": true, "requires": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" @@ -1662,6 +1659,11 @@ } } }, + "call-me-maybe": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/call-me-maybe/-/call-me-maybe-1.0.1.tgz", + "integrity": "sha1-JtII6onje1y95gJQoV8DHBak1ms=" + }, "callsites": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", @@ -1991,6 +1993,11 @@ "delayed-stream": "~1.0.0" } }, + "commander": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/commander/-/commander-4.0.1.tgz", + "integrity": "sha512-IPF4ouhCP+qdlcmCedhxX4xiGBPyigb8v5NeUp+0LyhwLgxMqyp3S0vl7TAPfS/hiP7FC3caI/PB9lTmP8r1NA==" + }, "component-emitter": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.3.0.tgz", @@ -2000,8 +2007,7 @@ "concat-map": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", - "dev": true + "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=" }, "configstore": { "version": "3.1.2", @@ -2258,6 +2264,14 @@ "integrity": "sha512-nFIfVk5B/NStCsJ+zaPO4vYuLjlzQ6uFvPxzYyHlejNZ/UGa7G/n7peOXVrVNvRuyfstt+mZQYGpjxg9Z6N8Kw==", "dev": true }, + "doctrine": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", + "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", + "requires": { + "esutils": "^2.0.2" + } + }, "domexception": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/domexception/-/domexception-1.0.1.tgz", @@ -2396,8 +2410,7 @@ "esprima": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", - "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", - "dev": true + "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==" }, "estraverse": { "version": "4.3.0", @@ -2408,8 +2421,7 @@ "esutils": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", - "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", - "dev": true + "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==" }, "etag": { "version": "1.8.1", @@ -2795,8 +2807,7 @@ "fs.realpath": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", - "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", - "dev": true + "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=" }, "fsevents": { "version": "1.2.8", @@ -3389,7 +3400,6 @@ "version": "7.1.6", "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz", "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==", - "dev": true, "requires": { "fs.realpath": "^1.0.0", "inflight": "^1.0.4", @@ -3626,7 +3636,6 @@ "version": "1.0.6", "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", - "dev": true, "requires": { "once": "^1.3.0", "wrappy": "1" @@ -5327,7 +5336,6 @@ "version": "3.13.1", "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.13.1.tgz", "integrity": "sha512-YfbcO7jXDdyj0DGxYVSlSeQNHbD7XPWvrVWeVUujrQEoZzWJIRrCPoyk6kL6IAjAG2IolMK4T0hNUe0HOUs5Jw==", - "dev": true, "requires": { "argparse": "^1.0.7", "esprima": "^4.0.0" @@ -5385,6 +5393,23 @@ "integrity": "sha1-tIDIkuWaLwWVTOcnvT8qTogvnhM=", "dev": true }, + "json-schema-ref-parser": { + "version": "7.1.4", + "resolved": "https://registry.npmjs.org/json-schema-ref-parser/-/json-schema-ref-parser-7.1.4.tgz", + "integrity": "sha512-AD7bvav0vak1/63w3jH8F7eHId/4E4EPdMAEZhGxtjktteUv9dnNB/cJy6nVnMyoTPBJnLwFK6tiQPSTeleCtQ==", + "requires": { + "call-me-maybe": "^1.0.1", + "js-yaml": "^3.13.1", + "ono": "^6.0.0" + }, + "dependencies": { + "ono": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/ono/-/ono-6.0.1.tgz", + "integrity": "sha512-5rdYW/106kHqLeG22GE2MHKq+FlsxMERZev9DCzQX1zwkxnFwBivSn5i17a5O/rDmOJOdf4Wyt80UZljzx9+DA==" + } + } + }, "json-schema-traverse": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", @@ -5488,6 +5513,16 @@ "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.15.tgz", "integrity": "sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A==" }, + "lodash.get": { + "version": "4.4.2", + "resolved": "https://registry.npmjs.org/lodash.get/-/lodash.get-4.4.2.tgz", + "integrity": "sha1-LRd/ZS+jHpObRDjVNBSZ36OCXpk=" + }, + "lodash.isequal": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/lodash.isequal/-/lodash.isequal-4.5.0.tgz", + "integrity": "sha1-QVxEePK8wwEgwizhDtMib30+GOA=" + }, "lodash.sortby": { "version": "4.7.0", "resolved": "https://registry.npmjs.org/lodash.sortby/-/lodash.sortby-4.7.0.tgz", @@ -5638,7 +5673,6 @@ "version": "3.0.4", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", - "dev": true, "requires": { "brace-expansion": "^1.1.7" } @@ -5975,7 +6009,6 @@ "version": "1.4.0", "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", - "dev": true, "requires": { "wrappy": "1" } @@ -5994,6 +6027,21 @@ "mimic-fn": "^2.1.0" } }, + "ono": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/ono/-/ono-5.1.0.tgz", + "integrity": "sha512-GgqRIUWErLX4l9Up0khRtbrlH8Fyj59A0nKv8V6pWEto38aUgnOGOOF7UmgFFLzFnDSc8REzaTXOc0hqEe7yIw==" + }, + "openapi-schemas": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/openapi-schemas/-/openapi-schemas-1.0.3.tgz", + "integrity": "sha512-KtMWcK2VtOS+nD8RKSIyScJsj8JrmVWcIX7Kjx4xEHijFYuvMTDON8WfeKOgeSb4uNG6UsqLj5Na7nKbSav9RQ==" + }, + "openapi-types": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/openapi-types/-/openapi-types-1.3.5.tgz", + "integrity": "sha512-11oi4zYorsgvg5yBarZplAqbpev5HkuVNPlZaPTknPDzAynq+lnJdXAmruGWP0s+dNYZS7bjM+xrTpJw7184Fg==" + }, "optionator": { "version": "0.8.3", "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.8.3.tgz", @@ -6093,8 +6141,7 @@ "path-is-absolute": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", - "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", - "dev": true + "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=" }, "path-is-inside": { "version": "1.0.2", @@ -7402,8 +7449,7 @@ "sprintf-js": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", - "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=", - "dev": true + "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=" }, "sshpk": { "version": "1.16.1", @@ -7607,6 +7653,50 @@ } } }, + "swagger-jsdoc": { + "version": "3.5.0", + "resolved": "https://registry.npmjs.org/swagger-jsdoc/-/swagger-jsdoc-3.5.0.tgz", + "integrity": "sha512-TSmCgPodvVGGIXEU+zDp03Gau4pZxgVXiZMIjOgBQHEsRzPLoo2XvF9oQ8Nf1Wf29qsXebLb6qqlo6CIwCQ0Cw==", + "requires": { + "commander": "4.0.1", + "doctrine": "3.0.0", + "glob": "7.1.6", + "js-yaml": "3.13.1", + "swagger-parser": "8.0.3" + } + }, + "swagger-methods": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/swagger-methods/-/swagger-methods-2.0.2.tgz", + "integrity": "sha512-/RNqvBZkH8+3S/FqBPejHxJxZenaYq3MrpeXnzi06aDIS39Mqf5YCUNb/ZBjsvFFt8h9FxfKs8EXPtcYdfLiRg==" + }, + "swagger-parser": { + "version": "8.0.3", + "resolved": "https://registry.npmjs.org/swagger-parser/-/swagger-parser-8.0.3.tgz", + "integrity": "sha512-y2gw+rTjn7Z9J+J1qwbBm0UL93k/VREDCveKBK6iGjf7KXC6QGshbnpEmeHL0ZkCgmIghsXzpNzPSbBH91BAEQ==", + "requires": { + "call-me-maybe": "^1.0.1", + "json-schema-ref-parser": "^7.1.1", + "ono": "^5.1.0", + "openapi-schemas": "^1.0.2", + "openapi-types": "^1.3.5", + "swagger-methods": "^2.0.1", + "z-schema": "^4.1.1" + } + }, + "swagger-ui-dist": { + "version": "3.25.0", + "resolved": "https://registry.npmjs.org/swagger-ui-dist/-/swagger-ui-dist-3.25.0.tgz", + "integrity": "sha512-vwvJPPbdooTvDwLGzjIXinOXizDJJ6U1hxnJL3y6U3aL1d2MSXDmKg2139XaLBhsVZdnQJV2bOkX4reB+RXamg==" + }, + "swagger-ui-express": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/swagger-ui-express/-/swagger-ui-express-4.1.3.tgz", + "integrity": "sha512-f8SEn4YWkKh/HGK0ZjuA2VqA78i1aY6OIa5cqYNgOkBobfHV6Mz4dphQW/us8HYhEFfbENq329PyfIonWfzFrw==", + "requires": { + "swagger-ui-dist": "^3.18.1" + } + }, "symbol-tree": { "version": "3.2.4", "resolved": "https://registry.npmjs.org/symbol-tree/-/symbol-tree-3.2.4.tgz", @@ -8234,8 +8324,7 @@ "wrappy": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", - "dev": true + "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=" }, "write-file-atomic": { "version": "2.4.2", @@ -8353,6 +8442,30 @@ "dev": true } } + }, + "z-schema": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/z-schema/-/z-schema-4.2.2.tgz", + "integrity": "sha512-7bGR7LohxSdlK1EOdvA/OHksvKGE4jTLSjd8dBj9YKT0S43N9pdMZ0Z7GZt9mHrBFhbNTRh3Ky6Eu2MHsPJe8g==", + "requires": { + "commander": "^2.7.1", + "lodash.get": "^4.4.2", + "lodash.isequal": "^4.5.0", + "validator": "^11.0.0" + }, + "dependencies": { + "commander": { + "version": "2.20.3", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", + "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", + "optional": true + }, + "validator": { + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/validator/-/validator-11.1.0.tgz", + "integrity": "sha512-qiQ5ktdO7CD6C/5/mYV4jku/7qnqzjrxb3C/Q5wR3vGGinHTgJZN/TdFT3ZX4vXhX2R1PXx42fB1cn5W+uJ4lg==" + } + } } } } diff --git a/package.json b/package.json index 185c817..2df03d4 100644 --- a/package.json +++ b/package.json @@ -18,6 +18,8 @@ "express-validator": "^6.4.0", "morgan": "^1.9.0", "pg": "^7.10.0", + "swagger-jsdoc": "^3.5.0", + "swagger-ui-express": "^4.1.3", "winston": "^3.0.0" }, "nodemonConfig": { diff --git a/src/index.js b/src/index.js index de5b541..3e5271b 100644 --- a/src/index.js +++ b/src/index.js @@ -5,9 +5,12 @@ const bodyParser = require("body-parser"); const router = require("./api"); const { logger } = require("./utils/logger"); const { errorHandler } = require("./middleware/error-handler"); +const swaggerDocs = require("./../swaggerDocs") // Create a new express application instance const app = express(); +// Add swagger documentation + // The port the express app will listen on const port = 3000; @@ -18,6 +21,7 @@ app.use(bodyParser.json()); app.use(morgan("tiny", { stream: logger.stream })); // Top-level router - makes it easier to version apis (seperation of concerns) app.use("/", router); +swaggerDocs(app); app.use(errorHandler); // app-level middleware // this needs to be the very last middleware that needs to be defined // (doesn't crash whole app, when running to an error) diff --git a/src/routes/groups/groups.router.js b/src/routes/groups/groups.router.js index a585bab..5a57c02 100644 --- a/src/routes/groups/groups.router.js +++ b/src/routes/groups/groups.router.js @@ -4,8 +4,32 @@ const { check, param } = require('express-validator'); const { validateBody } = require('../../middleware/validate-body'); const router = express.Router(); - +/** + * @swagger + * /groups: + * get: + * description: Retreive a list of community groups + * produces: + * - application/json + * responses: + * 200: + * description: List of community groups successfully retrieved + * schema: + * type: array + */ router.get("", listGroup); +/** + * @swagger + * /groups: + * post: + * description: Creates a new community group entry + * produces: + * - application/json + * responses: + * 201: + * description: Community group entry has been created + */ + router.post("", [ check("id") .not() @@ -18,10 +42,46 @@ router.post("", [ ], validateBody, postGroup); // For middleware, we need to use the .next() function to move to the next set of middleware // the validator methods from the express-library below already have a .next() function embeded under the hood +/** + * @swagger + * /groups/{groupId}: + * put: + * description: Updates existing community group entry by group id + * produces: + * - application/json + * parameters: + * - in: path + * description: group id + * required: true + * type: number + * responses: + * 200: + * description: Succcessfully updated existing community group entry + */ router.put("/:groupId", [param('groupId').isInt().withMessage('must contain a number')], validateBody, updateGroup); +/** + * @swagger + * /groups/{groupId}: + * delete: + * description: Deletes existing community group entry by group id + * produces: + * - application/json + * parameters: + * - in: path + * description: group id + * required: true + * type: number + * responses: + * 200: + * description: Succcessfully delete existing community group entry + */ router.delete("/:groupId", [param('groupId').isInt().withMessage('must contain a number')], validateBody, deleteGroup); module.exports = { groupsRouter: router } +// QUESTIONS: +// What should I include as the schema for post, put, delete? It would return the request body at this point, so should the schema be an object? +// When I hit http://localhost:3000/api-docs/, I am getting a "undefined - group id" in my PUT & DELETE sections, +// would you happen to know what the issue is here? diff --git a/swaggerDocs.js b/swaggerDocs.js new file mode 100644 index 0000000..50a855d --- /dev/null +++ b/swaggerDocs.js @@ -0,0 +1,27 @@ +const swaggerUI = require('swagger-ui-express'); +const swaggerJSDoc = require('swagger-jsdoc'); + +const options = { + definition: { + openapi: '3.0.0', // Specification (optional, defaults to swagger: '2.0') + info: { + title: 'Community Group API', // Title (required) + version: '1.0.0', // Version (required) + }, + // basePath: '/', + }, + // Path to the API docs + apis: ['./src/routes/groups/groups.router.js'], +}; + +// Initialize swagger-jsdoc -> returns validated swagger spec in json format +const swaggerSpec = swaggerJSDoc(options); + +module.exports = (app) => { + app.use('/api-docs', swaggerUI.serve, swaggerUI.setup(swaggerSpec)); +} + +// app.get('/api-docs.json', (req, res) => { +// res.setHeader('Content-Type', 'application/json'); +// res.send(swaggerSpec); +// }); \ No newline at end of file