diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml
index ac542fe1..bfb8a8a9 100644
--- a/.github/workflows/release.yml
+++ b/.github/workflows/release.yml
@@ -231,7 +231,7 @@ jobs:
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
registry: ghcr.io
- workdir: prometheus
+ workdir: gatewayservice/monitoring/prometheus
docker-push-grafana:
name: Push Grafana Docker Image to GitHub Packages
runs-on: ubuntu-latest
@@ -248,12 +248,13 @@ jobs:
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
registry: ghcr.io
- workdir: grafana
+ workdir: gatewayservice/monitoring/grafana
deploy:
name: Deploy over SSH
runs-on: ubuntu-latest
needs: [docker-push-userservice,docker-push-authservice,docker-push-gatewayservice,docker-push-webapp,
- docker-push-questiongenerator,docker-push-gamehistoryservice,docker-push-perfilservice,docker-push-allquestionservice,docker-push-alluserservice]
+ docker-push-questiongenerator,docker-push-gamehistoryservice,docker-push-perfilservice,docker-push-allquestionservice,docker-push-alluserservice,
+ docker-push-prometheus, docker-push-grafana]
steps:
- name: Deploy over SSH
uses: fifsky/ssh-action@master
diff --git a/docs/src/12_test_report.adoc b/docs/src/12_test_report.adoc
index 2c22eaa0..1b1e047d 100644
--- a/docs/src/12_test_report.adoc
+++ b/docs/src/12_test_report.adoc
@@ -4,6 +4,27 @@ ifndef::imagesdir[:imagesdir: ../images]
== Informe de pruebas
=== Pruebas de cobertura
+Las pruebas de cobertura prueban la funcionalidad de la aplicación creando, al mismo tiempo, una métrica que indica cuanto del código creado está cubierto por dichas pruebas.
+Las pruebas se han realizado en todos los servicios de la aplicación, a fin de comprobar que la funcionalidad de estos es la esperada.
+
+Para las pruebas de cobertura se ha utilizado, principalmente, las liberías:
+
+* testing-library/react
+* supertest
+* axios
+* sinon
+
+A continuación, se explica para que se ha utilizado cada una de dichas librerías:
+[options="header",cols="1,1,1"]
+|===
+|Librería|Contenido|Uso
+| testing-library/react | Contiene todas las funciones necesarias para hacer pruebas con los componentes de REACT como: render, fireEvent, act o waitFor | Para los tests de los componentes de REACT que se encuentran en webapp
+| supertest | La función request que se utiliza para realizar peticiones | Para todas aquellas pruebas que requieran comprobar una petición a una URL, incluyendo el envío de parámetros y la comprobación de la respuesta
+| axios | Todas las funciones necesarias para hacer Mocks | Para todos los tests que requerían del uso de mocks. Por ejemplo, para probar el juego hemos mockeado las llamadas al generador de preguntas, para no depender de este
+| sinon | Contiene la función stub que permite sobresscribir los métodos HTTP al realizar peticiones | Principalmente, para los tests en los que había que simular un cierto valor de respuesta o un error en la petición sin necesidad de causar dicho error al hacer la petición
+|===
+
+Además de todas estas librerías externas, utilizamos, para practicamente todas las pruebas, el framework jest, muy utilizado para hacer las pruebas de proyectos que utilizan REACT, como es nuestro caso. Este framework es el que nos permite definir los casos de prueba y controlar las peticiones que realizamos utilizando, por ejemplo, la función spyOn que nos permite espionar una función o petición.
=== Pruebas de usabilidad
diff --git a/gatewayservice/gateway-service.js b/gatewayservice/gateway-service.js
index 78170d57..9cbe75da 100644
--- a/gatewayservice/gateway-service.js
+++ b/gatewayservice/gateway-service.js
@@ -21,7 +21,7 @@ const allUsersServiceUrl = process.env.ALLUSERS_SERVICE_URL || 'http://localhost
const allQuestionsServiceUrl = process.env.ALLQUESTIONS_SERVICE_URL || 'http://localhost:8007';
const corsOptions = {
- origin: originEndpoint,
+ origin: `${originEndpoint}`,
methods: ['GET', 'POST'],
allowedHeaders: ['Content-Type', 'Authorization']
};
diff --git a/gatewayservice/monitoring/grafana/Dockerfile b/gatewayservice/monitoring/grafana/Dockerfile
new file mode 100644
index 00000000..af00b6db
--- /dev/null
+++ b/gatewayservice/monitoring/grafana/Dockerfile
@@ -0,0 +1,13 @@
+# Usa la imagen base de Grafana desde Docker Hub
+FROM grafana/grafana
+
+USER root
+
+RUN addgroup -S nonroot \
+ && adduser -S nonroot -G nonroot
+
+RUN chown -R nonroot:nonroot ./
+
+COPY provisioning ./provisioning
+
+USER nonroot
\ No newline at end of file
diff --git a/gatewayservice/monitoring/grafana/provisioning/dashboards/example-service-dashboard.json b/gatewayservice/monitoring/grafana/provisioning/dashboards/dashboard.json
similarity index 100%
rename from gatewayservice/monitoring/grafana/provisioning/dashboards/example-service-dashboard.json
rename to gatewayservice/monitoring/grafana/provisioning/dashboards/dashboard.json
diff --git a/gatewayservice/monitoring/prometheus/Dockerfile b/gatewayservice/monitoring/prometheus/Dockerfile
new file mode 100644
index 00000000..811b8da8
--- /dev/null
+++ b/gatewayservice/monitoring/prometheus/Dockerfile
@@ -0,0 +1,13 @@
+# Usa la imagen base de Prometheus desde Docker Hub
+FROM prom/prometheus
+
+USER root
+
+RUN addgroup -S nonroot \
+ && adduser -S nonroot -G nonroot
+
+RUN chown -R nonroot:nonroot ./
+
+COPY prometheus.yml ./
+
+USER nonroot
\ No newline at end of file
diff --git a/gatewayservice/openapi.yaml b/gatewayservice/openapi.yaml
index 8656f624..9ff2939f 100644
--- a/gatewayservice/openapi.yaml
+++ b/gatewayservice/openapi.yaml
@@ -4,9 +4,9 @@ info:
description: Gateway OpenAPI specification.
version: 0.2.0
servers:
- - url: http://${{ secrets.DEPLOY_HOST }}:8000
+ - url: http://localhost:8000
description: Development server
- - url: http://${{ secrets.DEPLOY_HOST }}:8000
+ - url: http://${{secrets.DEPLOY_HOST}}:8000
description: Production server
paths:
/adduser:
@@ -23,11 +23,15 @@ paths:
username:
type: string
description: User ID.
- example: student
+ example: Pepito
+ email:
+ type: string
+ description: User email.
+ example: pepito@gmail.com
password:
type: string
description: User password.
- example: pass
+ example: passPepito
responses:
'200':
description: User added successfully.
@@ -95,11 +99,11 @@ paths:
username:
type: string
description: User ID.
- example: student
+ example: Pepito
password:
type: string
description: User password.
- example: pass
+ example: passPepito
responses:
'200':
description: Login successful. Returns user token, username, and creation date.
@@ -115,7 +119,7 @@ paths:
username:
type: string
description: Username.
- example: student
+ example: Pepito
createdAt:
type: string
description: Creation date.
@@ -146,6 +150,28 @@ paths:
get:
summary: Generate a question.
operationId: generateQuestion
+ parameters:
+ - name: user
+ in: query
+ description: User of the game.
+ example: testuser
+ required: true
+ schema:
+ type: string
+ - name: thematic
+ in: query
+ description: The thematic of the questions.
+ example: Geografia
+ required: true
+ schema:
+ type: string
+ - name: language
+ in: query
+ description: The language of the questions.
+ example: es
+ required: true
+ schema:
+ type: string
responses:
'200':
description: Generation successful. Returns the question, the options, the correct option, the image (if neccessary) and the question id
@@ -197,14 +223,14 @@ paths:
summary: Update a question.
operationId: updateQuestion
parameters:
- time:
+ - name: time
in: query
description: Time of the question.
example: 10
required: true
schema:
type: string
- correct:
+ - name: correct
in: query
description: If the question was answered correctly.
example: true
@@ -303,6 +329,17 @@ paths:
post:
summary: Configure the game.
operationId: configureGame
+ requestBody:
+ required: true
+ content:
+ application/json:
+ schema:
+ type: object
+ properties:
+ maxQuestions:
+ type: integer
+ description: Number of questions.
+ example: 10
responses:
'200':
description: Succesful configure the game number of questions
@@ -313,7 +350,7 @@ paths:
properties:
maxQuestions:
type: int
- description: The new number of questions of the question.
+ description: The new number of questions of the game.
example: 10
'400':
description: Error when configuring the game
@@ -342,7 +379,7 @@ paths:
summary: Charge the game history.
operationId: gamehistory
parameters:
- username:
+ - name: username
in: query
description: User which game history we want to recover.
example: Pepito
@@ -352,6 +389,39 @@ paths:
responses:
'200':
description: Succesful charge the game history
+ content:
+ application/json:
+ schema:
+ type: object
+ properties:
+ userId:
+ type: string
+ description: User ID.
+ example: Pepito
+ totalGamesPlayed:
+ type: integer
+ description: Number of games played by the user.
+ example: 10
+ totalQuestionsAnswered:
+ type: integer
+ description: Number of questions answered by the user.
+ example: 30
+ totalRightQuestions:
+ type: integer
+ description: Number of questions answered correctly by the user.
+ example: 15
+ totalIncorrectQuestions:
+ type: integer
+ description: Number of questions answered incorrectly by the user.
+ example: 15
+ ratio:
+ type: string
+ description: Ratio between correct and incorrect questions.
+ example: 50%
+ totalTime:
+ type: string
+ description: Time the user spend in the game.
+ example: 150s
'400':
description: Error when charging the game history
'500':
@@ -370,7 +440,7 @@ paths:
summary: Get an specific user
operationId: getUser
parameters:
- username:
+ - name: username
in: query
description: The name of the user we want to get.
example: Pepito
@@ -381,22 +451,22 @@ paths:
'200':
description: Succesful get the user
content:
- application/json:
- schema:
- type: object
- properties:
- username:
- type: string
- description: The name of the user.
- example: Pepito
- email:
- type: string
- description: The email of the user.
- example: pepito@gmail.com
- creado:
- type: string
- description: The date where the user account was created.
- example: 01/01/2024
+ application/json:
+ schema:
+ type: object
+ properties:
+ user- name:
+ type: string
+ description: The name of the user.
+ example: Pepito
+ email:
+ type: string
+ description: The email of the user.
+ example: pepito@gmail.com
+ creado:
+ type: string
+ description: The date where the user account was created.
+ example: 01/01/2024
'400':
description: Error when getting the user
'500':
@@ -418,14 +488,14 @@ paths:
'200':
description: Succesful get all the users
content:
- application/json:
- schema:
- type: object
- properties:
- users:
- type: array
- description: The information about the users (username, email, creation date).
- example: [Pepito, pepito@gmail.com, 01/01/2024]
+ application/json:
+ schema:
+ type: object
+ properties:
+ users:
+ type: array
+ description: The information about the users (username, email, creation date).
+ example: [Pepito, pepito@gmail.com, 01/01/2024]
'400':
description: Error when getting the users
'500':
@@ -447,14 +517,14 @@ paths:
'200':
description: Succesful get all the questions
content:
- application/json:
- schema:
- type: object
- properties:
- users:
- type: array
- description: The information about the questions (question an correct answer).
- example: [¿Cual es la capital de España?,Madrid]
+ application/json:
+ schema:
+ type: object
+ properties:
+ users:
+ type: array
+ description: The information about the questions (question an correct answer).
+ example: [¿Cual es la capital de España?,Madrid]
'400':
description: Error when getting the questions
'500':
@@ -497,12 +567,56 @@ paths:
type: string
description: Error information.
example: Internal Server Error
+ /ranking:
+ get:
+ summary: Charge the ranking.
+ operationId: topUsers
+ parameters:
+ - name: sortBy
+ in: query
+ description: What we want to use to sort the ranking.
+ example: ratio
+ required: true
+ schema:
+ type: string
+ - name: userLimit
+ in: query
+ description: The number of users we want to recover.
+ example: 5
+ required: true
+ schema:
+ type: integer
+ responses:
+ '200':
+ description: Succesful charge the ranking
+ content:
+ application/json:
+ schema:
+ type: object
+ properties:
+ users:
+ type: array
+ description: The ranking of users.
+ example: [Pepito, Fulanito, Menganito, Juan, Laura]
+ '400':
+ description: Error when charging the ranking
+ '500':
+ description: Internal server error.
+ content:
+ application/json:
+ schema:
+ type: object
+ properties:
+ error:
+ type: string
+ description: Error information.
+ example: Internal Server Error
/endgamestats:
get:
summary: Charge the end game statistics.
operationId: endgamestats
parameters:
- username:
+ - name: username
in: query
description: User which game statistics we want to charge.
example: Pepito
@@ -513,30 +627,30 @@ paths:
'200':
description: Succesful charge the end game statitics
content:
- application/json:
- schema:
- type: object
- properties:
- totalRightQuestions:
- type: integer
- description: The number of questions that the user has answered correctly.
- example: 5
- totalIncorrectQuestions:
- type: integer
- description: The number of questions that the user has answered incorrectly.
- example: 0
- ratio:
- type: string
- description: The ratio between the correct and incorrect questions.
- example: 100%
- totalTime:
- type: string
- description: The time the user has spend at the game.
- example: 20s
- endgameImageWithRatio:
- type: string
- description: The end game ratio of questions
- example: 100%
+ application/json:
+ schema:
+ type: object
+ properties:
+ totalRightQuestions:
+ type: integer
+ description: The number of questions that the user has answered correctly.
+ example: 5
+ totalIncorrectQuestions:
+ type: integer
+ description: The number of questions that the user has answered incorrectly.
+ example: 0
+ ratio:
+ type: string
+ description: The ratio between the correct and incorrect questions.
+ example: 100%
+ totalTime:
+ type: string
+ description: The time the user has spend at the game.
+ example: 20s
+ endgameImageWithRatio:
+ type: string
+ description: The end game ratio of questions
+ example: 100%
'400':
description: Error when charging the end game statistics
'500':
@@ -555,7 +669,7 @@ paths:
summary: Restart the game.
operationId: endgamestats
parameters:
- username:
+ - name: username
in: query
description: User which game statistics we want to charge.
example: Pepito
@@ -566,18 +680,18 @@ paths:
'200':
description: Succesful charge the end game statistics
content:
- application/json:
- schema:
- type: object
- properties:
- message:
- type: string
- description: A message indicating the number of questions has been modified.
- example: Número de preguntas actualizado
- numberOfQuestions:
- type: integer
- description: The number of questions that has been modified.
- example: 5
+ application/json:
+ schema:
+ type: object
+ properties:
+ message:
+ type: string
+ description: A message indicating the number of questions has been modified.
+ example: Número de preguntas actualizado
+ numberOfQuestions:
+ type: integer
+ description: The number of questions that has been modified.
+ example: 5
'400':
description: Error when charging the end game statistics
'500':
diff --git a/questiongenerator/question.test.js b/questiongenerator/question.test.js
index a587c1f4..0dc2ad9c 100644
--- a/questiongenerator/question.test.js
+++ b/questiongenerator/question.test.js
@@ -47,7 +47,7 @@ describe('Question Generator test', () => {
expect(response.status).toBe(200);
expect(response.body).toHaveProperty('responseQuestion', 'responseOptions', 'responseCorrectOption', 'question_Id', 'responseImage');
- }, 10000);
+ }, 15000);
it('Should manager errors when calling /generateQuestion', async () => {
await simulateError('get', '/generateQuestion', 'Error al obtener datos', { error: "Error al obtener datos RangeError [ERR_OUT_OF_RANGE]: The value of \"max\" is out of range. It must be greater than the value of \"min\" (0). Received 0" });
diff --git a/webapp/public/brain-icon2.ico b/webapp/public/brain-icon2.ico
new file mode 100644
index 00000000..3c569191
Binary files /dev/null and b/webapp/public/brain-icon2.ico differ
diff --git a/webapp/public/brain-icon2.png b/webapp/public/brain-icon2.png
new file mode 100644
index 00000000..3c569191
Binary files /dev/null and b/webapp/public/brain-icon2.png differ
diff --git a/webapp/public/favicon.ico b/webapp/public/favicon.ico
deleted file mode 100644
index 390d4dcf..00000000
Binary files a/webapp/public/favicon.ico and /dev/null differ
diff --git a/webapp/public/index.html b/webapp/public/index.html
index 799d79de..1dd0bcd6 100644
--- a/webapp/public/index.html
+++ b/webapp/public/index.html
@@ -9,7 +9,7 @@
name="description"
content="Web site created using create-react-app"
/>
-
+
-
WIQ2C
+ BrainWIQ
diff --git a/webapp/public/logo192.png b/webapp/public/logo192.png
deleted file mode 100644
index 390d4dcf..00000000
Binary files a/webapp/public/logo192.png and /dev/null differ
diff --git a/webapp/public/manifest.json b/webapp/public/manifest.json
index 080d6c77..671dd3d3 100644
--- a/webapp/public/manifest.json
+++ b/webapp/public/manifest.json
@@ -8,12 +8,12 @@
"type": "image/x-icon"
},
{
- "src": "logo192.png",
+ "src": "brain-icon2.png",
"type": "image/png",
"sizes": "192x192"
},
{
- "src": "logo512.png",
+ "src": "brain-icon2.png",
"type": "image/png",
"sizes": "512x512"
}
diff --git a/webapp/src/App.css b/webapp/src/App.css
index 20f30f96..3305bb1b 100644
--- a/webapp/src/App.css
+++ b/webapp/src/App.css
@@ -1,4 +1,5 @@
body {
- background-color: #F3D3FA;
-}
-
+ background-image: url('./components/images/fondo.png');
+ background-repeat: no-repeat;
+ background-size: cover;
+ }
\ No newline at end of file
diff --git a/webapp/src/App.js b/webapp/src/App.js
index fdcfb109..bfb1701a 100644
--- a/webapp/src/App.js
+++ b/webapp/src/App.js
@@ -2,22 +2,14 @@ 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 Box from '@mui/material/Box';
import './App.css';
-import { createTheme, ThemeProvider } from '@mui/material/styles';
import { useTranslation } from 'react-i18next';
+import { CustomContainer } from './CustomContainer';
-const theme = createTheme({
- palette: {
- background: {
- default: '#F3D3FA',
- },
- },
-});
-
function App() {
const [t] = useTranslation("global");
@@ -29,31 +21,23 @@ function App() {
};
return (
-
-
+
+
{showLogin ? : }
{showLogin ? (
-
+
{t("enlaceLogin")}
) : (
-
+
{t("enlaceRegistro")}
)}
-
-
+
+
);
}
diff --git a/webapp/src/CustomContainer.js b/webapp/src/CustomContainer.js
new file mode 100644
index 00000000..549a7492
--- /dev/null
+++ b/webapp/src/CustomContainer.js
@@ -0,0 +1,22 @@
+import Container from '@mui/material/Container';;
+
+export function CustomContainer({ children, ...props }) {
+ return (
+
+ {children}
+
+ );
+}
\ No newline at end of file
diff --git a/webapp/src/components/AddUser.js b/webapp/src/components/AddUser.js
index c2e43e6a..5e5395db 100644
--- a/webapp/src/components/AddUser.js
+++ b/webapp/src/components/AddUser.js
@@ -1,9 +1,10 @@
// src/components/AddUser.js
import React, { useState } from 'react';
import axios from 'axios';
-import { Container, Typography, TextField, Button, Snackbar } from '@mui/material';
+import {Typography, TextField, Button, Snackbar } from '@mui/material';
import '../App.css';
import { useTranslation } from 'react-i18next';
+import { CustomContainer } from '../CustomContainer';
const apiEndpoint = process.env.REACT_APP_API_ENDPOINT || 'http://localhost:8000';
@@ -36,7 +37,7 @@ const AddUser = () => {
};
return (
- {
alignItems: 'center',
}}>