Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Autologin, changes on displayed information and api-doc updated #166

Merged
merged 17 commits into from
Apr 7, 2024
Merged
19 changes: 18 additions & 1 deletion .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -31,4 +31,21 @@ jobs:
uses: sonarsource/sonarcloud-github-action@master
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}
SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}
e2e-tests:
needs: [unit-tests]
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 20
- run: npm --prefix users/authservice install
- run: npm --prefix users/userservice install
- run: npm --prefix question_generator install
- run: npm --prefix questionservice install
- run: npm --prefix gameservice install
- run: npm --prefix gatewayservice install
- run: npm --prefix webapp install
- run: npm --prefix webapp run build
- run: npm --prefix webapp run test:e2e
2 changes: 1 addition & 1 deletion .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -185,4 +185,4 @@ jobs:
wget https://raw.githubusercontent.com/arquisoft/wiq_es3b/master/docker-compose.yml -O docker-compose.yml
wget https://raw.githubusercontent.com/arquisoft/wiq_es3b/master/.env -O .env
docker compose --profile prod down
docker-compose --profile prod up -d --pull always
docker compose --profile prod up -d --pull always
1 change: 1 addition & 0 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{}
106 changes: 77 additions & 29 deletions gatewayservice/gateway-service.js
Original file line number Diff line number Diff line change
Expand Up @@ -50,20 +50,49 @@ app.get('/health', (_req, res) => {
app.post('/login', async (req, res) => {
try {
// Forward the login request to the authentication service
const authResponse = await axios.post(authServiceUrl+'/login', req.body);
res.json(authResponse.data);
try{
const authResponse = await axios.post(authServiceUrl+'/login', req.body);
res.json(authResponse.data);
} catch (error) {
res.status(error.response.status).json(error.response.data);
}
} catch (error) {
res.status(500).json({ error: 'Service down' });
}
});
app.get('/verify', async (req, res) => {
try {
if (req.headers.authorization) {
try{
const authResponse = await axios.get(authServiceUrl+'/verify', {
Dismissed Show dismissed Hide dismissed
headers: {
Authorization: req.headers.authorization
}
});
res.json(authResponse.data);
} catch (error) {
res.status(error.response.status).json(error.response.data);
}
} else {
res.status(401).json({ error: 'Unauthorized' });
}
} catch (error) {
res.status(500).json({ error: error.message||'Service down' });
res.status(500).json({ error: 'Service down' });
}
});


app.post('/adduser', async (req, res) => {
try {
// Forward the add user request to the user service
const userResponse = await axios.post(userServiceUrl+'/adduser', req.body);
res.json(userResponse.data);
try{
const userResponse = await axios.post(userServiceUrl+'/adduser', req.body);
res.json(userResponse.data);
} catch (error) {
res.status(error.response.status).json(error.response.data);
}
} catch (error) {
res.status(500).json({ error: error.message||'Service down' });
res.status(500).json({ error: 'Service down' });
}
});

Expand All @@ -73,13 +102,17 @@ app.get('/api/questions/create', async (req, res) => {
const apiUrl = new URL('/api/questions/create', questionGenerationServiceUrl);
apiUrl.search = queryParams.toString();

// Forward the add user request to the user service
const userResponse = await axios.get(apiUrl.toString(), {
headers: {
Authorization: req.headers.authorization
}
});
res.json(userResponse.data);
try{
// Forward the add user request to the user service
const userResponse = await axios.get(apiUrl.toString(), {
headers: {
Authorization: req.headers.authorization
}
});
res.json(userResponse.data);
}catch(error){
res.status(error.response.status).json(error.response.data);
}
} catch (error) {
res.status(500).json({ error: "Service down" });
}
Expand All @@ -91,8 +124,12 @@ app.get('/api/info/questions', async function (req, res) {
if(Object.keys(req.query).length > 0) {
url += '?' + new URLSearchParams(req.query).toString();
}
const infoResponse = await axios.get(url);
res.json(infoResponse.data);
try{
const infoResponse = await axios.get(url);
res.json(infoResponse.data);
}catch(error){
res.status(error.response.status).json(error.response.data);
}
} catch (error) {
res.status(500).json({ error: "Service down" });
}
Expand All @@ -104,8 +141,12 @@ app.get('/api/info/users', async (req, res) => {
if(username){
url += '?user=' + username;
}
const infoResponse = await axios.get(url);
res.json(infoResponse.data);
try{
const infoResponse = await axios.get(url);
res.json(infoResponse.data);
}catch(error){
res.status(error.response.status).json(error.response.data);
}
} catch (error) {
res.status(500).json({ error: "Service down" });
}
Expand All @@ -117,8 +158,12 @@ app.get('/api/info/games', async (req, res) => {
if(username){
url += '?user=' + username;
}
const infoResponse = await axios.get(url);
res.json(infoResponse.data);
try{
const infoResponse = await axios.get(url);
res.json(infoResponse.data);
}catch(error){
res.status(error.response.status).json(error.response.data);
}
} catch (error) {
res.status(500).json({ error: "Service down" });
}
Expand All @@ -127,9 +172,13 @@ app.get('/api/info/games', 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);
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.response.data);
}
} catch (error) {
res.status(500).json({ error: "Service down" });
}
Expand All @@ -139,14 +188,13 @@ app.post('/addgame', async (req, res) => {
app.get('/getParticipation/:userId', async (req, res) => {
try {
const userId = req.params.userId;
if (!userId) {
res.status(404).json({ error: 'User ID not provided' });
return;
}
const apiUrl = `${gameServiceUrl}/getParticipation/${userId}`;
const gameResponse = await axios.get(apiUrl);

res.json(gameResponse.data);
try{
const gameResponse = await axios.get(apiUrl);
res.json(gameResponse.data);
}catch(error){
res.status(error.response.status).json(error.response.data);
}
} catch (error) {
res.status(500).json({ error: "Service down" });
}
Expand Down
118 changes: 102 additions & 16 deletions gatewayservice/gateway-service.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ let app;
beforeAll(async () => {
app = require('./gateway-service');
});

afterAll(async () => {
app.close();
});
Expand All @@ -13,9 +14,7 @@ jest.mock('axios');
describe('Gateway Service', () => {
// Mock responses from external services
axios.post.mockImplementation((url, data) => {
if (url.endsWith('/login')) {
return Promise.resolve({ data: { token: 'mockedToken' } });
} else if (url.endsWith('/adduser')) {
if (url.endsWith('/adduser')) {
return Promise.resolve({ data: { userId: 'mockedUserId' } });
}else if (url.endsWith('/addquestion')) {
return Promise.resolve({
Expand All @@ -28,15 +27,7 @@ describe('Gateway Service', () => {
}
});
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']
}
});
}else if(url.endsWith('/api/questions/create?lang=es&category=sports')){
if(url.endsWith('/api/questions/create?lang=es&category=sports')){
return Promise.resolve({
data: {
question: 'Mocked Question',
Expand Down Expand Up @@ -65,27 +56,79 @@ describe('Gateway Service', () => {
}
]
});
}else if(url.endsWith('/verify')){
return Promise.resolve({ data: { username: 'testuser' } });
}
});

// 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 () => {
axios.post.mockResolvedValueOnce({ data: { token: 'mockedToken' } });
const response = await request(app)
.post('/login')
.send({ username: 'testuser', password: 'testpassword' });

expect(response.statusCode).toBe(200);
expect(response.body.token).toBe('mockedToken');
});

it('should return an error when the user service is down', async () => {
jest.spyOn(axios, 'post').mockRejectedValueOnce({ response: { status: 500, data: { error: 'Service down' } } });
const response = await request(app)
.post('/login')
.send({ username: 'testuser', password: 'testpassword' });
expect(response.status).toBe(500);
expect(response.body).toEqual({ error: 'Service down' });
});
// Test /verify endpoint
it('should verify authorization token with auth service', async () => {
const mockedToken = 'mockedToken';
const authResponse = { username: 'testuser' };
axios.get.mockResolvedValueOnce({ data: authResponse });
const response = await request(app)
.get('/verify')
.set('Authorization', `Bearer ${mockedToken}`);
expect(response.statusCode).toBe(200);
expect(response.body).toEqual(authResponse);
});
it("should return 401 Unauthorized for unauthorized request", async () => {
const response = await request(app).get("/verify");
expect(response.statusCode).toBe(401);
expect(response.body).toEqual({ error: "Unauthorized" });
});
// Test /verify endpoint when auth service is down
it('should return an error when the auth service is down', async () => {
const mockedToken = 'mockedToken';
axios.get.mockImplementation({ response: { status: 500, data: { error: 'Service down' } } });
const response = await request(app)
.get('/verify')
.set('Authorization', `Bearer ${mockedToken}`);
expect(response.statusCode).toBe(500);
expect(response.body).toEqual({ error: 'Service down' });
});

// Test /verify endpoint when auth service returns an error
it('should return an error when the auth service returns an error', async () => {
const mockedToken = 'mockedToken';
const mockedError = { error: 'Unauthorized' };
axios.get.mockRejectedValueOnce({ response: { status: 401, data: mockedError } });
const response = await request(app)
.get('/verify')
.set('Authorization', `Bearer ${mockedToken}`);
expect(response.statusCode).toBe(401);
expect(response.body).toEqual(mockedError);
});




// Test /adduser endpoint
it('should forward add user request to user service', async () => {
const response = await request(app)
Expand All @@ -95,15 +138,24 @@ describe('Gateway Service', () => {
expect(response.statusCode).toBe(200);
expect(response.body.userId).toBe('mockedUserId');
});
it('should return an error when the user service is down', async () => {
axios.post.mockRejectedValueOnce({ response: { status: 500, data: { error: 'Service down' } } });
const response = await request(app)
.post('/adduser')
.send({ username: 'testuser', password: 'testpassword' });
expect(response.statusCode).toBe(500);
expect(response.body).toEqual({ error: 'Service down' });
});

// Test /api/info/questions endpoint
it('should forward info request with id to question service', async () => {
axios.get.mockResolvedValueOnce({ data: { question: 'Mocked Question', correct: 'Mocked Correct Answer', incorrects: ['Mocked Option 1', 'Mocked Option 2'] } });
const response = await request(app)
.get('/api/info/questions');
expect(response.statusCode).toBe(200);
expect(response.headers['content-type']).toContain('application/json');
expect(response.body).toEqual(expect.any(Array));
});
expect(response.body).toEqual({ question: 'Mocked Question', correct: 'Mocked Correct Answer', incorrects: ['Mocked Option 1', 'Mocked Option 2'] });
});

// Test /api/info/questions endpoint when service is down
it('should return an error when the question service is down', async () => {
Expand All @@ -120,13 +172,29 @@ describe('Gateway Service', () => {

// Test /api/questions/create endpoint
it('should forward create question request to question generation service', async () => {
axios.get.mockImplementation(() => {
return Promise.resolve({
data: {
question: 'Mocked Question',
correct: 'Mocked Correct Answer',
incorrects: ['Mocked Option 1', 'Mocked Option 2']
}
});
});
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);
it('should return an error when the user service is down', async () => {
axios.get.mockRejectedValueOnce({ response: { status: 500, data: { error: 'Service down' } } });
const response = await request(app)
.get('/api/questions/create')
expect(response.statusCode).toBe(500);
expect(response.body).toEqual({ error: 'Service down' });
});

it('should forward create question request to question generation service', async () => {
const response = await request(app)
Expand Down Expand Up @@ -238,4 +306,22 @@ describe('GET /api/info/games', () => {
expect(response.body).toEqual(mockedError);
});
});
describe('addGame', () =>{
it('should return 401 Unauthorized if authorization token is missing', async () => {
const response = await request(app).post('/addgame');
expect(response.statusCode).toBe(401);
expect(response.body).toEqual({ error: 'Unauthorized' });
});

it('should return 401 Unauthorized if authorization token is invalid', async () => {
const mockedToken = 'invalidToken';
axios.get.mockRejectedValueOnce({ response: { status: 401 } });
const response = await request(app)
.post('/addgame')
.set('Authorization', `Bearer ${mockedToken}`);
expect(response.statusCode).toBe(401);
expect(response.body).toEqual({ error: 'Unauthorized' });
});


});
Loading