From aa09f8c9a778fd29b17722774c6c00b0dcf25a74 Mon Sep 17 00:00:00 2001 From: Tomas Date: Fri, 11 Oct 2024 20:33:26 -0300 Subject: [PATCH] v0.5.6 fix: arreglado error al obtener la frase con 4 personajes, correcciones con nombres de personajes, agregados tests al ejecutar npm run dev, modificacion de tokens, mas cambios en el changelog. --- changelog.md | 19 ++ docker-compose.yml | 32 ++-- package-lock.json | 11 +- package.json | 5 +- readme.md | 11 +- src/account/login.js | 7 +- src/account/register.js | 9 +- src/app.js | 12 +- src/controllers/triviaControllers.js | 232 ++++++++++++------------- src/dbFiles/queries.js | 27 ++- src/routes/apiRoutesDoc.yaml | 6 +- src/routes/routes.js | 9 +- src/routes/swaggerDocs.js | 12 +- src/scrapQuotes/characters_simpson.csv | 16 +- src/scrapQuotes/quotes_simpson.csv | 6 +- tests/queries.test.js | 15 +- 16 files changed, 258 insertions(+), 171 deletions(-) diff --git a/changelog.md b/changelog.md index a107b52..4e4e333 100644 --- a/changelog.md +++ b/changelog.md @@ -1,5 +1,24 @@ # Changelog +## Version 0.5.6: - 2024-10-11 - + +### Agregado: + - Dependencia swagger-themes para personalizar la documentacion de swagger (modo oscuro y mas temas) en [swaggerDocs](./src/routes/swaggerDocs.js) + +### Modificado: + - Agregados mas detalles al momento de hacer deploy y ver en la consola/terminal en [app](./src/app.js) + - Exportada y acortada la routeapi del archivo [routes](./src/routes/routes.js) + - Se mejoro la ruta "home" para que muestre correctamente la url segun el entorno de desarrollo en [routes](./src/routes/routes.js) + - [docker-compose](./docker-compose.yml) mejorado + - Se arreglo un bug que al consultar la frase y personajes devolvia siempre los mismos 3 personajes erroneos, ahora obtiene 10 personajes y de esos 10 obtiene 3 como incorrectos en el archvio [queries](./src/dbFiles/queries.js) + - Corregida la identacion del archivo [triviaControllers](./src/controllers/triviaControllers.js) + - Arreglados los nombres de algunos personajes en [characters_simpsons](./src/scrapQuotes/characters_simpson.csv) + - Al ejecutar el comando npm run dev se modifico para que antes de ejecutar la api se realicen los tests correspondientes en [package](./package.json) + - Todos los usuarios al registrarse/logearse recibiran un token, esta modificacion fue realizada en [login](./src/account/login.js) y [register](./src/account/register.js) + +### Obsoleto: + - Se retiro la implementacion del deploy en render y se transfirio definitivamente a flyio en [workflows](.github/workflows/node.js.yml) + ## Version 0.5.5: - 2024-10-05 - ### Agregado: diff --git a/docker-compose.yml b/docker-compose.yml index a01f156..6c77982 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -10,17 +10,23 @@ services: env_file: - .env restart: unless-stopped + # networks: Los contenedores que pertenecen a esa red pueden comunicarse entre sí, mientras que los contenedores que no están en la misma red no pueden hacerlo. + # Esto proporciona un nivel de aislamiento y seguridad. + networks: + - app-network - # dev: - # build: - # context: . - # target: dev - # ports: - # - "3000:3000" - # environment: - # - NODE_ENV=development - # env_file: - # - .env - # volumes: - # - ./src:/app/src - # command: npm run dev \ No newline at end of file + dev: + build: + context: . + target: dev + ports: + - "3000:3000" + environment: + - NODE_ENV=development + env_file: + - .env + volumes: + - ./src:/app/src + command: npm run dev + networks: + - app-network \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index ae9eeac..03fb4ad 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "proyecto-devops", - "version": "0.5.5", + "version": "0.5.6", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "proyecto-devops", - "version": "0.5.5", + "version": "0.5.6", "license": "MIT", "dependencies": { "@sentry/node": "^8.33.1", @@ -22,6 +22,7 @@ "passport-jwt": "^4.0.1", "passport-local": "^1.0.0", "pg": "^8.12.0", + "swagger-themes": "^1.4.3", "swagger-ui-express": "^5.0.1", "yamljs": "^0.3.0" }, @@ -6139,6 +6140,12 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/swagger-themes": { + "version": "1.4.3", + "resolved": "https://registry.npmjs.org/swagger-themes/-/swagger-themes-1.4.3.tgz", + "integrity": "sha512-1G0CqJC1IBbNxkAOyJoREd9hfwXH1R6+3GOFxLhQho2w2i+AbaJqkF4mTJhkce4yhaEMUXvv4KKu1YO/qpe6nQ==", + "license": "MIT" + }, "node_modules/swagger-ui-dist": { "version": "5.17.14", "resolved": "https://registry.npmjs.org/swagger-ui-dist/-/swagger-ui-dist-5.17.14.tgz", diff --git a/package.json b/package.json index 2577860..b806ad3 100644 --- a/package.json +++ b/package.json @@ -1,11 +1,11 @@ { "name": "proyecto-devops", - "version": "0.5.5", + "version": "0.5.6", "description": "Proyecto universitario para devops", "main": "../src/routes/routes.js", "scripts": { "test": "jest", - "dev": "nodemon --env-file=.env src/app.js", + "dev": "npm run test && nodemon --env-file=.env src/app.js", "lint": "eslint . --max-warnings 0", "start": "node src/app.js" }, @@ -25,6 +25,7 @@ "passport-jwt": "^4.0.1", "passport-local": "^1.0.0", "pg": "^8.12.0", + "swagger-themes": "^1.4.3", "swagger-ui-express": "^5.0.1", "yamljs": "^0.3.0" }, diff --git a/readme.md b/readme.md index a6fe6a7..cb2495e 100644 --- a/readme.md +++ b/readme.md @@ -12,6 +12,7 @@ [![Actions](https://img.shields.io/static/v1?style=for-the-badge&message=Actions&color=555&logo=githubactions&logoColor=3333&label=)](https://docs.github.com/en/actions) [![Supabase](https://img.shields.io/static/v1?style=for-the-badge&message=Supabase&color=555&logo=supabase&logoColor=3333&label=)](https://supabase.com/) [![Render](https://img.shields.io/static/v1?style=for-the-badge&message=Render&color=555&logo=Render&logoColor=3333&label=)](https://render.com/) +[![Flyio](https://img.shields.io/static/v1?style=for-the-badge&message=Fly.io&color=555&logo=Fly.io&logoColor=3333&label=)](https://Fly.io/) [![Docker](https://img.shields.io/static/v1?style=for-the-badge&message=Docker&color=555&logo=Docker&logoColor=3333&label=)](https://docker.com/) [![Sentry](https://img.shields.io/static/v1?style=for-the-badge&message=Sentry&color=555&logo=Sentry&logoColor=3333&label=)](https://sentry.com/) [![Grafana](https://img.shields.io/static/v1?style=for-the-badge&message=Grafana&color=555&logo=Grafana&logoColor=3333&label=)](https://grafana.com/) @@ -64,7 +65,7 @@ - **Testing:** Jest -- **Host:** Render +- **Host:** Flyio - **Documentacion:** Swagger @@ -148,7 +149,7 @@ Variables de entorno para la conexion con postgress

Host

(donde se aloja la aplicacion)
> [!NOTE] -> Por defecto tiene 'localhost:' debes cambiarlo al subirlo a la nube +> Por defecto tiene 'localhost:' debes cambiarlo al subirlo a la nube o puedes dejarlo vacio como en mi caso `HOST` @@ -176,7 +177,7 @@ Variables de entorno para la conexion con postgress

URLHOST

(la url donde esta tu proyecto alojado)
> [!NOTE] -> En mi caso lo coloque en render por lo que estara apuntando a https://simpsons-trivia.onrender.com pero debes colocar el tuyo segun la url que te entregue el proveedor. +> En mi caso lo coloque en flyio por lo que estara apuntando a simpsons-trivia.fly.dev pero debes colocar el tuyo segun la url que te entregue el proveedor. `URLHOST=` @@ -240,7 +241,7 @@ npm run lint 📂 PROYECTO-DEVOPS ├── 📂.github │ └── 📂 workflows # Contiene los archivos de CI/CD para GitHub Actions -│ ├── node.js.yml # Pipeline de CI y CD para la construccion, subida a docker hub y el deploy en render +│ ├── node.js.yml # Pipeline de CI y CD para la construccion, subida a docker hub y el deploy en flyio o render │ └── release.yml # Archvio que se encarga de hacer un release automatico en github │ ├── 📂 media # Contiene imagenes para el readme @@ -298,7 +299,7 @@ npm run lint
-
+
diff --git a/src/account/login.js b/src/account/login.js index 55acfce..b12c123 100644 --- a/src/account/login.js +++ b/src/account/login.js @@ -35,18 +35,15 @@ async function loginUser(req, res, next) { const responseData = { success: true, + token: token, user: { id: user.id, email: user.email, username: user.username, role: user.role - } + }, }; - if (user.role === 'admin') { - responseData.token = token; - } - return res.status(200).json(responseData) }); })(req, res, next); diff --git a/src/account/register.js b/src/account/register.js index 16ba6b6..f232271 100644 --- a/src/account/register.js +++ b/src/account/register.js @@ -1,5 +1,5 @@ const bcryptjs = require('bcryptjs'); -//const jwt = require('jsonwebtoken'); +const jwt = require('jsonwebtoken'); const queries = require('../dbFiles/queries'); // Funcion para registrar un usuario @@ -21,8 +21,15 @@ async function registerUser (username, email, password, role = 'user', res) { const result = await queries.createUser(username, email, hashedPassword, role, created_at); const user = result.rows[0]; + const token = jwt.sign( + { id: result.id, email: result.email, role: result.role }, + process.env.JWT_SECRET, + { expiresIn: '1h' } + ); + res.status(201).json({ success: true, + token: token, data: { id: user.id, username: user.username, diff --git a/src/app.js b/src/app.js index 87fbf9a..a670273 100644 --- a/src/app.js +++ b/src/app.js @@ -4,11 +4,12 @@ const userTables = require('./dbFiles/creatingTables/userTables'); const express = require('express'); const cors = require('cors'); const passport = require('passport'); -const { setupRoutesV1 } = require('../src/routes/routes'); +const { setupRoutesV1, routeapi } = require('../src/routes/routes'); const securityRoutes = require('../src/routes/securityRoutes'); const sentryConfig = require('./monitoring/sentryConfig'); const app = express(); +// Configura la aplicación para que confíe en el encabezado X-Forwarded-For establecido por el proxy. app.set('trust proxy', 1); // Inicializa Sentry @@ -24,7 +25,7 @@ app.use(cors({ callback(new Error('Not allowed by CORS')); } }, - methods: ['GET', 'POST', 'PUT', 'DELETE'], + methods: ['GET', 'POST', 'PUT', 'DELETE', 'PATCH'], allowedHeaders: ['Origin', 'X-Requested-With', 'Content-Type', 'Accept'] })); app.use(express.json()); @@ -47,6 +48,7 @@ const RESET = '\x1b[0m'; const RED = '\x1b[31m'; const GREEN = '\x1b[32m'; const YELLOW = '\x1b[33m'; +const BLUE = '\x1b[96m'; const BOLD = '\x1b[1m'; // Verifica si la conexión a postgres está habilitada @@ -82,6 +84,11 @@ async function configureApp() { if (isProduction) { console.log(`${RED}${BOLD}⚠️ Advertencia: ¡Estás en modo producción!${RESET}`); app.listen(PORT, () => console.log(`${GREEN}${BOLD}🚀 Servidor en producción corriendo en el puerto: ${PORT}!`)); + if (hostname == undefined){ + console.log(`${YELLOW}${BOLD} ❗ Configura la variable de entorno *URLHOST* en tu host para poder ver la ruta de desplegue.${RESET}`); + } else { + console.log(`${BLUE}${BOLD} 🌐 Puedes acceder a traves de https://${hostname}${routeapi} ${RESET}`); + } } else { console.log(`${GREEN}${BOLD}🔧 Estás en modo desarrollo.${RESET}`); app.listen(PORT, () => console.log(`${GREEN}${BOLD}🚀 Servidor de desarrollo corriendo en: http://${HOST}:${PORT}${RESET}`)); @@ -101,4 +108,5 @@ process.on('SIGINT', () => { module.exports = { app, + isProduction }; \ No newline at end of file diff --git a/src/controllers/triviaControllers.js b/src/controllers/triviaControllers.js index e7374ba..33fbaa3 100644 --- a/src/controllers/triviaControllers.js +++ b/src/controllers/triviaControllers.js @@ -12,25 +12,25 @@ function validateEmail(email) { // Funcion para registrar un usuario async function registerUserReq (req, res) { try { - const { email, username, password } = req.body; - const defaultRole = rolesManager.getDefaultRole(); + const { email, username, password } = req.body; + const defaultRole = rolesManager.getDefaultRole(); - if (!email) { - return res.status(400).json({ success: false, error: "The request needs the 'email' field!" }); - } - else if (!validateEmail(email)) { - return res.status(400).json({ success: false, error: "The email is not valid!" }); - } - else if (!username) { - return res.status(400).json({ success: false, error: "The request needs the 'username' field!" }); - } - else if (!password) { - return res.status(400).json({ success: false, error: "The request needs the 'password' field!" }); - } - accountRegister.registerUser(username, email, password, defaultRole, res); + if (!email) { + return res.status(400).json({ success: false, error: "The request needs the 'email' field!" }); + } + else if (!validateEmail(email)) { + return res.status(400).json({ success: false, error: "The email is not valid!" }); + } + else if (!username) { + return res.status(400).json({ success: false, error: "The request needs the 'username' field!" }); + } + else if (!password) { + return res.status(400).json({ success: false, error: "The request needs the 'password' field!" }); + } + accountRegister.registerUser(username, email, password, defaultRole, res); } catch (e) { - console.log(e); + console.log(e); } }; @@ -91,38 +91,38 @@ async function changeUserRole(req, res) { // Funcion para loguear un usuario async function loginUserReq (req, res, next) { try { - const { email, password } = req.body; - const fields = Object.keys(req.body); + const { email, password } = req.body; + const fields = Object.keys(req.body); - // Comprueba que solo se envien 2 campos, usuario y password - if (fields.length > 2) { - return res.status(400).json({ success: false, error: "The request has more than 2 fields!" }); - } - else if (!email) { - return res.status(409).json({ success: false, error: "The request needs the 'email' field!" }); - } - else if (!validateEmail(email)) { - return res.status(409).json({ success: false, error: "The email is not valid!" }); - } - else if (!password) { - return res.status(409).json({ success: false, error: "The request needs the 'password' field!" }); - } - await accountLogin.loginUser(req, res, next); + // Comprueba que solo se envien 2 campos, usuario y password + if (fields.length > 2) { + return res.status(400).json({ success: false, error: "The request has more than 2 fields!" }); + } + else if (!email) { + return res.status(409).json({ success: false, error: "The request needs the 'email' field!" }); + } + else if (!validateEmail(email)) { + return res.status(409).json({ success: false, error: "The email is not valid!" }); + } + else if (!password) { + return res.status(409).json({ success: false, error: "The request needs the 'password' field!" }); + } + await accountLogin.loginUser(req, res, next); } catch (e) { - console.log(e); - next(e); + console.log(e); + next(e); } }; // Funcion para obtener la lista de usuarios async function getUsersList (req, res) { try { - const response = await queries.getUsers(); - res.status(200).json({ - success: true, - data: response, - }); + const response = await queries.getUsers(); + res.status(200).json({ + success: true, + data: response, + }); } catch (error) { res.status(500).json({ success: false, @@ -134,11 +134,11 @@ async function getUsersList (req, res) { // Funcion para obtener los puntajes de los usuarios async function getUsersScores (req, res) { try { - const response = await queries.getScores(); - res.status(200).json({ - success: true, - data: response, - }); + const response = await queries.getScores(); + res.status(200).json({ + success: true, + data: response, + }); } catch (error) { res.status(500).json({ success: false, @@ -149,11 +149,11 @@ async function getUsersScores (req, res) { async function getQuestionsTrivia (req, res) { try { - const response = await queries.getQuestions(); - res.status(200).json({ - success: true, - data: response, - }); + const response = await queries.getQuestions(); + res.status(200).json({ + success: true, + data: response, + }); } catch (error) { res.status(500).json({ success: false, @@ -164,99 +164,99 @@ async function getQuestionsTrivia (req, res) { async function getQuote(req, res) { try { - const question = await queries.getRandomQuestion(); - - if (!question) { - return res.status(404).json({ success: false, error: "No questions available" }); - } + const question = await queries.getRandomQuestion(); + + if (!question) { + return res.status(404).json({ success: false, error: "No questions available" }); + } - // Mezclar las opciones - const allOptions = [question.correct_character, ...question.incorrect_options]; - // Selecciona 4 opciones de la BD 3 erroneas y 1 correcta - if (allOptions.length > 4) { - allOptions.splice(4); - } else if (allOptions.length < 4) { - console.error("Not enough options for question ID:", question.id); - return res.status(500).json({ success: false, error: "Not enough options available" }); - } + // Mezclar las opciones + const allOptions = [question.correct_character, ...question.incorrect_options]; + // Selecciona 4 opciones de la BD 3 erroneas y 1 correcta + if (allOptions.length > 4) { + allOptions.splice(4); + } else if (allOptions.length < 4) { + console.error("Not enough options for question ID:", question.id); + return res.status(500).json({ success: false, error: "Not enough options available" }); + } - const shuffledOptions = allOptions.sort(() => Math.random() - 0.5); + const shuffledOptions = allOptions.sort(() => Math.random() - 0.5); - res.status(200).json({ - success: true, - data: { - id: question.id, - quote: question.quote, - options: shuffledOptions - } - }); + res.status(200).json({ + success: true, + data: { + id: question.id, + quote: question.quote, + options: shuffledOptions + } + }); } catch (error) { res.status(500).json({ - success: false, - error: error.message, + success: false, + error: error.message, }); } } async function healthCheck(req, res) { res.status(200).json({ - success: true, - message: 'La API está funcionando correctamente', - timestamp: new Date().toISOString() + success: true, + message: 'La API está funcionando correctamente', + timestamp: new Date().toISOString() }); } async function getQuotesByCharacter(req, res) { try { - const characterId = parseInt(req.params.characterId); - - if (isNaN(characterId)) { - return res.status(400).json({ - success: false, - message: 'Character ID must be a number' - }); - } + const characterId = parseInt(req.params.characterId); + + if (isNaN(characterId)) { + return res.status(400).json({ + success: false, + message: 'Character ID must be a number' + }); + } - const result = await queries.getQuotesByCharacter(characterId); - - if (!result) { - return res.status(404).json({ - success: false, - message: `Character with ID ${characterId} does not exist` - }); - } + const result = await queries.getQuotesByCharacter(characterId); + + if (!result) { + return res.status(404).json({ + success: false, + message: `Character with ID ${characterId} does not exist` + }); + } - if (result.quotes.length === 0) { - return res.status(404).json({ - success: false, - message: `No quotes found for character ${result.character.name}` - }); - } + if (result.quotes.length === 0) { + return res.status(404).json({ + success: false, + message: `No quotes found for character ${result.character.name}` + }); + } // Convertir el array de quotes en un objeto con el indice como clave const quotesObject = result.quotes.reduce((acc, quote, index) => { acc[index + 1] = { - id: quote.id, - quote: quote.quote + id: quote.id, + quote: quote.quote }; return acc; }, {}); - res.json({ - success: true, - data: { - character: result.character.name, - totalQuotes: result.totalQuotes, - quotes: quotesObject - } - }); + res.json({ + success: true, + data: { + character: result.character.name, + totalQuotes: result.totalQuotes, + quotes: quotesObject + } + }); } catch (error) { - console.error('Error getting quotes by character:', error); - res.status(500).json({ - success: false, - message: 'Error getting quotes by character' - }); + console.error('Error getting quotes by character:', error); + res.status(500).json({ + success: false, + message: 'Error getting quotes by character' + }); } }; @@ -266,8 +266,8 @@ const getCharacters = async (req, res) => { if (characters.length === 0) { return res.status(404).json({ - success: false, - message: "No characters found" + success: false, + message: "No characters found" }); } diff --git a/src/dbFiles/queries.js b/src/dbFiles/queries.js index 603cbf2..526e28c 100644 --- a/src/dbFiles/queries.js +++ b/src/dbFiles/queries.js @@ -154,13 +154,36 @@ async function getRandomQuestion() { try { const result = await databaseManager.query(` SELECT q.id, q.quote, c.name as correct_character, - (SELECT array_agg(c2.name) FROM ${userTables.tableNameCharacter} c2 WHERE c2.id != q.character_id ORDER BY RANDOM() LIMIT 3) as incorrect_options + (SELECT array_agg(c2.name) + FROM ${userTables.tableNameCharacter} c2 + WHERE c2.id != q.character_id + ORDER BY RANDOM() + LIMIT 10) as incorrect_options FROM ${userTables.tableNameQuotes} q JOIN ${userTables.tableNameCharacter} c ON q.character_id = c.id ORDER BY RANDOM() LIMIT 1 `); - return result.rows[0]; + + // Verificar si se obtuvo un resultado + if (result.rows.length === 0) { + console.error("No questions found in the database."); + return undefined; + } + + const incorrectOptions = result.rows[0].incorrect_options; + + // Asegurarse de que incorrectOptions tenga al menos 3 elementos + if (!incorrectOptions || incorrectOptions.length < 3) { + console.error("Not enough incorrect options available."); + return undefined; + } + + const shuffledOptions = incorrectOptions.sort(() => Math.random() - 0.5).slice(0, 3); + return { + ...result.rows[0], + incorrect_options: shuffledOptions + }; } catch (e) { console.log(e); throw e; diff --git a/src/routes/apiRoutesDoc.yaml b/src/routes/apiRoutesDoc.yaml index 793e348..b9e8a5d 100644 --- a/src/routes/apiRoutesDoc.yaml +++ b/src/routes/apiRoutesDoc.yaml @@ -1,11 +1,9 @@ openapi: 3.0.0 servers: - description: API Simpsons Trivia Flyio server - url: https://simpsons-trivia.fly.dev/triviasimpsons/api/v1 - - description: API Simpsons Trivia Render server - url: https://simpsons-trivia.onrender.com/triviasimpsons/api/v1 + url: https://simpsons-trivia.fly.dev/api/v1 - description: API Simpsons Trivia localhost - url: http://localhost:3001/triviasimpsons/api/v1 + url: http://localhost:3001/api/v1 info: description: This is a simple API of Simpsons Trivia diff --git a/src/routes/routes.js b/src/routes/routes.js index ba061f6..e95e618 100644 --- a/src/routes/routes.js +++ b/src/routes/routes.js @@ -1,16 +1,16 @@ require('../account/passportConfig'); const triviaControll = require('../controllers/triviaControllers'); -const { swaggerDocument, swaggerUi } = require('./swaggerDocs'); +const { swaggerDocument, swaggerUi, options } = require('./swaggerDocs'); const securityRoutes = require('./securityRoutes'); const { checkRole } = require('../account/roles/roleMiddleware'); const rolesManager = require('../account/roles/rolesManager'); -const routeapi = "/triviasimpsons/api/v1"; +const routeapi = "/api/v1"; function setupRoutesV1(app, hostname) { app.get([routeapi+'/', '/'], (req, res) => { const hostnameapp = hostname || req.get('host'); - const LinkDocs = `${hostnameapp}${routeapi}/docs` + let LinkDocs = `${hostnameapp}${routeapi}/docs`; res.send(`English:
Welcome to the Simpsons Trivia API, you can test the api if you want, go to ${LinkDocs} to see all the routes

Spanish:
Bienvenido a la API Trivia de los Simpsons, puedes probar la api si quieres, ve a ${LinkDocs} para ver todas las rutas disponibles`); }); @@ -49,7 +49,7 @@ function setupRoutesV1(app, hostname) { app.get(routeapi+'/characters', triviaControll.getCharacters); // Rutas de la API documentada con Swagger usando el archivo apiRoutesDoc.yaml - app.use(routeapi + '/docs', swaggerUi.serve, swaggerUi.setup(swaggerDocument)); + app.use(routeapi + '/docs', swaggerUi.serve, swaggerUi.setup(swaggerDocument, options)); // Middleware para manejar rutas no encontradas (404) app.use((req, res) => { @@ -72,4 +72,5 @@ function setupRoutesV1(app, hostname) { module.exports = { setupRoutesV1, + routeapi, } \ No newline at end of file diff --git a/src/routes/swaggerDocs.js b/src/routes/swaggerDocs.js index 441f70e..4fd0b77 100644 --- a/src/routes/swaggerDocs.js +++ b/src/routes/swaggerDocs.js @@ -1,12 +1,22 @@ // Swagger const swaggerUi = require('swagger-ui-express'); +const { SwaggerTheme, SwaggerThemeNameEnum } = require('swagger-themes'); const YAML = require('yamljs'); const path = require('path'); // Cargar el documento Swagger const swaggerDocument = YAML.load(path.join(__dirname, 'apiRoutesDoc.yaml')); +// Modo oscuro para swagger https://www.npmjs.com/package/swagger-themes#themes +const theme = new SwaggerTheme(); + +const options = { + explorer: true, + customCss: theme.getBuffer(SwaggerThemeNameEnum.GRUVBOX) +}; + module.exports = { swaggerDocument, - swaggerUi + swaggerUi, + options } diff --git a/src/scrapQuotes/characters_simpson.csv b/src/scrapQuotes/characters_simpson.csv index daf66a3..b13d3b0 100644 --- a/src/scrapQuotes/characters_simpson.csv +++ b/src/scrapQuotes/characters_simpson.csv @@ -4,7 +4,7 @@ 3,"Helen Lovejoy" 4,"Moe Szyslak" 5,"Marge Simpson" -6,"Jefe GóRgory (Clancy Wiggum)" +6,"Jefe Górgory (Clancy Wiggum)" 7,"Abraham Simpson" 8,"Barney Gumble" 9,"Charles Montgomery Burns" @@ -18,21 +18,21 @@ 17,"Hank Scorpio" 18,"Castigador" 19,"Ralph Wiggum" -20,"CapitáN Tenille" +20,"Capitán Tenille" 21,"Gordo Tony" 22,"Mujer" 23,"Intendente Chalmers" 24,"Lisa Simpson" -25,"Miembro De La Logia" -26,"Johnny Boca Cerrada" -27,"Sr Bolainas Sr Burns" +25,"Miembro de la Logia" +26,"Johnny boca cerrada" +27,"Sr Bolainas (Sr Burns)" 28,"Criador De Abejas" -29,"CompañEros De Escuela" +29,"Compañeros De Escuela" 30,"Profesor Skinner" 31,"Apu Nahasapeemapetilon" 32,"Vendedor" -33,"Profesor De MúSica" +33,"Profesor de Música" 34,"Jasper Beardley" 35,"Hombre Radioactivo" 36,"Willy "El escoses"" -37,"PolíTico" \ No newline at end of file +37,"Político" \ No newline at end of file diff --git a/src/scrapQuotes/quotes_simpson.csv b/src/scrapQuotes/quotes_simpson.csv index 300779f..25a09aa 100644 --- a/src/scrapQuotes/quotes_simpson.csv +++ b/src/scrapQuotes/quotes_simpson.csv @@ -464,9 +464,9 @@ 462,, 463,"A veces siento que la familia me vuelve loca y quiero ¡estallar!",5 464,"Ajajajaj jajajaj jajajaja el concurso se acabo, denle a ese hombre los 10 mil dolares... jajajaja pero la bola, su ingle hahajaja funciona a muchos niveles",2 -465,, -466,, -467,, +465,"¡¡¡Deme 700 hamburguesas Krusty!!!",2 +466,"Lo lograste papa, nos salvaste, estoy muy orgulloso de ti padre santo",1 +467,"Segun este mapa, hay una hamburguesas krusty en una plataforma petrolera... ¡¡¡No importa la niebla, es por allá!!! ",2 468,, 469,, 470,, diff --git a/tests/queries.test.js b/tests/queries.test.js index 943ab65..f0d18c3 100644 --- a/tests/queries.test.js +++ b/tests/queries.test.js @@ -45,14 +45,23 @@ test('getRandomQuestion returns a question with correct structure', async () => incorrect_options: ['Bart Simpson', 'Lisa Simpson', 'Marge Simpson'] }; + // Simular la respuesta de la base de datos databaseManager.query.mockResolvedValue({ rows: [mockQuestion] }); const result = await getRandomQuestion(); - expect(result).toEqual(mockQuestion); - expect(result.quote).toBeDefined(); - expect(result.correct_character).toBeDefined(); + // Verificar que el resultado tenga la estructura correcta + expect(result).toHaveProperty('id'); + expect(result).toHaveProperty('quote'); + expect(result).toHaveProperty('correct_character'); + expect(result).toHaveProperty('incorrect_options'); + + // Verificar que las opciones incorrectas sean 3 expect(result.incorrect_options).toHaveLength(3); + + // Verificar que la respuesta correcta esté presente en las opciones + expect(result.incorrect_options).toEqual(expect.arrayContaining(mockQuestion.incorrect_options)); + expect(result.correct_character).toBe(mockQuestion.correct_character); }); // Test para comprobar que la función getRandomQuestion maneja correctamente un resultado vacío