Skip to content

Commit

Permalink
🚚 chore(api): move student certification to src
Browse files Browse the repository at this point in the history
  • Loading branch information
yaf committed Dec 27, 2024
1 parent bcfb00c commit d6b9a7b
Show file tree
Hide file tree
Showing 17 changed files with 387 additions and 382 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -2,25 +2,6 @@ import * as divisionSerializer from '../../../src/prescription/campaign/infrastr
import * as certificationCenterMembershipSerializer from '../../../src/shared/infrastructure/serializers/jsonapi/certification-center-membership.serializer.js';
import { usecases as teamUsecases } from '../../../src/team/domain/usecases/index.js';
import { usecases } from '../../domain/usecases/index.js';
import * as studentCertificationSerializer from '../../infrastructure/serializers/jsonapi/student-certification-serializer.js';

const getStudents = async function (request) {
const certificationCenterId = request.params.certificationCenterId;
const sessionId = request.params.sessionId;

const { filter, page } = request.query;
if (filter.divisions && !Array.isArray(filter.divisions)) {
filter.divisions = [filter.divisions];
}

const { data, pagination } = await usecases.findStudentsForEnrolment({
certificationCenterId,
sessionId,
page,
filter,
});
return studentCertificationSerializer.serialize(data, pagination);
};

const getDivisions = async function (request) {
const certificationCenterId = request.params.certificationCenterId;
Expand Down Expand Up @@ -77,7 +58,6 @@ const certificationCenterController = {
createCertificationCenterMembershipByEmail,
findCertificationCenterMembershipsByCertificationCenter,
getDivisions,
getStudents,
updateReferer,
};

Expand Down
33 changes: 0 additions & 33 deletions api/lib/application/certification-centers/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -93,39 +93,6 @@ const register = async function (server) {
server.route([
...adminRoutes,
...certifRoutes,
{
method: 'GET',
path: '/api/certification-centers/{certificationCenterId}/sessions/{sessionId}/students',
config: {
validate: {
params: Joi.object({
certificationCenterId: identifiersType.certificationCenterId,
sessionId: identifiersType.sessionId,
}),
query: Joi.object({
filter: Joi.object({
divisions: Joi.array().items(Joi.string()),
}).default({}),
page: {
number: Joi.number().integer(),
size: Joi.number().integer(),
},
}),
},
pre: [
{
method: securityPreHandlers.checkUserIsMemberOfCertificationCenter,
assign: 'isMemberOfCertificationCenter',
},
],
handler: certificationCenterController.getStudents,
notes: [
'- **Cette route est restreinte aux utilisateurs authentifiés**\n' +
"- Récupération d'une liste d'élèves SCO à partir d'un identifiant de centre de certification",
],
tags: ['api', 'certification-center', 'students', 'session'],
},
},
{
method: 'GET',
path: '/api/certification-centers/{certificationCenterId}/divisions',
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import { usecases } from '../domain/usecases/index.js';
import * as studentCertificationSerializer from '../infrastructure/serializers/student-certification-serializer.js';

const getStudents = async function (request) {
const certificationCenterId = request.params.certificationCenterId;
const sessionId = request.params.sessionId;

const { filter, page } = request.query;
if (filter.divisions && !Array.isArray(filter.divisions)) {
filter.divisions = [filter.divisions];
}

const { data, pagination } = await usecases.findStudentsForEnrolment({
certificationCenterId,
sessionId,
page,
filter,
});
return studentCertificationSerializer.serialize(data, pagination);
};

const certificationCenterController = { getStudents };

export { certificationCenterController };
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
import Joi from 'joi';

import { securityPreHandlers } from '../../../shared/application/security-pre-handlers.js';
import { identifiersType } from '../../../shared/domain/types/identifiers-type.js';
import { certificationCenterController } from './certification-center-controller.js';

const register = async function (server) {
server.route([
{
method: 'GET',
path: '/api/certification-centers/{certificationCenterId}/sessions/{sessionId}/students',
config: {
validate: {
params: Joi.object({
certificationCenterId: identifiersType.certificationCenterId,
sessionId: identifiersType.sessionId,
}),
query: Joi.object({
filter: Joi.object({
divisions: Joi.array().items(Joi.string()),
}).default({}),
page: {
number: Joi.number().integer(),
size: Joi.number().integer(),
},
}),
},
pre: [
{
method: securityPreHandlers.checkUserIsMemberOfCertificationCenter,
assign: 'isMemberOfCertificationCenter',
},
],
handler: certificationCenterController.getStudents,
notes: [
'- **Cette route est restreinte aux utilisateurs authentifiés**\n' +
"- Récupération d'une liste d'élèves SCO à partir d'un identifiant de centre de certification",
],
tags: ['api', 'certification-center', 'students', 'session'],
},
},
]);
};

const name = 'certification-centers-session-students-api';
export { name, register };
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { NotFoundError } from '../../../src/shared/domain/errors.js';
import { StudentForEnrolment } from '../../../src/shared/domain/read-models/StudentForEnrolment.js';
import { NotFoundError } from '../../../../shared/domain/errors.js';
import { StudentForEnrolment } from '../../../../shared/domain/read-models/StudentForEnrolment.js';

const findStudentsForEnrolment = async function ({
certificationCenterId,
Expand Down
13 changes: 13 additions & 0 deletions api/src/certification/enrolment/domain/usecases/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,17 @@ import { fileURLToPath } from 'node:url';

import * as languageService from '../../../../shared/domain/services/language-service.js';
import * as placementProfileService from '../../../../shared/domain/services/placement-profile-service.js';
import * as organizationRepository from '../../../../shared/infrastructure/repositories/organization-repository.js';
import { injectDependencies } from '../../../../shared/infrastructure/utils/dependency-injection.js';
import { importNamedExportsFromDirectory } from '../../../../shared/infrastructure/utils/import-named-exports-from-directory.js';
import * as attendanceSheetPdfUtils from '../../../enrolment/infrastructure/utils/pdf/attendance-sheet-pdf.js';
import * as certificationBadgesService from '../../../shared/domain/services/certification-badges-service.js';
import * as certificationCpfService from '../../../shared/domain/services/certification-cpf-service.js';
import * as sessionValidator from '../../../shared/domain/validators/session-validator.js';
import * as certificationCandidateRepository from '../../../shared/infrastructure/repositories/certification-candidate-repository.js';
import * as complementaryCertificationBadgesRepository from '../../../shared/infrastructure/repositories/complementary-certification-badge-repository.js';
import { enrolmentRepositories } from '../../infrastructure/repositories/index.js';
import * as organizationLearnerRepository from '../../infrastructure/repositories/organization-learner-repository.js';
import * as certificationCandidatesOdsService from '../services/certification-candidates-ods-service.js';
import * as sessionCodeService from '../services/session-code-service.js';
import * as sessionsImportValidationService from '../services/sessions-import-validation-service.js';
Expand Down Expand Up @@ -40,6 +43,9 @@ import * as temporarySessionsStorageForMassImportService from '../services/tempo
* @typedef {import('../services/certification-candidates-ods-service.js')} CertificationCandidatesOdsService
* @typedef {import('../../../../shared/domain/services/placement-profile-service.js')} PlacementProfileService
* @typedef {import('../../../../shared/domain/services/language-service.js')} languageService
* @typedef {import('../../../../shared/infrastructure/repositories/organization-repository.js')} organizationRepository
* @typedef {import('../../infrastructure/repositories/organization-learner-repository.js')} organizationLearnerRepository
* @typedef {import('../../../shared/infrastructure/repositories/certification-candidate-repository.js')} certificationCandidateRepository
**/

/**
Expand Down Expand Up @@ -70,6 +76,10 @@ import * as temporarySessionsStorageForMassImportService from '../services/tempo
* @typedef {PlacementProfileService} PlacementProfileService
* @typedef {LanguageService} LanguageService
* @typedef {complementaryCertificationBadgesRepository} ComplementaryCertificationBadgesRepository
* @typedef {organizationRepository} OrganizationRepository
* @typedef {organizationLearnerRepository} OrganizationLearnerRepository
* @typedef {certificationCandidateRepository} CertificationCandidateRepository
*
**/
const dependencies = {
certificationBadgesService,
Expand All @@ -84,6 +94,9 @@ const dependencies = {
placementProfileService,
languageService,
complementaryCertificationBadgesRepository,
organizationRepository,
organizationLearnerRepository,
certificationCandidateRepository,
};

const path = dirname(fileURLToPath(import.meta.url));
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
import { knex } from '../../../db/knex-database-connection.js';
import { ORGANIZATION_FEATURE } from '../../../src/shared/domain/constants.js';
import { OrganizationLearnerNotFound, UserNotFoundError } from '../../../src/shared/domain/errors.js';
import { OrganizationLearner } from '../../../src/shared/domain/models/OrganizationLearner.js';
import { ParticipantRepartition } from '../../../src/shared/domain/models/ParticipantRepartition.js';
import { fetchPage } from '../../../src/shared/infrastructure/utils/knex-utils.js';
import { DomainTransaction } from '../DomainTransaction.js';
import { knex } from '../../../../../db/knex-database-connection.js';
import { ORGANIZATION_FEATURE } from '../../../../shared/domain/constants.js';
import { DomainTransaction } from '../../../../shared/domain/DomainTransaction.js';
import { OrganizationLearnerNotFound, UserNotFoundError } from '../../../../shared/domain/errors.js';
import { OrganizationLearner } from '../../../../shared/domain/models/OrganizationLearner.js';
import { ParticipantRepartition } from '../../../../shared/domain/models/ParticipantRepartition.js';
import { fetchPage } from '../../../../shared/infrastructure/utils/knex-utils.js';

const findByIds = async function ({ ids }) {
const rawOrganizationLearners = await knex
Expand Down
2 changes: 2 additions & 0 deletions api/src/certification/enrolment/routes.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import * as country from './application/country-route.js';
import * as enrolment from './application/enrolment-route.js';
import * as sessionMassImport from './application/session-mass-import-route.js';
import * as session from './application/session-route.js';
import * as sessionStudents from './application/session-students-route.js';
import * as subscription from './application/subscription-route.js';
import * as user from './application/user-route.js';

Expand All @@ -14,6 +15,7 @@ const certificationEnrolmentRoutes = [
enrolment,
session,
sessionMassImport,
sessionStudents,
subscription,
user,
];
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,104 +49,6 @@ describe('Acceptance | API | Certification Center', function () {
});
});

describe('GET /api/certification-centers/{certificationCenterId}/sessions/{sessionId}/students', function () {
let request;
const externalId = 'XXXX';

function _buildOrganizationLearnersWithConnectedUserRequest(user, certificationCenter, session) {
return {
method: 'GET',
url:
'/api/certification-centers/' +
certificationCenter.id +
'/sessions/' +
session.id +
'/students?page[size]=10&page[number]=1',
headers: { authorization: generateValidRequestAuthorizationHeader(user.id) },
};
}
function _buildOrganizationLearnersNotConnectedUserRequest(certificationCenter, session) {
return {
method: 'GET',
url:
'/api/certification-centers/' +
certificationCenter.id +
'/sessions/' +
session.id +
'/students?page[size]=10&page[number]=1',
};
}

context('when user is connected', function () {
it('should return 200 HTTP status', async function () {
// given
const { certificationCenter, user } = _buildUserWithCertificationCenterMemberShip(externalId);
const organization = databaseBuilder.factory.buildOrganization({ externalId, type: 'SCO' });
const session = databaseBuilder.factory.buildSession({ certificationCenterId: certificationCenter.id });
_buildOrganizationLearners(organization, { firstName: 'Laura', lastName: 'certifForEver', division: '2ndB' });
await databaseBuilder.commit();

const request = _buildOrganizationLearnersWithConnectedUserRequest(user, certificationCenter, session);

// when
const response = await server.inject(request);

// then
expect(response.statusCode).to.equal(200);
});

it('should return the organization learners asked', async function () {
// given
const { certificationCenter, user } = _buildUserWithCertificationCenterMemberShip(externalId);
const organization = databaseBuilder.factory.buildOrganization({ type: 'SCO', externalId });
const session = databaseBuilder.factory.buildSession({ certificationCenterId: certificationCenter.id });
_buildOrganizationLearners(
organization,
{ id: 1, division: '2ndB', firstName: 'Laura', lastName: 'Certif4Ever' },
{ id: 2, division: '2ndA', firstName: 'Laura', lastName: 'Booooo' },
{ id: 3, division: '2ndA', firstName: 'Laura', lastName: 'aaaaa' },
{ id: 4, division: '2ndA', firstName: 'Bart', lastName: 'Coucou' },
{ id: 5, division: '2ndA', firstName: 'Arthur', lastName: 'Coucou' },
);
await databaseBuilder.commit();

const request = _buildOrganizationLearnersWithConnectedUserRequest(user, certificationCenter, session);

// when
const response = await server.inject(request);

// then
expect(_.map(response.result.data, 'id')).to.deep.equal(['3', '2', '5', '4', '1']);
});
});

context('when user is not connected', function () {
it('should return 401 HTTP status code if user is not authenticated', async function () {
// given
_buildUserWithCertificationCenterMemberShip(externalId);
databaseBuilder.factory.buildOrganization({ externalId });
const certificationCenterWhereUserDoesNotHaveAccess = databaseBuilder.factory.buildCertificationCenter({
externalId,
});
const session = databaseBuilder.factory.buildSession({
certificationCenterId: certificationCenterWhereUserDoesNotHaveAccess.id,
});
await databaseBuilder.commit();

request = _buildOrganizationLearnersNotConnectedUserRequest(
certificationCenterWhereUserDoesNotHaveAccess,
session,
);

// when
const response = await server.inject(request);

// then
expect(response.statusCode).to.equal(401);
});
});
});

describe('GET /api/admin/certification-centers/{id}/certification-center-memberships', function () {
context('when certification center membership is linked to the certification center', function () {
it('should return 200 HTTP status', async function () {
Expand Down
Loading

0 comments on commit d6b9a7b

Please sign in to comment.