Skip to content

Commit

Permalink
✨ feat(api) add new parcoursup endpoint
Browse files Browse the repository at this point in the history
Co-Authored-By: GUL <guillaume.lagorce@pix.fr>
  • Loading branch information
yaf and HEYGUL committed Jan 2, 2025
1 parent 4a8c7c3 commit 90c3f25
Show file tree
Hide file tree
Showing 8 changed files with 418 additions and 48 deletions.
2 changes: 1 addition & 1 deletion api/src/parcoursup/application/certification-controller.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { usecases } from '../domain/usecases/index.js';

const getCertificationResult = async function (request) {
const ine = request.params.ine;
return usecases.getCertificationResult({ ine });
return usecases.getCertificationResult({ ine, ...request.query });
};

const certificationController = {
Expand Down
132 changes: 91 additions & 41 deletions api/src/parcoursup/application/certification-route.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,51 +5,101 @@ import { responseObjectErrorDoc } from '../../shared/infrastructure/open-api-doc
import { certificationController } from './certification-controller.js';

const register = async function (server) {
server.route({
method: 'GET',
path: '/api/parcoursup/students/{ine}/certification',
config: {
auth: 'jwt-parcoursup',
validate: {
params: Joi.object({
ine: studentIdentifierType,
}),
server.route([
{
method: 'GET',
path: '/api/parcoursup/students/{ine}/certification',
config: {
auth: 'jwt-parcoursup',
validate: {
params: Joi.object({
ine: studentIdentifierType,
}),
},
handler: certificationController.getCertificationResult,
tags: ['api', 'parcoursup'],
notes: [
'- **Cette route est accessible uniquement à Parcours Sup**\n' +
'- Récupère les informations de la dernière certification de l‘année en cours pour l‘élève identifié via son INE',
],
response: {
failAction: 'log',
status: {
200: Joi.object({
organizationUai: Joi.string().description('UAI de l‘établissement scolaire'),
ine: Joi.string().description('INE de l‘élève'),
lastName: Joi.string().description('Nom de famille de l‘élève'),
firstName: Joi.string().description('Prénom de l‘élève'),
birthdate: Joi.date().description('Date de naissance au format JJ/MM/AAAA'),
status: Joi.string().description('Statut de la certification'),
pixScore: Joi.number().min(0).max(1024).description('Score en nombre de pix'),
certificationDate: Joi.date().description('Date de passage de la certification'),
competences: Joi.array()
.items(
Joi.object({
id: Joi.string().description('Identifiant unique de la compétence'),
level: Joi.number().min(0).max(8).description('Niveau obtenu sur la compétence'),
}).label('Competence-Result-Object'),
)
.description('Résultats par compétence')
.label('Competence-Results-Array'),
}).label('Certification-Result-Object'),
401: responseObjectErrorDoc,
403: responseObjectErrorDoc,
404: responseObjectErrorDoc,
},
},
},
handler: certificationController.getCertificationResult,
tags: ['api', 'parcoursup'],
notes: [
'- **Cette route est accessible uniquement à Parcours Sup**\n' +
'- Récupère les informations de la dernière certification de l‘année en cours pour l‘élève identifié via son INE',
],
response: {
failAction: 'log',
status: {
200: Joi.object({
organizationUai: Joi.string().description('UAI de l‘établissement scolaire'),
ine: Joi.string().description('INE de l‘élève'),
lastName: Joi.string().description('Nom de famille de l‘élève'),
firstName: Joi.string().description('Prénom de l‘élève'),
birthdate: Joi.date().description('Date de naissance au format JJ/MM/AAAA'),
status: Joi.string().description('Statut de la certification'),
pixScore: Joi.number().min(0).max(1024).description('Score en nombre de pix'),
certificationDate: Joi.date().description('Date de passage de la certification'),
competences: Joi.array()
.items(
Joi.object({
id: Joi.string().description('Identifiant unique de la compétence'),
level: Joi.number().min(0).max(8).description('Niveau obtenu sur la compétence'),
}).label('Competence-Result-Object'),
)
.description('Résultats par compétence')
.label('Competence-Results-Array'),
}).label('Certification-Result-Object'),
401: responseObjectErrorDoc,
403: responseObjectErrorDoc,
404: responseObjectErrorDoc,
},
{
method: 'GET',
path: '/api/parcoursup/certification/search',
config: {
auth: 'jwt-parcoursup',
validate: {
query: Joi.object({
organizationUai: Joi.string().required(),
lastName: Joi.string().required(),
firstName: Joi.string().required(),
birthdate: Joi.string().required(),
}),
},
handler: certificationController.getCertificationResult,
tags: ['api', 'parcoursup'],
notes: [
'- **Cette route est accessible uniquement à Parcours Sup**\n' +
'- Récupère les informations de la dernière certification de l‘année en cours pour l‘élève identifié via l’UAI de son établissement, ainsi que le nom, prénom et date de naissance de l’élève.',
],
response: {
failAction: 'log',
status: {
200: Joi.object({
organizationUai: Joi.string().description('UAI de l‘établissement scolaire'),
ine: Joi.string().description('INE de l‘élève'),
lastName: Joi.string().description('Nom de famille de l‘élève'),
firstName: Joi.string().description('Prénom de l‘élève'),
birthdate: Joi.date().description('Date de naissance au format JJ/MM/AAAA'),
status: Joi.string().description('Statut de la certification'),
pixScore: Joi.number().min(0).max(1024).description('Score en nombre de pix'),
certificationDate: Joi.date().description('Date de passage de la certification'),
competences: Joi.array()
.items(
Joi.object({
id: Joi.string().description('Identifiant unique de la compétence'),
level: Joi.number().min(0).max(8).description('Niveau obtenu sur la compétence'),
}).label('Competence-Result-Object'),
)
.description('Résultats par compétence')
.label('Competence-Results-Array'),
}).label('Certification-Result-Object'),
401: responseObjectErrorDoc,
403: responseObjectErrorDoc,
404: responseObjectErrorDoc,
},
},
},
},
});
]);
};

const name = 'parcoursup-api';
Expand Down
17 changes: 15 additions & 2 deletions api/src/parcoursup/domain/usecases/get-certification-result.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,18 @@
const getCertificationResult = function ({ ine, certificationRepository }) {
return certificationRepository.get({ ine });
const getCertificationResult = function ({
ine,
organizationUai,
lastName,
firstName,
birthdate,
certificationRepository,
}) {
if (ine) {
return certificationRepository.get({ ine });
}

if (organizationUai && lastName && firstName && birthdate) {
return certificationRepository.getByStudentDetails({ organizationUai, lastName, firstName, birthdate });
}
};

export { getCertificationResult };
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,24 @@ import { NotFoundError } from '../../../shared/domain/errors.js';
import { CertificationResult } from '../../domain/read-models/CertificationResult.js';

const get = async ({ ine }) => {
const certificationResultDto = await datamartKnex('data_export_parcoursup_certif_result').where({
return _getBySearchParams({
national_student_id: ine,
});
};

const getByStudentDetails = async ({ organizationUai, lastName, firstName, birthdate }) => {
return _getBySearchParams({
organization_uai: organizationUai,
last_name: lastName,
first_name: firstName,
birthdate,
});
};

const _getBySearchParams = async (searchParams) => {
const certificationResultDto = await datamartKnex('data_export_parcoursup_certif_result').where(searchParams);
if (!certificationResultDto.length) {
throw new NotFoundError('No certifications found for given INE');
throw new NotFoundError('No certifications found for given search parameters');
}

return _toDomain(certificationResultDto);
Expand All @@ -32,4 +45,4 @@ const _toDomain = (certificationResultDto) => {
});
};

export { get };
export { get, getByStudentDetails };
Original file line number Diff line number Diff line change
Expand Up @@ -87,4 +87,77 @@ describe('Parcoursup | Acceptance | Application | certification-route', function
expect(response.result).to.deep.equal(expectedCertification);
});
});

describe('GET /api/parcoursup/certification/search?organizationUai={UAI}&lastName={lastName}&firstName={firstName}&birthdate={birthdate}', function () {
it('should return 200 HTTP status code and a certification for a given UAI, last name, first name and birthdate', async function () {
// given
const organizationUai = '1234567A';
const lastName = 'NOM-ELEVE';
const firstName = 'PRENOM-ELEVE';
const birthdate = '2000-01-01';
const certificationResultData = {
nationalStudentId: '123456789OK',
organizationUai,
lastName,
firstName,
birthdate,
status: 'validated',
pixScore: 327,
certificationDate: '2024-11-22T09:39:54Z',
};
datamartBuilder.factory.buildCertificationResult({
...certificationResultData,
competenceId: 'xzef1223443',
competenceLevel: 3,
});
datamartBuilder.factory.buildCertificationResult({
...certificationResultData,
competenceId: 'otherCompetenceId',
competenceLevel: 5,
});
await datamartBuilder.commit();

const options = {
method: 'GET',
url: `/api/parcoursup/certification/search?organizationUai=${organizationUai}&lastName=${lastName}&firstName=${firstName}&birthdate=${birthdate}`,
headers: {
authorization: generateValidRequestAuthorizationHeaderForApplication(
PARCOURSUP_CLIENT_ID,
PARCOURSUP_SOURCE,
PARCOURSUP_SCOPE,
),
},
};

await databaseBuilder.commit();

const expectedCertification = {
organizationUai,
ine: '123456789OK',
lastName,
firstName,
birthdate,
status: 'validated',
pixScore: 327,
certificationDate: new Date('2024-11-22T09:39:54Z'),
competences: [
{
id: 'xzef1223443',
level: 3,
},
{
id: 'otherCompetenceId',
level: 5,
},
],
};

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

// then
expect(response.statusCode).to.equal(200);
expect(response.result).to.deep.equal(expectedCertification);
});
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,93 @@ describe('Parcoursup | Infrastructure | Integration | Repositories | certificati

// then
expect(err).to.be.instanceOf(NotFoundError);
expect(err.message).to.deep.equal('No certifications found for given INE');
expect(err.message).to.deep.equal('No certifications found for given search parameters');
});
});
});

describe('#getByStudentDetails', function () {
describe('when a certification is found', function () {
it('should return the certification', async function () {
// given
const organizationUai = '1234567A';
const lastName = 'LEPONGE';
const firstName = 'Bob';
const birthdate = '2000-01-01';
const certificationResultData = {
nationalStudentId: '1234',
organizationUai,
lastName,
firstName,
birthdate,
status: 'validated',
pixScore: 327,
certificationDate: '2024-11-22T09:39:54',
};
datamartBuilder.factory.buildCertificationResult({
...certificationResultData,
competenceId: 'xzef1223443',
competenceLevel: 3,
});
datamartBuilder.factory.buildCertificationResult({
...certificationResultData,
competenceId: 'otherCompetenceId',
competenceLevel: 5,
});
await datamartBuilder.commit();

// when
const result = await certificationRepository.getByStudentDetails({
organizationUai,
lastName,
firstName,
birthdate,
});

// then
const expectedCertification = domainBuilder.parcoursup.buildCertificationResult({
ine: '1234',
organizationUai,
lastName,
firstName,
birthdate,
status: 'validated',
pixScore: 327,
certificationDate: new Date('2024-11-22T09:39:54Z'),
competences: [
{
id: 'xzef1223443',
level: 3,
},
{
id: 'otherCompetenceId',
level: 5,
},
],
});
expect(result).to.deep.equal(expectedCertification);
});
});

describe('when no certifications are found for given organizationUai', function () {
it('should throw Not Found Error', async function () {
// given
const organizationUai = '1234567B';
const lastName = 'LEPONGE';
const firstName = 'Bob';
const birthdate = '2000-01-01';

// when
const err = await catchErr(certificationRepository.getByStudentDetails)({
organizationUai,
lastName,
firstName,
birthdate,
});

// then
expect(err).to.be.instanceOf(NotFoundError);
expect(err.message).to.deep.equal('No certifications found for given search parameters');
});
});
});
Expand Down
Loading

0 comments on commit 90c3f25

Please sign in to comment.