From c8db3b70433e55e6d46722756c0fdaeae42290c7 Mon Sep 17 00:00:00 2001 From: lauracc97 Date: Mon, 8 Apr 2024 14:52:48 +0200 Subject: [PATCH 01/22] JSON externo --- .../template-questions/baseQuestions.json | 83 +++++++++++++------ .../template-questions/question-service.js | 5 ++ 2 files changed, 62 insertions(+), 26 deletions(-) diff --git a/questions/template-questions/baseQuestions.json b/questions/template-questions/baseQuestions.json index a85619cd..fe4dde4c 100644 --- a/questions/template-questions/baseQuestions.json +++ b/questions/template-questions/baseQuestions.json @@ -1,28 +1,59 @@ { - "questions":[ - { - "id": "Q123", - "text": "¿Cuál es la capital de Francia?", - "answers": [ - { - "true": "París", - "false1": "Madrid", - "false2": "Berlín", - "false3": "Londres" - } - ] - }, - { - "id": "Q1234", - "text": "¿Cuál es la capital de España?", - "answers": [ - { - "true": "Madrid", - "false1": "Paris", - "false2": "Berlín", - "false3": "Londres" - } - ] - } - ] +"jsonPreg": [ + { + "textStart": "¿Cuál es la capital de ", + "textEnd": "?", + "queryCorrect": "SELECT ?preguntaLabel ?respuestaLabel WHERE {?pregunta wdt:P31 wd:Q6256. ?pregunta wdt:P36 ?respuesta. SERVICE wikibase:label { bd:serviceParam wikibase:language '[AUTO_LANGUAGE],es,en'.}}" + }, + { + "textStart": "¿Quién es el director de la película ", + "textEnd": "?", + "queryCorrect": "SELECT DISTINCT ?preguntaLabel ?respuestaLabel WHERE {?pregunta wdt:P31 wd:Q11424. ?pregunta wdt:P57 ?respuesta. SERVICE wikibase:label { bd:serviceParam wikibase:language '[AUTO_LANGUAGE],es,en'. }} LIMIT 100" + }, + { + "textStart": "¿Quién es el autor del libro ", + "textEnd": "?", + "queryCorrect": "SELECT ?preguntaLabel ?respuestaLabel WHERE { ?pregunta wdt:P31 wd:Q7725634. ?pregunta wdt:P50 ?respuesta. SERVICE wikibase:label { bd:serviceParam wikibase:language \"[AUTO_LANGUAGE],es,en\". }} LIMIT 100" + }, + { + "textStart": "¿Quién interpreta la canción ", + "textEnd": "?", + "queryCorrect": "SELECT ?preguntaLabel ?respuestaLabel WHERE { ?pregunta wdt:P31 wd:Q134556. ?pregunta wdt:P175 ?respuesta. SERVICE wikibase:label { bd:serviceParam wikibase:language \"[AUTO_LANGUAGE],es,en\". } } LIMIT 100" + }, + { + "textStart": "¿Dónde se encuentra el monumento ", + "textEnd": "?", + "queryCorrect": "SELECT ?preguntaLabel ?respuestaLabel WHERE { ?pregunta wdt:P31 wd:Q570116. ?pregunta wdt:P17 ?respuesta. SERVICE wikibase:label { bd:serviceParam wikibase:language \"[AUTO_LANGUAGE],es,en\". } } LIMIT 100" + }, + { + "textStart": "¿Cuál es la población de ", + "textEnd": "?", + "queryCorrect": "SELECT DISTINCT ?preguntaLabel (CONCAT(REPLACE(STR((ROUND(?respuesta / 1000) * 1000)), \"(\\\\d)(?=(\\\\d{3})+$)\", \"$1.\"), \"\") AS ?respuestaLabel) WHERE { ?pregunta wdt:P31 wd:Q6256. ?pregunta wdt:P1082 ?respuesta. SERVICE wikibase:label { bd:serviceParam wikibase:language \"[AUTO_LANGUAGE],es,en\". }}" + } + , + { + "textStart": "¿Cuál es el gentilicio de ", + "textEnd": "?", + "queryCorrect": "SELECT DISTINCT ?preguntaLabel ?respuestaLabel WHERE { ?pregunta wdt:P31 wd:Q2074737; wdt:P17 wd:Q29; wdt:P1549 ?respuesta. OPTIONAL { ?respuesta rdfs:label ?respuestaLabel. FILTER(LANG(?respuestaLabel) = \"es\") } SERVICE wikibase:label { bd:serviceParam wikibase:language \"[AUTO_LANGUAGE],es,en\". }}" + } + , + { + "textStart": "¿A qué grupo pertenece ", + "textEnd": "?", + "queryCorrect": "SELECT DISTINCT ?preguntaLabel ?respuestaLabel WHERE { ?pregunta wdt:P106 wd:Q177220; wdt:P463 ?respuesta. SERVICE wikibase:label { bd:serviceParam wikibase:language \"[AUTO_LANGUAGE],es,en\". }} LIMIT 100" + } + , + { + "textStart": "¿Dónde nació el compositor ", + "textEnd": "?", + "queryCorrect": "SELECT ?preguntaLabel ?respuestaLabel WHERE { ?pregunta wdt:P106 wd:Q36834; wdt:P19 ?respuesta. SERVICE wikibase:label { bd:serviceParam wikibase:language \"es,en\" }} LIMIT 100" + } + , + { + "textStart": "En qué país se encuentra la atracción turística ", + "textEnd": "?", + "queryCorrect": "SELECT ?preguntaLabel ?respuestaLabel WHERE { ?pregunta wdt:P31 wd:Q570116; wdt:P17 ?respuesta. SERVICE wikibase:label { bd:serviceParam wikibase:language \"[AUTO_LANGUAGE],es,en\". }} LIMIT 200" + } + +] } diff --git a/questions/template-questions/question-service.js b/questions/template-questions/question-service.js index f094cd64..ff7fcaac 100644 --- a/questions/template-questions/question-service.js +++ b/questions/template-questions/question-service.js @@ -1,6 +1,11 @@ const express = require('express'); const bodyParser = require('body-parser'); const Wikidata = require('./wikidata-query'); +//import { readFile } from 'fs/promises' + +// leemos el archivo usando top-level await y con +// codificación utf-8 +//const file = await readFile('./baseQuestions.json', 'utf-8') const app = express(); const port = 8004; From df1b48508a506b1ba0ac6f1b459bcf7507a0793d Mon Sep 17 00:00:00 2001 From: uo283182 Date: Tue, 9 Apr 2024 22:53:21 +0200 Subject: [PATCH 02/22] =?UTF-8?q?arreglar=20barra=20nav=20y=20a=C3=B1adir?= =?UTF-8?q?=20boton=20para=20cerrar=20sesi=C3=B3n?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- webapp/src/components/Welcome.js | 28 ++++++++++++---------------- 1 file changed, 12 insertions(+), 16 deletions(-) diff --git a/webapp/src/components/Welcome.js b/webapp/src/components/Welcome.js index 89628352..82fac277 100644 --- a/webapp/src/components/Welcome.js +++ b/webapp/src/components/Welcome.js @@ -1,23 +1,17 @@ -import React, { useState } from 'react'; -import AddUser from './AddUser'; -import Login from './Login'; import './Welcome.css' import Typography from '@mui/material/Typography'; - +import { useNavigate } from "react-router-dom"; const HomeScreen = () => { - const [showLogin, setShowLogin] = useState(false); - const [showSignUp, setShowSignUp] = useState(false); - + const navigate = useNavigate(); const handleLogin = () => { - setShowLogin(true); + navigate("/login"); }; const handleSignUp = () => { - setShowSignUp(true); + navigate("/adduser"); }; const renderButtons = () => { - if (!showLogin && !showSignUp) { return ( <> @@ -30,13 +24,17 @@ const HomeScreen = () => { ↓↓↓

-
- - +
+
+ +
+
+ +
); - } + }; return ( @@ -45,8 +43,6 @@ const HomeScreen = () => {
{renderButtons()}
- {showLogin && } - {showSignUp && }
); }; From 51cc5bb8773ccabfab8b20ded92c380f237b2d11 Mon Sep 17 00:00:00 2001 From: uo283182 Date: Tue, 9 Apr 2024 22:54:23 +0200 Subject: [PATCH 03/22] =?UTF-8?q?A=C3=B1adir=20rutas=20a=20todas=20las=20p?= =?UTF-8?q?antallas=20y=20comprobar=20si=20hay=20un=20usuario=20logeado?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- webapp/src/App.css | 17 ++++++++++++ webapp/src/App.js | 43 +++++++++++++++++------------ webapp/src/components/Game.js | 40 +++++++++++++++++++-------- webapp/src/components/History.js | 30 ++++++++++++++------ webapp/src/components/HomeScreen.js | 32 ++++++++++++--------- webapp/src/components/Login.js | 21 ++++---------- 6 files changed, 116 insertions(+), 67 deletions(-) diff --git a/webapp/src/App.css b/webapp/src/App.css index 776755da..65b89654 100644 --- a/webapp/src/App.css +++ b/webapp/src/App.css @@ -45,3 +45,20 @@ body { background-image: url('../public/fondo.png'); background-size: cover; } +#btSaberYGanar { + height: 3.5em; + border: none; + padding: 0; + background: none; + cursor: pointer; + margin-left: 45vw; +} + +#btSaberYGanar img { + width: 100%; + height: 100%; +} + +#btLogout { + margin-right: 0.5em; +} \ No newline at end of file diff --git a/webapp/src/App.js b/webapp/src/App.js index 4be09cb8..0a6061f9 100644 --- a/webapp/src/App.js +++ b/webapp/src/App.js @@ -1,8 +1,7 @@ -import React, { useState } from 'react'; + import AddUser from './components/AddUser'; import Login from './components/Login'; import Container from '@mui/material/Container'; -import Typography from '@mui/material/Typography'; import { BrowserRouter as Router, Route, Routes, Link, Navigate } from 'react-router-dom'; import HomeScreen from './components/HomeScreen'; import Game from './components/Game'; @@ -10,27 +9,37 @@ import Welcome from './components/Welcome'; import ImagenA from './LogoSaberYGanar2.png'; import History from './components/History.js'; import './App.css'; - +import { useNavigate } from "react-router-dom"; function App() { - const [showLogin, setShowLogin] = useState(true); + const navigate = useNavigate(); + const token = localStorage.getItem('token'); - const handleToggleView = () => { - setShowLogin(!showLogin); + const logout = () => { + localStorage.removeItem('token'); + localStorage.removeItem('numQuestions'); + navigate("/"); }; - const [isAuthenticated, setAuthenticated] = useState(false); - const handleLogin = () => { - // Lógica para autenticar al usuario (por ejemplo, verificar credenciales) - setAuthenticated(true); + const volverHome = () => { + if (token!=null) { + navigate("/home"); + } + else { + navigate("/"); + } }; return (
-
); diff --git a/webapp/src/components/Game.js b/webapp/src/components/Game.js index 3d870648..8bab445e 100644 --- a/webapp/src/components/Game.js +++ b/webapp/src/components/Game.js @@ -4,7 +4,7 @@ import Grid from '@mui/material/Grid'; import axios from 'axios'; import { useEffect } from 'react'; import './Game.css'; -import { Link } from 'react-router-dom'; +import { useNavigate } from 'react-router-dom'; import { jwtDecode } from 'jwt-decode'; const StyledContainer = styled(Container)({ @@ -15,7 +15,7 @@ const StyledContainer = styled(Container)({ const apiEndpoint = process.env.REACT_APP_API_ENDPOINT || 'http://localhost:8000'; -const Game = ({numQuestions}) => { +const Game = () => { const [respuestas, setRespuestas] = useState(Array(4).fill({ data: '', isCorrect: '' })); const [textoPregunta, setTextoPregunta] = useState('Cargando...'); const [preguntasAcertadas, setPreguntasAcertadas] = useState(0); @@ -25,14 +25,10 @@ const Game = ({numQuestions}) => { const [tiempoTotal, setTiempoTotal] = useState(0); var tiempoInicial = 0; var tiempoFinal = 0; - // Obtén el token del almacenamiento local -const token = localStorage.getItem('token'); -// Decodifica el token para obtener la información del usuario -const decoded = jwtDecode(token); + const navigate = useNavigate(); -// Accede al nombre de usuario desde la información decodificada -const username = decoded.username; + const numQuestions = localStorage.getItem('numQuestions'); // Función para iniciar el tiempo const startTime = () => { @@ -176,6 +172,14 @@ const username = decoded.username; const addRecord = async () => { try { + // Obtén el token del almacenamiento local + let token = localStorage.getItem('token'); + + // Decodifica el token para obtener la información del usuario + let decoded = jwtDecode(token); + + // Accede al nombre de usuario desde la información decodificada + let username = decoded.username; //Llamada al post para obtener los resultados de Wikidata await axios.post(`${apiEndpoint}/addRecord`, { user_id: username, @@ -184,16 +188,28 @@ const username = decoded.username; totalTime: tiempoTotal }); - window.location.href = '/home'; + navigate("/home"); } catch (error) { console.log(error.response.data.error); } }; + const checkUserLogin = () => { + let token = localStorage.getItem('token'); + if (token==null) { + navigate("/"); + } + else { + if (numQuestions==null) { + localStorage.setItem('numQuestions', 10); + } + addPregunta(); + } + } useEffect(() => { - addPregunta(); + checkUserLogin(); }, []) return ( @@ -202,7 +218,7 @@ const username = decoded.username;

Has acertado {preguntasAcertadas}/{numQuestions} preguntas en {tiempoTotal} segundos

- +
) : ( @@ -211,7 +227,7 @@ const username = decoded.username;

Pregunta Nº{numPreguntas}

-
{contadorGlobal}
+
{contadorGlobal}
)} diff --git a/webapp/src/components/History.js b/webapp/src/components/History.js index 220e5f70..c23244e9 100644 --- a/webapp/src/components/History.js +++ b/webapp/src/components/History.js @@ -4,20 +4,24 @@ import { jwtDecode } from 'jwt-decode'; import axios from 'axios'; import './History.css'; import { useEffect } from 'react'; -import { Link } from 'react-router-dom'; +import { useNavigate } from 'react-router-dom'; const apiEndpoint = process.env.REACT_APP_API_ENDPOINT || 'http://localhost:8000'; const History = () => { const [historyData, setHistoryData] = useState([]); + const navigate = useNavigate(); + const getHistory = async () => { try { + let token = localStorage.getItem('token'); + let decoded = jwtDecode(token); + let username = decoded.username; let result = await axios.post(`${apiEndpoint}/getRecords`, { username: username, }); setHistoryData(result.data); - console.log(result.data) } catch (error) { console.log(error.response.data.error); @@ -25,16 +29,24 @@ const History = () => { }; useEffect(() => { - getHistory(); + checkUserLogin(); }, []) - const token = localStorage.getItem('token'); - const decoded = jwtDecode(token); - const username = decoded.username; + const volverHome = () => { - window.location.href = '/home'; - }; + navigate('/home'); + }; + + const checkUserLogin = () => { + let token = localStorage.getItem('token'); + if (token==null) { + navigate("/"); + } + else { + getHistory(); + } + } return ( @@ -60,7 +72,7 @@ const History = () => { ))} - + ) }; diff --git a/webapp/src/components/HomeScreen.js b/webapp/src/components/HomeScreen.js index 9f6e323e..5cfd13d4 100644 --- a/webapp/src/components/HomeScreen.js +++ b/webapp/src/components/HomeScreen.js @@ -1,15 +1,18 @@ -import React, { useState } from 'react'; +import React, { useState,useEffect } from 'react'; import { Container} from '@mui/material'; import Game from './Game'; import './HomeScreen.css'; -import { Link } from "react-router-dom"; +import { useNavigate } from "react-router-dom"; const HomeScreen = () => { - const [mostrarJuego, setMostrarJuego] = useState(false); const [defecto, setDefecto] = useState("15"); + const navigate = useNavigate(); + const token = localStorage.getItem('token'); + const handleStartButtonClick = () => { - setMostrarJuego(true); + localStorage.setItem('numQuestions', defecto); + navigate("/game"); }; const changeNumber = (event) => { @@ -17,26 +20,29 @@ const HomeScreen = () => { }; const handleHistoryButton = () => { - window.location.href = '/history'; + navigate("/history"); + } + const checkUserLogin = () => { + if (token==null) { + navigate("/"); + } } + + useEffect(() => { + checkUserLogin(); + }, []) return (
- {mostrarJuego ? ( - // Muestra otro componente o contenido cuando el juego está iniciado - - ) : ( - // Muestra el contenido inicial con el botón "Jugar"
- - + +
- )}
) diff --git a/webapp/src/components/Login.js b/webapp/src/components/Login.js index 4ba39832..c0139468 100644 --- a/webapp/src/components/Login.js +++ b/webapp/src/components/Login.js @@ -2,17 +2,15 @@ import React, { useState } from 'react'; import axios from 'axios'; import { Container, Typography, TextField, Button, Snackbar } from '@mui/material'; -import HomeScreen from './HomeScreen'; import { Link } from "react-router-dom"; +import { useNavigate } from "react-router-dom"; const Login = () => { const [username, setUsername] = useState(''); const [password, setPassword] = useState(''); const [error, setError] = useState(''); - const [loginSuccess, setLoginSuccess] = useState(false); - const [createdAt, setCreatedAt] = useState(''); const [openSnackbar, setOpenSnackbar] = useState(false); - + const navigate = useNavigate(); const apiEndpoint = process.env.REACT_APP_API_ENDPOINT || 'http://localhost:8000'; const loginUser = async () => { @@ -20,13 +18,12 @@ const Login = () => { const response = await axios.post(`${apiEndpoint}/login`, { username, password }); // Extract data from the response - const { createdAt: userCreatedAt, token } = response.data; + const { token } = response.data; // Store the token in localStorage - localStorage.setItem('token', token); - setCreatedAt(userCreatedAt); - setLoginSuccess(true); + localStorage.setItem('token', token); setOpenSnackbar(true); + navigate("/home"); } catch (error) { setError(error.response.data.error); } @@ -38,11 +35,6 @@ const Login = () => { return ( - {loginSuccess ? ( -
- -
- ) : (
@@ -77,8 +69,7 @@ const Login = () => {
- - )} +
); }; From b6dc151ad2f92b79c40cb71d7d5cd2ae9fa590f3 Mon Sep 17 00:00:00 2001 From: uo283182 Date: Tue, 9 Apr 2024 23:53:09 +0200 Subject: [PATCH 04/22] =?UTF-8?q?A=C3=B1adido=20slider=20para=20elegir=20n?= =?UTF-8?q?um=20preguntas?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- webapp/src/components/HomeScreen.css | 5 +++ webapp/src/components/HomeScreen.js | 47 +++++++++++++++++++++------- 2 files changed, 40 insertions(+), 12 deletions(-) diff --git a/webapp/src/components/HomeScreen.css b/webapp/src/components/HomeScreen.css index 7f0ea26a..f9278bc3 100644 --- a/webapp/src/components/HomeScreen.css +++ b/webapp/src/components/HomeScreen.css @@ -5,3 +5,8 @@ flex-direction: column; align-items: center; } + +#formNumPreguntas { + display:flex; + flex-direction: column; +} \ No newline at end of file diff --git a/webapp/src/components/HomeScreen.js b/webapp/src/components/HomeScreen.js index 5cfd13d4..e1ffaa63 100644 --- a/webapp/src/components/HomeScreen.js +++ b/webapp/src/components/HomeScreen.js @@ -1,24 +1,18 @@ import React, { useState,useEffect } from 'react'; -import { Container} from '@mui/material'; -import Game from './Game'; +import { Container,Slider,TextField} from '@mui/material'; import './HomeScreen.css'; import { useNavigate } from "react-router-dom"; const HomeScreen = () => { - const [defecto, setDefecto] = useState("15"); - + const [value, setValue] = useState(15); const navigate = useNavigate(); const token = localStorage.getItem('token'); const handleStartButtonClick = () => { - localStorage.setItem('numQuestions', defecto); + localStorage.setItem('numQuestions', value); navigate("/game"); }; - const changeNumber = (event) => { - setDefecto(event.target.value); - }; - const handleHistoryButton = () => { navigate("/history"); } @@ -31,15 +25,44 @@ const HomeScreen = () => { useEffect(() => { checkUserLogin(); }, []) + + const handleChange = (event, newValue) => { + setValue(newValue); + }; return (
-
+
- -
+ + { + const newValue = e.target.value === '' ? 0 : Number(e.target.value); + setValue(Math.min(Math.max(newValue, 1), 30)); + }} + /> + + +
From e320dc079c4213d6c032e0593825d2b3ff25cd4d Mon Sep 17 00:00:00 2001 From: uo283182 Date: Wed, 10 Apr 2024 00:12:58 +0200 Subject: [PATCH 05/22] =?UTF-8?q?correci=C3=B3n?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- webapp/src/components/Welcome.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/webapp/src/components/Welcome.js b/webapp/src/components/Welcome.js index 82fac277..a9223a84 100644 --- a/webapp/src/components/Welcome.js +++ b/webapp/src/components/Welcome.js @@ -18,7 +18,7 @@ const HomeScreen = () => { Tu juego favorito de televisión, ¡ahora en tu ordenador! - Accede a tu cuenta o registrarte para comenzar + Accede a tu cuenta o regístrate para comenzar ↓↓↓ From 01ab3006b13e0b52a77ae207daf6af66fe04737c Mon Sep 17 00:00:00 2001 From: uo283182 Date: Wed, 10 Apr 2024 00:13:47 +0200 Subject: [PATCH 06/22] =?UTF-8?q?a=C3=B1adida=20scroll=20bar=20en=20la=20t?= =?UTF-8?q?abla=20del=20historial?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- webapp/src/components/History.css | 72 +++++++++++++++++-------------- webapp/src/components/History.js | 3 ++ 2 files changed, 42 insertions(+), 33 deletions(-) diff --git a/webapp/src/components/History.css b/webapp/src/components/History.css index 846063ff..403fd514 100644 --- a/webapp/src/components/History.css +++ b/webapp/src/components/History.css @@ -1,36 +1,42 @@ +.table-container { + overflow-y: auto; + max-height: 20em; + position: relative; /* Necesario para fijar el thead */ +} #table { - width: 100%; - border-collapse: separate; - } - - /* Estilos para las celdas de encabezado */ - th { - background-color: #59b494d1; - color: #333; - padding: 10px; - } - - /* Estilos para las celdas de datos */ - td { - padding: 10px; - } - - /* Estilos para las filas impares */ - tr:nth-child(odd) { - background-color: #bee5c480; - } - - /* Estilos para las filas pares */ - tr:nth-child(even) { - background-color: #d3f0e8ad; - } - - td, th { - font-size: 1em; - } + border-collapse: separate; + width: 100%; +} - h1 { - text-align: center; - } - \ No newline at end of file +#table th, +#table td { + padding: 8px; + text-align: left; + border-bottom: 1px solid #ddd; +} + +#table th { + background-color: #30ab7ff5; + color: #333; +} + +#table thead th { + position: sticky; + top: 0; + z-index: 1; +} + +/* Estilos para las filas impares */ +#table tbody tr:nth-child(odd) { + background-color: #bee5c480; +} + +/* Estilos para las filas pares */ +#table tbody tr:nth-child(even) { + background-color: #d3f0e8ad; +} + +h1 { + text-align: center; +} \ No newline at end of file diff --git a/webapp/src/components/History.js b/webapp/src/components/History.js index c23244e9..6fbd9eb3 100644 --- a/webapp/src/components/History.js +++ b/webapp/src/components/History.js @@ -52,6 +52,7 @@ const History = () => {

Historial de partidas



+
@@ -72,6 +73,8 @@ const History = () => { ))}
+
+

) From bba8d52b530d4b88652611d53ee818e49048557c Mon Sep 17 00:00:00 2001 From: Laura Cordero Date: Wed, 10 Apr 2024 19:52:41 +0200 Subject: [PATCH 07/22] Wiq0 OpenAPI --- .github/workflows/release.yml | 4 + gatewayservice/gateway-service.js | 21 +++ gatewayservice/openapi.yaml | 144 +++++++++++++++++ gatewayservice/package-lock.json | 261 +++++++++++++++++++++++++++--- gatewayservice/package.json | 5 +- 5 files changed, 414 insertions(+), 21 deletions(-) create mode 100644 gatewayservice/openapi.yaml diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 5e4f9440..35ba770e 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -102,6 +102,10 @@ jobs: needs: [e2e-tests] steps: - uses: actions/checkout@v4 + - name: Update OpenAPI configuration + run: | + DEPLOY_HOST=${{ secrets.DEPLOY_HOST }} + sed -i "s/SOMEIP/${DEPLOY_HOST}/g" gatewayservice/openapi.yaml - name: Publish to Registry uses: elgohr/Publish-Docker-Github-Action@v5 with: diff --git a/gatewayservice/gateway-service.js b/gatewayservice/gateway-service.js index 3c6d6b87..43e8b022 100644 --- a/gatewayservice/gateway-service.js +++ b/gatewayservice/gateway-service.js @@ -3,6 +3,11 @@ const axios = require('axios'); const cors = require('cors'); const promBundle = require('express-prom-bundle'); +//libraries required for OpenAPI-Swagger +const swaggerUi = require('swagger-ui-express'); +const fs = require("fs") +const YAML = require('yaml') + const app = express(); const port = 8000; @@ -70,6 +75,22 @@ app.post('/getRecords', async (req, res) => { } }); +// Read the OpenAPI YAML file synchronously +openapiPath='./openapi.yaml' +if (fs.existsSync(openapiPath)) { + const file = fs.readFileSync(openapiPath, 'utf8'); + + // Parse the YAML content into a JavaScript object representing the Swagger document + const swaggerDocument = YAML.parse(file); + + // Serve the Swagger UI documentation at the '/api-doc' endpoint + // This middleware serves the Swagger UI files and sets up the Swagger UI page + // It takes the parsed Swagger document as input + app.use('/api-doc', swaggerUi.serve, swaggerUi.setup(swaggerDocument)); +} else { + console.log("Not configuring OpenAPI. Configuration file not present.") +} + // Start the gateway service const server = app.listen(port, () => { console.log(`Gateway Service listening at http://localhost:${port}`); diff --git a/gatewayservice/openapi.yaml b/gatewayservice/openapi.yaml new file mode 100644 index 00000000..67bffccb --- /dev/null +++ b/gatewayservice/openapi.yaml @@ -0,0 +1,144 @@ +openapi: 3.0.0 +info: + title: Gatewayservice API + description: Gateway OpenAPI specification. + version: 0.2.0 +servers: + - url: http://localhost:8000 + description: Development server + - url: http://SOMEIP:8000 + description: Production server +paths: + /adduser: + post: + summary: Add a new user to the database. + operationId: addUser + requestBody: + required: true + content: + application/json: + schema: + type: object + properties: + username: + type: string + description: User ID. + example: student + password: + type: string + description: User password. + example: pass + responses: + '200': + description: User added successfully. + content: + application/json: + schema: + type: object + properties: + username: + type: string + description: User ID + password: + type: string + description: Hashed password + example: $2b$10$ZKdNYLWFQxzt5Rei/YTc/OsZNi12YiWz30JeUFHNdAt7MyfmkTuvC + _id: + type: string + description: Identification + example: 65f756db3fa22d227a4b7c7d + createdAt: + type: string + description: Creation date. + example: '2024-03-17T20:47:23.935Z' + ___v: + type: integer + example: '0' + '400': + description: Failed to add user. + content: + application/json: + schema: + type: object + properties: + error: + type: string + description: Error information. + example: getaddrinfo EAI_AGAIN mongodb + /health: + get: + summary: Check the health status of the service. + operationId: checkHealth + responses: + '200': + description: Service is healthy. + content: + application/json: + schema: + type: object + properties: + status: + type: string + description: Health status. + example: OK + /login: + post: + summary: Log in to the system. + operationId: loginUser + requestBody: + required: true + content: + application/json: + schema: + type: object + properties: + username: + type: string + description: User ID. + example: student + password: + type: string + description: User password. + example: pass + responses: + '200': + description: Login successful. Returns user token, username, and creation date. + content: + application/json: + schema: + type: object + properties: + token: + type: string + description: User token. + example: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VySWQiOiI2NWY3NTZkYjNmYTIyZDIyN2E0YjdjN2QiLCJpYXQiOjE3MTA3MDg3NDUsImV4cCI6MTcxMDcxMjM0NX0.VMG_5DOyQ4GYlJQRcu1I6ICG1IGzuo2Xuei093ONHxw + username: + type: string + description: Username. + example: student + createdAt: + type: string + description: Creation date. + example: '2024-03-17T20:47:23.935Z' + '401': + description: Invalid credentials. + content: + application/json: + schema: + type: object + properties: + error: + type: string + description: Shows the error info.. + example: Invalid credentials + '500': + description: Internal server error. + content: + application/json: + schema: + type: object + properties: + error: + type: string + description: Error information. + example: Internal Server Error diff --git a/gatewayservice/package-lock.json b/gatewayservice/package-lock.json index fc5f2d60..95b84d7b 100644 --- a/gatewayservice/package-lock.json +++ b/gatewayservice/package-lock.json @@ -12,7 +12,10 @@ "axios": "^1.6.5", "cors": "^2.8.5", "express": "^4.18.2", - "express-prom-bundle": "^7.0.0" + "express-openapi": "^12.1.3", + "express-prom-bundle": "^7.0.0", + "swagger-ui-express": "^5.0.0", + "yaml": "^2.4.1" }, "devDependencies": { "jest": "^29.7.0", @@ -1277,6 +1280,37 @@ "node": ">= 0.6" } }, + "node_modules/ajv": { + "version": "8.12.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.12.0.tgz", + "integrity": "sha512-sRu1kpcO9yLtYxBKvqfTeh9KzZEwO3STyX1HT+4CaDzC6HpTGYhIhPIzj9XuKU7KYDwnaeh5hcOwjy1QuJzBPA==", + "dependencies": { + "fast-deep-equal": "^3.1.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/ajv-formats": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ajv-formats/-/ajv-formats-2.1.1.tgz", + "integrity": "sha512-Wx0Kx52hxE7C18hkMEggYlEifqWZtYaRgouJor+WMdPnQyEK13vgEWyVNup7SoeeoLMsr4kf5h6dOW11I15MUA==", + "dependencies": { + "ajv": "^8.0.0" + }, + "peerDependencies": { + "ajv": "^8.0.0" + }, + "peerDependenciesMeta": { + "ajv": { + "optional": true + } + } + }, "node_modules/ansi-escapes": { "version": "4.3.2", "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz", @@ -1333,7 +1367,6 @@ "version": "1.0.10", "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", - "dev": true, "dependencies": { "sprintf-js": "~1.0.2" } @@ -1474,8 +1507,7 @@ "node_modules/balanced-match": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", - "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", - "dev": true + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==" }, "node_modules/bintrees": { "version": "1.0.2", @@ -1510,7 +1542,6 @@ "version": "1.1.11", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "dev": true, "dependencies": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" @@ -1751,8 +1782,7 @@ "node_modules/concat-map": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", - "dev": true + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==" }, "node_modules/content-disposition": { "version": "0.5.4", @@ -1942,6 +1972,14 @@ "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, + "node_modules/difunc": { + "version": "0.0.4", + "resolved": "https://registry.npmjs.org/difunc/-/difunc-0.0.4.tgz", + "integrity": "sha512-zBiL4ALDmviHdoLC0g0G6wVme5bwAow9WfhcZLLopXCAWgg3AEf7RYTs2xugszIGulRHzEVDF/SHl9oyQU07Pw==", + "dependencies": { + "esprima": "^4.0.0" + } + }, "node_modules/ee-first": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", @@ -2015,7 +2053,6 @@ "version": "4.0.1", "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", - "dev": true, "bin": { "esparse": "bin/esparse.js", "esvalidate": "bin/esvalidate.js" @@ -2121,6 +2158,21 @@ "node": ">= 0.10.0" } }, + "node_modules/express-normalize-query-params-middleware": { + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/express-normalize-query-params-middleware/-/express-normalize-query-params-middleware-0.5.1.tgz", + "integrity": "sha512-KUBjEukYL9KJkrphVX3ZgMHgMTdgaSJe+FIOeWwJIJpCw8UZQPIylt0MYddSyUwbms4LQ8RC4wmavcLUP9uduA==" + }, + "node_modules/express-openapi": { + "version": "12.1.3", + "resolved": "https://registry.npmjs.org/express-openapi/-/express-openapi-12.1.3.tgz", + "integrity": "sha512-F570dVC5ENSkLu1SpDFPRQ13Y3a/7Udh0rfHyn3O1QrE81fPmlhnAo1JRgoNtbMRJ6goHNymxU1TVSllgFZBlQ==", + "dependencies": { + "express-normalize-query-params-middleware": "^0.5.0", + "openapi-framework": "^12.1.3", + "openapi-types": "^12.1.3" + } + }, "node_modules/express-prom-bundle": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/express-prom-bundle/-/express-prom-bundle-7.0.0.tgz", @@ -2138,6 +2190,11 @@ "prom-client": ">=15.0.0" } }, + "node_modules/fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==" + }, "node_modules/fast-json-stable-stringify": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", @@ -2264,11 +2321,18 @@ "node": ">= 0.6" } }, + "node_modules/fs-routes": { + "version": "12.1.3", + "resolved": "https://registry.npmjs.org/fs-routes/-/fs-routes-12.1.3.tgz", + "integrity": "sha512-Vwxi5StpKj/pgH7yRpNpVFdaZr16z71KNTiYuZEYVET+MfZ31Zkf7oxUmNgyZxptG8BolRtdMP90agIhdyiozg==", + "peerDependencies": { + "glob": ">=7.1.6" + } + }, "node_modules/fs.realpath": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", - "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", - "dev": true + "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==" }, "node_modules/fsevents": { "version": "2.3.3", @@ -2349,7 +2413,6 @@ "version": "7.2.3", "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", - "dev": true, "dependencies": { "fs.realpath": "^1.0.0", "inflight": "^1.0.4", @@ -2526,7 +2589,6 @@ "version": "1.0.6", "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", - "dev": true, "dependencies": { "once": "^1.3.0", "wrappy": "1" @@ -2563,6 +2625,11 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/is-dir": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-dir/-/is-dir-1.0.0.tgz", + "integrity": "sha512-vLwCNpTNkFC5k7SBRxPubhOCryeulkOsSkjbGyZ8eOzZmzMS+hSEO/Kn9ZOVhFNAlRZTFc4ZKql48hESuYUPIQ==" + }, "node_modules/is-fullwidth-code-point": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", @@ -3328,7 +3395,6 @@ "version": "3.14.1", "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz", "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==", - "dev": true, "dependencies": { "argparse": "^1.0.7", "esprima": "^4.0.0" @@ -3355,6 +3421,11 @@ "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==", "dev": true }, + "node_modules/json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==" + }, "node_modules/json5": { "version": "2.2.3", "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", @@ -3403,6 +3474,11 @@ "node": ">=8" } }, + "node_modules/lodash.merge": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", + "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==" + }, "node_modules/lru-cache": { "version": "5.1.1", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", @@ -3552,7 +3628,6 @@ "version": "3.1.2", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "dev": true, "dependencies": { "brace-expansion": "^1.1.7" }, @@ -3643,7 +3718,6 @@ "version": "1.4.0", "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", - "dev": true, "dependencies": { "wrappy": "1" } @@ -3663,6 +3737,97 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/openapi-default-setter": { + "version": "12.1.3", + "resolved": "https://registry.npmjs.org/openapi-default-setter/-/openapi-default-setter-12.1.3.tgz", + "integrity": "sha512-wHKwvEuOWwke5WcQn8pyCTXT5WQ+rm9FpJmDeEVECEBWjEyB/MVLYfXi+UQeSHTTu2Tg4VDHHmzbjOqN6hYeLQ==", + "dependencies": { + "openapi-types": "^12.1.3" + } + }, + "node_modules/openapi-framework": { + "version": "12.1.3", + "resolved": "https://registry.npmjs.org/openapi-framework/-/openapi-framework-12.1.3.tgz", + "integrity": "sha512-p30PHWVXda9gGxm+t/1X2XvEcufW1YhzeDQwc5SsgDnBXt8gkuu1SwrioGJ66wxVYEzfSRTTf/FMLhI49ut8fQ==", + "dependencies": { + "difunc": "0.0.4", + "fs-routes": "^12.1.3", + "glob": "*", + "is-dir": "^1.0.0", + "js-yaml": "^3.10.0", + "openapi-default-setter": "^12.1.3", + "openapi-request-coercer": "^12.1.3", + "openapi-request-validator": "^12.1.3", + "openapi-response-validator": "^12.1.3", + "openapi-schema-validator": "^12.1.3", + "openapi-security-handler": "^12.1.3", + "openapi-types": "^12.1.3", + "ts-log": "^2.1.4" + } + }, + "node_modules/openapi-jsonschema-parameters": { + "version": "12.1.3", + "resolved": "https://registry.npmjs.org/openapi-jsonschema-parameters/-/openapi-jsonschema-parameters-12.1.3.tgz", + "integrity": "sha512-aHypKxWHwu2lVqfCIOCZeJA/2NTDiP63aPwuoIC+5ksLK5/IQZ3oKTz7GiaIegz5zFvpMDxDvLR2DMQQSkOAug==", + "dependencies": { + "openapi-types": "^12.1.3" + } + }, + "node_modules/openapi-request-coercer": { + "version": "12.1.3", + "resolved": "https://registry.npmjs.org/openapi-request-coercer/-/openapi-request-coercer-12.1.3.tgz", + "integrity": "sha512-CT2ZDhBmAZpHhAzHhEN+/J5oMK3Ds99ayLLdXh2Aw1DCcn72EM8VuIGVwG5fSjvkMsgtn7FgltFosHqeM6PRFQ==", + "dependencies": { + "openapi-types": "^12.1.3", + "ts-log": "^2.1.4" + } + }, + "node_modules/openapi-request-validator": { + "version": "12.1.3", + "resolved": "https://registry.npmjs.org/openapi-request-validator/-/openapi-request-validator-12.1.3.tgz", + "integrity": "sha512-HW1sG00A9Hp2oS5g8CBvtaKvRAc4h5E4ksmuC5EJgmQ+eAUacL7g+WaYCrC7IfoQaZrjxDfeivNZUye/4D8pwA==", + "dependencies": { + "ajv": "^8.3.0", + "ajv-formats": "^2.1.0", + "content-type": "^1.0.4", + "openapi-jsonschema-parameters": "^12.1.3", + "openapi-types": "^12.1.3", + "ts-log": "^2.1.4" + } + }, + "node_modules/openapi-response-validator": { + "version": "12.1.3", + "resolved": "https://registry.npmjs.org/openapi-response-validator/-/openapi-response-validator-12.1.3.tgz", + "integrity": "sha512-beZNb6r1SXAg1835S30h9XwjE596BYzXQFAEZlYAoO2imfxAu5S7TvNFws5k/MMKMCOFTzBXSjapqEvAzlblrQ==", + "dependencies": { + "ajv": "^8.4.0", + "openapi-types": "^12.1.3" + } + }, + "node_modules/openapi-schema-validator": { + "version": "12.1.3", + "resolved": "https://registry.npmjs.org/openapi-schema-validator/-/openapi-schema-validator-12.1.3.tgz", + "integrity": "sha512-xTHOmxU/VQGUgo7Cm0jhwbklOKobXby+/237EG967+3TQEYJztMgX9Q5UE2taZKwyKPUq0j11dngpGjUuxz1hQ==", + "dependencies": { + "ajv": "^8.1.0", + "ajv-formats": "^2.0.2", + "lodash.merge": "^4.6.1", + "openapi-types": "^12.1.3" + } + }, + "node_modules/openapi-security-handler": { + "version": "12.1.3", + "resolved": "https://registry.npmjs.org/openapi-security-handler/-/openapi-security-handler-12.1.3.tgz", + "integrity": "sha512-25UTAflxqqpjCLrN6rRhINeM1L+MCDixMltiAqtBa9Zz/i7UkWwYwdzqgZY3Cx3vRZElFD09brYxo5VleeP3HQ==", + "dependencies": { + "openapi-types": "^12.1.3" + } + }, + "node_modules/openapi-types": { + "version": "12.1.3", + "resolved": "https://registry.npmjs.org/openapi-types/-/openapi-types-12.1.3.tgz", + "integrity": "sha512-N4YtSYJqghVu4iek2ZUvcN/0aqH1kRDuNqzcycDxhOUpg7GdvLa2F3DgS6yBNhInhv2r/6I0Flkn7CqL8+nIcw==" + }, "node_modules/p-limit": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", @@ -3753,7 +3918,6 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", - "dev": true, "engines": { "node": ">=0.10.0" } @@ -3886,6 +4050,14 @@ "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==" }, + "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/pure-rand": { "version": "6.0.4", "resolved": "https://registry.npmjs.org/pure-rand/-/pure-rand-6.0.4.tgz", @@ -3953,6 +4125,14 @@ "node": ">=0.10.0" } }, + "node_modules/require-from-string": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", + "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/resolve": { "version": "1.22.8", "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.8.tgz", @@ -4171,8 +4351,7 @@ "node_modules/sprintf-js": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", - "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==", - "dev": true + "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==" }, "node_modules/stack-utils": { "version": "2.0.6", @@ -4389,6 +4568,25 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/swagger-ui-dist": { + "version": "5.15.0", + "resolved": "https://registry.npmjs.org/swagger-ui-dist/-/swagger-ui-dist-5.15.0.tgz", + "integrity": "sha512-1zd4cNaUayXCWFSdBGNB+CYGISbe7M4FSgPqOjrgqKi1oEZfXzrOrjIHa0jHf5uSDN0X/mXmhFgKR9Jrr+fvqQ==" + }, + "node_modules/swagger-ui-express": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/swagger-ui-express/-/swagger-ui-express-5.0.0.tgz", + "integrity": "sha512-tsU9tODVvhyfkNSvf03E6FAk+z+5cU3lXAzMy6Pv4av2Gt2xA0++fogwC4qo19XuFf6hdxevPuVCSKFuMHJhFA==", + "dependencies": { + "swagger-ui-dist": ">=5.0.0" + }, + "engines": { + "node": ">= v0.10.32" + }, + "peerDependencies": { + "express": ">=4.0.0 || >=5.0.0-beta" + } + }, "node_modules/tdigest": { "version": "0.1.2", "resolved": "https://registry.npmjs.org/tdigest/-/tdigest-0.1.2.tgz", @@ -4447,6 +4645,11 @@ "node": ">=0.6" } }, + "node_modules/ts-log": { + "version": "2.2.5", + "resolved": "https://registry.npmjs.org/ts-log/-/ts-log-2.2.5.tgz", + "integrity": "sha512-PGcnJoTBnVGy6yYNFxWVNkdcAuAMstvutN9MgDJIV6L0oG8fB+ZNNy1T+wJzah8RPGor1mZuPQkVfXNDpy9eHA==" + }, "node_modules/type-detect": { "version": "4.0.8", "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz", @@ -4523,6 +4726,14 @@ "browserslist": ">= 4.21.0" } }, + "node_modules/uri-js": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", + "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", + "dependencies": { + "punycode": "^2.1.0" + } + }, "node_modules/url-value-parser": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/url-value-parser/-/url-value-parser-2.2.0.tgz", @@ -4605,8 +4816,7 @@ "node_modules/wrappy": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", - "dev": true + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==" }, "node_modules/write-file-atomic": { "version": "4.0.2", @@ -4636,6 +4846,17 @@ "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", "dev": true }, + "node_modules/yaml": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.4.1.tgz", + "integrity": "sha512-pIXzoImaqmfOrL7teGUBt/T7ZDnyeGBWyXQBvOVhLkWLN37GXv8NMLK406UY6dS51JfcQHsmcW5cJ441bHg6Lg==", + "bin": { + "yaml": "bin.mjs" + }, + "engines": { + "node": ">= 14" + } + }, "node_modules/yargs": { "version": "17.7.2", "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", diff --git a/gatewayservice/package.json b/gatewayservice/package.json index 12db5c01..b05ba672 100644 --- a/gatewayservice/package.json +++ b/gatewayservice/package.json @@ -21,7 +21,10 @@ "axios": "^1.6.5", "cors": "^2.8.5", "express": "^4.18.2", - "express-prom-bundle": "^7.0.0" + "express-openapi": "^12.1.3", + "express-prom-bundle": "^7.0.0", + "swagger-ui-express": "^5.0.0", + "yaml": "^2.4.1" }, "devDependencies": { "jest": "^29.7.0", From 9bf1d85c0f1f194979948c0e26a37c8832f8c424 Mon Sep 17 00:00:00 2001 From: Laura Cordero Date: Fri, 12 Apr 2024 09:45:59 +0200 Subject: [PATCH 08/22] Questions API created --- gatewayservice/openapi.yaml | 41 +++++++++++++++++++++++++++++++++++++ 1 file changed, 41 insertions(+) diff --git a/gatewayservice/openapi.yaml b/gatewayservice/openapi.yaml index 67bffccb..ded06913 100644 --- a/gatewayservice/openapi.yaml +++ b/gatewayservice/openapi.yaml @@ -142,3 +142,44 @@ paths: type: string description: Error information. example: Internal Server Error + + paths: + /questions: + post: + summary: Create a question and its answers. + operationId: questions + responses: + '200': + description: Question created successfully. + content: + application/json: + schema: + type: object + properties: + pregunta: + type: string + description: Question text + example: Who was the author of The Hunger Games? + correcta: + type: string + description: Correct answer + example: Suzzanne Collins + incorrectas: + type: array + items: + type: string + example: + - Sarah J. Maas + - Alice Oseman + - Veronica Roth + '400': + description: Failed to create question. + content: + application/json: + schema: + type: object + properties: + error: + type: string + description: Wikidata error conection. + example: Cannot connect with Wikidata From 47f8dc5f1335de8b37c429ef767aa083bfcf7cd2 Mon Sep 17 00:00:00 2001 From: uo283182 Date: Sun, 14 Apr 2024 23:15:50 +0200 Subject: [PATCH 09/22] =?UTF-8?q?Validaci=C3=B3n=20de=20campos=20de=20logi?= =?UTF-8?q?n=20y=20add=20user?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- users/authservice/auth-service.js | 19 +++++++++++++++++-- users/userservice/user-service.js | 18 ++++++++++++++++++ 2 files changed, 35 insertions(+), 2 deletions(-) diff --git a/users/authservice/auth-service.js b/users/authservice/auth-service.js index 49a7f612..90777aa3 100644 --- a/users/authservice/auth-service.js +++ b/users/authservice/auth-service.js @@ -23,12 +23,27 @@ function validateRequiredFields(req, requiredFields) { } } +function validateFieldsNotEmpty(req) { + + if (req.body.username.trim().length === 0) { + throw new Error(`El nombre de usuario no puede estar vacío`); + } + if (req.body.password.trim().length === 0) { + throw new Error(`La contraseña no puede estar vacía`); + } + +} + // Route for user login app.post('/login', async (req, res) => { try { // Check if required fields are present in the request body validateRequiredFields(req, ['username', 'password']); - + try { + validateFieldsNotEmpty(req); + } catch (validationError) { + return res.status(400).json({ error: validationError.message }); + } const { username, password } = req.body; // Find the user by username in the database @@ -42,7 +57,7 @@ app.post('/login', async (req, res) => { // Respond with the token and user information res.json({ token: token, username: username, createdAt: user.createdAt }); } else { - res.status(401).json({ error: 'Invalid credentials' }); + res.status(401).json({ error: 'Credenciales inválidas' }); } } catch (error) { res.status(500).json({ error: 'Internal Server Error' }); diff --git a/users/userservice/user-service.js b/users/userservice/user-service.js index 06b99b9c..28905853 100644 --- a/users/userservice/user-service.js +++ b/users/userservice/user-service.js @@ -26,11 +26,29 @@ function validateRequiredFields(req, requiredFields) { } } +function validateFieldsNotEmpty(req) { + + if (req.body.username.trim().length === 0) { + throw new Error(`El nombre de usuario no puede estar vacío`); + } + if (req.body.password.trim().length === 0) { + throw new Error(`La contraseña no puede estar vacía`); + } + +} + app.post('/adduser', async (req, res) => { try { // Check if required fields are present in the request body validateRequiredFields(req, ['username', 'password']); + validateFieldsNotEmpty(req); + + const user = await User.findOne({username: req.body.username}); + console.log(user); + if (user) { + throw new Error(`Este nombre de usuario está en uso`); + } // Encrypt the password before saving it const hashedPassword = await bcrypt.hash(req.body.password, 10); From 7498fc7a2c954d4a04226274396b70babeb8dca9 Mon Sep 17 00:00:00 2001 From: Laura Cordero Date: Mon, 15 Apr 2024 07:05:45 +0200 Subject: [PATCH 10/22] API getRecords, Changes y logout button doesn't work yet --- gatewayservice/openapi.yaml | 57 +++++++++++++++++++++++++++++ recordhistory/record-service.js | 2 +- webapp/src/App.js | 7 ++-- webapp/src/components/HomeScreen.js | 1 + 4 files changed, 63 insertions(+), 4 deletions(-) diff --git a/gatewayservice/openapi.yaml b/gatewayservice/openapi.yaml index ded06913..e5a196a4 100644 --- a/gatewayservice/openapi.yaml +++ b/gatewayservice/openapi.yaml @@ -183,3 +183,60 @@ paths: type: string description: Wikidata error conection. example: Cannot connect with Wikidata + + /getRecords: + post: + summary: Obtains the records of the user. + operationId: userRecords + requestBody: + required: true + content: + application/json: + schema: + type: object + properties: + user_id: + type: string + description: User ID. + example: student + responses: + '200': + description: Get Records successful. + content: + application/json: + schema: + type: array + properties: + user_id: + type: string + description: Username. + example: student + correctQuestions: + type: number + description: Number of correct questions. + example: 2 + totalQuestions: + type: number + description: Number of questions. + example: 5 + totalTime: + type: number + description: Time spent playing in seconds. + example: 26 + doneAt: + type: string + description: When was the game done. + example: '2024-03-17T20:47:23.935Z' + + + '400': + description: User is no valid or doesn't exist. + content: + application/json: + schema: + type: object + properties: + error: + type: string + description: Shows the error info.. + example: Invalid user diff --git a/recordhistory/record-service.js b/recordhistory/record-service.js index 33ae28b3..b5b8ad21 100644 --- a/recordhistory/record-service.js +++ b/recordhistory/record-service.js @@ -46,7 +46,7 @@ app.post('/addRecord', async (req, res) => { try { const username = req.body.username; - const records = await Record.find({ user_id: username }); + const records = await Record.find({ user_id: username }).sort({doneAt: -1}); res.json(records); } catch (error) { diff --git a/webapp/src/App.js b/webapp/src/App.js index 0a6061f9..ab7ce594 100644 --- a/webapp/src/App.js +++ b/webapp/src/App.js @@ -16,9 +16,10 @@ function App() { const token = localStorage.getItem('token'); const logout = () => { - localStorage.removeItem('token'); - localStorage.removeItem('numQuestions'); - navigate("/"); + localStorage.removeItem('token'); + localStorage.removeItem('numQuestions'); + document.getElementById('btLogout').style.display = 'none'; + navigate("/"); }; const volverHome = () => { diff --git a/webapp/src/components/HomeScreen.js b/webapp/src/components/HomeScreen.js index e1ffaa63..9d94754b 100644 --- a/webapp/src/components/HomeScreen.js +++ b/webapp/src/components/HomeScreen.js @@ -24,6 +24,7 @@ const HomeScreen = () => { useEffect(() => { checkUserLogin(); + document.getElementById('btLogout').style.display = 'inline-block'; }, []) From 967385b1af988b536672b65901cedb535a5c48ef Mon Sep 17 00:00:00 2001 From: lauracc97 Date: Mon, 15 Apr 2024 13:45:33 +0200 Subject: [PATCH 11/22] Prueba --- gatewayservice/gateway-service.js | 4 ++-- recordhistory/record-service.js | 2 +- webapp/package-lock.json | 1 - webapp/src/App.js | 2 +- webapp/src/components/History.js | 2 +- webapp/src/components/HomeScreen.js | 2 +- 6 files changed, 6 insertions(+), 7 deletions(-) diff --git a/gatewayservice/gateway-service.js b/gatewayservice/gateway-service.js index 43e8b022..c2c98206 100644 --- a/gatewayservice/gateway-service.js +++ b/gatewayservice/gateway-service.js @@ -66,9 +66,9 @@ app.post('/addRecord', async (req, res) => { } }); -app.post('/getRecords', async (req, res) => { +app.get('/getRecords', async (req, res) => { try { - const userResponse = await axios.post(recordService+'/getRecords', req.body); + const userResponse = await axios.get(recordService+'/getRecords', req.body); res.json(userResponse.data); } catch (error) { res.status(error.response.status).json({ error: error.response.data.error }); diff --git a/recordhistory/record-service.js b/recordhistory/record-service.js index b5b8ad21..f61d7475 100644 --- a/recordhistory/record-service.js +++ b/recordhistory/record-service.js @@ -42,7 +42,7 @@ app.post('/addRecord', async (req, res) => { res.status(400).json({ error: error.message }); }}); - app.post('/getRecords', async (req, res) => { + app.get('/getRecords', async (req, res) => { try { const username = req.body.username; diff --git a/webapp/package-lock.json b/webapp/package-lock.json index 7fbcd5fc..6ca88122 100644 --- a/webapp/package-lock.json +++ b/webapp/package-lock.json @@ -5,7 +5,6 @@ "requires": true, "packages": { "": { - "name": "webapp", "version": "0.1.0", "dependencies": { "@emotion/react": "^11.11.3", diff --git a/webapp/src/App.js b/webapp/src/App.js index ab7ce594..bf0d63b2 100644 --- a/webapp/src/App.js +++ b/webapp/src/App.js @@ -18,7 +18,7 @@ function App() { const logout = () => { localStorage.removeItem('token'); localStorage.removeItem('numQuestions'); - document.getElementById('btLogout').style.display = 'none'; + //document.getElementById('btLogout').style.display = 'none'; navigate("/"); }; diff --git a/webapp/src/components/History.js b/webapp/src/components/History.js index 6fbd9eb3..c0641363 100644 --- a/webapp/src/components/History.js +++ b/webapp/src/components/History.js @@ -18,7 +18,7 @@ const History = () => { let token = localStorage.getItem('token'); let decoded = jwtDecode(token); let username = decoded.username; - let result = await axios.post(`${apiEndpoint}/getRecords`, { + let result = await axios.get(`${apiEndpoint}/getRecords`, { username: username, }); setHistoryData(result.data); diff --git a/webapp/src/components/HomeScreen.js b/webapp/src/components/HomeScreen.js index 9d94754b..b724088a 100644 --- a/webapp/src/components/HomeScreen.js +++ b/webapp/src/components/HomeScreen.js @@ -24,7 +24,7 @@ const HomeScreen = () => { useEffect(() => { checkUserLogin(); - document.getElementById('btLogout').style.display = 'inline-block'; + //document.getElementById('btLogout').style.display = 'inline-block'; }, []) From 36df152f1032aed20a65e502d9852a27b42dc8a4 Mon Sep 17 00:00:00 2001 From: uo283182 Date: Mon, 15 Apr 2024 13:52:13 +0200 Subject: [PATCH 12/22] =?UTF-8?q?Arreglar=20error=20de=20indentacion=20y?= =?UTF-8?q?=20bot=C3=B3n=20de=20cerrar=20sesi=C3=B3n?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- gatewayservice/openapi.yaml | 94 ++++++++++++++++++------------------- webapp/package-lock.json | 1 - webapp/src/App.js | 7 +++ 3 files changed, 54 insertions(+), 48 deletions(-) diff --git a/gatewayservice/openapi.yaml b/gatewayservice/openapi.yaml index e5a196a4..6f2816a4 100644 --- a/gatewayservice/openapi.yaml +++ b/gatewayservice/openapi.yaml @@ -185,58 +185,58 @@ paths: example: Cannot connect with Wikidata /getRecords: - post: - summary: Obtains the records of the user. - operationId: userRecords - requestBody: - required: true - content: - application/json: - schema: - type: object - properties: - user_id: - type: string - description: User ID. - example: student - responses: - '200': - description: Get Records successful. + post: + summary: Obtains the records of the user. + operationId: userRecords + requestBody: + required: true content: application/json: schema: - type: array + type: object properties: user_id: type: string - description: Username. + description: User ID. example: student - correctQuestions: - type: number - description: Number of correct questions. - example: 2 - totalQuestions: - type: number - description: Number of questions. - example: 5 - totalTime: - type: number - description: Time spent playing in seconds. - example: 26 - doneAt: - type: string - description: When was the game done. - example: '2024-03-17T20:47:23.935Z' - + responses: + '200': + description: Get Records successful. + content: + application/json: + schema: + type: array + properties: + user_id: + type: string + description: Username. + example: student + correctQuestions: + type: number + description: Number of correct questions. + example: 2 + totalQuestions: + type: number + description: Number of questions. + example: 5 + totalTime: + type: number + description: Time spent playing in seconds. + example: 26 + doneAt: + type: string + description: When was the game done. + example: '2024-03-17T20:47:23.935Z' + - '400': - description: User is no valid or doesn't exist. - content: - application/json: - schema: - type: object - properties: - error: - type: string - description: Shows the error info.. - example: Invalid user + '400': + description: User is no valid or doesn't exist. + content: + application/json: + schema: + type: object + properties: + error: + type: string + description: Shows the error info.. + example: Invalid user diff --git a/webapp/package-lock.json b/webapp/package-lock.json index 7fbcd5fc..6ca88122 100644 --- a/webapp/package-lock.json +++ b/webapp/package-lock.json @@ -5,7 +5,6 @@ "requires": true, "packages": { "": { - "name": "webapp", "version": "0.1.0", "dependencies": { "@emotion/react": "^11.11.3", diff --git a/webapp/src/App.js b/webapp/src/App.js index ab7ce594..07c918ea 100644 --- a/webapp/src/App.js +++ b/webapp/src/App.js @@ -10,6 +10,7 @@ import ImagenA from './LogoSaberYGanar2.png'; import History from './components/History.js'; import './App.css'; import { useNavigate } from "react-router-dom"; +import { useEffect } from 'react'; function App() { const navigate = useNavigate(); @@ -31,6 +32,10 @@ function App() { } }; + useEffect(() => { + document.getElementById('btLogout').style.display = 'none'; + }, []) + return (