From c5c2bfd734a05848fd1e83597db272b22707276a Mon Sep 17 00:00:00 2001 From: UO276976 Date: Mon, 12 Feb 2024 09:55:05 +0100 Subject: [PATCH 01/34] =?UTF-8?q?A=C3=B1adido=20Participation.js=20compone?= =?UTF-8?q?nt?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- webapp/src/components/Participation.js | 1 + 1 file changed, 1 insertion(+) create mode 100644 webapp/src/components/Participation.js diff --git a/webapp/src/components/Participation.js b/webapp/src/components/Participation.js new file mode 100644 index 00000000..4fc2c40b --- /dev/null +++ b/webapp/src/components/Participation.js @@ -0,0 +1 @@ +import React, { useState } from 'react'; \ No newline at end of file From 76d608f7d19e5530a584851d926f338a0965f298 Mon Sep 17 00:00:00 2001 From: UO276976 Date: Mon, 12 Feb 2024 10:39:08 +0100 Subject: [PATCH 02/34] Updating Participation component --- webapp/src/App.js | 7 +++++++ webapp/src/components/Participation.js | 17 ++++++++++++++++- 2 files changed, 23 insertions(+), 1 deletion(-) diff --git a/webapp/src/App.js b/webapp/src/App.js index d932005b..4228c09f 100644 --- a/webapp/src/App.js +++ b/webapp/src/App.js @@ -5,10 +5,16 @@ import CssBaseline from '@mui/material/CssBaseline'; import Container from '@mui/material/Container'; import Typography from '@mui/material/Typography'; import Link from '@mui/material/Link'; +import Participation from './components/Participation'; function App() { + const [showComponent, setShowComponent] = useState(0); const [showLogin, setShowLogin] = useState(true); + const handleToggleComponentView = () => { + setShowComponent(showComponent); + } + const handleToggleView = () => { setShowLogin(!showLogin); }; @@ -19,6 +25,7 @@ function App() { Welcome to the 2024 edition of the Software Architecture course + {showLogin ? : } {showLogin ? ( diff --git a/webapp/src/components/Participation.js b/webapp/src/components/Participation.js index 4fc2c40b..df22caae 100644 --- a/webapp/src/components/Participation.js +++ b/webapp/src/components/Participation.js @@ -1 +1,16 @@ -import React, { useState } from 'react'; \ No newline at end of file +import React, { useState } from 'react'; +import { Container, Typography } from '@mui/material'; + +const Participation = () => { + return ( + + + User Participation + + + + + ); + }; + + export default Participation; \ No newline at end of file From 0bc503b01d5e6ea928484efcea9c35e07c7a7d6f Mon Sep 17 00:00:00 2001 From: Juan Date: Mon, 12 Feb 2024 10:46:49 +0100 Subject: [PATCH 03/34] =?UTF-8?q?Cron=C3=B3metro=20dependiente=20de=20un?= =?UTF-8?q?=20bot=C3=B3n?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- webapp/src/components/Login.js | 21 ++++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/webapp/src/components/Login.js b/webapp/src/components/Login.js index 0ad6268e..da55f4b0 100644 --- a/webapp/src/components/Login.js +++ b/webapp/src/components/Login.js @@ -10,6 +10,8 @@ const Login = () => { const [loginSuccess, setLoginSuccess] = useState(false); const [createdAt, setCreatedAt] = useState(''); const [openSnackbar, setOpenSnackbar] = useState(false); + const [timeStart, setTimeStart] = useState(0); + const [timeElapsed, setTimeElapsed] = useState(0); const apiEndpoint = process.env.REACT_APP_API_ENDPOINT || 'http://localhost:8000'; @@ -19,9 +21,12 @@ const Login = () => { // Extract data from the response const { createdAt: userCreatedAt } = response.data; - + + setTimeStart(Date.now()); setCreatedAt(userCreatedAt); setLoginSuccess(true); + + setOpenSnackbar(true); } catch (error) { @@ -29,6 +34,14 @@ const Login = () => { } }; + const calculateTime = async () => { + try { + setTimeElapsed((Date.now() - timeStart) / 1000); + } catch (error) { + setError(error.response.data.error); + } + }; + const handleCloseSnackbar = () => { setOpenSnackbar(false); }; @@ -43,6 +56,12 @@ const Login = () => { Your account was created on {new Date(createdAt).toLocaleDateString()}. + + Han pasado {timeElapsed} segundos. + + ) : (
From 89989c001b421ad788e14b0596c3bf68c68c751b Mon Sep 17 00:00:00 2001 From: Alfonso Date: Sat, 17 Feb 2024 20:23:40 +0100 Subject: [PATCH 04/34] Update menu and participation --- webapp/src/App.js | 45 -------------------------- webapp/src/App.jsx | 21 ++++++++++++ webapp/src/components/Game.js | 10 ++++++ webapp/src/components/Participation.js | 24 ++++++-------- 4 files changed, 40 insertions(+), 60 deletions(-) delete mode 100644 webapp/src/App.js create mode 100644 webapp/src/App.jsx create mode 100644 webapp/src/components/Game.js diff --git a/webapp/src/App.js b/webapp/src/App.js deleted file mode 100644 index 4228c09f..00000000 --- a/webapp/src/App.js +++ /dev/null @@ -1,45 +0,0 @@ -import React, { useState } from 'react'; -import AddUser from './components/AddUser'; -import Login from './components/Login'; -import CssBaseline from '@mui/material/CssBaseline'; -import Container from '@mui/material/Container'; -import Typography from '@mui/material/Typography'; -import Link from '@mui/material/Link'; -import Participation from './components/Participation'; - -function App() { - const [showComponent, setShowComponent] = useState(0); - const [showLogin, setShowLogin] = useState(true); - - const handleToggleComponentView = () => { - setShowComponent(showComponent); - } - - const handleToggleView = () => { - setShowLogin(!showLogin); - }; - - return ( - - - - Welcome to the 2024 edition of the Software Architecture course - - - {showLogin ? : } - - {showLogin ? ( - - Don't have an account? Register here. - - ) : ( - - Already have an account? Login here. - - )} - - - ); -} - -export default App; diff --git a/webapp/src/App.jsx b/webapp/src/App.jsx new file mode 100644 index 00000000..ee71223d --- /dev/null +++ b/webapp/src/App.jsx @@ -0,0 +1,21 @@ +import React, { useState } from 'react'; +import './App.css'; +import { Game } from './components/Game'; +import { Participation } from './components/Participation'; + +function App() { + const [menuState, setMenuState] = useState(0); + + const goTo = (parameter) => { + setMenuState(parameter); + }; + + return ( + <> + {menuState === 0 && goTo(x)} />} + {menuState === 1 && goTo(x)} />} + + ); +} + +export default App; diff --git a/webapp/src/components/Game.js b/webapp/src/components/Game.js new file mode 100644 index 00000000..4d94bd05 --- /dev/null +++ b/webapp/src/components/Game.js @@ -0,0 +1,10 @@ +import React from 'react'; + +export const Game = ({ goTo }) => { + return ( +
+

Game

+ +
+ ); +}; diff --git a/webapp/src/components/Participation.js b/webapp/src/components/Participation.js index df22caae..06e820fa 100644 --- a/webapp/src/components/Participation.js +++ b/webapp/src/components/Participation.js @@ -1,16 +1,10 @@ -import React, { useState } from 'react'; -import { Container, Typography } from '@mui/material'; +import React from 'react'; -const Participation = () => { - return ( - - - User Participation - - - - - ); - }; - - export default Participation; \ No newline at end of file +export const Participation = ({ goTo }) => { + return ( +
+

Participation

+ +
+ ); +}; From ea9835a4a4fd8f038740a4c71f98ee96afa53886 Mon Sep 17 00:00:00 2001 From: UO276976 Date: Mon, 19 Feb 2024 10:53:46 +0100 Subject: [PATCH 05/34] update participation --- package-lock.json | 68 ++++++++++++++++++++++++++ package.json | 6 +++ webapp/src/components/Participation.js | 7 +++ 3 files changed, 81 insertions(+) create mode 100644 package-lock.json create mode 100644 package.json diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 00000000..d08fd260 --- /dev/null +++ b/package-lock.json @@ -0,0 +1,68 @@ +{ + "name": "wiq_es3b", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "dependencies": { + "chart.js": "^4.4.1", + "react-chartjs-2": "^5.2.0" + } + }, + "node_modules/@kurkle/color": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/@kurkle/color/-/color-0.3.2.tgz", + "integrity": "sha512-fuscdXJ9G1qb7W8VdHi+IwRqij3lBkosAm4ydQtEmbY58OzHXqQhvlxqEkoz0yssNVn38bcpRWgA9PP+OGoisw==" + }, + "node_modules/chart.js": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/chart.js/-/chart.js-4.4.1.tgz", + "integrity": "sha512-C74QN1bxwV1v2PEujhmKjOZ7iUM4w6BWs23Md/6aOZZSlwMzeCIDGuZay++rBgChYru7/+QFeoQW0fQoP534Dg==", + "dependencies": { + "@kurkle/color": "^0.3.0" + }, + "engines": { + "pnpm": ">=7" + } + }, + "node_modules/js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", + "peer": true + }, + "node_modules/loose-envify": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", + "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", + "peer": true, + "dependencies": { + "js-tokens": "^3.0.0 || ^4.0.0" + }, + "bin": { + "loose-envify": "cli.js" + } + }, + "node_modules/react": { + "version": "18.2.0", + "resolved": "https://registry.npmjs.org/react/-/react-18.2.0.tgz", + "integrity": "sha512-/3IjMdb2L9QbBdWiW5e3P2/npwMBaU9mHCSCUzNln0ZCYbcfTsGbTJrU/kGemdH2IWmB2ioZ+zkxtmq6g09fGQ==", + "peer": true, + "dependencies": { + "loose-envify": "^1.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/react-chartjs-2": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/react-chartjs-2/-/react-chartjs-2-5.2.0.tgz", + "integrity": "sha512-98iN5aguJyVSxp5U3CblRLH67J8gkfyGNbiK3c+l1QI/G4irHMPQw44aEPmjVag+YKTyQ260NcF82GTQ3bdscA==", + "peerDependencies": { + "chart.js": "^4.1.1", + "react": "^16.8.0 || ^17.0.0 || ^18.0.0" + } + } + } +} diff --git a/package.json b/package.json new file mode 100644 index 00000000..28aa4d71 --- /dev/null +++ b/package.json @@ -0,0 +1,6 @@ +{ + "dependencies": { + "chart.js": "^4.4.1", + "react-chartjs-2": "^5.2.0" + } +} diff --git a/webapp/src/components/Participation.js b/webapp/src/components/Participation.js index 06e820fa..4fce5e5c 100644 --- a/webapp/src/components/Participation.js +++ b/webapp/src/components/Participation.js @@ -1,4 +1,11 @@ import React from 'react'; +import Chart from "chart.js/auto"; + +const mongoose = require('mongoose'); + +// Número de juegos, preguntas acertadas/falladas, tiempos +// Esquema de preguntas -> question, correct, incorrects[] +// Esquema de Juegos -> usuario, preguntas[], respuestas[], tiempo export const Participation = ({ goTo }) => { return ( From 2992aec694d9cb4b7bf93e811a0f2930df8f4b73 Mon Sep 17 00:00:00 2001 From: UO276976 Date: Mon, 26 Feb 2024 10:25:36 +0100 Subject: [PATCH 06/34] Added game model and service --- docker-compose.yml | 15 + gameservice/.dockerignore | 2 + gameservice/Dockerfile | 20 + gameservice/game-model.js | 26 + gameservice/game-service.js | 53 ++ gameservice/package-lock.json | 909 ++++++++++++++++++++++++++++++ gameservice/package.json | 25 + gatewayservice/gateway-service.js | 12 + 8 files changed, 1062 insertions(+) create mode 100644 gameservice/.dockerignore create mode 100644 gameservice/Dockerfile create mode 100644 gameservice/game-model.js create mode 100644 gameservice/game-service.js create mode 100644 gameservice/package-lock.json create mode 100644 gameservice/package.json diff --git a/docker-compose.yml b/docker-compose.yml index 6acbff77..7dd0b251 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -63,6 +63,20 @@ services: environment: MONGODB_URI: mongodb://mongodb:27017/questionsdb + gameservice: + container_name: gameservice-${teamname:-defaultASW} + image: ghcr.io/arquisoft/wiq_es3b/gameservice:latest + profiles: ["dev", "prod"] + build: ./gameservice + depends_on: + - mongodb + ports: + - "8005:8005" + networks: + - mynetwork + environment: + MONGODB_URI: mongodb://mongodb:27017/gamesdb + gatewayservice: container_name: gatewayservice-${teamname:-defaultASW} image: ghcr.io/arquisoft/wiq_es3b/gatewayservice:latest @@ -74,6 +88,7 @@ services: - authservice - questiongenerationservice - questionservice + - gameservice ports: - "8000:8000" networks: diff --git a/gameservice/.dockerignore b/gameservice/.dockerignore new file mode 100644 index 00000000..3091757a --- /dev/null +++ b/gameservice/.dockerignore @@ -0,0 +1,2 @@ +node_modules +coverage \ No newline at end of file diff --git a/gameservice/Dockerfile b/gameservice/Dockerfile new file mode 100644 index 00000000..7acae80a --- /dev/null +++ b/gameservice/Dockerfile @@ -0,0 +1,20 @@ +# Use an official Node.js runtime as a parent image +FROM node:20 + +# Set the working directory in the container +WORKDIR /usr/src/gameservice + +# Copy package.json and package-lock.json to the working directory +COPY package*.json ./ + +# Install app dependencies +RUN npm install + +# Copy the app source code to the working directory +COPY . . + +# Expose the port the app runs on +EXPOSE 8005 + +# Define the command to run your app +CMD ["node", "game-service.js"] diff --git a/gameservice/game-model.js b/gameservice/game-model.js new file mode 100644 index 00000000..d6375494 --- /dev/null +++ b/gameservice/game-model.js @@ -0,0 +1,26 @@ +const mongoose = require('mongoose'); + +//usuario, preguntas[], respuestas[], tiempo + +const gameSchema = new mongoose.Schema({ + user: { + type: String, + required: true, + }, + questions: { + type: [String], + required: true, + }, + answers: { + type: [Number], + required: true, + }, + time: { + type: Number, + required: true, + }, +}); + +const Game = mongoose.model('game', gameSchema); + +module.exports = Game; diff --git a/gameservice/game-service.js b/gameservice/game-service.js new file mode 100644 index 00000000..d5fae985 --- /dev/null +++ b/gameservice/game-service.js @@ -0,0 +1,53 @@ +const express = require('express'); +const mongoose = require('mongoose');requeridos +const Game = require('./game-model'); // Importa el modelo de preguntas + +const app = express(); +const port = 8005; // Puerto para el servicio de preguntas + +app.use(express.json()); + +// Función para validar campos requeridos +const validateRequiredFields = (req, fields) => { + for (const field of fields) { + if (!(field in req.body)) { + throw new Error(`Field '${field}' is required.`); + } + } +}; + +// Ruta para agregar una nueva pregunta +app.post('/addgame', async (req, res) => { + try { + validateRequiredFields(req, ['user', 'questions', 'answers', 'time']); + + const { user, questions, answers, time } = req.body; + + // Crea una nueva instancia del modelo de games + const newGame = new Game({ + user, + questions, + answers, + time, + }); + + // Guarda el nuevo Game en la base de datos + const savedGame = await newGame.save(); + + res.status(201).json(savedGame); + } catch (error) { + res.status(500).json({ error: 'Internal Server Error' }); + } +}); + +// logica para games?? + +// Conecta a la base de datos de games +const mongoUri = process.env.MONGODB_URI || 'mongodb://localhost:27017/gamesdb'; +mongoose.connect(mongoUri); + +const server = app.listen(port, () => { + console.log(`Games Service listening at http://localhost:${port}`); +}); + +module.exports = server; \ No newline at end of file diff --git a/gameservice/package-lock.json b/gameservice/package-lock.json new file mode 100644 index 00000000..26e66353 --- /dev/null +++ b/gameservice/package-lock.json @@ -0,0 +1,909 @@ +{ + "name": "gameservice", + "version": "1.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "gameservice", + "version": "1.0.0", + "license": "ISC", + "dependencies": { + "express": "^4.18.2", + "mongoose": "^8.2.0" + } + }, + "node_modules/@mongodb-js/saslprep": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/@mongodb-js/saslprep/-/saslprep-1.1.4.tgz", + "integrity": "sha512-8zJ8N1x51xo9hwPh6AWnKdLGEC5N3lDa6kms1YHmFBoRhTpJR6HG8wWk0td1MVCu9cD4YBrvjZEtd5Obw0Fbnw==", + "dependencies": { + "sparse-bitfield": "^3.0.3" + } + }, + "node_modules/@types/webidl-conversions": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/@types/webidl-conversions/-/webidl-conversions-7.0.3.tgz", + "integrity": "sha512-CiJJvcRtIgzadHCYXw7dqEnMNRjhGZlYK05Mj9OyktqV8uVT8fD2BFOB7S1uwBE3Kj2Z+4UyPmFw/Ixgw/LAlA==" + }, + "node_modules/@types/whatwg-url": { + "version": "11.0.4", + "resolved": "https://registry.npmjs.org/@types/whatwg-url/-/whatwg-url-11.0.4.tgz", + "integrity": "sha512-lXCmTWSHJvf0TRSO58nm978b8HJ/EdsSsEKLd3ODHFjo+3VGAyyTp4v50nWvwtzBxSMQrVOK7tcuN0zGPLICMw==", + "dependencies": { + "@types/webidl-conversions": "*" + } + }, + "node_modules/accepts": { + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz", + "integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==", + "dependencies": { + "mime-types": "~2.1.34", + "negotiator": "0.6.3" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/array-flatten": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", + "integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==" + }, + "node_modules/body-parser": { + "version": "1.20.1", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.1.tgz", + "integrity": "sha512-jWi7abTbYwajOytWCQc37VulmWiRae5RyTpaCyDcS5/lMdtwSz5lOpDE67srw/HYe35f1z3fDQw+3txg7gNtWw==", + "dependencies": { + "bytes": "3.1.2", + "content-type": "~1.0.4", + "debug": "2.6.9", + "depd": "2.0.0", + "destroy": "1.2.0", + "http-errors": "2.0.0", + "iconv-lite": "0.4.24", + "on-finished": "2.4.1", + "qs": "6.11.0", + "raw-body": "2.5.1", + "type-is": "~1.6.18", + "unpipe": "1.0.0" + }, + "engines": { + "node": ">= 0.8", + "npm": "1.2.8000 || >= 1.4.16" + } + }, + "node_modules/bson": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/bson/-/bson-6.3.0.tgz", + "integrity": "sha512-balJfqwwTBddxfnidJZagCBPP/f48zj9Sdp3OJswREOgsJzHiQSaOIAtApSgDQFYgHqAvFkp53AFSqjMDZoTFw==", + "engines": { + "node": ">=16.20.1" + } + }, + "node_modules/bytes": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", + "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/call-bind": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.7.tgz", + "integrity": "sha512-GHTSNSYICQ7scH7sZ+M2rFopRoLh8t2bLSW6BbgrtLsahOIB5iyAVJf9GjWK3cYTDaMj4XdBpM1cA6pIS0Kv2w==", + "dependencies": { + "es-define-property": "^1.0.0", + "es-errors": "^1.3.0", + "function-bind": "^1.1.2", + "get-intrinsic": "^1.2.4", + "set-function-length": "^1.2.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/content-disposition": { + "version": "0.5.4", + "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz", + "integrity": "sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==", + "dependencies": { + "safe-buffer": "5.2.1" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/content-type": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz", + "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/cookie": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.5.0.tgz", + "integrity": "sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/cookie-signature": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", + "integrity": "sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==" + }, + "node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/define-data-property": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz", + "integrity": "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==", + "dependencies": { + "es-define-property": "^1.0.0", + "es-errors": "^1.3.0", + "gopd": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/depd": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", + "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/destroy": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz", + "integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==", + "engines": { + "node": ">= 0.8", + "npm": "1.2.8000 || >= 1.4.16" + } + }, + "node_modules/ee-first": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", + "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==" + }, + "node_modules/encodeurl": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", + "integrity": "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/es-define-property": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.0.tgz", + "integrity": "sha512-jxayLKShrEqqzJ0eumQbVhTYQM27CfT1T35+gCgDFoL82JLsXqTJ76zv6A0YLOgEnLUMvLzsDsGIrl8NFpT2gQ==", + "dependencies": { + "get-intrinsic": "^1.2.4" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-errors": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", + "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/escape-html": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", + "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==" + }, + "node_modules/etag": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", + "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/express": { + "version": "4.18.2", + "resolved": "https://registry.npmjs.org/express/-/express-4.18.2.tgz", + "integrity": "sha512-5/PsL6iGPdfQ/lKM1UuielYgv3BUoJfz1aUwU9vHZ+J7gyvwdQXFEBIEIaxeGf0GIcreATNyBExtalisDbuMqQ==", + "dependencies": { + "accepts": "~1.3.8", + "array-flatten": "1.1.1", + "body-parser": "1.20.1", + "content-disposition": "0.5.4", + "content-type": "~1.0.4", + "cookie": "0.5.0", + "cookie-signature": "1.0.6", + "debug": "2.6.9", + "depd": "2.0.0", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "finalhandler": "1.2.0", + "fresh": "0.5.2", + "http-errors": "2.0.0", + "merge-descriptors": "1.0.1", + "methods": "~1.1.2", + "on-finished": "2.4.1", + "parseurl": "~1.3.3", + "path-to-regexp": "0.1.7", + "proxy-addr": "~2.0.7", + "qs": "6.11.0", + "range-parser": "~1.2.1", + "safe-buffer": "5.2.1", + "send": "0.18.0", + "serve-static": "1.15.0", + "setprototypeof": "1.2.0", + "statuses": "2.0.1", + "type-is": "~1.6.18", + "utils-merge": "1.0.1", + "vary": "~1.1.2" + }, + "engines": { + "node": ">= 0.10.0" + } + }, + "node_modules/finalhandler": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.2.0.tgz", + "integrity": "sha512-5uXcUVftlQMFnWC9qu/svkWv3GTd2PfUhK/3PLkYNAe7FbqJMt3515HaxE6eRL74GdsriiwujiawdaB1BpEISg==", + "dependencies": { + "debug": "2.6.9", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "on-finished": "2.4.1", + "parseurl": "~1.3.3", + "statuses": "2.0.1", + "unpipe": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/forwarded": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", + "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/fresh": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", + "integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/function-bind": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-intrinsic": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.4.tgz", + "integrity": "sha512-5uYhsJH8VJBTv7oslg4BznJYhDoRI6waYCxMmCdnTrcCrHA/fCFKoTFz2JKKE0HdDFUF7/oQuhzumXJK7paBRQ==", + "dependencies": { + "es-errors": "^1.3.0", + "function-bind": "^1.1.2", + "has-proto": "^1.0.1", + "has-symbols": "^1.0.3", + "hasown": "^2.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/gopd": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.0.1.tgz", + "integrity": "sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==", + "dependencies": { + "get-intrinsic": "^1.1.3" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-property-descriptors": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz", + "integrity": "sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==", + "dependencies": { + "es-define-property": "^1.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-proto": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.0.3.tgz", + "integrity": "sha512-SJ1amZAJUiZS+PhsVLf5tGydlaVB8EdFpaSO4gmiUKUOxk8qzn5AIy4ZeJUmh22znIdk/uMAUT2pl3FxzVUH+Q==", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-symbols": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz", + "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/hasown": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.1.tgz", + "integrity": "sha512-1/th4MHjnwncwXsIW6QMzlvYL9kG5e/CpVvLRZe4XPa8TOUNbCELqmvhDmnkNsAjwaG4+I8gJJL0JBvTTLO9qA==", + "dependencies": { + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/http-errors": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz", + "integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==", + "dependencies": { + "depd": "2.0.0", + "inherits": "2.0.4", + "setprototypeof": "1.2.0", + "statuses": "2.0.1", + "toidentifier": "1.0.1" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/iconv-lite": { + "version": "0.4.24", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", + "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" + }, + "node_modules/ipaddr.js": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", + "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==", + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/kareem": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/kareem/-/kareem-2.5.1.tgz", + "integrity": "sha512-7jFxRVm+jD+rkq3kY0iZDJfsO2/t4BBPeEb2qKn2lR/9KhuksYk5hxzfRYWMPV8P/x2d0kHD306YyWLzjjH+uA==", + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/media-typer": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", + "integrity": "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/memory-pager": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/memory-pager/-/memory-pager-1.5.0.tgz", + "integrity": "sha512-ZS4Bp4r/Zoeq6+NLJpP+0Zzm0pR8whtGPf1XExKLJBAczGMnSi3It14OiNCStjQjM6NU1okjQGSxgEZN8eBYKg==" + }, + "node_modules/merge-descriptors": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz", + "integrity": "sha512-cCi6g3/Zr1iqQi6ySbseM1Xvooa98N0w31jzUYrXPX2xqObmFGHJ0tQ5u74H3mVh7wLouTseZyYIq39g8cNp1w==" + }, + "node_modules/methods": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", + "integrity": "sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", + "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", + "bin": { + "mime": "cli.js" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mongodb": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/mongodb/-/mongodb-6.3.0.tgz", + "integrity": "sha512-tt0KuGjGtLUhLoU263+xvQmPHEGTw5LbcNC73EoFRYgSHwZt5tsoJC110hDyO1kjQzpgNrpdcSza9PknWN4LrA==", + "dependencies": { + "@mongodb-js/saslprep": "^1.1.0", + "bson": "^6.2.0", + "mongodb-connection-string-url": "^3.0.0" + }, + "engines": { + "node": ">=16.20.1" + }, + "peerDependencies": { + "@aws-sdk/credential-providers": "^3.188.0", + "@mongodb-js/zstd": "^1.1.0", + "gcp-metadata": "^5.2.0", + "kerberos": "^2.0.1", + "mongodb-client-encryption": ">=6.0.0 <7", + "snappy": "^7.2.2", + "socks": "^2.7.1" + }, + "peerDependenciesMeta": { + "@aws-sdk/credential-providers": { + "optional": true + }, + "@mongodb-js/zstd": { + "optional": true + }, + "gcp-metadata": { + "optional": true + }, + "kerberos": { + "optional": true + }, + "mongodb-client-encryption": { + "optional": true + }, + "snappy": { + "optional": true + }, + "socks": { + "optional": true + } + } + }, + "node_modules/mongodb-connection-string-url": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/mongodb-connection-string-url/-/mongodb-connection-string-url-3.0.0.tgz", + "integrity": "sha512-t1Vf+m1I5hC2M5RJx/7AtxgABy1cZmIPQRMXw+gEIPn/cZNF3Oiy+l0UIypUwVB5trcWHq3crg2g3uAR9aAwsQ==", + "dependencies": { + "@types/whatwg-url": "^11.0.2", + "whatwg-url": "^13.0.0" + } + }, + "node_modules/mongoose": { + "version": "8.2.0", + "resolved": "https://registry.npmjs.org/mongoose/-/mongoose-8.2.0.tgz", + "integrity": "sha512-la93n6zCYRbPS+c5N9oTDAktvREy5OT9OCljp1Tah0y3+p8UPMTAoabWaLZMdzYruOtF9/9GRf6MasaZjiZP1A==", + "dependencies": { + "bson": "^6.2.0", + "kareem": "2.5.1", + "mongodb": "6.3.0", + "mpath": "0.9.0", + "mquery": "5.0.0", + "ms": "2.1.3", + "sift": "16.0.1" + }, + "engines": { + "node": ">=16.20.1" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/mongoose" + } + }, + "node_modules/mongoose/node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" + }, + "node_modules/mpath": { + "version": "0.9.0", + "resolved": "https://registry.npmjs.org/mpath/-/mpath-0.9.0.tgz", + "integrity": "sha512-ikJRQTk8hw5DEoFVxHG1Gn9T/xcjtdnOKIU1JTmGjZZlg9LST2mBLmcX3/ICIbgJydT2GOc15RnNy5mHmzfSew==", + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/mquery": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/mquery/-/mquery-5.0.0.tgz", + "integrity": "sha512-iQMncpmEK8R8ncT8HJGsGc9Dsp8xcgYMVSbs5jgnm1lFHTZqMJTUWTDx1LBO8+mK3tPNZWFLBghQEIOULSTHZg==", + "dependencies": { + "debug": "4.x" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/mquery/node_modules/debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/mquery/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/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" + }, + "node_modules/negotiator": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", + "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/object-inspect": { + "version": "1.13.1", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.1.tgz", + "integrity": "sha512-5qoj1RUiKOMsCCNLV1CBiPYE10sziTsnmNxkAI/rZhiD63CF7IqdFGC/XzjWjpSgLf0LxXX3bDFIh0E18f6UhQ==", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/on-finished": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", + "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==", + "dependencies": { + "ee-first": "1.1.1" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/parseurl": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", + "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/path-to-regexp": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", + "integrity": "sha512-5DFkuoqlv1uYQKxy8omFBeJPQcdoE07Kv2sferDCrAq1ohOU+MSDswDIbnx3YAM60qIOnYa53wBhXW0EbMonrQ==" + }, + "node_modules/proxy-addr": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", + "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==", + "dependencies": { + "forwarded": "0.2.0", + "ipaddr.js": "1.9.1" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/punycode": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", + "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", + "engines": { + "node": ">=6" + } + }, + "node_modules/qs": { + "version": "6.11.0", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.11.0.tgz", + "integrity": "sha512-MvjoMCJwEarSbUYk5O+nmoSzSutSsTwF85zcHPQ9OrlFoZOYIjaqBAJIqIXjptyD5vThxGq52Xu/MaJzRkIk4Q==", + "dependencies": { + "side-channel": "^1.0.4" + }, + "engines": { + "node": ">=0.6" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/range-parser": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", + "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/raw-body": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.1.tgz", + "integrity": "sha512-qqJBtEyVgS0ZmPGdCFPWJ3FreoqvG4MVQln/kCgF7Olq95IbOp0/BWyMwbdtn4VTvkM8Y7khCQ2Xgk/tcrCXig==", + "dependencies": { + "bytes": "3.1.2", + "http-errors": "2.0.0", + "iconv-lite": "0.4.24", + "unpipe": "1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "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/safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" + }, + "node_modules/send": { + "version": "0.18.0", + "resolved": "https://registry.npmjs.org/send/-/send-0.18.0.tgz", + "integrity": "sha512-qqWzuOjSFOuqPjFe4NOsMLafToQQwBSOEpS+FwEt3A2V3vKubTquT3vmLTQpFgMXp8AlFWFuP1qKaJZOtPpVXg==", + "dependencies": { + "debug": "2.6.9", + "depd": "2.0.0", + "destroy": "1.2.0", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "fresh": "0.5.2", + "http-errors": "2.0.0", + "mime": "1.6.0", + "ms": "2.1.3", + "on-finished": "2.4.1", + "range-parser": "~1.2.1", + "statuses": "2.0.1" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/send/node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" + }, + "node_modules/serve-static": { + "version": "1.15.0", + "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.15.0.tgz", + "integrity": "sha512-XGuRDNjXUijsUL0vl6nSD7cwURuzEgglbOaFuZM9g3kwDXOWVTck0jLzjPzGD+TazWbboZYu52/9/XPdUgne9g==", + "dependencies": { + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "parseurl": "~1.3.3", + "send": "0.18.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/set-function-length": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.1.tgz", + "integrity": "sha512-j4t6ccc+VsKwYHso+kElc5neZpjtq9EnRICFZtWyBsLojhmeF/ZBd/elqm22WJh/BziDe/SBiOeAt0m2mfLD0g==", + "dependencies": { + "define-data-property": "^1.1.2", + "es-errors": "^1.3.0", + "function-bind": "^1.1.2", + "get-intrinsic": "^1.2.3", + "gopd": "^1.0.1", + "has-property-descriptors": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/setprototypeof": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", + "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==" + }, + "node_modules/side-channel": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.5.tgz", + "integrity": "sha512-QcgiIWV4WV7qWExbN5llt6frQB/lBven9pqliLXfGPB+K9ZYXxDozp0wLkHS24kWCm+6YXH/f0HhnObZnZOBnQ==", + "dependencies": { + "call-bind": "^1.0.6", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.4", + "object-inspect": "^1.13.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/sift": { + "version": "16.0.1", + "resolved": "https://registry.npmjs.org/sift/-/sift-16.0.1.tgz", + "integrity": "sha512-Wv6BjQ5zbhW7VFefWusVP33T/EM0vYikCaQ2qR8yULbsilAT8/wQaXvuQ3ptGLpoKx+lihJE3y2UTgKDyyNHZQ==" + }, + "node_modules/sparse-bitfield": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/sparse-bitfield/-/sparse-bitfield-3.0.3.tgz", + "integrity": "sha512-kvzhi7vqKTfkh0PZU+2D2PIllw2ymqJKujUcyPMd9Y75Nv4nPbGJZXNhxsgdQab2BmlDct1YnfQCguEvHr7VsQ==", + "dependencies": { + "memory-pager": "^1.0.2" + } + }, + "node_modules/statuses": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", + "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/toidentifier": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", + "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==", + "engines": { + "node": ">=0.6" + } + }, + "node_modules/tr46": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-4.1.1.tgz", + "integrity": "sha512-2lv/66T7e5yNyhAAC4NaKe5nVavzuGJQVVtRYLyQ2OI8tsJ61PMLlelehb0wi2Hx6+hT/OJUWZcw8MjlSRnxvw==", + "dependencies": { + "punycode": "^2.3.0" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/type-is": { + "version": "1.6.18", + "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", + "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", + "dependencies": { + "media-typer": "0.3.0", + "mime-types": "~2.1.24" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/unpipe": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", + "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/utils-merge": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", + "integrity": "sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==", + "engines": { + "node": ">= 0.4.0" + } + }, + "node_modules/vary": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", + "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/webidl-conversions": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-7.0.0.tgz", + "integrity": "sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g==", + "engines": { + "node": ">=12" + } + }, + "node_modules/whatwg-url": { + "version": "13.0.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-13.0.0.tgz", + "integrity": "sha512-9WWbymnqj57+XEuqADHrCJ2eSXzn8WXIW/YSGaZtb2WKAInQ6CHfaUUcTyyver0p8BDg5StLQq8h1vtZuwmOig==", + "dependencies": { + "tr46": "^4.1.1", + "webidl-conversions": "^7.0.0" + }, + "engines": { + "node": ">=16" + } + } + } +} diff --git a/gameservice/package.json b/gameservice/package.json new file mode 100644 index 00000000..08f5de43 --- /dev/null +++ b/gameservice/package.json @@ -0,0 +1,25 @@ +{ + "name": "gameservice", + "version": "1.0.0", + "description": "Game service, in charge of adding the games to the application", + "main": "service.js", + "scripts": { + "start": "node game-service.js", + "test": "jest" + }, + "repository": { + "type": "git", + "url": "git+https://github.com/arquisoft/wiq_es3b.git" + }, + "author": "", + "license": "ISC", + "bugs": { + "url": "https://github.com/arquisoft/wiq_es3b/issues" + }, + "homepage": "https://github.com/arquisoft/wiq_es3b#readme", + "dependencies": { + "express": "^4.18.2", + "mongoose": "^8.2.0" + } + } + \ No newline at end of file diff --git a/gatewayservice/gateway-service.js b/gatewayservice/gateway-service.js index 88679a43..8225dac3 100644 --- a/gatewayservice/gateway-service.js +++ b/gatewayservice/gateway-service.js @@ -10,6 +10,7 @@ const authServiceUrl = process.env.AUTH_SERVICE_URL || 'http://localhost:8002'; const userServiceUrl = process.env.USER_SERVICE_URL || 'http://localhost:8001'; const questionGenerationServiceUrl = process.env.QUESTION_GENERATION_SERVICE_URL || 'http://localhost:8003'; const questionServiceUrl = process.env.QUESTIONS_SERVICE_URL || 'http://localhost:8004'; +const gameServiceUrl = process.env.GAME_SERVICE_URL || 'http://localhost:8005'; app.use(cors()); app.use(express.json()); @@ -66,6 +67,17 @@ app.post('/addquestion', async (req, res) => { } }); +// Ruta para agregar una nuevo game +app.post('/addgame', async (req, res) => { + try { + // Forward the add game request to the games service + const gameResponse = await axios.post(gameServiceUrl + '/addgame', req.body); + res.json(gameResponse.data); + } catch (error) { + res.status(error.response.status).json({ error: error.response.data.error }); + } +}); + // Start the gateway service const server = app.listen(port, () => { console.log(`Gateway Service listening at http://localhost:${port}`); From db34105c9d37f18d39e93586e2ce194ad5459a10 Mon Sep 17 00:00:00 2001 From: UO276976 Date: Mon, 26 Feb 2024 10:42:22 +0100 Subject: [PATCH 07/34] Updated question model --- docker-compose.yml | 1 + questionservice/question-model.js | 20 +++++++------------- questionservice/question-service.js | 8 ++++---- questionservice/question-service.test.js | 4 ++-- 4 files changed, 14 insertions(+), 19 deletions(-) diff --git a/docker-compose.yml b/docker-compose.yml index 7dd0b251..4683cd54 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -98,6 +98,7 @@ services: USER_SERVICE_URL: http://userservice:8001 QUESTION_GENERATION_SERVICE_URL: http://questiongenerationservice:8003 QUESTIONS_SERVICE_URL: http://questionservice:8004 + GAME_SERVICE_URL: http://gameservice:8005 webapp: container_name: webapp-${teamname:-defaultASW} diff --git a/questionservice/question-model.js b/questionservice/question-model.js index 89bf65ed..1d258bfd 100644 --- a/questionservice/question-model.js +++ b/questionservice/question-model.js @@ -5,24 +5,18 @@ const questionSchema = new mongoose.Schema({ type: String, required: true, }, - options: { + correct: { + type: String, + required: true, + }, + incorrects: { type: [String], required: true, validate: { validator: function (options) { - return options.length >= 2 && options.length <= 4; + return options.length >= 1 && options.length <= 3; }, - message: 'Options must have between 2 and 4 elements.', - }, - }, - correctOptionIndex: { - type: Number, - required: true, - validate: { - validator: function (index) { - return index >= 0 && index < 4; // Max of 4 options - }, - message: 'Correct option index must be between 0 and 3.', + message: 'Options must be between 2 and 4 elements.', }, }, }); diff --git a/questionservice/question-service.js b/questionservice/question-service.js index 8748e0a3..242c93b6 100644 --- a/questionservice/question-service.js +++ b/questionservice/question-service.js @@ -19,15 +19,15 @@ const validateRequiredFields = (req, fields) => { // Ruta para agregar una nueva pregunta app.post('/addquestion', async (req, res) => { try { - validateRequiredFields(req, ['question', 'options', 'correctOptionIndex']); + validateRequiredFields(req, ['question', 'correct', 'incorrects']); - const { question, options, correctOptionIndex } = req.body; + const { question, correct, incorrects } = req.body; // Crea una nueva instancia del modelo de preguntas const newQuestion = new Question({ question, - options, - correctOptionIndex, + correct, + incorrects, }); // Guarda la nueva pregunta en la base de datos diff --git a/questionservice/question-service.test.js b/questionservice/question-service.test.js index 06af2ee5..5b79c90d 100644 --- a/questionservice/question-service.test.js +++ b/questionservice/question-service.test.js @@ -21,8 +21,8 @@ describe('Question Service', () => { it('should add a new question on POST /addquestion', async () => { const newQuestion = { question: 'What is the capital of France?', - options: ['Paris', 'Berlin', 'Madrid', 'Rome'], - correctOptionIndex: 0, + correct: "Paris", + incorrects: ['Berlin', 'Madrid', 'Rome'], }; const response = await request(app).post('/addquestion').send(newQuestion); From 4ad5356ec87ee1b8e8fb3ac8cb9c9fc1b7838354 Mon Sep 17 00:00:00 2001 From: Alfonso Date: Wed, 28 Feb 2024 22:16:30 +0100 Subject: [PATCH 08/34] Updated Game model --- gameservice/game-model.js | 30 +++++++++++++++++++----------- gameservice/game-service.js | 22 +++++++++++----------- 2 files changed, 30 insertions(+), 22 deletions(-) diff --git a/gameservice/game-model.js b/gameservice/game-model.js index d6375494..8856fb69 100644 --- a/gameservice/game-model.js +++ b/gameservice/game-model.js @@ -1,26 +1,34 @@ const mongoose = require('mongoose'); -//usuario, preguntas[], respuestas[], tiempo +//usuario, preguntas[], respuestas[], tiempo const gameSchema = new mongoose.Schema({ user: { - type: String, + type: String, required: true, }, questions: { - type: [String], - required: true, - }, - answers: { - type: [Number], - required: true, + type: mongoose.Schema.Types.ObjectId, + ref: 'Question', }, - time: { - type: Number, + answers: [ + { + response: { + type: String, + required: true, + }, + isCorrect: { + type: Boolean, + required: true, + }, + } + ], + totalTime: { + type: Number, required: true, }, }); -const Game = mongoose.model('game', gameSchema); +const Game = mongoose.model('Game', gameSchema); module.exports = Game; diff --git a/gameservice/game-service.js b/gameservice/game-service.js index d5fae985..90f0f99b 100644 --- a/gameservice/game-service.js +++ b/gameservice/game-service.js @@ -1,9 +1,9 @@ const express = require('express'); -const mongoose = require('mongoose');requeridos -const Game = require('./game-model'); // Importa el modelo de preguntas +const mongoose = require('mongoose'); +const Game = require('./game-model'); // Importa el modelo de juegos const app = express(); -const port = 8005; // Puerto para el servicio de preguntas +const port = 8005; // Puerto para el servicio de juegos app.use(express.json()); @@ -16,22 +16,22 @@ const validateRequiredFields = (req, fields) => { } }; -// Ruta para agregar una nueva pregunta +// Ruta para agregar un nuevo juego app.post('/addgame', async (req, res) => { try { - validateRequiredFields(req, ['user', 'questions', 'answers', 'time']); + validateRequiredFields(req, ['user', 'questions', 'answers', 'totalTime']); - const { user, questions, answers, time } = req.body; + const { user, questions, answers, totalTime } = req.body; - // Crea una nueva instancia del modelo de games + // Crea una nueva instancia del modelo de juegos const newGame = new Game({ user, questions, answers, - time, + totalTime, }); - // Guarda el nuevo Game en la base de datos + // Guarda el nuevo juego en la base de datos const savedGame = await newGame.save(); res.status(201).json(savedGame); @@ -40,9 +40,9 @@ app.post('/addgame', async (req, res) => { } }); -// logica para games?? +// Lógica para juegos?? -// Conecta a la base de datos de games +// Conecta a la base de datos de juegos const mongoUri = process.env.MONGODB_URI || 'mongodb://localhost:27017/gamesdb'; mongoose.connect(mongoUri); From a5b83bf6471a18a420d7bebda01bb4ea6627f8b9 Mon Sep 17 00:00:00 2001 From: Alfonso Date: Wed, 28 Feb 2024 22:21:50 +0100 Subject: [PATCH 09/34] Update user in game model --- gameservice/game-model.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/gameservice/game-model.js b/gameservice/game-model.js index 8856fb69..15b3a6ef 100644 --- a/gameservice/game-model.js +++ b/gameservice/game-model.js @@ -4,12 +4,13 @@ const mongoose = require('mongoose'); const gameSchema = new mongoose.Schema({ user: { - type: String, + type: mongoose.Schema.Types.ObjectId, required: true, }, questions: { type: mongoose.Schema.Types.ObjectId, ref: 'Question', + required: true, }, answers: [ { From d0e473d71d911b7966f1db940bf5294223cfe63e Mon Sep 17 00:00:00 2001 From: Alfonso Date: Thu, 29 Feb 2024 20:55:53 +0100 Subject: [PATCH 10/34] Updated game service related test --- gameservice/game-service.test.js | 38 ++++++++++++++++++++++++ gatewayservice/gateway-service.test.js | 2 +- questionservice/question-service.test.js | 4 +-- 3 files changed, 41 insertions(+), 3 deletions(-) create mode 100644 gameservice/game-service.test.js diff --git a/gameservice/game-service.test.js b/gameservice/game-service.test.js new file mode 100644 index 00000000..beb77164 --- /dev/null +++ b/gameservice/game-service.test.js @@ -0,0 +1,38 @@ +const request = require('supertest'); +const { MongoMemoryServer } = require('mongodb-memory-server'); +const mongoose = require('mongoose'); +const app = require('./game-service'); + +let mongoServer; + +beforeAll(async () => { + mongoServer = await MongoMemoryServer.create(); + const mongoUri = mongoServer.getUri(); + process.env.MONGODB_URI = mongoUri; + mongoose.connect(mongoUri); +}); + +afterAll(async () => { + await mongoose.disconnect(); + await mongoServer.stop(); +}); + +describe('Game Service', () => { + it('should add a new game on POST /addgame', async () => { + const newGame = { + user: mongoose.Types.ObjectId(), // ID de usuario simulado + questions: mongoose.Types.ObjectId(), // ID de pregunta simulado + answers: [ + { + response: 'User response', + isCorrect: true, + }, + ], + totalTime: 120, // Tiempo total de la partida en segundos + }; + + const response = await request(app).post('/addgame').send(newGame); + expect(response.status).toBe(201); + expect(response.body).toHaveProperty('user', newGame.user.toString()); + }); +}); diff --git a/gatewayservice/gateway-service.test.js b/gatewayservice/gateway-service.test.js index bc532ffe..3eb6943d 100644 --- a/gatewayservice/gateway-service.test.js +++ b/gatewayservice/gateway-service.test.js @@ -88,7 +88,7 @@ describe('Gateway Service', () => { correctOptionIndex: 0, }); - expect(response.statusCode).toBe(200); + expect(response.statusCode).toBe(201); expect(response.body).toHaveProperty('question', 'What is the capital of France?'); }); }); \ No newline at end of file diff --git a/questionservice/question-service.test.js b/questionservice/question-service.test.js index 5b79c90d..f1bf7ed1 100644 --- a/questionservice/question-service.test.js +++ b/questionservice/question-service.test.js @@ -8,7 +8,7 @@ let mongoServer; beforeAll(async () => { mongoServer = await MongoMemoryServer.create(); const mongoUri = mongoServer.getUri(); - process.env.MONGODB_QUESTIONS_URI = mongoUri; + process.env.MONGODB_URI = mongoUri; mongoose.connect(mongoUri); }); @@ -26,7 +26,7 @@ describe('Question Service', () => { }; const response = await request(app).post('/addquestion').send(newQuestion); - expect(response.status).toBe(200); + expect(response.status).toBe(201); expect(response.body).toHaveProperty('question', 'What is the capital of France?'); }); From 775d05898f3c8b843c0027b73a4ec47c15402605 Mon Sep 17 00:00:00 2001 From: Alfonso Date: Sun, 3 Mar 2024 21:56:17 +0100 Subject: [PATCH 11/34] Update participation --- gameservice/game-service.js | 32 ++++++++++++++++- webapp/src/components/Participation.jsx | 47 ++++++++++++++++++------- 2 files changed, 65 insertions(+), 14 deletions(-) diff --git a/gameservice/game-service.js b/gameservice/game-service.js index 90f0f99b..5862559f 100644 --- a/gameservice/game-service.js +++ b/gameservice/game-service.js @@ -40,7 +40,37 @@ app.post('/addgame', async (req, res) => { } }); -// Lógica para juegos?? +// Ruta para obtener datos de participación del usuario +app.get('/getParticipation/:userId', async (req, res) => { + try { + const userId = req.params.userId; + + // Consulta para obtener los datos de participación del usuario + const participationData = await Game.aggregate([ + { $match: { user: mongoose.Types.ObjectId(userId) } }, + { + $group: { + _id: null, + totalGames: { $sum: 1 }, + correctAnswers: { $sum: { $size: { $filter: { input: "$answers", as: "answer", cond: "$$answer.isCorrect" } } } }, + incorrectAnswers: { $sum: { $size: { $filter: { input: "$answers", as: "answer", cond: { $eq: ["$$answer.isCorrect", false] } } } } }, + totalTime: { $sum: "$totalTime" }, + }, + }, + ]); + + if (participationData.length === 0) { + // No se encontraron datos para el usuario + res.status(404).json({ error: 'No participation data found for the user.' }); + return; + } + + res.status(200).json(participationData[0]); + } catch (error) { + console.error('Error al obtener datos de participación:', error); + res.status(500).json({ error: 'Internal Server Error' }); + } +}); // Conecta a la base de datos de juegos const mongoUri = process.env.MONGODB_URI || 'mongodb://localhost:27017/gamesdb'; diff --git a/webapp/src/components/Participation.jsx b/webapp/src/components/Participation.jsx index f9c009f1..d7e76fc2 100644 --- a/webapp/src/components/Participation.jsx +++ b/webapp/src/components/Participation.jsx @@ -1,17 +1,38 @@ import React from 'react'; -//import Chart from "chart.js/auto"; +import Chart from "chart.js/auto"; +import axios from 'axios'; -//const mongoose = require('mongoose'); +export const Participation = ({ userId, goTo }) => { + const [participationData, setParticipationData] = useState(null); -// Número de juegos, preguntas acertadas/falladas, tiempos -// Esquema de preguntas -> question, correct, incorrects[] -// Esquema de Juegos -> usuario, preguntas[], respuestas[], tiempo + useEffect(() => { + // Realizar la solicitud al servidor para obtener los datos de participación + const fetchData = async () => { + try { + const response = await axios.get(`http://localhost:8005/getParticipation/${userId}`); + setParticipationData(response.data); + } catch (error) { + console.error('Error al obtener los datos de participación:', error); + } + }; -export const Participation = ({ goTo }) => { - return ( -
-

Participation

- -
- ); - }; + fetchData(); + }, [userId]); + + return ( +
+

Participation

+ {participationData ? ( +
+

Número de partidas jugadas: {participationData.totalGames}

+

Preguntas acertadas: {participationData.correctAnswers}

+

Preguntas falladas: {participationData.incorrectAnswers}

+

Tiempo total jugando: {participationData.totalTime} segundos

+
+ ) : ( +

Cargando datos de participación...

+ )} + +
+ ); +}; \ No newline at end of file From dbab03246e40ee3d781e9867e85b67f0d5545a7f Mon Sep 17 00:00:00 2001 From: Alfonso Date: Sun, 3 Mar 2024 22:02:22 +0100 Subject: [PATCH 12/34] Added graphic Correct/Incorrect --- webapp/src/components/Game.js | 10 ---------- webapp/src/components/Participation.js | 17 ---------------- webapp/src/components/Participation.jsx | 26 +++++++++++++++++++++++-- 3 files changed, 24 insertions(+), 29 deletions(-) delete mode 100644 webapp/src/components/Game.js delete mode 100644 webapp/src/components/Participation.js diff --git a/webapp/src/components/Game.js b/webapp/src/components/Game.js deleted file mode 100644 index 4d94bd05..00000000 --- a/webapp/src/components/Game.js +++ /dev/null @@ -1,10 +0,0 @@ -import React from 'react'; - -export const Game = ({ goTo }) => { - return ( -
-

Game

- -
- ); -}; diff --git a/webapp/src/components/Participation.js b/webapp/src/components/Participation.js deleted file mode 100644 index 4fce5e5c..00000000 --- a/webapp/src/components/Participation.js +++ /dev/null @@ -1,17 +0,0 @@ -import React from 'react'; -import Chart from "chart.js/auto"; - -const mongoose = require('mongoose'); - -// Número de juegos, preguntas acertadas/falladas, tiempos -// Esquema de preguntas -> question, correct, incorrects[] -// Esquema de Juegos -> usuario, preguntas[], respuestas[], tiempo - -export const Participation = ({ goTo }) => { - return ( -
-

Participation

- -
- ); -}; diff --git a/webapp/src/components/Participation.jsx b/webapp/src/components/Participation.jsx index d7e76fc2..a355a393 100644 --- a/webapp/src/components/Participation.jsx +++ b/webapp/src/components/Participation.jsx @@ -1,5 +1,5 @@ -import React from 'react'; -import Chart from "chart.js/auto"; +import React, { useState, useEffect } from 'react'; +import { Bar } from 'react-chartjs-2'; import axios from 'axios'; export const Participation = ({ userId, goTo }) => { @@ -19,6 +19,27 @@ export const Participation = ({ userId, goTo }) => { fetchData(); }, [userId]); + //Gráfica + const data = { + labels: ['Preguntas Acertadas', 'Preguntas Falladas'], + datasets: [ + { + label: 'Número de preguntas', + data: [participationData?.correctAnswers || 0, participationData?.incorrectAnswers || 0], + backgroundColor: ['green', 'red'], + }, + ], + }; + + const options = { + scales: { + y: { + beginAtZero: true, + max: Math.max(participationData?.correctAnswers || 0, participationData?.incorrectAnswers || 0) + 1, + }, + }, + }; + return (

Participation

@@ -28,6 +49,7 @@ export const Participation = ({ userId, goTo }) => {

Preguntas acertadas: {participationData.correctAnswers}

Preguntas falladas: {participationData.incorrectAnswers}

Tiempo total jugando: {participationData.totalTime} segundos

+
) : (

Cargando datos de participación...

From edaf8d624419f9bc5e2ee27be039e1013c4aa99d Mon Sep 17 00:00:00 2001 From: UO276976 Date: Mon, 4 Mar 2024 09:44:56 +0100 Subject: [PATCH 13/34] format get participation --- gameservice/game-service.js | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/gameservice/game-service.js b/gameservice/game-service.js index 5862559f..340ded0f 100644 --- a/gameservice/game-service.js +++ b/gameservice/game-service.js @@ -51,9 +51,14 @@ app.get('/getParticipation/:userId', async (req, res) => { { $group: { _id: null, - totalGames: { $sum: 1 }, - correctAnswers: { $sum: { $size: { $filter: { input: "$answers", as: "answer", cond: "$$answer.isCorrect" } } } }, - incorrectAnswers: { $sum: { $size: { $filter: { input: "$answers", as: "answer", cond: { $eq: ["$$answer.isCorrect", false] } } } } }, + totalGames: { $sum: 1 }, //$sum -> Returns a sum of numerical values + correctAnswers: { $sum: { $size: { + $filter: { + input: "$answers", as: "answer", cond: "$$answer.isCorrect" } + } } }, + incorrectAnswers: { $sum: { $size: { + $filter: { input: "$answers", as: "answer", cond: { $eq: ["$$answer.isCorrect", false] } } + } } }, totalTime: { $sum: "$totalTime" }, }, }, From 178a6081c2b7c9316127e4c065cb987b185c3525 Mon Sep 17 00:00:00 2001 From: UO276976 Date: Mon, 4 Mar 2024 10:48:25 +0100 Subject: [PATCH 14/34] Updated question model and tests --- gameservice/game-service.js | 2 +- gameservice/game-service.test.js | 17 ++++++++++++++--- gatewayservice/gateway-service.test.js | 12 ++++++------ questionservice/question-model.js | 22 ++++++++-------------- questionservice/question-service.js | 10 +++++----- questionservice/question-service.test.js | 8 ++++---- 6 files changed, 38 insertions(+), 33 deletions(-) diff --git a/gameservice/game-service.js b/gameservice/game-service.js index 90f0f99b..4598d2cf 100644 --- a/gameservice/game-service.js +++ b/gameservice/game-service.js @@ -34,7 +34,7 @@ app.post('/addgame', async (req, res) => { // Guarda el nuevo juego en la base de datos const savedGame = await newGame.save(); - res.status(201).json(savedGame); + res.status(200).json(savedGame); } catch (error) { res.status(500).json({ error: 'Internal Server Error' }); } diff --git a/gameservice/game-service.test.js b/gameservice/game-service.test.js index a4953fca..37a333ed 100644 --- a/gameservice/game-service.test.js +++ b/gameservice/game-service.test.js @@ -20,8 +20,19 @@ afterAll(async () => { describe('Game Service', () => { it('should add a new game on POST /addgame', async () => { const newGame = { - user: mongoose.Types.ObjectId(), // ID de usuario simulado - questions: mongoose.Types.ObjectId(), // ID de pregunta simulado + user: 'testUser', + questions: [ + { + question: 'Mocked Question', + correct: 'Mocked Correct Answer', + incorrects: ['Mocked Option 1', 'Mocked Option 2'] + }, + { + question: 'Mocked Question2', + correct: 'Mocked Correct Answer2', + incorrects: ['Mocked Option 1', 'Mocked Option 2'] + } + ], answers: [ { response: 'User response', @@ -32,7 +43,7 @@ describe('Game Service', () => { }; const response = await request(app).post('/addgame').send(newGame); - expect(response.status).toBe(201); + expect(response.status).toBe(200); expect(response.body).toHaveProperty('user', newGame.user.toString()); }); }); diff --git a/gatewayservice/gateway-service.test.js b/gatewayservice/gateway-service.test.js index 73547264..cfef67d6 100644 --- a/gatewayservice/gateway-service.test.js +++ b/gatewayservice/gateway-service.test.js @@ -20,9 +20,9 @@ describe('Gateway Service', () => { }else if (url.endsWith('/addquestion')) { return Promise.resolve({ data: { - question: 'What is the capital of France?', - options: ['Paris', 'Berlin', 'Madrid', 'Rome'], - correctOptionIndex: 0 + question: 'Mocked Question', + correct: 'Mocked Correct Answer', + incorrects: ['Mocked Option 1', 'Mocked Option 2'] } }); } @@ -100,9 +100,9 @@ describe('Gateway Service', () => { const response = await request(app) .post('/addquestion') .send({ - question: 'What is the capital of France?', - options: ['Paris', 'Berlin', 'Madrid', 'Rome'], - correctOptionIndex: 0, + question: 'Mocked Question', + correct: 'Mocked Correct Answer', + incorrects: ['Mocked Option 1', 'Mocked Option 2'] }); expect(response.statusCode).toBe(200); diff --git a/questionservice/question-model.js b/questionservice/question-model.js index 89bf65ed..5e3e72aa 100644 --- a/questionservice/question-model.js +++ b/questionservice/question-model.js @@ -5,28 +5,22 @@ const questionSchema = new mongoose.Schema({ type: String, required: true, }, - options: { + correct: { + type: String, + required: true, + }, + incorrects: { type: [String], required: true, validate: { validator: function (options) { - return options.length >= 2 && options.length <= 4; + return options.length >= 1 && options.length <= 3; }, - message: 'Options must have between 2 and 4 elements.', - }, - }, - correctOptionIndex: { - type: Number, - required: true, - validate: { - validator: function (index) { - return index >= 0 && index < 4; // Max of 4 options - }, - message: 'Correct option index must be between 0 and 3.', + message: 'Options must be between 2 and 4 elements.', }, }, }); const Question = mongoose.model('Question', questionSchema); -module.exports = Question; +module.exports = Question; \ No newline at end of file diff --git a/questionservice/question-service.js b/questionservice/question-service.js index 8748e0a3..5977b501 100644 --- a/questionservice/question-service.js +++ b/questionservice/question-service.js @@ -19,21 +19,21 @@ const validateRequiredFields = (req, fields) => { // Ruta para agregar una nueva pregunta app.post('/addquestion', async (req, res) => { try { - validateRequiredFields(req, ['question', 'options', 'correctOptionIndex']); + validateRequiredFields(req, ['question', 'correct', 'incorrects']); - const { question, options, correctOptionIndex } = req.body; + const { question, correct, incorrects } = req.body; // Crea una nueva instancia del modelo de preguntas const newQuestion = new Question({ question, - options, - correctOptionIndex, + correct, + incorrects, }); // Guarda la nueva pregunta en la base de datos const savedQuestion = await newQuestion.save(); - res.status(201).json(savedQuestion); + res.status(200).json(savedQuestion); } catch (error) { res.status(500).json({ error: 'Internal Server Error' }); } diff --git a/questionservice/question-service.test.js b/questionservice/question-service.test.js index 980b79f6..dcac9805 100644 --- a/questionservice/question-service.test.js +++ b/questionservice/question-service.test.js @@ -19,13 +19,13 @@ afterAll(async () => { describe('Question Service', () => { it('should add a new question on POST /addquestion', async () => { const newQuestion = { - question: 'What is the capital of France?', - options: ['Paris', 'Berlin', 'Madrid', 'Rome'], - correctOptionIndex: 0, + question: 'Mocked Question', + correct: 'Mocked Correct Answer', + incorrects: ['Mocked Option 1', 'Mocked Option 2'] }; const response = await request(app).post('/addquestion').send(newQuestion); - expect(response.status).toBe(201); + expect(response.status).toBe(200); expect(response.body).toHaveProperty('question', 'What is the capital of France?'); }); From 7427c303c4e30ac565996332b356c9971e1e9b2e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miguel=20M=C3=A9ndez=20Murias?= Date: Mon, 4 Mar 2024 10:50:41 +0100 Subject: [PATCH 15/34] Question service test updated --- questionservice/question-service.test.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/questionservice/question-service.test.js b/questionservice/question-service.test.js index dcac9805..91d2c29e 100644 --- a/questionservice/question-service.test.js +++ b/questionservice/question-service.test.js @@ -26,7 +26,7 @@ describe('Question Service', () => { const response = await request(app).post('/addquestion').send(newQuestion); expect(response.status).toBe(200); - expect(response.body).toHaveProperty('question', 'What is the capital of France?'); + expect(response.body).toHaveProperty('question', 'Mocked Question'); }); }); From 86a83059824f7b85a31344739c4104ff5fb19830 Mon Sep 17 00:00:00 2001 From: UO287747 Date: Mon, 4 Mar 2024 10:52:41 +0100 Subject: [PATCH 16/34] =?UTF-8?q?Modificaci=C3=B3n=20orden=20interfaces=20?= =?UTF-8?q?y=20interfaz=20fin=20de=20juego?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- webapp/src/App.js | 6 ++-- webapp/src/components/Game.jsx | 28 ++++++++----------- webapp/src/components/PostGame.jsx | 45 +++++++++++++++++++++++++++--- webapp/src/test/PostGame.test.js | 11 ++++++-- 4 files changed, 64 insertions(+), 26 deletions(-) diff --git a/webapp/src/App.js b/webapp/src/App.js index 966d01c8..db9ed9b2 100644 --- a/webapp/src/App.js +++ b/webapp/src/App.js @@ -3,7 +3,6 @@ import { useState } from 'react' import Nav from './components/Nav' import { Start } from './components/Start' import { Game } from './components/Game' -import { PostGame } from './components/PostGame' import { Participation } from './components/Participation' import User from './components/User' @@ -19,9 +18,8 @@ function App() { {menuState === 0 && goTo(x)}/>} {menuState > 0 &&