Skip to content

Commit

Permalink
tech(api): migrate route / controller
Browse files Browse the repository at this point in the history
  • Loading branch information
xav-car authored Nov 21, 2024
1 parent 82305b2 commit e03541d
Show file tree
Hide file tree
Showing 9 changed files with 381 additions and 353 deletions.
31 changes: 0 additions & 31 deletions api/lib/application/sco-organization-learners/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,37 +21,6 @@ const inaPattern = new RegExp('^[0-9]{10}[a-zA-Z]{1}$');

const register = async function (server) {
server.route([
{
method: 'POST',
path: '/api/sco-organization-learners/association',
config: {
handler: scoOrganizationLearnerController.reconcileScoOrganizationLearnerManually,
validate: {
options: {
allowUnknown: false,
},
payload: Joi.object({
data: {
attributes: {
'first-name': Joi.string().empty(Joi.string().regex(/^\s*$/)).required(),
'last-name': Joi.string().empty(Joi.string().regex(/^\s*$/)).required(),
birthdate: Joi.date().format('YYYY-MM-DD').required(),
'campaign-code': Joi.string().empty(Joi.string().regex(/^\s*$/)).required(),
},
type: 'sco-organization-learners',
},
}),
failAction: (request, h) => {
return sendJsonApiError(new UnprocessableEntityError('Un des champs saisis n’est pas valide.'), h);
},
},
notes: [
'- **Cette route est restreinte aux utilisateurs authentifiés**\n' +
'- Elle associe des données saisies par l’utilisateur à l’inscription de l’élève dans cette organisation',
],
tags: ['api', 'sco-organization-learners'],
},
},
{
method: 'PUT',
path: '/api/sco-organization-learners/possibilities',
Expand Down
Original file line number Diff line number Diff line change
@@ -1,44 +1,11 @@
import dayjs from 'dayjs';

import * as scoOrganizationLearnerSerializer from '../../../src/prescription/learner-management/infrastructure/serializers/jsonapi/sco-organization-learner-serializer.js';
import * as requestResponseUtils from '../../../src/shared/infrastructure/utils/request-response-utils.js';
import { usecases } from '../../domain/usecases/index.js';
import { DomainTransaction } from '../../infrastructure/DomainTransaction.js';
import * as scoOrganizationLearnerSerializer from '../../infrastructure/serializers/jsonapi/sco-organization-learner-serializer.js';
import * as studentInformationForAccountRecoverySerializer from '../../infrastructure/serializers/jsonapi/student-information-for-account-recovery-serializer.js';

const reconcileScoOrganizationLearnerManually = async function (
request,
h,
dependencies = { scoOrganizationLearnerSerializer },
) {
const authenticatedUserId = request.auth.credentials.userId;
const payload = request.payload.data.attributes;
const campaignCode = payload['campaign-code'];
const withReconciliation = request.query.withReconciliation === 'true';

const reconciliationInfo = {
id: authenticatedUserId,
firstName: payload['first-name'],
lastName: payload['last-name'],
birthdate: payload['birthdate'],
};

const organizationLearner = await usecases.reconcileScoOrganizationLearnerManually({
campaignCode,
reconciliationInfo,
withReconciliation,
});

let response;
if (withReconciliation) {
const serializedData = dependencies.scoOrganizationLearnerSerializer.serializeIdentity(organizationLearner);
response = h.response(serializedData).code(200);
} else {
response = h.response().code(204);
}
return response;
};

const generateUsername = async function (request, h, dependencies = { scoOrganizationLearnerSerializer }) {
const payload = request.payload.data.attributes;
const { 'campaign-code': campaignCode } = payload;
Expand Down Expand Up @@ -191,7 +158,6 @@ const batchGenerateOrganizationLearnersUsernameWithTemporaryPassword = async fun
};

const scoOrganizationLearnerController = {
reconcileScoOrganizationLearnerManually,
generateUsername,
createAndReconcileUserToOrganizationLearner,
createUserAndReconcileToOrganizationLearnerFromExternalUser,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import * as scoOrganizationLearnerSerializer from '../../../../lib/infrastructure/serializers/jsonapi/sco-organization-learner-serializer.js';
import { DomainTransaction } from '../../../shared/domain/DomainTransaction.js';
import { ApplicationTransaction } from '../../shared/infrastructure/ApplicationTransaction.js';
import { usecases } from '../domain/usecases/index.js';
import * as scoOrganizationLearnerSerializer from '../infrastructure/serializers/jsonapi/sco-organization-learner-serializer.js';

const deleteOrganizationLearners = async function (request, h) {
const authenticatedUserId = request.auth.credentials.userId;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import {
} from '../../../../src/shared/infrastructure/monitoring-tools.js';
import { usecases } from '../domain/usecases/index.js';
import { OrganizationLearnerParser } from '../infrastructure/serializers/csv/organization-learner-parser.js';
import * as scoOrganizationLearnerSerializer from '../infrastructure/serializers/jsonapi/sco-organization-learner-serializer.js';

const INVALID_FILE_EXTENSION_ERROR = 'INVALID_FILE_EXTENSION';

Expand Down Expand Up @@ -66,8 +67,42 @@ const importOrganizationLearnersFromSIECLE = async function (
return h.response(null).code(204);
};

const reconcileScoOrganizationLearnerManually = async function (
request,
h,
dependencies = { scoOrganizationLearnerSerializer },
) {
const authenticatedUserId = request.auth.credentials.userId;
const payload = request.payload.data.attributes;
const campaignCode = payload['campaign-code'];
const withReconciliation = request.query.withReconciliation === 'true';

const reconciliationInfo = {
id: authenticatedUserId,
firstName: payload['first-name'],
lastName: payload['last-name'],
birthdate: payload['birthdate'],
};

const organizationLearner = await usecases.reconcileScoOrganizationLearnerManually({
campaignCode,
reconciliationInfo,
withReconciliation,
});

let response;
if (withReconciliation) {
const serializedData = dependencies.scoOrganizationLearnerSerializer.serializeIdentity(organizationLearner);
response = h.response(serializedData).code(200);
} else {
response = h.response().code(204);
}
return response;
};

const scoOrganizationManagementController = {
importOrganizationLearnersFromSIECLE,
reconcileScoOrganizationLearnerManually,
};

export { scoOrganizationManagementController };
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import JoiDate from '@joi/date';
import BaseJoi from 'joi';
const Joi = BaseJoi.extend(JoiDate);

import { sendJsonApiError } from '../../../shared/application/http-errors.js';
import { sendJsonApiError, UnprocessableEntityError } from '../../../shared/application/http-errors.js';
import { securityPreHandlers } from '../../../shared/application/security-pre-handlers.js';
import { identifiersType } from '../../../shared/domain/types/identifiers-type.js';
import { usecases } from '../domain/usecases/index.js';
Expand All @@ -12,6 +12,37 @@ const TWENTY_MEGABYTES = 1048576 * 20;

const register = async function (server) {
server.route([
{
method: 'POST',
path: '/api/sco-organization-learners/association',
config: {
handler: scoOrganizationManagementController.reconcileScoOrganizationLearnerManually,
validate: {
options: {
allowUnknown: false,
},
payload: Joi.object({
data: {
attributes: {
'first-name': Joi.string().empty(Joi.string().regex(/^\s*$/)).required(),
'last-name': Joi.string().empty(Joi.string().regex(/^\s*$/)).required(),
birthdate: Joi.date().format('YYYY-MM-DD').required(),
'campaign-code': Joi.string().empty(Joi.string().regex(/^\s*$/)).required(),
},
type: 'sco-organization-learners',
},
}),
failAction: (request, h) => {
return sendJsonApiError(new UnprocessableEntityError('Un des champs saisis n’est pas valide.'), h);
},
},
notes: [
'- **Cette route est restreinte aux utilisateurs authentifiés**\n' +
'- Elle associe des données saisies par l’utilisateur à l’inscription de l’élève dans cette organisation',
],
tags: ['api', 'sco-organization-learners'],
},
},
{
method: 'POST',
path: '/api/organizations/{id}/sco-organization-learners/import-siecle',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,103 +15,6 @@ describe('Acceptance | Controller | sco-organization-learners', function () {
server = await createServer();
});

describe('POST /api/sco-organization-learners/association', function () {
let organization;
let campaign;
let options;
let organizationLearner;
let user;

beforeEach(async function () {
// given
options = {
method: 'POST',
url: '/api/sco-organization-learners/association',
headers: {},
payload: {},
};

user = databaseBuilder.factory.buildUser();
organization = databaseBuilder.factory.buildOrganization({ type: 'SCO' });
organizationLearner = databaseBuilder.factory.buildOrganizationLearner({
firstName: 'france',
lastName: 'gall',
birthdate: '2001-01-01',
organizationId: organization.id,
userId: null,
nationalStudentId: 'francegall123',
});
campaign = databaseBuilder.factory.buildCampaign({ organizationId: organization.id });

await databaseBuilder.commit();
});

context('associate user with firstName, lastName and birthdate', function () {
it('should return an 200 status after having successfully associated user to organizationLearner', async function () {
// given
options.headers.authorization = generateValidRequestAuthorizationHeader(user.id);
options.payload.data = {
attributes: {
'campaign-code': campaign.code,
'first-name': organizationLearner.firstName,
'last-name': organizationLearner.lastName,
birthdate: organizationLearner.birthdate,
},
};
options.url += '?withReconciliation=true';

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

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

context('when user is not authenticated', function () {
it('should respond with a 401 - unauthorized access', async function () {
// given
options.headers.authorization = 'invalid.access.token';

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

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

context('When withReconciliation query param is set to false', function () {
it('should not reconcile user and return a 204 No Content', async function () {
// given
options.headers.authorization = generateValidRequestAuthorizationHeader(user.id);
options.payload.data = {
attributes: {
'campaign-code': campaign.code,
'first-name': organizationLearner.firstName,
'last-name': organizationLearner.lastName,
birthdate: organizationLearner.birthdate,
},
};
options.url += '?withReconciliation=false';

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

// then
expect(response.statusCode).to.equal(204);
const organizationLearnerInDB = await knex('organization-learners')
.where({
firstName: organizationLearner.firstName,
lastName: organizationLearner.lastName,
birthdate: organizationLearner.birthdate,
})
.select();
expect(organizationLearnerInDB.userId).to.be.undefined;
});
});
});
});

describe('PUT /api/sco-organization-learners/possibilities', function () {
it('returns the organizationLearner linked to the user and a 200 status code response', async function () {
//given
Expand Down
Loading

0 comments on commit e03541d

Please sign in to comment.