Skip to content

Commit bf71519

Browse files
authoredMar 19, 2025
[TECH] Regrouper l'ensemble des actions de finalisation dans une transaction (PIX-17013)
#11676
2 parents 9b92ce9 + 48fd29c commit bf71519

File tree

8 files changed

+196
-380
lines changed

8 files changed

+196
-380
lines changed
 

Diff for: ‎api/src/certification/session-management/application/finalize-controller.js

+3-6
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,6 @@ const finalize = async function (request, h, dependencies = { certificationRepor
1414
.map((data) => dependencies.certificationReportSerializer.deserialize({ data })),
1515
);
1616

17-
let certificationJuryDoneEvents, autoJuryDone;
1817
await DomainTransaction.execute(async () => {
1918
const sessionFinalized = await usecases.finalizeSession({
2019
sessionId,
@@ -24,12 +23,10 @@ const finalize = async function (request, h, dependencies = { certificationRepor
2423
certificationReports,
2524
});
2625

27-
const events = await usecases.processAutoJury({ sessionFinalized });
28-
certificationJuryDoneEvents = events.certificationJuryDoneEvents;
29-
autoJuryDone = events.autoJuryDone;
26+
const autoJuryDone = await usecases.processAutoJury({ sessionFinalized });
27+
28+
await usecases.registerPublishableSession({ autoJuryDone });
3029
});
31-
await dependencies.events.eventDispatcher.dispatch(certificationJuryDoneEvents);
32-
await usecases.registerPublishableSession({ autoJuryDone });
3330

3431
return h.response().code(200);
3532
};

Diff for: ‎api/src/certification/session-management/domain/usecases/process-auto-jury.js

+81-82
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,6 @@
1+
/**
2+
* @typedef {import('./index.js').CertificationRescoringRepository} CertificationRescoringRepository
3+
*/
14
import { logger } from '../../../../shared/infrastructure/utils/logger.js';
25
import { PromiseUtils } from '../../../../shared/infrastructure/utils/promise-utils.js';
36
import { AutoJuryDone } from '../events/AutoJuryDone.js';
@@ -6,125 +9,120 @@ import { CertificationAssessment } from '../models/CertificationAssessment.js';
69
import { CertificationIssueReportResolutionAttempt } from '../models/CertificationIssueReportResolutionAttempt.js';
710
import { CertificationIssueReportResolutionStrategies } from '../models/CertificationIssueReportResolutionStrategies.js';
811

9-
export const processAutoJury = async ({
12+
/**
13+
* @param {Object} params
14+
* @param {CertificationRescoringRepository} params.certificationRescoringRepository
15+
*/
16+
export async function processAutoJury({
1017
sessionFinalized,
1118
certificationIssueReportRepository,
1219
certificationAssessmentRepository,
1320
certificationCourseRepository,
1421
challengeRepository,
15-
}) => {
22+
certificationRescoringRepository,
23+
}) {
1624
const certificationCourses = await certificationCourseRepository.findCertificationCoursesBySessionId({
1725
sessionId: sessionFinalized.sessionId,
1826
});
1927

20-
if (_areV3CertificationCourses(certificationCourses)) {
21-
return await _handleAutoJuryV3({
22-
sessionFinalized,
23-
certificationCourses,
24-
certificationAssessmentRepository,
28+
for (const certificationCourse of certificationCourses) {
29+
const certificationAssessment = await certificationAssessmentRepository.getByCertificationCourseId({
30+
certificationCourseId: certificationCourse.getId(),
2531
});
32+
if (_areV3CertificationCourses(certificationCourses)) {
33+
await _handleAutoJuryV3({
34+
certificationAssessment,
35+
certificationCourse,
36+
certificationAssessmentRepository,
37+
certificationRescoringRepository,
38+
});
39+
} else {
40+
const resolutionStrategies = new CertificationIssueReportResolutionStrategies({
41+
certificationIssueReportRepository,
42+
challengeRepository,
43+
});
44+
45+
await _handleAutoJuryV2({
46+
certificationAssessment,
47+
resolutionStrategies,
48+
certificationCourse,
49+
certificationIssueReportRepository,
50+
certificationAssessmentRepository,
51+
certificationRescoringRepository,
52+
});
53+
}
2654
}
2755

28-
return await _handleAutoJuryV2({
29-
sessionFinalized,
30-
certificationCourses,
31-
certificationIssueReportRepository,
32-
challengeRepository,
33-
certificationAssessmentRepository,
56+
return new AutoJuryDone({
57+
sessionId: sessionFinalized.sessionId,
58+
finalizedAt: sessionFinalized.finalizedAt,
59+
certificationCenterName: sessionFinalized.certificationCenterName,
60+
sessionDate: sessionFinalized.sessionDate,
61+
sessionTime: sessionFinalized.sessionTime,
62+
hasExaminerGlobalComment: sessionFinalized.hasExaminerGlobalComment,
3463
});
35-
};
64+
}
3665

3766
async function _handleAutoJuryV2({
38-
sessionFinalized,
39-
certificationCourses,
67+
certificationCourse,
4068
certificationIssueReportRepository,
41-
challengeRepository,
4269
certificationAssessmentRepository,
70+
certificationRescoringRepository,
71+
resolutionStrategies,
72+
certificationAssessment,
4373
}) {
44-
const resolutionStrategies = new CertificationIssueReportResolutionStrategies({
45-
certificationIssueReportRepository,
46-
challengeRepository,
74+
const hasAutoCompleteAnEffectOnScoring = await _autoCompleteUnfinishedTest({
75+
certificationCourse,
76+
certificationAssessment,
77+
certificationAssessmentRepository,
4778
});
4879

49-
const certificationJuryDoneEvents = [];
80+
const hasAutoResolutionAnEffectOnScoring = await _autoResolveCertificationIssueReport({
81+
certificationCourse,
82+
certificationAssessment,
83+
certificationIssueReportRepository,
84+
certificationAssessmentRepository,
85+
resolutionStrategies,
86+
});
5087

51-
for (const certificationCourse of certificationCourses) {
52-
const certificationAssessment = await certificationAssessmentRepository.getByCertificationCourseId({
88+
if (hasAutoResolutionAnEffectOnScoring || hasAutoCompleteAnEffectOnScoring) {
89+
const certificationJuryDoneEvent = new CertificationJuryDone({
5390
certificationCourseId: certificationCourse.getId(),
5491
});
5592

56-
const hasAutoCompleteAnEffectOnScoring = await _autoCompleteUnfinishedTest({
57-
certificationCourse,
58-
certificationAssessment,
59-
certificationAssessmentRepository,
93+
await certificationRescoringRepository.execute({
94+
event: certificationJuryDoneEvent,
6095
});
61-
62-
const hasAutoResolutionAnEffectOnScoring = await _autoResolveCertificationIssueReport({
63-
certificationCourse,
64-
certificationAssessment,
65-
certificationIssueReportRepository,
66-
certificationAssessmentRepository,
67-
resolutionStrategies,
68-
});
69-
70-
if (hasAutoResolutionAnEffectOnScoring || hasAutoCompleteAnEffectOnScoring) {
71-
const certificationJuryDoneEvent = new CertificationJuryDone({
72-
certificationCourseId: certificationCourse.getId(),
73-
});
74-
75-
certificationJuryDoneEvents.push(certificationJuryDoneEvent);
76-
}
7796
}
78-
79-
return {
80-
certificationJuryDoneEvents,
81-
autoJuryDone: new AutoJuryDone({
82-
sessionId: sessionFinalized.sessionId,
83-
finalizedAt: sessionFinalized.finalizedAt,
84-
certificationCenterName: sessionFinalized.certificationCenterName,
85-
sessionDate: sessionFinalized.sessionDate,
86-
sessionTime: sessionFinalized.sessionTime,
87-
hasExaminerGlobalComment: sessionFinalized.hasExaminerGlobalComment,
88-
}),
89-
};
9097
}
9198

9299
function _areV3CertificationCourses(certificationCourses) {
93100
return certificationCourses[0].isV3();
94101
}
95102

96-
async function _handleAutoJuryV3({ sessionFinalized, certificationCourses, certificationAssessmentRepository }) {
97-
const certificationJuryDoneEvents = [];
98-
99-
for (const certificationCourse of certificationCourses) {
100-
const certificationAssessment = await certificationAssessmentRepository.getByCertificationCourseId({
103+
/**
104+
* @param {Object} params
105+
* @param {CertificationRescoringRepository} params.certificationRescoringRepository
106+
*/
107+
async function _handleAutoJuryV3({
108+
certificationCourse,
109+
certificationAssessment,
110+
certificationAssessmentRepository,
111+
certificationRescoringRepository,
112+
}) {
113+
if (_v3CertificationShouldBeScored(certificationAssessment)) {
114+
const certificationJuryDoneEvent = new CertificationJuryDone({
101115
certificationCourseId: certificationCourse.getId(),
102116
});
103117

104-
if (_v3CertificationShouldBeScored(certificationAssessment)) {
105-
const certificationJuryDoneEvent = new CertificationJuryDone({
106-
certificationCourseId: certificationCourse.getId(),
107-
});
108-
109-
certificationJuryDoneEvents.push(certificationJuryDoneEvent);
110-
}
111-
112-
certificationAssessment.endDueToFinalization();
113-
114-
await certificationAssessmentRepository.save(certificationAssessment);
118+
await certificationRescoringRepository.execute({
119+
event: certificationJuryDoneEvent,
120+
});
115121
}
116122

117-
return {
118-
certificationJuryDoneEvents,
119-
autoJuryDone: new AutoJuryDone({
120-
sessionId: sessionFinalized.sessionId,
121-
finalizedAt: sessionFinalized.finalizedAt,
122-
certificationCenterName: sessionFinalized.certificationCenterName,
123-
sessionDate: sessionFinalized.sessionDate,
124-
sessionTime: sessionFinalized.sessionTime,
125-
hasExaminerGlobalComment: sessionFinalized.hasExaminerGlobalComment,
126-
}),
127-
};
123+
certificationAssessment.endDueToFinalization();
124+
125+
await certificationAssessmentRepository.save(certificationAssessment);
128126
}
129127

130128
function _v3CertificationShouldBeScored(certificationAssessment) {
@@ -165,6 +163,7 @@ async function _autoResolveCertificationIssueReport({
165163
const certificationIssueReports = await certificationIssueReportRepository.findByCertificationCourseId({
166164
certificationCourseId: certificationCourse.getId(),
167165
});
166+
168167
if (certificationIssueReports.length === 0) {
169168
return null;
170169
}

Diff for: ‎api/src/certification/session-management/infrastructure/repositories/finalized-session-repository.js

+2-1
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,8 @@ import { NotFoundError } from '../../../../shared/domain/errors.js';
66
import { FinalizedSession } from '../../domain/models/FinalizedSession.js';
77

88
const save = async function ({ finalizedSession }) {
9-
await knex('finalized-sessions').insert(_toDTO(finalizedSession)).onConflict('sessionId').merge();
9+
const knexConn = DomainTransaction.getConnection();
10+
await knexConn('finalized-sessions').insert(_toDTO(finalizedSession)).onConflict('sessionId').merge();
1011
return finalizedSession;
1112
};
1213

Diff for: ‎api/src/certification/session-management/infrastructure/repositories/jury-certification-summary-repository.js

+8-5
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import _ from 'lodash';
22

3-
import { knex } from '../../../../../db/knex-database-connection.js';
3+
import { DomainTransaction } from '../../../../shared/domain/DomainTransaction.js';
44
import { Assessment } from '../../../../shared/domain/models/Assessment.js';
55
import { fetchPage } from '../../../../shared/infrastructure/utils/knex-utils.js';
66
import { CertificationIssueReport } from '../../../shared/domain/models/CertificationIssueReport.js';
@@ -37,8 +37,9 @@ const findBySessionIdPaginated = async function ({ page, sessionId }) {
3737
export { findBySessionId, findBySessionIdPaginated };
3838

3939
async function _getJuryCertificationSummaries(results) {
40+
const knexConn = DomainTransaction.getConnection();
4041
const certificationCourseIds = results.map((row) => row.id);
41-
const certificationIssueReportRows = await knex('certification-issue-reports').whereIn(
42+
const certificationIssueReportRows = await knexConn('certification-issue-reports').whereIn(
4243
'certificationCourseId',
4344
certificationCourseIds,
4445
);
@@ -48,7 +49,8 @@ async function _getJuryCertificationSummaries(results) {
4849
}
4950

5051
async function _getByCertificationCourseIds(orderedCertificationCourseIds) {
51-
const results = await knex
52+
const knexConn = DomainTransaction.getConnection();
53+
const results = await knexConn
5254
.select('certification-courses.*', 'assessment-results.pixScore')
5355
.select({
5456
assessmentResultStatus: 'assessment-results.status',
@@ -92,7 +94,8 @@ async function _getByCertificationCourseIds(orderedCertificationCourseIds) {
9294
}
9395

9496
function _getCertificationCoursesIdBySessionIdQuery(sessionId) {
95-
return knex
97+
const knexConn = DomainTransaction.getConnection();
98+
return knexConn
9699
.with('impactful-categories', (qb) => {
97100
qb.select('id').from('issue-report-categories').where({ isImpactful: true });
98101
})
@@ -103,7 +106,7 @@ function _getCertificationCoursesIdBySessionIdQuery(sessionId) {
103106
.onNull('certification-issue-reports.resolvedAt')
104107
.on((qb2) => {
105108
qb2
106-
.onIn('categoryId', knex.select('id').from('impactful-categories'))
109+
.onIn('categoryId', knexConn.select('id').from('impactful-categories'))
107110
.orOnNull('certification-issue-reports.id');
108111
});
109112
})

Diff for: ‎api/src/certification/session-management/infrastructure/repositories/supervisor-access-repository.js

+3-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import { knex } from '../../../../../db/knex-database-connection.js';
2+
import { DomainTransaction } from '../../../../shared/domain/DomainTransaction.js';
23

34
const create = async function ({ sessionId, userId }) {
45
await knex('supervisor-accesses').insert({ sessionId, userId });
@@ -10,7 +11,8 @@ const isUserSupervisorForSession = async function ({ sessionId, userId }) {
1011
};
1112

1213
const sessionHasSupervisorAccess = async function ({ sessionId }) {
13-
const result = await knex.select(1).from('supervisor-accesses').where({ sessionId }).first();
14+
const knexConn = DomainTransaction.getConnection();
15+
const result = await knexConn.select(1).from('supervisor-accesses').where({ sessionId }).first();
1416
return Boolean(result);
1517
};
1618

Diff for: ‎api/tests/certification/session-management/acceptance/application/finalize-route_test.js

+25-8
Original file line numberDiff line numberDiff line change
@@ -81,12 +81,18 @@ describe('Certification | Session Management | Acceptance | Application | Route
8181
options.headers = generateAuthenticatedUserRequestHeaders({ userId });
8282

8383
// when
84-
await server.inject(options);
84+
const response = await server.inject(options);
85+
expect(response.statusCode).to.equal(200);
8586

86-
// then
87-
const finalizedSession = await knex.from('sessions').where({ id: session.id }).first();
88-
expect(finalizedSession.hasIncident).to.be.true;
89-
expect(finalizedSession.hasJoiningIssue).to.be.true;
87+
//const finalizedSession = await knex.from('sessions').where({ id: session.id }).first();
88+
//expect(finalizedSession.hasIncident).to.be.true;
89+
90+
setTimeout(async function () {
91+
// then
92+
const finalizedSession = await knex.from('sessions').where({ id: session.id }).first();
93+
expect(finalizedSession.hasIncident).to.be.true;
94+
expect(finalizedSession.hasJoiningIssue).to.be.true;
95+
}, 0);
9096
});
9197

9298
it('should neutralize auto-neutralizable challenges', async function () {
@@ -201,6 +207,7 @@ describe('Certification | Session Management | Acceptance | Application | Route
201207
userId,
202208
sessionId: session.id,
203209
completedAt: new Date(),
210+
hasSeenEndTestScreen: true,
204211
}).id;
205212
databaseBuilder.factory.buildCertificationCandidate({
206213
sessionId: session.id,
@@ -216,12 +223,13 @@ describe('Certification | Session Management | Acceptance | Application | Route
216223
sessionId: session.id,
217224
});
218225

219-
const assessmentId = databaseBuilder.factory.buildAssessment({ certificationCourseId }).id;
226+
const assessmentId = databaseBuilder.factory.buildAssessment({ certificationCourseId, userId }).id;
220227
databaseBuilder.factory.buildCertificationIssueReport({
221228
certificationCourseId,
222229
category: CertificationIssueReportCategory.IN_CHALLENGE,
223230
description: '',
224231
subcategory: CertificationIssueReportSubcategories.WEBSITE_BLOCKED,
232+
resolvedAt: new Date(),
225233
questionNumber: 1,
226234
});
227235

@@ -419,6 +427,7 @@ describe('Certification | Session Management | Acceptance | Application | Route
419427
sessionId: session.id,
420428
completedAt: new Date(),
421429
version: AlgorithmEngineVersion.V3,
430+
hasSeenEndTestScreen: true,
422431
}).id;
423432
databaseBuilder.factory.buildCertificationCenterMembership({
424433
userId,
@@ -667,7 +676,12 @@ describe('Certification | Session Management | Acceptance | Application | Route
667676

668677
const _createSession = async ({ version = 2 } = {}) => {
669678
const session = databaseBuilder.factory.buildSession({ version });
670-
const certificationCourseId = databaseBuilder.factory.buildCertificationCourse({ sessionId: session.id, version }).id;
679+
const certificationCourse = databaseBuilder.factory.buildCertificationCourse({ sessionId: session.id, version });
680+
const certificationCourseId = certificationCourse.id;
681+
databaseBuilder.factory.buildCertificationCandidate({
682+
userId: certificationCourse.userId,
683+
sessionId: certificationCourse.sessionId,
684+
});
671685
const report1 = databaseBuilder.factory.buildCertificationReport({
672686
sessionId: session.id,
673687
certificationCourseId,
@@ -676,7 +690,10 @@ const _createSession = async ({ version = 2 } = {}) => {
676690
sessionId: session.id,
677691
certificationCourseId,
678692
});
679-
const assessmentId = databaseBuilder.factory.buildAssessment({ certificationCourseId }).id;
693+
const assessmentId = databaseBuilder.factory.buildAssessment({
694+
certificationCourseId,
695+
userId: certificationCourse.userId,
696+
}).id;
680697

681698
databaseBuilder.factory.buildCertificationIssueReport({
682699
certificationCourseId,

0 commit comments

Comments
 (0)
Failed to load comments.