Skip to content

Commit

Permalink
Merge pull request #61 from Arquisoft/Develop
Browse files Browse the repository at this point in the history
Trabajar con la rama Develop con las nuevas modificaciones
  • Loading branch information
UO287747 authored Feb 26, 2024
2 parents ca93378 + 08a31ba commit 1ab8df1
Show file tree
Hide file tree
Showing 29 changed files with 9,670 additions and 4 deletions.
2 changes: 2 additions & 0 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ jobs:
node-version: 20
- run: npm --prefix users/authservice ci
- run: npm --prefix users/userservice ci
- run: npm --prefix question_generator ci
- run: npm --prefix questionservice ci
- run: npm --prefix gatewayservice ci
- run: npm --prefix webapp ci
- run: npm --prefix users/authservice test -- --coverage
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,7 @@ The deploy action is the following:
deploy:
name: Deploy over SSH
runs-on: ubuntu-latest
needs: [docker-push-userservice,docker-push-authservice,docker-push-gatewayservice,docker-push-webapp]
needs: [docker-push-userservice,docker-push-authservice,docker-push-gatewayservice,docker-push-webapp,docker-push-questiongenerationservice, docker-push-questionservice]
steps:
- name: Deploy over SSH
uses: fifsky/ssh-action@master
Expand Down
28 changes: 28 additions & 0 deletions docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,30 @@ services:
environment:
MONGODB_URI: mongodb://mongodb:27017/userdb

questiongenerationservice:
container_name: questiongenerationservice-${teamname:-defaultASW}
image: ghcr.io/arquisoft/wiq_es3b/questiongenerationservice:latest
profiles: ["dev", "prod"]
build: ./question_generator
ports:
- "8003:8003"
networks:
- mynetwork

questionservice:
container_name: questionservice-${teamname:-defaultASW}
image: ghcr.io/arquisoft/wiq_es3b/questionservice:latest
profiles: ["dev", "prod"]
build: ./questionservice
depends_on:
- mongodb
ports:
- "8004:8004"
networks:
- mynetwork
environment:
MONGODB_URI: mongodb://mongodb:27017/questionsdb

gatewayservice:
container_name: gatewayservice-${teamname:-defaultASW}
image: ghcr.io/arquisoft/wiq_es3b/gatewayservice:latest
Expand All @@ -48,13 +72,17 @@ services:
- mongodb
- userservice
- authservice
- questiongenerationservice
- questionservice
ports:
- "8000:8000"
networks:
- mynetwork
environment:
AUTH_SERVICE_URL: http://authservice:8002
USER_SERVICE_URL: http://userservice:8001
QUESTION_GENERATION_SERVICE_URL: http://questiongenerationservice:8003
QUESTIONS_SERVICE_URL: http://questionservice:8004

webapp:
container_name: webapp-${teamname:-defaultASW}
Expand Down
25 changes: 25 additions & 0 deletions gatewayservice/gateway-service.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ const port = 8000;

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';

app.use(cors());
app.use(express.json());
Expand Down Expand Up @@ -41,6 +43,29 @@ app.post('/adduser', async (req, res) => {
}
});

app.get('/api/questions/create', async (req, res) => {
console.log('Create Question')
try {
// Forward the add user request to the user service
const userResponse = await axios.get(questionGenerationServiceUrl+'/api/questions/create');
console.log(userResponse.data);
res.json(userResponse.data);
} catch (error) {
res.status(error.response.status).json({ error: error.response.data.error });
}
});

// Ruta para agregar una nueva pregunta
app.post('/addquestion', async (req, res) => {
try {
// Forward the add question request to the questions service
const questionResponse = await axios.post(questionServiceUrl + '/addquestion', req.body);
res.json(questionResponse.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}`);
Expand Down
58 changes: 56 additions & 2 deletions gatewayservice/gateway-service.test.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
const request = require('supertest');
const axios = require('axios');
const app = require('./gateway-service');

let app;
beforeAll(async () => {
app = require('./gateway-service');
});
afterAll(async () => {
app.close();
});
Expand All @@ -15,8 +17,35 @@ describe('Gateway Service', () => {
return Promise.resolve({ data: { token: 'mockedToken' } });
} else if (url.endsWith('/adduser')) {
return Promise.resolve({ data: { userId: 'mockedUserId' } });
}else if (url.endsWith('/addquestion')) {
return Promise.resolve({
data: {
question: 'What is the capital of France?',
options: ['Paris', 'Berlin', 'Madrid', 'Rome'],
correctOptionIndex: 0
}
});
}
});
axios.get.mockImplementation((url) => {
if (url.endsWith('/api/questions/create')) {
return Promise.resolve({
data: {
question: 'Mocked Question',
correct: 'Mocked Correct Answer',
incorrects: ['Mocked Option 1', 'Mocked Option 2']
}
});
}
});
// Test /health endpoint
it('should give information of the status', async () => {
const response = await request(app)
.get('/health');

expect(response.statusCode).toBe(200);
expect(response.body.status).toBe('OK');
});

// Test /login endpoint
it('should forward login request to auth service', async () => {
Expand All @@ -37,4 +66,29 @@ describe('Gateway Service', () => {
expect(response.statusCode).toBe(200);
expect(response.body.userId).toBe('mockedUserId');
});

// Test /api/questions/create endpoint
it('should forward create question request to question generation service', async () => {
const response = await request(app)
.get('/api/questions/create');
expect(response.statusCode).toBe(200);
expect(response.body).toHaveProperty('question');
expect(response.body).toHaveProperty('correct');
expect(response.body).toHaveProperty('incorrects');
},10000);


// Test /addquestion endpoint
it('should add a new question', async () => {
const response = await request(app)
.post('/addquestion')
.send({
question: 'What is the capital of France?',
options: ['Paris', 'Berlin', 'Madrid', 'Rome'],
correctOptionIndex: 0,
});

expect(response.statusCode).toBe(200);
expect(response.body).toHaveProperty('question', 'What is the capital of France?');
});
});
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,4 @@
"devDependencies": {
"serve": "^14.2.1"
}
}
}
20 changes: 20 additions & 0 deletions question_generator/Dockerfile
Original file line number Diff line number Diff line change
@@ -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/questiongenerationservice

# 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 8003

# Define the command to run your app
CMD ["node", "questionGenerationService.js"]
134 changes: 134 additions & 0 deletions question_generator/cities/citiesQuestions.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,134 @@
const queryExecutor=require("../queryExecutor")
class CitiesQuestions{
#citiesQuestions=null;
static getInstance(){
if (!this.citiesQuestions) {
this.citiesQuestions = new CitiesQuestions();
}
return this.citiesQuestions;
}
constructor(){
this.cities={};
}
async loadData(){
if (Object.keys(this.cities).length === 0) {//Se obtienen 100 ciudades relevantes
const query=`
SELECT ?city ?cityLabel ?population ?countryLabel ?elevation_above_sea_level
WITH{
SELECT ?city ?cityLabel
WHERE{
?city wdt:P31 wd:Q515
}
LIMIT 1000
} AS %i
WHERE {
INCLUDE %i
OPTIONAL{
?city wdt:P1082 ?population.
?city wdt:P17 ?country.
?city wdt:P2044 ?elevation_above_sea_level
}
FILTER EXISTS{
?city wdt:P1082 ?population.
?city wdt:P17 ?country.
?city wdt:P2044 ?elevation_above_sea_level
}
SERVICE wikibase:label { bd:serviceParam wikibase:language "[AUTO_LANGUAGE],en". }
}
ORDER BY DESC(?population)
LIMIT 100
`
let cities = await queryExecutor.execute(query);
cities.forEach(city => {
const cityId = city.city.value;
const cityName = city.cityLabel.value;
const population = city.population.value;
const country = city.countryLabel.value;
const elevationAboveSeaLevel = city.elevation_above_sea_level.value;

if (!this.cities[cityId]) {
this.cities[cityId] = {
cityId: cityId,
cityName: cityName,
population: population,
country: country,
elevation_above_sea_level: []
};
}

this.cities[cityId].elevation_above_sea_level.push(parseFloat(elevationAboveSeaLevel));
});

}
}
async getRandomCities(numberOfCities){
await this.loadData();
const citiesArray = Object.values(this.cities);
const randomResults = citiesArray.sort(() => Math.random() - 0.5).slice(0, numberOfCities);
return randomResults
}
async getMostPopulatedCity(){
let numberOfCities=4
const results=await this.getRandomCities(numberOfCities);
const formattedResults = await results.map(result => {
return {
item: result.cityName,
value:parseFloat(result.population),
};
}).sort((a, b) => b.value - a.value);
const finalResults={
correct: null,
incorrects: []
}
for(let i=0;i<numberOfCities;i++){
if(i==0){
finalResults.correct=formattedResults[i].item
}
else{
finalResults.incorrects.push(formattedResults[i].item)
}
}
return finalResults
}
async getCityForCountry(){
let numberOfCities=4;
let result =(await this.getRandomCities(1))[0];
let country=result.country;

let correct = result.cityName;
let incorrects = []
let i=1;
while(i<numberOfCities){
let city=(await this.getRandomCities(1))[0];
if(city.country!=country){
incorrects.push(city.cityName);
i++;
}
}
return {
country:country,
correct:correct,
incorrects:incorrects
}
}
async getHigherCity(){
let numberOfCities=4;
let result =await this.getRandomCities(numberOfCities);
//Using first value in the array for elevation_above_sea_level
const formattedResults = await result.sort((a, b) => b.elevation_above_sea_level[0] - a.elevation_above_sea_level[0]);
const finalResults={
correct: null,
incorrects: []
}
for(let i=0;i<numberOfCities;i++){
if(i==0){
finalResults.correct=formattedResults[i].cityName
}
else{
finalResults.incorrects.push(formattedResults[i].cityName)
}
}
return finalResults;
}
}
module.exports=CitiesQuestions;
38 changes: 38 additions & 0 deletions question_generator/cities/citiesTemplates.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
const cities=require('./citiesQuestions');
const citiesQuery=cities.getInstance();
function loadData(){
citiesQuery.loadData();
}
const templates=[
async ()=>
{
const results= await citiesQuery.getMostPopulatedCity();
return{
"question":"Which city has more population?",
"correct":results.correct,
"incorrects":results.incorrects
}
},
async ()=>
{
const results= await citiesQuery.getCityForCountry();
return{
"question":"Which city is in "+results.country+"?",
"correct":results.correct,
"incorrects":results.incorrects
}
},
async ()=>
{
const results= await citiesQuery.getHigherCity();
return{
"question":"Which city is higher above sea level?",
"correct":results.correct,
"incorrects":results.incorrects
}
}


]
module.exports.getRandomQuestion = () => templates[Math.floor(Math.random()*templates.length)]();
module.exports.loadData = () =>loadData();
Loading

0 comments on commit 1ab8df1

Please sign in to comment.