Skip to content

Commit e3a2791

Browse files
[FEATURE] Valider l'alerte du candidat en certif v3 (PIX-9690).
#7288
2 parents 9ec46da + 140fe8b commit e3a2791

File tree

23 files changed

+276
-47
lines changed

23 files changed

+276
-47
lines changed

api/lib/domain/models/CertificationChallengeLiveAlert.js

-29
This file was deleted.

api/lib/domain/models/index.js

-2
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,6 @@ import { CertificationCenterInvitation } from './CertificationCenterInvitation.j
4242
import { CertificationCenterInvitedUser } from './CertificationCenterInvitedUser.js';
4343
import { CertificationCenterMembership } from './CertificationCenterMembership.js';
4444
import { CertificationChallenge } from './CertificationChallenge.js';
45-
import { CertificationChallengeLiveAlert } from './CertificationChallengeLiveAlert.js';
4645
import { CertificationChallengeWithType } from './CertificationChallengeWithType.js';
4746
import { CertificationContract } from './CertificationContract.js';
4847
import { CertificationCourse } from './CertificationCourse.js';
@@ -197,7 +196,6 @@ export {
197196
CertificationCenterInvitedUser,
198197
CertificationCenterMembership,
199198
CertificationChallenge,
200-
CertificationChallengeLiveAlert,
201199
CertificationChallengeWithType,
202200
CertificationContract,
203201
CertificationCourse,

api/lib/domain/usecases/create-certification-challenge-live-alert.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { CertificationChallengeLiveAlert } from '../models/CertificationChallengeLiveAlert.js';
1+
import { CertificationChallengeLiveAlert } from '../../../src/certification/session/domain/models/CertificationChallengeLiveAlert.js';
22

33
const createCertificationChallengeLiveAlert = async function ({
44
assessmentId,

api/lib/infrastructure/repositories/sessions/session-for-supervising-repository.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { knex } from '../../../../db/knex-database-connection.js';
22
import { NotFoundError } from '../../../domain/errors.js';
33
import { CertificationCandidateForSupervising } from '../../../domain/models/CertificationCandidateForSupervising.js';
4-
import { CertificationChallengeLiveAlertStatus } from '../../../domain/models/CertificationChallengeLiveAlert.js';
4+
import { CertificationChallengeLiveAlertStatus } from '../../../../src/certification/session/domain/models/CertificationChallengeLiveAlert.js';
55
import { ComplementaryCertificationForSupervising } from '../../../domain/models/ComplementaryCertificationForSupervising.js';
66
import { SessionForSupervising } from '../../../domain/read-models/SessionForSupervising.js';
77
import { CertificationCandidateForSupervisingV3 } from '../../../../src/certification/supervision/domain/models/CertificationCandidateForSupervisingV3.js';

api/src/certification/session/application/session-live-alert-controller.js

+9
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,15 @@ const dismissLiveAlert = async function (request, h) {
88
return h.response().code(204);
99
};
1010

11+
const validateLiveAlert = async function (request, h) {
12+
const { id: sessionId, candidateId: userId } = request.params;
13+
14+
await usecases.validateLiveAlert({ sessionId, userId });
15+
16+
return h.response().code(204);
17+
};
18+
1119
export const sessionLiveAlertController = {
1220
dismissLiveAlert,
21+
validateLiveAlert,
1322
};

api/src/certification/session/application/session-live-alert-route.js

+38
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,44 @@ const register = async function (server) {
4444
],
4545
},
4646
},
47+
{
48+
method: 'PATCH',
49+
path: '/api/sessions/{id}/candidates/{candidateId}/validate-live-alert',
50+
config: {
51+
plugins: {
52+
'hapi-swagger': {
53+
produces: ['application/json'],
54+
consumes: ['application/json'],
55+
},
56+
},
57+
response: {
58+
failAction: 'log',
59+
status: {
60+
204: Joi.string.empty,
61+
401: responseObjectErrorDoc,
62+
403: responseObjectErrorDoc,
63+
},
64+
},
65+
validate: {
66+
params: Joi.object({
67+
id: identifiersType.sessionId,
68+
candidateId: identifiersType.userId,
69+
}),
70+
},
71+
pre: [
72+
{
73+
method: assessmentSupervisorAuthorization.verifyBySessionId,
74+
assign: 'isSupervisorForSession',
75+
},
76+
],
77+
handler: sessionLiveAlertController.validateLiveAlert,
78+
tags: ['api', 'sessions', 'liveAlerts', 'certifV3'],
79+
notes: [
80+
'Cette route est restreinte au surveillant',
81+
'Elle permet de rejeter une alerte relevée par le candidat',
82+
],
83+
},
84+
},
4785
]);
4886
};
4987

api/src/certification/session/domain/models/CertificationChallengeLiveAlert.js

+5-1
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
const CertificationChallengeLiveAlertStatus = Object.freeze({
22
ONGOING: 'ongoing',
33
DISMISSED: 'dismissed',
4-
ACCEPTED: 'accepted',
4+
VALIDATED: 'validated',
55
});
66

77
class CertificationChallengeLiveAlert {
@@ -24,6 +24,10 @@ class CertificationChallengeLiveAlert {
2424
dismiss() {
2525
this.status = CertificationChallengeLiveAlertStatus.DISMISSED;
2626
}
27+
28+
validate() {
29+
this.status = CertificationChallengeLiveAlertStatus.VALIDATED;
30+
}
2731
}
2832

2933
export { CertificationChallengeLiveAlert, CertificationChallengeLiveAlertStatus };
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
import { NotFoundError } from '../../../../../lib/domain/errors.js';
2+
3+
export const validateLiveAlert = async ({ userId, sessionId, certificationChallengeLiveAlertRepository }) => {
4+
const certificationChallengeLiveAlert =
5+
await certificationChallengeLiveAlertRepository.getOngoingBySessionIdAndUserId({
6+
sessionId,
7+
userId,
8+
});
9+
10+
if (!certificationChallengeLiveAlert) {
11+
throw new NotFoundError('There is no ongoing alert for this user');
12+
}
13+
14+
certificationChallengeLiveAlert.validate();
15+
16+
await certificationChallengeLiveAlertRepository.save({
17+
certificationChallengeLiveAlert,
18+
});
19+
};

api/src/certification/session/infrastructure/repositories/certification-challenge-live-alert-repository.js

+4-2
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
import { knex } from '../../../../../db/knex-database-connection.js';
2-
import { CertificationChallengeLiveAlert } from '../../../../../lib/domain/models/index.js';
3-
import { CertificationChallengeLiveAlertStatus } from '../../../../../lib/domain/models/CertificationChallengeLiveAlert.js';
2+
import {
3+
CertificationChallengeLiveAlert,
4+
CertificationChallengeLiveAlertStatus,
5+
} from '../../domain/models/CertificationChallengeLiveAlert.js';
46

57
const save = async function ({ certificationChallengeLiveAlert }) {
68
return knex('certification-challenge-live-alerts')

api/src/shared/domain/models/Assessment.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { ObjectValidationError } from '../../../../lib/domain/errors.js';
22
import { Answer } from '../../../evaluation/domain/models/Answer.js';
3-
import { CertificationChallengeLiveAlertStatus } from '../../../../lib/domain/models/CertificationChallengeLiveAlert.js';
3+
import { CertificationChallengeLiveAlertStatus } from '../../../certification/session/domain/models/CertificationChallengeLiveAlert.js';
44

55
const courseIdMessage = {
66
COMPETENCE_EVALUATION: '[NOT USED] CompetenceId is in Competence Evaluation.',

api/tests/certification/session/acceptance/application/session-live-alert-controller_test.js

+84
Original file line numberDiff line numberDiff line change
@@ -92,4 +92,88 @@ describe('Certification | Session | Acceptance | Controller | session-live-alert
9292
});
9393
});
9494
});
95+
96+
describe('PATCH /sessions/{id}/candidates/{candidateId}/validate-live-alert', function () {
97+
describe('when user has supervisor authorization', function () {
98+
it('should return 204 when the alert is ongoing', async function () {
99+
// given
100+
const certificationCenter = databaseBuilder.factory.buildCertificationCenter();
101+
const session = databaseBuilder.factory.buildSession({ certificationCenterId: certificationCenter.id });
102+
const certificationCourse = databaseBuilder.factory.buildCertificationCourse({
103+
sessionId: session.id,
104+
});
105+
const supervisorUserId = databaseBuilder.factory.buildUser().id;
106+
databaseBuilder.factory.buildSupervisorAccess({ userId: supervisorUserId, sessionId: session.id });
107+
108+
const assessment = databaseBuilder.factory.buildAssessment({
109+
certificationCourseId: certificationCourse.id,
110+
userId: certificationCourse.userId,
111+
});
112+
113+
databaseBuilder.factory.buildCertificationChallengeLiveAlert({
114+
assessmentId: assessment.id,
115+
status: CertificationChallengeLiveAlertStatus.DISMISSED,
116+
});
117+
118+
const certificationChallengeLiveAlert = databaseBuilder.factory.buildCertificationChallengeLiveAlert({
119+
assessmentId: assessment.id,
120+
status: CertificationChallengeLiveAlertStatus.ONGOING,
121+
});
122+
123+
await databaseBuilder.commit();
124+
125+
const headers = {
126+
authorization: generateValidRequestAuthorizationHeader(supervisorUserId, 'pix-certif'),
127+
};
128+
const options = {
129+
headers,
130+
method: 'PATCH',
131+
url: `/api/sessions/${certificationCourse.sessionId}/candidates/${certificationCourse.userId}/validate-live-alert`,
132+
};
133+
134+
// when
135+
const response = await server.inject(options);
136+
137+
const liveAlert = await knex('certification-challenge-live-alerts')
138+
.where({ id: certificationChallengeLiveAlert.id })
139+
.first();
140+
141+
// then
142+
expect(response.statusCode).to.equal(204);
143+
expect(liveAlert.status).to.equal(CertificationChallengeLiveAlertStatus.VALIDATED);
144+
});
145+
});
146+
147+
describe('when user does not have supervisor authorization', function () {
148+
it('should return 401 when the alert is ongoing', async function () {
149+
// given
150+
const certificationCenter = databaseBuilder.factory.buildCertificationCenter();
151+
const session = databaseBuilder.factory.buildSession({ certificationCenterId: certificationCenter.id });
152+
const certificationCourse = databaseBuilder.factory.buildCertificationCourse({
153+
sessionId: session.id,
154+
});
155+
const supervisorUserId = databaseBuilder.factory.buildUser().id;
156+
databaseBuilder.factory.buildSupervisorAccess({ userId: supervisorUserId, sessionId: session.id });
157+
158+
const userNotLinkedToTheSessionId = databaseBuilder.factory.buildUser().id;
159+
160+
await databaseBuilder.commit();
161+
162+
const headers = {
163+
authorization: generateValidRequestAuthorizationHeader(userNotLinkedToTheSessionId, 'pix-certif'),
164+
};
165+
const options = {
166+
headers,
167+
method: 'PATCH',
168+
url: `/api/sessions/${certificationCourse.sessionId}/candidates/${certificationCourse.userId}/validate-live-alert`,
169+
};
170+
171+
// when
172+
const response = await server.inject(options);
173+
174+
// then
175+
expect(response.statusCode).to.equal(401);
176+
});
177+
});
178+
});
95179
});

api/tests/certification/session/integration/infrastructure/repositories/certification-challenge-live-alert-repository_test.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { databaseBuilder, domainBuilder, expect, knex } from '../../../../../test-helper.js';
22
import * as certificationChallengeLiveAlertRepository from '../../../../../../src/certification/session/infrastructure/repositories/certification-challenge-live-alert-repository.js';
3-
import { CertificationChallengeLiveAlertStatus } from '../../../../../../lib/domain/models/CertificationChallengeLiveAlert.js';
3+
import { CertificationChallengeLiveAlertStatus } from '../../../../../../src/certification/session/domain/models/CertificationChallengeLiveAlert.js';
44

55
const assessmentIdWithNoAlerts = 123;
66
const assessmentIdWithLiveAlert = 456;

api/tests/certification/session/unit/domain/usecases/dismiss-live-alert_test.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { catchErr, domainBuilder, expect, sinon } from '../../../../../test-helper.js';
2-
import { CertificationChallengeLiveAlertStatus } from '../../../../../../lib/domain/models/CertificationChallengeLiveAlert.js';
2+
import { CertificationChallengeLiveAlertStatus } from '../../../../../../src/certification/session/domain/models/CertificationChallengeLiveAlert.js';
33
import { dismissLiveAlert } from '../../../../../../src/certification/session/domain/usecases/dismiss-live-alert.js';
44
import { NotFoundError } from '../../../../../../lib/domain/errors.js';
55

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
import { catchErr, domainBuilder, expect, sinon } from '../../../../../test-helper.js';
2+
import { CertificationChallengeLiveAlertStatus } from '../../../../../../src/certification/session/domain/models/CertificationChallengeLiveAlert.js';
3+
import { NotFoundError } from '../../../../../../lib/domain/errors.js';
4+
import { validateLiveAlert } from '../../../../../../src/certification/session/domain/usecases/validate-live-alert.js';
5+
6+
describe('Unit | UseCase | validate-live-alert', function () {
7+
let certificationChallengeLiveAlertRepository;
8+
9+
beforeEach(function () {
10+
certificationChallengeLiveAlertRepository = {
11+
getOngoingBySessionIdAndUserId: sinon.stub(),
12+
save: sinon.stub().resolves(),
13+
};
14+
});
15+
16+
describe('when the liveAlert does not exist', function () {
17+
it('should throw a NotFoundError', async function () {
18+
// given
19+
const sessionId = 123;
20+
const userId = 456;
21+
certificationChallengeLiveAlertRepository.getOngoingBySessionIdAndUserId
22+
.withArgs({
23+
sessionId,
24+
userId,
25+
})
26+
.resolves(null);
27+
28+
// when
29+
const error = await catchErr(validateLiveAlert)({
30+
certificationChallengeLiveAlertRepository,
31+
sessionId,
32+
userId,
33+
});
34+
35+
// then
36+
expect(error).to.be.instanceOf(NotFoundError);
37+
});
38+
});
39+
40+
describe('when the liveAlert exists', function () {
41+
it('should update the LiveAlert', async function () {
42+
// given
43+
const liveAlert = domainBuilder.buildCertificationChallengeLiveAlert();
44+
const sessionId = 123;
45+
const userId = 456;
46+
certificationChallengeLiveAlertRepository.getOngoingBySessionIdAndUserId
47+
.withArgs({
48+
sessionId,
49+
userId,
50+
})
51+
.resolves(liveAlert);
52+
53+
const validatedLiveAlert = domainBuilder.buildCertificationChallengeLiveAlert({
54+
assessmentId: liveAlert.assessmentId,
55+
challengeId: liveAlert.challengeId,
56+
status: CertificationChallengeLiveAlertStatus.VALIDATED,
57+
});
58+
59+
// when
60+
await validateLiveAlert({
61+
certificationChallengeLiveAlertRepository,
62+
sessionId,
63+
userId,
64+
});
65+
66+
// then
67+
expect(certificationChallengeLiveAlertRepository.save).to.have.been.calledWithExactly({
68+
certificationChallengeLiveAlert: domainBuilder.buildCertificationChallengeLiveAlert(validatedLiveAlert),
69+
});
70+
});
71+
});
72+
});

api/tests/integration/infrastructure/repositories/sessions/session-for-supervising-repository_test.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import { SessionForSupervising } from '../../../../../lib/domain/read-models/Ses
55
import * as sessionForSupervisingRepository from '../../../../../lib/infrastructure/repositories/sessions/session-for-supervising-repository.js';
66
import { Assessment } from '../../../../../src/shared/domain/models/Assessment.js';
77
import { CertificationVersion } from '../../../../../src/shared/domain/models/CertificationVersion.js';
8-
import { CertificationChallengeLiveAlertStatus } from '../../../../../lib/domain/models/CertificationChallengeLiveAlert.js';
8+
import { CertificationChallengeLiveAlertStatus } from '../../../../../src/certification/session/domain/models/CertificationChallengeLiveAlert.js';
99

1010
describe('Integration | Repository | SessionForSupervising', function () {
1111
describe('#get', function () {

api/tests/shared/unit/domain/models/Assessment_test.js

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { expect, domainBuilder } from '../../../../test-helper.js';
22
import { Assessment } from '../../../../../src/shared/domain/models/Assessment.js';
3-
import { CertificationChallengeLiveAlertStatus } from '../../../../../lib/domain/models/CertificationChallengeLiveAlert.js';
3+
import { CertificationChallengeLiveAlertStatus } from '../../../../../src/certification/session/domain/models/CertificationChallengeLiveAlert.js';
44

55
describe('Unit | Domain | Models | Assessment', function () {
66
describe('#constuctor', function () {
@@ -496,7 +496,7 @@ describe('Unit | Domain | Models | Assessment', function () {
496496
status: CertificationChallengeLiveAlertStatus.DISMISSED,
497497
}),
498498
domainBuilder.buildCertificationChallengeLiveAlert({
499-
status: CertificationChallengeLiveAlertStatus.ACCEPTED,
499+
status: CertificationChallengeLiveAlertStatus.VALIDATED,
500500
}),
501501
],
502502
});

api/tests/tooling/domain-builder/factory/build-certification-challenge-live-alert.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import {
22
CertificationChallengeLiveAlert,
33
CertificationChallengeLiveAlertStatus,
4-
} from '../../../../lib/domain/models/CertificationChallengeLiveAlert.js';
4+
} from '../../../../src/certification/session/domain/models/CertificationChallengeLiveAlert.js';
55

66
const buildCertificationChallengeLiveAlert = function ({
77
id = 456,

0 commit comments

Comments
 (0)