Skip to content

Commit

Permalink
feat(api): move validate into async job
Browse files Browse the repository at this point in the history
  • Loading branch information
xav-car authored Nov 22, 2024
1 parent bfe4a0c commit 0672916
Show file tree
Hide file tree
Showing 6 changed files with 63 additions and 156 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { getI18n } from '../../../../shared/infrastructure/i18n/i18n.js';
import { logger as l } from '../../../../shared/infrastructure/utils/logger.js';
import { ValidateCsvOrganizationImportFileJob } from '../../domain/models/ValidateCsvOrganizationImportFileJob.js';
import { usecases } from '../../domain/usecases/index.js';
import { OrganizationLearnerParser } from '../../infrastructure/serializers/csv/organization-learner-parser.js';
import { SupOrganizationLearnerParser } from '../../infrastructure/serializers/csv/sup-organization-learner-parser.js';

class ValidateCsvOrganizationLearnersImportFileJobController extends JobController {
Expand All @@ -22,7 +23,7 @@ class ValidateCsvOrganizationLearnersImportFileJobController extends JobControll
async handle({ data }) {
const { organizationImportId, locale, type } = data;

const Parser = SupOrganizationLearnerParser;
const Parser = type !== 'FREGATA' ? SupOrganizationLearnerParser : OrganizationLearnerParser;
const i18n = getI18n(locale);

try {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,19 +31,13 @@ const importOrganizationLearnersFromSIECLE = async function (
payload: request.payload,
});
} else if (format === 'csv') {
const organizationImportId = await usecases.uploadCsvFile({
await usecases.uploadCsvFile({
Parser: OrganizationLearnerParser,
payload: request.payload,
organizationId,
userId,
i18n: request.i18n,
});

await usecases.validateCsvFile({
Parser: OrganizationLearnerParser,
organizationImportId,
i18n: request.i18n,
type: 'FREGATA',
i18n: request.i18n,
});
}
} catch (error) {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import { EventEmitter } from 'node:events';

import iconv from 'iconv-lite';
import _ from 'lodash';

import { OrganizationLearnerImportHeader } from '../../../../../src/prescription/learner-management/infrastructure/serializers/csv/organization-learner-import-header.js';
import { Membership } from '../../../../../src/shared/domain/models/Membership.js';
Expand All @@ -11,7 +10,6 @@ import {
databaseBuilder,
expect,
generateValidRequestAuthorizationHeader,
knex,
} from '../../../../test-helper.js';

EventEmitter.defaultMaxListeners = 60;
Expand Down Expand Up @@ -97,126 +95,6 @@ describe('Acceptance | Application | organization-controller-import-sco-organiza
expect(response.statusCode).to.equal(204);
});
});

context('SCO : when no organization learner has been imported yet', function () {
beforeEach(function () {
const input = `${organizationLearnerCsvColumns}
123F;Beatrix;The;Bride;Kiddo;Black Mamba;Féminin;01/01/1970;97422;;200;99100;ST;MEF1;Division 1;
456F;O-Ren;;;Ishii;Cottonmouth;Masculin;01/01/1980;;Shangai;99;99132;ST;MEF1;Division 2;
`;
const buffer = iconv.encode(input, 'UTF-8');

(options.url = `/api/organizations/${organizationId}/sco-organization-learners/import-siecle?format=csv`),
(options.payload = buffer);
});

it('should respond with a 204 - no content', async function () {
// when
const response = await server.inject(options);

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

context('when some organization learners data are not well formatted', function () {
it('should not save any organization learner with missing family name', async function () {
// given
const input = `${organizationLearnerCsvColumns}
123F;Beatrix;The;Bride;Kiddo;Black Mamba;Féminin;01/01/1970;97422;;200;99100;ST;MEF1;Division 1;
456F;O-Ren;;;;Cottonmouth;Féminin;01/01/1980;;Shangai;99;99132;ST;MEF1;Division 2;
`;
const buffer = iconv.encode(input, 'UTF-8');

(options.url = `/api/organizations/${organizationId}/sco-organization-learners/import-siecle?format=csv`),
(options.payload = buffer);

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

// then
const organizationLearners = await knex('organization-learners').where({ organizationId });
expect(organizationLearners).to.have.lengthOf(0);
expect(response.statusCode).to.equal(412);
expect(response.result.errors).lengthOf(1);
expect(response.result.errors[0].code).to.equal('FIELD_REQUIRED');
expect(response.result.errors[0].meta.field).to.equal('Nom de famille*');
});

it('should not save any organization learner with wrong birthCountryCode', async function () {
const wrongData = 'FRANC';
// given
const input = `${organizationLearnerCsvColumns}
123F;Beatrix;The;Bride;Kiddo;Black Mamba;Féminin;01/01/1970;51430;Reims;200;${wrongData};ST;MEF1;Division 1;
`;
const buffer = iconv.encode(input, 'UTF-8');

(options.url = `/api/organizations/${organizationId}/sco-organization-learners/import-siecle?format=csv`),
(options.payload = buffer);

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

// then
const organizationLearners = await knex('organization-learners').where({ organizationId });

expect(organizationLearners).to.have.lengthOf(0);
expect(response.statusCode).to.equal(412);
expect(response.result.errors[0].code).to.equal('INSEE_CODE_INVALID');
expect(response.result.errors[0].meta.field).to.equal('Code pays naissance*');
});

it('should not save any organization learner with wrong birthCityCode', async function () {
const wrongData = 'A1234';
// given
const input = `${organizationLearnerCsvColumns}
123F;Beatrix;The;Bride;Kiddo;Black Mamba;Féminin;01/01/1970;${wrongData};Reims;200;99100;ST;MEF1;Division 1;
`;
const buffer = iconv.encode(input, 'UTF-8');

(options.url = `/api/organizations/${organizationId}/sco-organization-learners/import-siecle?format=csv`),
(options.payload = buffer);

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

// then
const organizationLearners = await knex('organization-learners').where({ organizationId });

expect(organizationLearners).to.have.lengthOf(0);
expect(response.statusCode).to.equal(412);
expect(response.result.errors[0].code).to.equal('INSEE_CODE_INVALID');
expect(response.result.errors[0].meta.field).to.equal('Code commune naissance**');
});
});

context(
'when an organization learner has the same national student id than an other one in the file',
function () {
beforeEach(function () {
const input = `${organizationLearnerCsvColumns}
123F;Beatrix;The;Bride;Kiddo;Black Mamba;Féminin;01/01/1970;97422;;200;99100;ST;MEF1;Division 1;
123F;O-Ren;;;Ishii;Cottonmouth;Féminin;01/01/1980;99;Shangai;200;99132;ST;MEF1;Division 2;
`;
const buffer = iconv.encode(input, 'UTF-8');

(options.url = `/api/organizations/${organizationId}/sco-organization-learners/import-siecle?format=csv`),
(options.payload = buffer);
});

it('should not import any organizationLearner and return a 412', async function () {
// when
const response = await server.inject(options);

// then
const organizationLearners = await knex('organization-learners').where({ organizationId });

expect(organizationLearners).to.have.lengthOf(0);
expect(response.statusCode).to.equal(412);
expect(response.result.errors[0].code).to.equal('IDENTIFIER_UNIQUE');
});
},
);
});

context('Resource access management', function () {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { ValidateCsvOrganizationLearnersImportFileJobController } from '../../../../../../src/prescription/learner-management/application/jobs/validate-csv-organization-learners-import-file-job-controller.js';
import { usecases } from '../../../../../../src/prescription/learner-management/domain/usecases/index.js';
import { OrganizationLearnerParser } from '../../../../../../src/prescription/learner-management/infrastructure/serializers/csv/organization-learner-parser.js';
import { SupOrganizationLearnerParser } from '../../../../../../src/prescription/learner-management/infrastructure/serializers/csv/sup-organization-learner-parser.js';
import { S3FileDoesNotExistError } from '../../../../../../src/prescription/learner-management/infrastructure/storage/import-storage.js';
import { config } from '../../../../../../src/shared/config.js';
Expand Down Expand Up @@ -32,22 +33,64 @@ describe('Unit | Prescription | Application | Jobs | validateCsvOrganizationLear
});

describe('#handle', function () {
it('should call usecase', async function () {
sinon.stub(usecases, 'validateCsvFile');
// given
const handler = new ValidateCsvOrganizationLearnersImportFileJobController();
const data = { organizationImportId: Symbol('organizationImportId'), locale: 'en', type: 'ADDITIONAL_STUDENT' };
describe('SUP cases', function () {
it('should call usecase with correct parser on type `ADDITIONAL_STUDENT`', async function () {
sinon.stub(usecases, 'validateCsvFile');
// given
const handler = new ValidateCsvOrganizationLearnersImportFileJobController();
const data = { organizationImportId: Symbol('organizationImportId'), locale: 'en', type: 'ADDITIONAL_STUDENT' };

// when
await handler.handle({ data });

// then
expect(usecases.validateCsvFile).to.have.been.calledOnce;
expect(usecases.validateCsvFile).to.have.been.calledWithExactly({
organizationImportId: data.organizationImportId,
i18n: getI18n(data.locale),
type: data.type,
Parser: SupOrganizationLearnerParser,
});
});

// when
await handler.handle({ data });
it('should call usecase with correct parser on type `REPLACE_STUDENT`', async function () {
sinon.stub(usecases, 'validateCsvFile');
// given
const handler = new ValidateCsvOrganizationLearnersImportFileJobController();
const data = { organizationImportId: Symbol('organizationImportId'), locale: 'en', type: 'REPLACE_STUDENT' };

// when
await handler.handle({ data });

// then
expect(usecases.validateCsvFile).to.have.been.calledOnce;
expect(usecases.validateCsvFile).to.have.been.calledWithExactly({
organizationImportId: data.organizationImportId,
i18n: getI18n(data.locale),
type: data.type,
Parser: SupOrganizationLearnerParser,
});
});
});

// then
expect(usecases.validateCsvFile).to.have.been.calledOnce;
expect(usecases.validateCsvFile).to.have.been.calledWithExactly({
organizationImportId: data.organizationImportId,
i18n: getI18n(data.locale),
type: data.type,
Parser: SupOrganizationLearnerParser,
describe('FREGATA case', function () {
it('should call usecase with correct parser on type `FREGATA`', async function () {
sinon.stub(usecases, 'validateCsvFile');
// given
const handler = new ValidateCsvOrganizationLearnersImportFileJobController();
const data = { organizationImportId: Symbol('organizationImportId'), locale: 'en', type: 'FREGATA' };

// when
await handler.handle({ data });

// then
expect(usecases.validateCsvFile).to.have.been.calledOnce;
expect(usecases.validateCsvFile).to.have.been.calledWithExactly({
organizationImportId: data.organizationImportId,
i18n: getI18n(data.locale),
type: data.type,
Parser: OrganizationLearnerParser,
});
});
});

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,6 @@ describe('Unit | Application | Organizations | organization-controller', functio
sinon.stub(fs, 'unlink').resolves();
sinon.stub(usecases, 'uploadSiecleFile');
sinon.stub(usecases, 'uploadCsvFile');
sinon.stub(usecases, 'validateCsvFile');
sinon.stub(eventBus, 'publish');
sinon.stub(ApplicationTransaction, 'execute');
sinon.stub(ApplicationTransaction, 'getTransactionAsDomainTransaction');
Expand Down Expand Up @@ -116,15 +115,13 @@ describe('Unit | Application | Organizations | organization-controller', functio
it('should call the usecase uploadCsvFile to import organizationLearners csv', async function () {
// given
const userId = 1;
const organizationImportId = Symbol('organizationImportId');
request.auth = { credentials: { userId } };
request.query.format = 'csv';
const i18n = Symbol('i18n');
request.i18n = i18n;
hFake.request = {
path: '/api/organizations/145/sco-organization-learners/import-siecle',
};
usecases.uploadCsvFile.resolves(organizationImportId);
// when
await scoOrganizationManagementController.importOrganizationLearnersFromSIECLE(request, hFake, dependencies);

Expand All @@ -133,16 +130,10 @@ describe('Unit | Application | Organizations | organization-controller', functio
Parser: OrganizationLearnerParser,
userId,
organizationId,
type: 'FREGATA',
payload,
i18n,
});

expect(usecases.validateCsvFile).to.have.been.calledWithExactly({
Parser: OrganizationLearnerParser,
i18n,
organizationImportId,
type: 'FREGATA',
});
});
context('when file format is not supported', function () {
it('should throw a FileValidationError', async function () {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,7 @@ describe('Unit | UseCase | validateCsvFile', function () {
});

describe('performJob', function () {
it('should perform sup job when type other than FREGATE is detected', async function () {
it('should perform sup job when type other than FREGATA is detected', async function () {
// given
const type = Symbol('type');
organizationImportRepositoryStub.get.withArgs(organizationImportId).resolves(organizationImport);
Expand Down

0 comments on commit 0672916

Please sign in to comment.