diff --git a/README.md b/README.md index f1ebbc43..af3fd864 100644 --- a/README.md +++ b/README.md @@ -1,20 +1,66 @@ # Prueba de Backend NodeJS -Crear un CRUD para crear productos conectado a MongoDB. -### Instalación -``` -npm install -``` +¡Hola! Este un CRUD para crear productos conectado a MongoDB. -### Ejecución -``` -npm run start -``` +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. + +Puedes usar la aplicación en + +## Cómo usar la aplicación + +La aplicación está desplegada en Heroku. Es posible probarla usando los enlaces en Postman o en Insomnia. + +### 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 - description @@ -22,13 +68,32 @@ Un Producto debe tener los siguientes atributos: - image ### 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. - [ ] GET `/api/products/{id}/` Endpoint para retornar un producto. - [ ] POST `/api/products/` Endpoint para crear un producto. @@ -36,6 +101,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 +117,27 @@ 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/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/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/product.model.js b/app/models/product.model.js new file mode 100644 index 00000000..b44e4cb6 --- /dev/null +++ b/app/models/product.model.js @@ -0,0 +1,23 @@ +const mongoose = require("mongoose"); + +// Model for products + +const ProductSchema = mongoose.Schema( + { + name: { + type: String, + required: false + }, + price: { + type: Number + }, + description: String, + categoryId: [{ type: mongoose.Schema.Types.ObjectId, ref: "Category" }], + image: String + }, + { + timestamps: true + } +); + +module.exports = mongoose.model("Product", ProductSchema); 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/config/database.config.js b/config/database.config.js new file mode 100644 index 00000000..c09c64d6 --- /dev/null +++ b/config/database.config.js @@ -0,0 +1,12 @@ +module.exports = { + url: + "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 76a7fcef..865867b5 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", + "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" + "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", @@ -2728,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": { @@ -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", @@ -11947,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", @@ -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..dec30905 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", + "dotenv": "^8.6.0", "express": "^4.17.1", - "mongodb": "^3.6.6" + "mongodb": "^3.6.6", + "mongoose": "^5.13.3" }, "devDependencies": { "jest": "^26.6.3", @@ -16,16 +18,19 @@ }, "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" }, "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..b4f9f534 --- /dev/null +++ b/server.js @@ -0,0 +1,56 @@ +const express = require("express"); +const bodyParser = require("body-parser"); + +// Loading Environment variables +require("dotenv").config(); + +// 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"); +const mongoose = require("mongoose"); + +// To handle deprecation warning from findAndUpdate() +mongoose.set("useFindAndModify", false); + +mongoose.Promise = global.Promise; + +// Connecting to the database + +mongoose + .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/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(port, host, () => { + console.log("Server is listening on port " + port); +});