From 5412b593588310a9327770427df100e73dbbd29f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mario=20Garc=C3=ADa=20Meza?= Date: Mon, 26 Jul 2021 17:04:37 -0500 Subject: [PATCH 01/10] Primera version semi-funcional --- app/controllers/note.controller.js | 123 +++++++++++++ app/models/note.model.js | 13 ++ app/routes/note.routes.js | 18 ++ config/database.config.js | 4 + package-lock.json | 269 +++++++++++++++++++++++++++-- package.json | 10 +- server.js | 44 +++++ 7 files changed, 462 insertions(+), 19 deletions(-) create mode 100644 app/controllers/note.controller.js create mode 100644 app/models/note.model.js create mode 100644 app/routes/note.routes.js create mode 100644 config/database.config.js create mode 100644 server.js diff --git a/app/controllers/note.controller.js b/app/controllers/note.controller.js new file mode 100644 index 00000000..d22f7b65 --- /dev/null +++ b/app/controllers/note.controller.js @@ -0,0 +1,123 @@ +const Note = require("../models/note.model.js"); + +// Create and Save a new note +exports.create = (req, res) => { + // Validate request + if (!req.body.content) { + return res.status(400).send({ + message: "Note content cannot be empty" + }); + } + + // Create a Note + const note = new Note({ + title: req.body.title || "Untitled note", + content: req.body.content + }); + + // Save note int the database + note + .save() + .then(data => { + res.send(data); + }) + .catch(err => { + res.status(500).send({ + message: err.message || "Ocurrió un error mientras se creaba la nota" + }); + }); +}; + +// Retrieve and return all notes from database + +exports.findAll = (req, res) => { + Note.find() + .then(notes => { + res.send(notes); + }) + .catch(err => { + res.status(500).send({ + message: + err.message || "Some error occurred while retrieving your notes" + }); + }); +}; + +// Find a single note with a noteId + +exports.findOne = (req, res) => { + Note.findById(req.params.noteId) + .then(note => { + if (!note) { + return res.status(404).send({ + message: "Item not found with id " + req.params.noteId + }); + } + res.send(note); + }) + .catch(err => { + if (err.kind === "ObjectId") { + return res.status(404).send({ + message: "Item no encontrado usando id " + req.params.noteId + }); + } + return res.status(500).send({ + message: + "Ups! Hubo un error al obtener el item con la id " + req.params.noteId + }); + }); +}; + +// Update a note identified by noteId in the request +exports.update = (req, res) => { + // Validate request + if (!req.body.content) { + return res.status(400).send({ + message: "Note content cannot be empty" + }); + } + + // Find note and update it with request body + Note.findByIdAndUpdate( + req.params.noteId, + { + title: req.body.title || "Untitled note", + content: req.body.content + }, + { new: true } + ).then(note => { + // Validate if not empty + if (!note) { + return res.status(404).send({ + message: "No se encontró item con el id " + req.params.noteId + }); + } + return res.status(500).send({ + message: + "Hubo un error al actualizar la nota con el id " + req.params.noteId + }); + }); +}; + +// Delete a note with the specified noteId in the request +exports.delete = (req, res) => { + Note.findByIdAndRemove(req.params.noteId) + .then(note => { + if (!note) { + return res.status(404).send({ + message: "No encontré ningun item con el id " + req.params.noteId + }); + } + res.send({ message: "El item se borró con éxito!" }); + }) + .catch(err => { + if (err.kind === "ObjectId" || err.name === "NotFound") { + return res.status(404).send({ + message: "No encontré el item con id " + req.params.noteId + }); + } + return res.status(500).send({ + message: "No pude borrar el item con el id " + req.params.noteId + }); + }); +}; diff --git a/app/models/note.model.js b/app/models/note.model.js new file mode 100644 index 00000000..0c188e55 --- /dev/null +++ b/app/models/note.model.js @@ -0,0 +1,13 @@ +const mongoose = require("mongoose"); + +const NoteSchema = mongoose.Schema( + { + title: String, + content: String + }, + { + timestamps: false + } +); + +module.exports = mongoose.model("Note", NoteSchema); diff --git a/app/routes/note.routes.js b/app/routes/note.routes.js new file mode 100644 index 00000000..dc089a35 --- /dev/null +++ b/app/routes/note.routes.js @@ -0,0 +1,18 @@ +module.exports = app => { + const notes = require("../controllers/note.controller.js"); + + // create new + app.post("/notes", notes.create); + + // Retrieve all + app.get("/notes", notes.findAll); + + // Retrieve a single item by ID + app.get("notes/:noteId", notes.findOne); + + // Update an item with ID + app.put("/notes/:noteId", notes.update); + + // Delete an intem by ID + app.delete("notes/:noteId", notes.delete); +}; diff --git a/config/database.config.js b/config/database.config.js new file mode 100644 index 00000000..4d46f2e5 --- /dev/null +++ b/config/database.config.js @@ -0,0 +1,4 @@ +module.exports = { + url: + "mongodb+srv://db_user_platzi_master:fptkvsclpn6dCjurwxxpsucjyyujUmu7dq@cluster0.xceta.mongodb.net/myFirstDatabase?retryWrites=true&w=majority" +}; diff --git a/package-lock.json b/package-lock.json index 76a7fcef..2bf46351 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,18 +1,20 @@ { - "name": "escuelajs-reto-09", + "name": "mycrud", "version": "1.0.0", "lockfileVersion": 2, "requires": true, "packages": { "": { - "name": "escuelajs-reto-09", + "name": "mycrud", "version": "1.0.0", "license": "ISC", "dependencies": { + "body-parser": "^1.19.0", "cors": "^2.8.5", "dotenv": "^8.2.0", "express": "^4.17.1", - "mongodb": "^3.6.6" + "mongodb": "^3.6.6", + "mongoose": "^5.13.3" }, "devDependencies": { "jest": "^26.6.3", @@ -1349,6 +1351,14 @@ "@babel/types": "^7.3.0" } }, + "node_modules/@types/bson": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/@types/bson/-/bson-4.0.4.tgz", + "integrity": "sha512-awqorHvQS0DqxkHQ/FxcPX9E+H7Du51Qw/2F+5TBMSaE3G0hm+8D3eXJ6MAzFw75nE8V7xF0QvzUSdxIjJb/GA==", + "dependencies": { + "@types/node": "*" + } + }, "node_modules/@types/graceful-fs": { "version": "4.1.5", "resolved": "https://registry.npmjs.org/@types/graceful-fs/-/graceful-fs-4.1.5.tgz", @@ -1382,11 +1392,19 @@ "@types/istanbul-lib-report": "*" } }, + "node_modules/@types/mongodb": { + "version": "3.6.20", + "resolved": "https://registry.npmjs.org/@types/mongodb/-/mongodb-3.6.20.tgz", + "integrity": "sha512-WcdpPJCakFzcWWD9juKoZbRtQxKIMYF/JIAM4JrNHrMcnJL6/a2NWjXxW7fo9hxboxxkg+icff8d7+WIEvKgYQ==", + "dependencies": { + "@types/bson": "*", + "@types/node": "*" + } + }, "node_modules/@types/node": { "version": "14.14.41", "resolved": "https://registry.npmjs.org/@types/node/-/node-14.14.41.tgz", - "integrity": "sha512-dueRKfaJL4RTtSa7bWeTK1M+VH+Gns73oCgzvYfHZywRCoPSd8EkXBL0mZ9unPTveBn+D9phZBaxuzpwjWkW0g==", - "dev": true + "integrity": "sha512-dueRKfaJL4RTtSa7bWeTK1M+VH+Gns73oCgzvYfHZywRCoPSd8EkXBL0mZ9unPTveBn+D9phZBaxuzpwjWkW0g==" }, "node_modules/@types/normalize-package-data": { "version": "2.4.0", @@ -1922,6 +1940,11 @@ "safe-buffer": "^5.1.1" } }, + "node_modules/bluebird": { + "version": "3.5.1", + "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.5.1.tgz", + "integrity": "sha512-MKiLiV+I1AA596t9w1sQJ8jkiSr5+ZKi0WKrYGUn6d1Fx+Ij4tIj+m2WMQSGczs5jZVxV339chE8iwk6F64wjA==" + }, "node_modules/body-parser": { "version": "1.19.0", "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.19.0.tgz", @@ -6360,6 +6383,11 @@ "verror": "1.10.0" } }, + "node_modules/kareem": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/kareem/-/kareem-2.3.2.tgz", + "integrity": "sha512-STHz9P7X2L4Kwn72fA4rGyqyXdmrMSdxqHx9IXon/FXluXieaFA6KJ2upcHAHxQPQ0LeM/OjLrhFxifHewOALQ==" + }, "node_modules/kind-of": { "version": "3.2.2", "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", @@ -6674,14 +6702,14 @@ } }, "node_modules/mongodb": { - "version": "3.6.6", - "resolved": "https://registry.npmjs.org/mongodb/-/mongodb-3.6.6.tgz", - "integrity": "sha512-WlirMiuV1UPbej5JeCMqE93JRfZ/ZzqE7nJTwP85XzjAF4rRSeq2bGCb1cjfoHLOF06+HxADaPGqT0g3SbVT1w==", + "version": "3.6.10", + "resolved": "https://registry.npmjs.org/mongodb/-/mongodb-3.6.10.tgz", + "integrity": "sha512-fvIBQBF7KwCJnDZUnFFy4WqEFP8ibdXeFANnylW19+vOwdjOAvqIzPdsNCEMT6VKTHnYu4K64AWRih0mkFms6Q==", "dependencies": { "bl": "^2.2.1", "bson": "^1.1.4", "denque": "^1.4.1", - "optional-require": "^1.0.2", + "optional-require": "^1.0.3", "safe-buffer": "^5.1.2" }, "engines": { @@ -6711,6 +6739,96 @@ } } }, + "node_modules/mongoose": { + "version": "5.13.3", + "resolved": "https://registry.npmjs.org/mongoose/-/mongoose-5.13.3.tgz", + "integrity": "sha512-q+zX6kqHAvwxf5speMWhq6qF4vdj+x6/kfD5RSKdZKNm52yGmaUygN+zgrtQjBZPFEzG0B3vF6GP0PoAGadE+w==", + "dependencies": { + "@types/mongodb": "^3.5.27", + "@types/node": "14.x || 15.x", + "bson": "^1.1.4", + "kareem": "2.3.2", + "mongodb": "3.6.10", + "mongoose-legacy-pluralize": "1.0.2", + "mpath": "0.8.3", + "mquery": "3.2.5", + "ms": "2.1.2", + "regexp-clone": "1.0.0", + "safe-buffer": "5.2.1", + "sift": "13.5.2", + "sliced": "1.0.1" + }, + "engines": { + "node": ">=4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/mongoose" + } + }, + "node_modules/mongoose-legacy-pluralize": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/mongoose-legacy-pluralize/-/mongoose-legacy-pluralize-1.0.2.tgz", + "integrity": "sha512-Yo/7qQU4/EyIS8YDFSeenIvXxZN+ld7YdV9LqFVQJzTLye8unujAWPZ4NWKfFA+RNjh+wvTWKY9Z3E5XM6ZZiQ==", + "peerDependencies": { + "mongoose": "*" + } + }, + "node_modules/mongoose/node_modules/ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + }, + "node_modules/mongoose/node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/mpath": { + "version": "0.8.3", + "resolved": "https://registry.npmjs.org/mpath/-/mpath-0.8.3.tgz", + "integrity": "sha512-eb9rRvhDltXVNL6Fxd2zM9D4vKBxjVVQNLNijlj7uoXUy19zNDsIif5zR+pWmPCWNKwAtqyo4JveQm4nfD5+eA==", + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/mquery": { + "version": "3.2.5", + "resolved": "https://registry.npmjs.org/mquery/-/mquery-3.2.5.tgz", + "integrity": "sha512-VjOKHHgU84wij7IUoZzFRU07IAxd5kWJaDmyUzQlbjHjyoeK5TNeeo8ZsFDtTYnSgpW6n/nMNIHvE3u8Lbrf4A==", + "dependencies": { + "bluebird": "3.5.1", + "debug": "3.1.0", + "regexp-clone": "^1.0.0", + "safe-buffer": "5.1.2", + "sliced": "1.0.1" + }, + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/mquery/node_modules/debug": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", + "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", + "dependencies": { + "ms": "2.0.0" + } + }, "node_modules/ms": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", @@ -7696,6 +7814,11 @@ "node": ">=0.10.0" } }, + "node_modules/regexp-clone": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/regexp-clone/-/regexp-clone-1.0.0.tgz", + "integrity": "sha512-TuAasHQNamyyJ2hb97IuBEif4qBHGjPHBS64sZwytpLEqtBQ1gPJTnOaQ6qmpET16cK14kkjbazl6+p0RRv0yw==" + }, "node_modules/registry-auth-token": { "version": "3.4.0", "resolved": "https://registry.npmjs.org/registry-auth-token/-/registry-auth-token-3.4.0.tgz", @@ -8197,6 +8320,11 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/sift": { + "version": "13.5.2", + "resolved": "https://registry.npmjs.org/sift/-/sift-13.5.2.tgz", + "integrity": "sha512-+gxdEOMA2J+AI+fVsCqeNn7Tgx3M9ZN9jdi95939l1IJ8cZsqS8sqpJyOkic2SJk+1+98Uwryt/gL6XDaV+UZA==" + }, "node_modules/signal-exit": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.3.tgz", @@ -8218,6 +8346,11 @@ "node": ">=8" } }, + "node_modules/sliced": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/sliced/-/sliced-1.0.1.tgz", + "integrity": "sha1-CzpmK10Ewxd7GSa+qCsD+Dei70E=" + }, "node_modules/snapdragon": { "version": "0.8.2", "resolved": "https://registry.npmjs.org/snapdragon/-/snapdragon-0.8.2.tgz", @@ -10832,6 +10965,14 @@ "@babel/types": "^7.3.0" } }, + "@types/bson": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/@types/bson/-/bson-4.0.4.tgz", + "integrity": "sha512-awqorHvQS0DqxkHQ/FxcPX9E+H7Du51Qw/2F+5TBMSaE3G0hm+8D3eXJ6MAzFw75nE8V7xF0QvzUSdxIjJb/GA==", + "requires": { + "@types/node": "*" + } + }, "@types/graceful-fs": { "version": "4.1.5", "resolved": "https://registry.npmjs.org/@types/graceful-fs/-/graceful-fs-4.1.5.tgz", @@ -10865,11 +11006,19 @@ "@types/istanbul-lib-report": "*" } }, + "@types/mongodb": { + "version": "3.6.20", + "resolved": "https://registry.npmjs.org/@types/mongodb/-/mongodb-3.6.20.tgz", + "integrity": "sha512-WcdpPJCakFzcWWD9juKoZbRtQxKIMYF/JIAM4JrNHrMcnJL6/a2NWjXxW7fo9hxboxxkg+icff8d7+WIEvKgYQ==", + "requires": { + "@types/bson": "*", + "@types/node": "*" + } + }, "@types/node": { "version": "14.14.41", "resolved": "https://registry.npmjs.org/@types/node/-/node-14.14.41.tgz", - "integrity": "sha512-dueRKfaJL4RTtSa7bWeTK1M+VH+Gns73oCgzvYfHZywRCoPSd8EkXBL0mZ9unPTveBn+D9phZBaxuzpwjWkW0g==", - "dev": true + "integrity": "sha512-dueRKfaJL4RTtSa7bWeTK1M+VH+Gns73oCgzvYfHZywRCoPSd8EkXBL0mZ9unPTveBn+D9phZBaxuzpwjWkW0g==" }, "@types/normalize-package-data": { "version": "2.4.0", @@ -11298,6 +11447,11 @@ "safe-buffer": "^5.1.1" } }, + "bluebird": { + "version": "3.5.1", + "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.5.1.tgz", + "integrity": "sha512-MKiLiV+I1AA596t9w1sQJ8jkiSr5+ZKi0WKrYGUn6d1Fx+Ij4tIj+m2WMQSGczs5jZVxV339chE8iwk6F64wjA==" + }, "body-parser": { "version": "1.19.0", "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.19.0.tgz", @@ -14712,6 +14866,11 @@ "verror": "1.10.0" } }, + "kareem": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/kareem/-/kareem-2.3.2.tgz", + "integrity": "sha512-STHz9P7X2L4Kwn72fA4rGyqyXdmrMSdxqHx9IXon/FXluXieaFA6KJ2upcHAHxQPQ0LeM/OjLrhFxifHewOALQ==" + }, "kind-of": { "version": "3.2.2", "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", @@ -14958,18 +15117,83 @@ } }, "mongodb": { - "version": "3.6.6", - "resolved": "https://registry.npmjs.org/mongodb/-/mongodb-3.6.6.tgz", - "integrity": "sha512-WlirMiuV1UPbej5JeCMqE93JRfZ/ZzqE7nJTwP85XzjAF4rRSeq2bGCb1cjfoHLOF06+HxADaPGqT0g3SbVT1w==", + "version": "3.6.10", + "resolved": "https://registry.npmjs.org/mongodb/-/mongodb-3.6.10.tgz", + "integrity": "sha512-fvIBQBF7KwCJnDZUnFFy4WqEFP8ibdXeFANnylW19+vOwdjOAvqIzPdsNCEMT6VKTHnYu4K64AWRih0mkFms6Q==", "requires": { "bl": "^2.2.1", "bson": "^1.1.4", "denque": "^1.4.1", - "optional-require": "^1.0.2", + "optional-require": "^1.0.3", "safe-buffer": "^5.1.2", "saslprep": "^1.0.0" } }, + "mongoose": { + "version": "5.13.3", + "resolved": "https://registry.npmjs.org/mongoose/-/mongoose-5.13.3.tgz", + "integrity": "sha512-q+zX6kqHAvwxf5speMWhq6qF4vdj+x6/kfD5RSKdZKNm52yGmaUygN+zgrtQjBZPFEzG0B3vF6GP0PoAGadE+w==", + "requires": { + "@types/mongodb": "^3.5.27", + "@types/node": "14.x || 15.x", + "bson": "^1.1.4", + "kareem": "2.3.2", + "mongodb": "3.6.10", + "mongoose-legacy-pluralize": "1.0.2", + "mpath": "0.8.3", + "mquery": "3.2.5", + "ms": "2.1.2", + "regexp-clone": "1.0.0", + "safe-buffer": "5.2.1", + "sift": "13.5.2", + "sliced": "1.0.1" + }, + "dependencies": { + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + }, + "safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==" + } + } + }, + "mongoose-legacy-pluralize": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/mongoose-legacy-pluralize/-/mongoose-legacy-pluralize-1.0.2.tgz", + "integrity": "sha512-Yo/7qQU4/EyIS8YDFSeenIvXxZN+ld7YdV9LqFVQJzTLye8unujAWPZ4NWKfFA+RNjh+wvTWKY9Z3E5XM6ZZiQ==", + "requires": {} + }, + "mpath": { + "version": "0.8.3", + "resolved": "https://registry.npmjs.org/mpath/-/mpath-0.8.3.tgz", + "integrity": "sha512-eb9rRvhDltXVNL6Fxd2zM9D4vKBxjVVQNLNijlj7uoXUy19zNDsIif5zR+pWmPCWNKwAtqyo4JveQm4nfD5+eA==" + }, + "mquery": { + "version": "3.2.5", + "resolved": "https://registry.npmjs.org/mquery/-/mquery-3.2.5.tgz", + "integrity": "sha512-VjOKHHgU84wij7IUoZzFRU07IAxd5kWJaDmyUzQlbjHjyoeK5TNeeo8ZsFDtTYnSgpW6n/nMNIHvE3u8Lbrf4A==", + "requires": { + "bluebird": "3.5.1", + "debug": "3.1.0", + "regexp-clone": "^1.0.0", + "safe-buffer": "5.1.2", + "sliced": "1.0.1" + }, + "dependencies": { + "debug": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", + "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", + "requires": { + "ms": "2.0.0" + } + } + } + }, "ms": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", @@ -15741,6 +15965,11 @@ } } }, + "regexp-clone": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/regexp-clone/-/regexp-clone-1.0.0.tgz", + "integrity": "sha512-TuAasHQNamyyJ2hb97IuBEif4qBHGjPHBS64sZwytpLEqtBQ1gPJTnOaQ6qmpET16cK14kkjbazl6+p0RRv0yw==" + }, "registry-auth-token": { "version": "3.4.0", "resolved": "https://registry.npmjs.org/registry-auth-token/-/registry-auth-token-3.4.0.tgz", @@ -16139,6 +16368,11 @@ "object-inspect": "^1.9.0" } }, + "sift": { + "version": "13.5.2", + "resolved": "https://registry.npmjs.org/sift/-/sift-13.5.2.tgz", + "integrity": "sha512-+gxdEOMA2J+AI+fVsCqeNn7Tgx3M9ZN9jdi95939l1IJ8cZsqS8sqpJyOkic2SJk+1+98Uwryt/gL6XDaV+UZA==" + }, "signal-exit": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.3.tgz", @@ -16157,6 +16391,11 @@ "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", "dev": true }, + "sliced": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/sliced/-/sliced-1.0.1.tgz", + "integrity": "sha1-CzpmK10Ewxd7GSa+qCsD+Dei70E=" + }, "snapdragon": { "version": "0.8.2", "resolved": "https://registry.npmjs.org/snapdragon/-/snapdragon-0.8.2.tgz", diff --git a/package.json b/package.json index 279a5416..7cf2a409 100644 --- a/package.json +++ b/package.json @@ -1,13 +1,15 @@ { - "name": "escuelajs-reto-09", + "name": "mycrud", "version": "1.0.0", "description": "Reto 9 Octubre 26: Curso de Backend con Node", "main": "index.js", "dependencies": { + "body-parser": "^1.19.0", "cors": "^2.8.5", "dotenv": "^8.2.0", "express": "^4.17.1", - "mongodb": "^3.6.6" + "mongodb": "^3.6.6", + "mongoose": "^5.13.3" }, "devDependencies": { "jest": "^26.6.3", @@ -24,8 +26,8 @@ "url": "git+https://github.com/platzi/escuelajs-reto-09.git" }, "keywords": [], - "author": "", - "license": "ISC", + "author": "Mario A. Garcia-Meza", + "license": "MIT", "bugs": { "url": "https://github.com/platzi/escuelajs-reto-09/issues" }, diff --git a/server.js b/server.js new file mode 100644 index 00000000..81e0f190 --- /dev/null +++ b/server.js @@ -0,0 +1,44 @@ +const express = require("express"); +const bodyParser = require("body-parser"); + +const MongoClient = require("mongodb").MongoClient; + +// create express app +const app = express(); + +// parse requestes for content-type urlencoded +app.use(bodyParser.urlencoded({ extended: true })); + +// parse requestes of content-type: application/json +app.use(bodyParser.json()); + +// Configuring database +const dbConfig = require("./config/database.config.js"); + +// Connecting to the database + +MongoClient.connect(dbConfig.url, { + useNewUrlParser: true, + useUnifiedTopology: true +}) + .then(() => { + console.log("Successfully connected to the database"); + }) + .catch(err => { + console.log("Could not connect to the database. Exiting..", err); + process.exit(); + }); + +// define a route + +app.get("/", (req, res) => { + res.json({ message: "Welcome to Platzi Master... I will be your guide" }); +}); + +// Require routes +require("./app/routes/note.routes.js")(app); + +// listen for requests +app.listen(3000, () => { + console.log("Server is listening on http://localhost:3000"); +}); From a11f65ec59e5baeb6c30200fc891b9c0b12dff52 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mario=20A=2E=20Garc=C3=ADa?= Date: Mon, 26 Jul 2021 19:28:52 -0500 Subject: [PATCH 02/10] Quitamos el link de la vdd --- config/database.config.js | 4 ---- 1 file changed, 4 deletions(-) delete mode 100644 config/database.config.js diff --git a/config/database.config.js b/config/database.config.js deleted file mode 100644 index 4d46f2e5..00000000 --- a/config/database.config.js +++ /dev/null @@ -1,4 +0,0 @@ -module.exports = { - url: - "mongodb+srv://db_user_platzi_master:fptkvsclpn6dCjurwxxpsucjyyujUmu7dq@cluster0.xceta.mongodb.net/myFirstDatabase?retryWrites=true&w=majority" -}; From 140b1d104db1db428596a114d3e4dd19ed7b30e2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mario=20Garc=C3=ADa=20Meza?= Date: Tue, 27 Jul 2021 13:33:30 -0500 Subject: [PATCH 03/10] Modificaciones al modelo para relacionar las bdd --- app/controllers/note.controller.js | 28 ++++++++++++++-------------- app/models/note.model.js | 29 ++++++++++++++++++++++++----- app/routes/note.routes.js | 4 ++-- config/database.config.js | 5 +++++ server.js | 18 +++++++++++++----- 5 files changed, 58 insertions(+), 26 deletions(-) create mode 100644 config/database.config.js diff --git a/app/controllers/note.controller.js b/app/controllers/note.controller.js index d22f7b65..00a167f4 100644 --- a/app/controllers/note.controller.js +++ b/app/controllers/note.controller.js @@ -1,18 +1,18 @@ -const Note = require("../models/note.model.js"); +const Product = require("../models/note.model.js"); // Create and Save a new note exports.create = (req, res) => { // Validate request - if (!req.body.content) { + if (!req.body.name) { return res.status(400).send({ - message: "Note content cannot be empty" + message: "field 'name' cannot be empty" }); } // Create a Note - const note = new Note({ - title: req.body.title || "Untitled note", - content: req.body.content + const note = new Product({ + name: req.body.name || "Untitled product", + price: req.body.price }); // Save note int the database @@ -31,7 +31,7 @@ exports.create = (req, res) => { // Retrieve and return all notes from database exports.findAll = (req, res) => { - Note.find() + Product.find() .then(notes => { res.send(notes); }) @@ -46,7 +46,7 @@ exports.findAll = (req, res) => { // Find a single note with a noteId exports.findOne = (req, res) => { - Note.findById(req.params.noteId) + Product.findById(req.params.noteId) .then(note => { if (!note) { return res.status(404).send({ @@ -71,18 +71,18 @@ exports.findOne = (req, res) => { // Update a note identified by noteId in the request exports.update = (req, res) => { // Validate request - if (!req.body.content) { + if (!req.body.name) { return res.status(400).send({ - message: "Note content cannot be empty" + message: "Name cannot be empty" }); } // Find note and update it with request body - Note.findByIdAndUpdate( + Product.findByIdAndUpdate( req.params.noteId, { - title: req.body.title || "Untitled note", - content: req.body.content + name: req.body.name || "Unnamed item", + price: req.body.price }, { new: true } ).then(note => { @@ -101,7 +101,7 @@ exports.update = (req, res) => { // Delete a note with the specified noteId in the request exports.delete = (req, res) => { - Note.findByIdAndRemove(req.params.noteId) + Product.findByIdAndRemove(req.params.noteId) .then(note => { if (!note) { return res.status(404).send({ diff --git a/app/models/note.model.js b/app/models/note.model.js index 0c188e55..be92c257 100644 --- a/app/models/note.model.js +++ b/app/models/note.model.js @@ -1,13 +1,32 @@ const mongoose = require("mongoose"); -const NoteSchema = mongoose.Schema( +// Model for categories + +const CategorySchema = mongoose.Schema({ + name: String, + image: Object +}); + +module.exports = mongoose.model("Category", CategorySchema); + +// Model for products + +const ProductSchema = mongoose.Schema( { - title: String, - content: String + name: { + type: String, + required: false + }, + price: { + type: Number + }, + description: String, + categoryId: [{ type: mongoose.Schema.Types.ObjectId, ref: "Category" }], + image: Object }, { - timestamps: false + timestamps: true } ); -module.exports = mongoose.model("Note", NoteSchema); +module.exports = mongoose.model("Product", ProductSchema); diff --git a/app/routes/note.routes.js b/app/routes/note.routes.js index dc089a35..03ac9bc4 100644 --- a/app/routes/note.routes.js +++ b/app/routes/note.routes.js @@ -8,11 +8,11 @@ module.exports = app => { app.get("/notes", notes.findAll); // Retrieve a single item by ID - app.get("notes/:noteId", notes.findOne); + app.get("/notes/:noteId", notes.findOne); // Update an item with ID app.put("/notes/:noteId", notes.update); // Delete an intem by ID - app.delete("notes/:noteId", notes.delete); + app.delete("/notes/:noteId", notes.delete); }; diff --git a/config/database.config.js b/config/database.config.js new file mode 100644 index 00000000..2f5d6e5e --- /dev/null +++ b/config/database.config.js @@ -0,0 +1,5 @@ +module.exports = { + url: + "mongodb+srv://db_user_platzi_master:fptkvsclpn6dCjurwxxpsucjyyujUmu7dq@cluster0.xceta.mongodb.net/myFirstDatabase?retryWrites=true&w=majority" + //"mongodb://localhost:27017/platzi-crud" +}; diff --git a/server.js b/server.js index 81e0f190..edfc1bf0 100644 --- a/server.js +++ b/server.js @@ -1,7 +1,7 @@ const express = require("express"); const bodyParser = require("body-parser"); -const MongoClient = require("mongodb").MongoClient; +//const MongoClient = require("mongodb").MongoClient; // create express app const app = express(); @@ -14,13 +14,21 @@ app.use(bodyParser.json()); // Configuring database const dbConfig = require("./config/database.config.js"); +const mongoose = require("mongoose"); +const Schema = mongoose.Schema; + +// To handle deprecation warning from findAndUpdate() +mongoose.set("useFindAndModify", false); + +mongoose.Promise = global.Promise; // Connecting to the database -MongoClient.connect(dbConfig.url, { - useNewUrlParser: true, - useUnifiedTopology: true -}) +mongoose + .connect(dbConfig.url, { + useNewUrlParser: true, + useUnifiedTopology: true + }) .then(() => { console.log("Successfully connected to the database"); }) From a5e4651bfb1d2c34f84ec5f7a80eaf280e620a5f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mario=20Garc=C3=ADa=20Meza?= Date: Wed, 28 Jul 2021 11:47:28 -0500 Subject: [PATCH 04/10] it's alive s q --- app/controllers/category.controller.js | 164 ++++++++++++++++++ app/controllers/note.controller.js | 123 ------------- app/controllers/product.controller.js | 144 +++++++++++++++ app/models/category.model.js | 10 ++ .../{note.model.js => product.model.js} | 11 +- app/routes/api.routes.js | 48 +++++ app/routes/note.routes.js | 18 -- server.js | 2 +- 8 files changed, 368 insertions(+), 152 deletions(-) create mode 100644 app/controllers/category.controller.js delete mode 100644 app/controllers/note.controller.js create mode 100644 app/controllers/product.controller.js create mode 100644 app/models/category.model.js rename app/models/{note.model.js => product.model.js} (68%) create mode 100644 app/routes/api.routes.js delete mode 100644 app/routes/note.routes.js diff --git a/app/controllers/category.controller.js b/app/controllers/category.controller.js new file mode 100644 index 00000000..226891b3 --- /dev/null +++ b/app/controllers/category.controller.js @@ -0,0 +1,164 @@ +/* ----------------------------------------- + Controllers for categories + ---------------------------------------*/ +const Category = require("../models/category.model.js"); +const Product = require("../models/product.model.js"); + +// Return list of categories +exports.findAllCategories = (req, res) => { + Category.find() + .then(categories => { + res.send(categories); + }) + .catch(err => { + message: err.message || + "Some error occurred while retrieving your categories"; + }); +}; + +// Return category by id +exports.findOneCategory = (req, res) => { + Category.findById(req.params.categoryId) + .then(category => { + if (!category) { + return res.status(404).send({ + message: "Category not found by id" + req.params.categoryId + }); + } + res.send(category); + }) + .catch(err => { + if (err.kind === "ObjectId") { + return res.status(404).send({ + message: "Category not found using id " + req.params.categoryId + }); + } + return res.status(500).send({ + message: + "There was an error retrieving category using id " + + req.params.categoryId + }); + }); +}; + +// Create a category +exports.createCategory = (req, res) => { + // Validate request + if (!req.body.name) { + return res.status(400).send({ + message: "field 'name' cannot be empty" + }); + } + + // Create a Category + const category = new Category({ + name: req.body.name || "Unnamed category", + image: req.body.image + }); + + // Save category in the database + category + .save() + .then(data => { + res.send(data); + }) + .catch(err => { + res.status(500).send({ + message: err.message || "There was an error while creating the category" + }); + }); +}; + +// Modify a category +exports.editCategory = (req, res) => { + // Validate request + if (!req.body.name) { + return res.status(400).send({ + message: "Name cannot be empty!" + }); + } + + // Find category and update it with request body + Category.findByIdAndUpdate( + req.params.categoryId, + { + name: req.body.name, + image: req.body.image + }, + { new: true } + ) + .then(category => { + // Validate if not empty + if (!category) { + return res.status(404).send({ + message: + "I couln't retrieve category with id " + req.params.categoryId + }); + } + res.send(category); + }) + .catch(err => { + if (err.kind === "ObjectId") { + return res.status(404).send({ + message: "Category not found using id " + req.params.categoryId + }); + } + return res.status(500).send({ + message: + "There was an error updating category with id " + + req.params.categoryId + }); + }); +}; + +exports.deleteCategory = (req, res) => { + Category.findByIdAndRemove(req.params.categoryId) + .then(category => { + if (!category) { + return res.status(404).send({ + message: "I didn't find any category with id " + req.params.categoryId + }); + } + res.send({ message: "Category was correctly deleted" }); + }) + .catch(err => { + if (err.kind === "ObjectId" || err.name === "NotFound") { + return res.status(404).send({ + message: "I couldn't find item with id " + req.params.categoryId + }); + } + return res.status(500).send({ + message: + "Sorry, I wasn't able to delete item with id " + req.params.categoryId + }); + }); +}; + +// Return list of products from a category +exports.allProductsInCategory = (req, res) => { + Product.find({ + categoryId: req.params.categoryId + }) + .then(products => { + if (!products) { + return res.status(404).send({ + message: + "There are no products with category id " + req.params.categoryId + }); + } + res.send(products); + }) + .catch(err => { + if (err.kind === "ObjectId") { + return res.status(404).send({ + message: + "There are no products with category id " + req.params.categoryId + }); + } + return res.status(500).send({ + message: + "There was an error retrieving products with category id " + + req.params.categoryId + }); + }); +}; diff --git a/app/controllers/note.controller.js b/app/controllers/note.controller.js deleted file mode 100644 index 00a167f4..00000000 --- a/app/controllers/note.controller.js +++ /dev/null @@ -1,123 +0,0 @@ -const Product = require("../models/note.model.js"); - -// Create and Save a new note -exports.create = (req, res) => { - // Validate request - if (!req.body.name) { - return res.status(400).send({ - message: "field 'name' cannot be empty" - }); - } - - // Create a Note - const note = new Product({ - name: req.body.name || "Untitled product", - price: req.body.price - }); - - // Save note int the database - note - .save() - .then(data => { - res.send(data); - }) - .catch(err => { - res.status(500).send({ - message: err.message || "Ocurrió un error mientras se creaba la nota" - }); - }); -}; - -// Retrieve and return all notes from database - -exports.findAll = (req, res) => { - Product.find() - .then(notes => { - res.send(notes); - }) - .catch(err => { - res.status(500).send({ - message: - err.message || "Some error occurred while retrieving your notes" - }); - }); -}; - -// Find a single note with a noteId - -exports.findOne = (req, res) => { - Product.findById(req.params.noteId) - .then(note => { - if (!note) { - return res.status(404).send({ - message: "Item not found with id " + req.params.noteId - }); - } - res.send(note); - }) - .catch(err => { - if (err.kind === "ObjectId") { - return res.status(404).send({ - message: "Item no encontrado usando id " + req.params.noteId - }); - } - return res.status(500).send({ - message: - "Ups! Hubo un error al obtener el item con la id " + req.params.noteId - }); - }); -}; - -// Update a note identified by noteId in the request -exports.update = (req, res) => { - // Validate request - if (!req.body.name) { - return res.status(400).send({ - message: "Name cannot be empty" - }); - } - - // Find note and update it with request body - Product.findByIdAndUpdate( - req.params.noteId, - { - name: req.body.name || "Unnamed item", - price: req.body.price - }, - { new: true } - ).then(note => { - // Validate if not empty - if (!note) { - return res.status(404).send({ - message: "No se encontró item con el id " + req.params.noteId - }); - } - return res.status(500).send({ - message: - "Hubo un error al actualizar la nota con el id " + req.params.noteId - }); - }); -}; - -// Delete a note with the specified noteId in the request -exports.delete = (req, res) => { - Product.findByIdAndRemove(req.params.noteId) - .then(note => { - if (!note) { - return res.status(404).send({ - message: "No encontré ningun item con el id " + req.params.noteId - }); - } - res.send({ message: "El item se borró con éxito!" }); - }) - .catch(err => { - if (err.kind === "ObjectId" || err.name === "NotFound") { - return res.status(404).send({ - message: "No encontré el item con id " + req.params.noteId - }); - } - return res.status(500).send({ - message: "No pude borrar el item con el id " + req.params.noteId - }); - }); -}; diff --git a/app/controllers/product.controller.js b/app/controllers/product.controller.js new file mode 100644 index 00000000..61ab555f --- /dev/null +++ b/app/controllers/product.controller.js @@ -0,0 +1,144 @@ +const Product = require("../models/product.model.js"); + +/* ----------------------------------------- + Controllers for products + ---------------------------------------*/ + +// Create and Save a new product +exports.create = (req, res) => { + // Validate request + if (!req.body.name) { + return res.status(400).send({ + message: "field 'name' cannot be empty" + }); + } + + // Create a Product + const product = new Product({ + name: req.body.name || "Untitled product", + price: req.body.price, + description: req.body.description, + categoryId: req.body.categoryId, + image: req.body.image + }); + + // Save product int the database + product + .save() + .then(data => { + res.send(data); + }) + .catch(err => { + res.status(500).send({ + message: err.message || "There was an error while creating the product" + }); + }); +}; + +// Retrieve and return all notes from database + +exports.findAllProducts = (req, res) => { + Product.find() + .then(products => { + res.send(products); + }) + .catch(err => { + res.status(500).send({ + message: + err.message || "Some error occurred while retrieving your products" + }); + }); +}; + +// Find a single note with a productId + +exports.findOne = (req, res) => { + Product.findById(req.params.productId) + .then(product => { + if (!product) { + return res.status(404).send({ + message: "Item not found with id " + req.params.productId + }); + } + res.send(product); + }) + .catch(err => { + if (err.kind === "ObjectId") { + return res.status(404).send({ + message: "Item no encontrado usando id " + req.params.productId + }); + } + return res.status(500).send({ + message: + "Ups! Hubo un error al obtener el item con la id " + + req.params.productId + }); + }); +}; + +// Update a product identified by noteId in the request +exports.update = (req, res) => { + // Validate request + if (!req.body.name) { + return res.status(400).send({ + message: "Name cannot be empty" + }); + } + + // Find product and update it with request body + Product.findByIdAndUpdate( + req.params.productId, + { + name: req.body.name || "Unnamed item", + price: req.body.price, + description: req.body.description, + categoryId: req.body.categoryId, + image: req.body.image + }, + { new: true } + ) + .then(product => { + // Validate if not empty + if (!product) { + return res.status(404).send({ + message: "No se encontró item con el id " + req.params.productId + }); + } + res.send(product); + }) + .catch(err => { + if (err.kind === "ObjectId") { + return res.status(404).send({ + message: "Product not found using id " + req.params.productId + }); + } + return res.status(500).send({ + message: + "Hubo un error al actualizar el producto con el id " + + req.params.productId + }); + }); +}; + +// Delete a note with the specified productId in the request +exports.delete = (req, res) => { + Product.findByIdAndRemove(req.params.productId) + .then(product => { + if (!product) { + return res.status(404).send({ + message: "No encontré ningun item con el id " + req.params.productId + }); + } + res.send({ message: "El item se borró con éxito!" }); + }) + .catch(err => { + if (err.kind === "ObjectId" || err.name === "NotFound") { + return res.status(404).send({ + message: "No encontré el item con id " + req.params.productId + }); + } + return res.status(500).send({ + message: "No pude borrar el item con el id " + req.params.productId + }); + }); +}; diff --git a/app/models/category.model.js b/app/models/category.model.js new file mode 100644 index 00000000..825e9d19 --- /dev/null +++ b/app/models/category.model.js @@ -0,0 +1,10 @@ +const mongoose = require("mongoose"); + +// Model for categories + +const CategorySchema = mongoose.Schema({ + name: String, + image: String +}); + +module.exports = mongoose.model("Category", CategorySchema); diff --git a/app/models/note.model.js b/app/models/product.model.js similarity index 68% rename from app/models/note.model.js rename to app/models/product.model.js index be92c257..b44e4cb6 100644 --- a/app/models/note.model.js +++ b/app/models/product.model.js @@ -1,14 +1,5 @@ const mongoose = require("mongoose"); -// Model for categories - -const CategorySchema = mongoose.Schema({ - name: String, - image: Object -}); - -module.exports = mongoose.model("Category", CategorySchema); - // Model for products const ProductSchema = mongoose.Schema( @@ -22,7 +13,7 @@ const ProductSchema = mongoose.Schema( }, description: String, categoryId: [{ type: mongoose.Schema.Types.ObjectId, ref: "Category" }], - image: Object + image: String }, { timestamps: true diff --git a/app/routes/api.routes.js b/app/routes/api.routes.js new file mode 100644 index 00000000..1c0b9d8f --- /dev/null +++ b/app/routes/api.routes.js @@ -0,0 +1,48 @@ +module.exports = app => { + const products = require("../controllers/product.controller.js"); + const categories = require("../controllers/category.controller.js"); + + /* -------------------------------------- + PRODUCT ROUTES + -----------------------------------*/ + + // create new + app.post("/api/products", products.create); + + // Retrieve all + app.get("/api/products", products.findAllProducts); + + // Retrieve a single item by ID + app.get("/api/products/:productId", products.findOne); + + // Update an item with ID + app.put("/api/products/:productId", products.update); + + // Delete an intem by ID + app.delete("/api/products/:productId", products.delete); + + /* -------------------------------------- + CATEGORY ROUTES + -----------------------------------*/ + + // Return all categories + app.get("/api/categories/", categories.findAllCategories); + + // Retrieve a category by id + app.get("/api/categories/:categoryId", categories.findOneCategory); + + // Create category + app.post("/api/categories/", categories.createCategory); + + // Modify category + app.put("/api/categories/:categoryId", categories.editCategory); + + // Delete Category + app.delete("/api/categories/:categoryId", categories.deleteCategory); + + // Returns all products in one category + app.get( + "/api/categories/:categoryId/products", + categories.allProductsInCategory + ); +}; diff --git a/app/routes/note.routes.js b/app/routes/note.routes.js deleted file mode 100644 index 03ac9bc4..00000000 --- a/app/routes/note.routes.js +++ /dev/null @@ -1,18 +0,0 @@ -module.exports = app => { - const notes = require("../controllers/note.controller.js"); - - // create new - app.post("/notes", notes.create); - - // Retrieve all - app.get("/notes", notes.findAll); - - // Retrieve a single item by ID - app.get("/notes/:noteId", notes.findOne); - - // Update an item with ID - app.put("/notes/:noteId", notes.update); - - // Delete an intem by ID - app.delete("/notes/:noteId", notes.delete); -}; diff --git a/server.js b/server.js index edfc1bf0..d7ca9431 100644 --- a/server.js +++ b/server.js @@ -44,7 +44,7 @@ app.get("/", (req, res) => { }); // Require routes -require("./app/routes/note.routes.js")(app); +require("./app/routes/api.routes.js")(app); // listen for requests app.listen(3000, () => { From 88fc30164da4f06393e5fc35a2830ff7cc9b5612 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mario=20Garc=C3=ADa=20Meza?= Date: Wed, 28 Jul 2021 12:21:43 -0500 Subject: [PATCH 05/10] Set environment variables --- config/database.config.js | 11 +++++++++-- package-lock.json | 18 +++++++++--------- package.json | 2 +- server.js | 3 ++- 4 files changed, 21 insertions(+), 13 deletions(-) diff --git a/config/database.config.js b/config/database.config.js index 2f5d6e5e..c09c64d6 100644 --- a/config/database.config.js +++ b/config/database.config.js @@ -1,5 +1,12 @@ module.exports = { url: - "mongodb+srv://db_user_platzi_master:fptkvsclpn6dCjurwxxpsucjyyujUmu7dq@cluster0.xceta.mongodb.net/myFirstDatabase?retryWrites=true&w=majority" - //"mongodb://localhost:27017/platzi-crud" + "mongodb+srv://" + + process.env.MONGO_USER + + ":" + + process.env.MONGO_PASSWORD + + "@" + + process.env.MONGO_HOST + + "/" + + process.env.MONGO_DB_NAME + + "?retryWrites=true&w=majority" }; diff --git a/package-lock.json b/package-lock.json index 2bf46351..865867b5 100644 --- a/package-lock.json +++ b/package-lock.json @@ -7,11 +7,11 @@ "": { "name": "mycrud", "version": "1.0.0", - "license": "ISC", + "license": "MIT", "dependencies": { "body-parser": "^1.19.0", "cors": "^2.8.5", - "dotenv": "^8.2.0", + "dotenv": "^8.6.0", "express": "^4.17.1", "mongodb": "^3.6.6", "mongoose": "^5.13.3" @@ -2751,11 +2751,11 @@ } }, "node_modules/dotenv": { - "version": "8.2.0", - "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-8.2.0.tgz", - "integrity": "sha512-8sJ78ElpbDJBHNeBzUbUVLsqKdccaa/BXF1uPTw3GrvQTBgrQrtObr2mUrE38vzYd8cEv+m/JBfDLioYcfXoaw==", + "version": "8.6.0", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-8.6.0.tgz", + "integrity": "sha512-IrPdXQsk2BbzvCBGBOTmmSH5SodmqZNt4ERAZDmW4CT+tL8VtvinqywuANaFu4bOMWki16nqf0e4oC0QIaDr/g==", "engines": { - "node": ">=8" + "node": ">=10" } }, "node_modules/duplexer3": { @@ -12101,9 +12101,9 @@ } }, "dotenv": { - "version": "8.2.0", - "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-8.2.0.tgz", - "integrity": "sha512-8sJ78ElpbDJBHNeBzUbUVLsqKdccaa/BXF1uPTw3GrvQTBgrQrtObr2mUrE38vzYd8cEv+m/JBfDLioYcfXoaw==" + "version": "8.6.0", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-8.6.0.tgz", + "integrity": "sha512-IrPdXQsk2BbzvCBGBOTmmSH5SodmqZNt4ERAZDmW4CT+tL8VtvinqywuANaFu4bOMWki16nqf0e4oC0QIaDr/g==" }, "duplexer3": { "version": "0.1.4", diff --git a/package.json b/package.json index 7cf2a409..97f104dd 100644 --- a/package.json +++ b/package.json @@ -6,7 +6,7 @@ "dependencies": { "body-parser": "^1.19.0", "cors": "^2.8.5", - "dotenv": "^8.2.0", + "dotenv": "^8.6.0", "express": "^4.17.1", "mongodb": "^3.6.6", "mongoose": "^5.13.3" diff --git a/server.js b/server.js index d7ca9431..39b36228 100644 --- a/server.js +++ b/server.js @@ -1,7 +1,8 @@ const express = require("express"); const bodyParser = require("body-parser"); -//const MongoClient = require("mongodb").MongoClient; +// Loading Environment variables +require("dotenv").config(); // create express app const app = express(); From 90571423f0d233b6579fe48aebd7ee4a8d0c1a48 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mario=20Garc=C3=ADa=20Meza?= Date: Fri, 30 Jul 2021 13:05:00 -0500 Subject: [PATCH 06/10] configure npm run start --- README.md | 16 +++++++++++++++- package.json | 5 ++++- server.js | 2 ++ 3 files changed, 21 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index f1ebbc43..1c4b666f 100644 --- a/README.md +++ b/README.md @@ -1,12 +1,17 @@ # Prueba de Backend NodeJS + Crear un CRUD para crear productos conectado a MongoDB. +La base de datos de prueba en este proyecto es una tienda de productos para la aplicación de pestañas postizas. En esta tienda hay pestañas y pegamento como categorías de producto y se consideran al menos dos marcas de cada uno de los productos. + ### Instalación + ``` npm install ``` ### Ejecución + ``` npm run start ``` @@ -14,7 +19,9 @@ npm run start ## Modelos ### Product + Un Producto debe tener los siguientes atributos: + - name - price - description @@ -22,13 +29,16 @@ Un Producto debe tener los siguientes atributos: - image ### Category + Una Categoría debe tener los siguientes atributos: + - name - image ## Requerimientos ### CRUD de productos + - [ ] GET `/api/products/` Endpoint para retornar la lista de productos. - [ ] GET `/api/products/{id}/` Endpoint para retornar un producto. - [ ] POST `/api/products/` Endpoint para crear un producto. @@ -36,6 +46,7 @@ Una Categoría debe tener los siguientes atributos: - [ ] DELETE `/api/products/{id}/` Endpoint para eliminar un producto. ### CRUD de categorías + - [ ] GET `/api/categories/` Endpoint para retornar la lista de categorías. - [ ] GET `/api/categories/{id}/` Endpoint para retornar un categoría. - [ ] POST `/api/categories/` Endpoint para crear un categoría. @@ -51,15 +62,18 @@ Una Categoría debe tener los siguientes atributos: 4. Realizar el deploy de tu aplicación. ### Correr pruebas en local + ``` npm run test:e2e ``` + 1. Debes tener mongoDB en local corriendo para hacer las pruebas. 2. Y poner las variables de ambiente en el archivo `.env`. - ## Enviar solución de reto + Debes de crear un "Fork" de este proyecto, revolverlo desde tu cuenta personal. ### Licencia + La licencia [MIT](https://opensource.org/licenses/MIT). diff --git a/package.json b/package.json index 97f104dd..dec30905 100644 --- a/package.json +++ b/package.json @@ -18,9 +18,12 @@ }, "scripts": { "dev": "DEBUG=app:* nodemon src/index.js", - "start": "NODE_ENV=production node src/index.js", + "start": "NODE_ENV=production node server.js", "test:e2e": "jest --forceExit --config ./e2e/jest-e2e.json" }, + "engines": { + "node": "15.13.0" + }, "repository": { "type": "git", "url": "git+https://github.com/platzi/escuelajs-reto-09.git" diff --git a/server.js b/server.js index 39b36228..3b3a6072 100644 --- a/server.js +++ b/server.js @@ -4,6 +4,8 @@ const bodyParser = require("body-parser"); // Loading Environment variables require("dotenv").config(); +console.log(process.env.MONGO_USER); + // create express app const app = express(); From c951732b38f91249ebbecdacc2e71a79991e4700 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mario=20Garc=C3=ADa=20Meza?= Date: Fri, 30 Jul 2021 15:25:04 -0500 Subject: [PATCH 07/10] little modification to allow for heroku port to be called with 3000 as a default --- server.js | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/server.js b/server.js index 3b3a6072..571a9a49 100644 --- a/server.js +++ b/server.js @@ -4,8 +4,6 @@ const bodyParser = require("body-parser"); // Loading Environment variables require("dotenv").config(); -console.log(process.env.MONGO_USER); - // create express app const app = express(); @@ -49,7 +47,11 @@ app.get("/", (req, res) => { // Require routes require("./app/routes/api.routes.js")(app); +// Leer localhost de variables y puerto +const host = process.env.HOST || "0.0.0.0"; +const port = process.env.PORT || 3000; + // listen for requests -app.listen(3000, () => { +app.listen(port, host, () => { console.log("Server is listening on http://localhost:3000"); }); From db91765e2b04e6aa4c87a9c6e8ad220decc676e4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mario=20Garc=C3=ADa=20Meza?= Date: Fri, 30 Jul 2021 15:33:40 -0500 Subject: [PATCH 08/10] Changes to log to verify --- server.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server.js b/server.js index 571a9a49..bcc865f6 100644 --- a/server.js +++ b/server.js @@ -53,5 +53,5 @@ const port = process.env.PORT || 3000; // listen for requests app.listen(port, host, () => { - console.log("Server is listening on http://localhost:3000"); + console.log("Server is listening on port " + port); }); From 7a317b68b6651bd217176ba13414ecff08ad240d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mario=20Garc=C3=ADa=20Meza?= Date: Fri, 30 Jul 2021 16:04:04 -0500 Subject: [PATCH 09/10] try change port in environment --- server.js | 1 - 1 file changed, 1 deletion(-) diff --git a/server.js b/server.js index bcc865f6..b4f9f534 100644 --- a/server.js +++ b/server.js @@ -16,7 +16,6 @@ app.use(bodyParser.json()); // Configuring database const dbConfig = require("./config/database.config.js"); const mongoose = require("mongoose"); -const Schema = mongoose.Schema; // To handle deprecation warning from findAndUpdate() mongoose.set("useFindAndModify", false); From 1ba008a73e2991e02ab412d9fc1df9a7913de857 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mario=20Garc=C3=ADa=20Meza?= Date: Fri, 30 Jul 2021 16:48:43 -0500 Subject: [PATCH 10/10] Instrucciones de la app --- README.md | 88 +++++++++++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 76 insertions(+), 12 deletions(-) diff --git a/README.md b/README.md index 1c4b666f..af3fd864 100644 --- a/README.md +++ b/README.md @@ -1,26 +1,65 @@ # Prueba de Backend NodeJS -Crear un CRUD para crear productos conectado a MongoDB. +¡Hola! Este un CRUD para crear productos conectado a MongoDB. La base de datos de prueba en este proyecto es una tienda de productos para la aplicación de pestañas postizas. En esta tienda hay pestañas y pegamento como categorías de producto y se consideran al menos dos marcas de cada uno de los productos. -### Instalación +Puedes usar la aplicación en -``` -npm install -``` +## Cómo usar la aplicación -### Ejecución +La aplicación está desplegada en Heroku. Es posible probarla usando los enlaces en Postman o en Insomnia. -``` -npm run start -``` +### Endpoints de productos + +- **Obtener la lista de todos los productos** + Realiza una petición GET con `https://stormy-forest-37132.herokuapp.com/api/products` + +- **Retorna un artículo particular** + Añade al enlace anterior un id y realiza una petición GET. Por ejemplo: + `https://stormy-forest-37132.herokuapp.com/api/products/610176b90e09e779cf996665` devuelve pestañas con marca Nagaraku. + +- **Crea un producto Nuevo** + En el endpoint `https://stormy-forest-37132.herokuapp.com/api/products/` usa una petición POST y agrega los datos de un nuevo producto. + Por ejemplo, puedes crear unas nuevas pinzas usando el siguiente código de JSON -## Modelos + `{"name":"Pinzas Pequeñas", "price":70, "description": "Pinzas para poner pestañas, tamaño pequeño.", "categoryId":"610312300bd2c18ebb22e7a3","image":"https://http2.mlstatic.com/D_NQ_NP_2X_931080-MLM32548632659_102019-F.jpg"}` + +- **Edita un producto** + Por ejemplo, las pinzas azules (id 61046ad6e78b5f001587d6ff) tienen la categoría mal. Editalo usando su id haciendo una petición PUT en el endpoint `https://stormy-forest-37132.herokuapp.com/api/products/61046ad6e78b5f001587d6ff` con el siguiente código JSON + +`{"name":"Pinzas Azules", "price":450, "description": "Pinzas para poner pestañas", "categoryId":"610312300bd2c18ebb22e7a3","image":"https://http2.mlstatic.com/D_NQ_NP_2X_931080-MLM32548632659_102019-F.jpg"}` + +- **Borrar un producto** + Puedes borrar un producto haciendo una petición DELETE con el endpoint de products seguido por el id del producto. Por ejemplo, puedes borrar las pinzas azules haciendo esta petición al endpoint `https://stormy-forest-37132.herokuapp.com/api/products/61046ad6e78b5f001587d6ff` + +### Enpoints de Categorías + +- **Ver la lista de las categorías** + Realiza una petición GET con `https://stormy-forest-37132.herokuapp.com/api/products` +- **Retorna una categoría particular** + Añade al enlace anterior un id y realiza una petición GET. Por ejemplo: + `https://stormy-forest-37132.herokuapp.com/api/products/610077b127ff3d7645072de4` devuelve la categoría _pestañas_. +- **Crear una categoría** + Usa el endpoint `https://stormy-forest-37132.herokuapp.com/api/categories/`con una petición POST para crear una categoría nueva. Por ejemplo, podemos crear la categoría _parches_ usando el código JSON + + `{"name":"Parchis", "image":"https://http2.mlstatic.com/D_NQ_NP_2X_709890-MLM45468647557_042021-F.jpg"}` + +- **Editar una Categoría** + Nos equivocamos en la categoría anterior. Tenemos que cambiar _parchis_ por _parches_ en el código anterior. Usa una petición PUT con el siguiente código JSON en el mismo endpoint + `{"name":"Parchis", "image":"https://http2.mlstatic.com/D_NQ_NP_2X_709890-MLM45468647557_042021-F.jpg"}` + +- **Borrar una categoría** + Aún no tenemos nada en stock en la categoría de parches. Vamos a borrarla usando la petición DELETE en la dirección `https://stormy-forest-37132.herokuapp.com/api/categories/:{id}` usando el id que se generó automáticamente en esta categoría al crearla. + +- **Ver todos los productos de una categoría** + Podemos ver cuántos productos tenemos de la categoría _Lashes_ usando una petición GET en el endpoint `https://stormy-forest-37132.herokuapp.com/api/categories/610077b127ff3d7645072de4/products`. Prueba la aplicación con la categoría de Pinzas o la de pegamento. También puedes probarlo con las categorías nuevas que hagas, pero necesitas generar productos con esta categoría para que muestre algo más que una lista vacía. + +## Características del modelo de la Base de Datos ### Product -Un Producto debe tener los siguientes atributos: +El producto tiene los siguientes atributos: - name - price @@ -30,13 +69,29 @@ Un Producto debe tener los siguientes atributos: ### Category -Una Categoría debe tener los siguientes atributos: +La Categoría tiene los siguientes atributos: - name - image ## Requerimientos +### Instalación + +```` + +npm install + +``` + +### Ejecución + +``` + +npm run start + +``` + ### CRUD de productos - [ ] GET `/api/products/` Endpoint para retornar la lista de productos. @@ -64,7 +119,9 @@ Una Categoría debe tener los siguientes atributos: ### Correr pruebas en local ``` + npm run test:e2e + ``` 1. Debes tener mongoDB en local corriendo para hacer las pruebas. @@ -77,3 +134,10 @@ Debes de crear un "Fork" de este proyecto, revolverlo desde tu cuenta personal. ### Licencia La licencia [MIT](https://opensource.org/licenses/MIT). + +``` + +``` + +``` +````