Skip to content

Commit

Permalink
refactor(referentiel): Standardize date validation and import mappers…
Browse files Browse the repository at this point in the history
… across referentiel modules

- Replaced direct date validation with ClockGateway's isValidDate method
- Renamed Mapper classes to ImportMapper for consistent naming
- Updated import use cases to use ClockGateway for date validation
- Removed deprecated isValidDate function from snu-lib
- Simplified date parsing and validation logic across Academie, Departement, and RegionAcademique imports
  • Loading branch information
Philippe de MANGOU committed Jan 31, 2025
1 parent 35d1b53 commit 0137daa
Show file tree
Hide file tree
Showing 13 changed files with 53 additions and 64 deletions.
4 changes: 2 additions & 2 deletions apiv2/src/admin/core/referentiel/academie/AcademieMapper.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { ACADEMIE_COLUMN_NAMES, ImportAcademieModel } from "./Academie.model";

export class AcademieMapper {
export class AcademieImportMapper {
static fromRecord(record: Record<string, string>): ImportAcademieModel {
const dateDerniereModificationSI = this.parseDate(record[ACADEMIE_COLUMN_NAMES.dateDerniereModificationSI]);
const dateCreationSI = this.parseDate(record[ACADEMIE_COLUMN_NAMES.dateCreationSI]);
Expand All @@ -15,7 +15,7 @@ export class AcademieMapper {
}

static fromRecords(records: Record<string, string>[]): ImportAcademieModel[] {
return records.map(record => AcademieMapper.fromRecord(record));
return records.map(record => AcademieImportMapper.fromRecord(record));
}

private static parseDate(dateString: string): Date {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,14 @@ import { AcademieImportService } from '../../AcademieImport.service';
import { ImporterAcademies } from './ImporterAcademies';
import { AcademieGateway } from '../../Academie.gateway';
import { ACADEMIE_COLUMN_NAMES } from '../../Academie.model';
import { AcademieMapper } from '../../AcademieMapper';
import { AcademieImportMapper } from '../../AcademieMapper';

describe('ImporterAcademies', () => {
let useCase: ImporterAcademies;
let academieImportService: AcademieImportService;
let academieGateway: AcademieGateway;
let fileGateway: FileGateway;
let clockGateway: ClockGateway;

const mockDate = "31/07/2024";
const mockDatePlus1 = "01/08/2024";
Expand All @@ -28,7 +29,7 @@ describe('ImporterAcademies', () => {
[ACADEMIE_COLUMN_NAMES.dateDerniereModificationSI]: mockDate
}

const importAcademieModel = AcademieMapper.fromRecord(academieRecord);
const importAcademieModel = AcademieImportMapper.fromRecord(academieRecord);

let mockAcademieDb = {
...importAcademieModel,
Expand Down Expand Up @@ -59,9 +60,10 @@ describe('ImporterAcademies', () => {
{
provide: ClockGateway,
useValue: {
getNowSafeIsoDate: jest.fn().mockReturnValue(mockDate)
getNowSafeIsoDate: jest.fn().mockReturnValue(mockDate),
isValidDate: jest.fn().mockReturnValue(true)
}
},
},
{
provide: NotificationGateway,
useValue: {
Expand Down Expand Up @@ -90,6 +92,7 @@ describe('ImporterAcademies', () => {
academieImportService = module.get<AcademieImportService>(AcademieImportService);
academieGateway = module.get<AcademieGateway>(AcademieGateway);
fileGateway = module.get<FileGateway>(FileGateway);
clockGateway = module.get<ClockGateway>(ClockGateway);
});

describe('execute', () => {
Expand Down Expand Up @@ -229,7 +232,7 @@ describe('ImporterAcademies', () => {
...academieRecord,
[ACADEMIE_COLUMN_NAMES.dateDerniereModificationSI]: ""
};

jest.spyOn(clockGateway, 'isValidDate').mockRestore();
jest.spyOn(fileGateway, 'parseXLS').mockResolvedValue([academieRecordEmpty]);

const result = await useCase.execute(mockParams);
Expand All @@ -248,11 +251,12 @@ describe('ImporterAcademies', () => {
expect(academieGateway.update).toHaveBeenCalledTimes(0);
});

it('Data Validation - Date creation SI - Invalid format - Invalid date format', async () => {
it('Data Validation - Date modification SI - Invalid format - Invalid date format', async () => {
jest.spyOn(fileGateway, 'parseXLS').mockResolvedValue([{
...academieRecord,
[ACADEMIE_COLUMN_NAMES.dateDerniereModificationSI]: "jeudi dernier",
}]);
jest.spyOn(clockGateway, 'isValidDate').mockRestore();

const result = await useCase.execute(mockParams);

Expand Down
Original file line number Diff line number Diff line change
@@ -1,18 +1,18 @@
import { Inject, Injectable, Logger } from "@nestjs/common";
import { FileGateway } from "@shared/core/File.gateway";
import { UseCase } from "@shared/core/UseCase";
import { isValidDate } from "snu-lib";
import { ReferentielImportTaskParameters } from "@admin/core/referentiel/ReferentielImportTask.model";
import { AcademieImportRapport, ImportAcademieModel } from "../../Academie.model";
import { AcademieImportService } from "../../AcademieImport.service";
import { AcademieValidationError } from "./AcademieValidationError";
import { AcademieMapper } from "../../AcademieMapper";

import { AcademieImportMapper } from "../../AcademieMapper";
import { ClockGateway } from "@shared/core/Clock.gateway";

@Injectable()
export class ImporterAcademies implements UseCase<AcademieImportRapport[]> {
constructor(@Inject( ) private readonly academieImportService: AcademieImportService,
@Inject(FileGateway) private readonly fileGateway: FileGateway,
@Inject(ClockGateway) private readonly clockGateway: ClockGateway,
private readonly logger: Logger,
) {}

Expand Down Expand Up @@ -57,11 +57,7 @@ export class ImporterAcademies implements UseCase<AcademieImportRapport[]> {
throw new AcademieValidationError('Invalid format - regionAcademique');
}

if (!academie.dateCreationSI || !isValidDate(academie.dateCreationSI)) {
throw new AcademieValidationError('Invalid format - dateCreationSI');
}

if (!academie.dateDerniereModificationSI || !isValidDate(academie.dateDerniereModificationSI)) {
if (!academie.dateDerniereModificationSI || !this.clockGateway.isValidDate(academie.dateDerniereModificationSI)) {
throw new AcademieValidationError('Invalid format - dateDerniereModificationSI');
}
}
Expand All @@ -72,7 +68,7 @@ export class ImporterAcademies implements UseCase<AcademieImportRapport[]> {
defval: "",
});

const academies = AcademieMapper.fromRecords(academiesXLSX);
const academies = AcademieImportMapper.fromRecords(academiesXLSX);

return academies;
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { ImportDepartementModel, DEPARTEMENT_COLUMN_NAMES } from "./Departement.model";

export class DepartementMapper {
export class DepartementImportMapper {
static fromRecord(record: Record<string, string>): ImportDepartementModel {
const dateDerniereModificationSI = this.parseDate(record[DEPARTEMENT_COLUMN_NAMES.dateDerniereModificationSI]);
const dateCreationSI = this.parseDate(record[DEPARTEMENT_COLUMN_NAMES.dateCreationSI]);
Expand All @@ -17,7 +17,7 @@ export class DepartementMapper {
}

static fromRecords(records: Record<string, string>[]): ImportDepartementModel[] {
return records.map(record => DepartementMapper.fromRecord(record));
return records.map(record => DepartementImportMapper.fromRecord(record));
}

private static parseDate(dateString: string): Date {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,14 @@ import { DepartementImportService } from '../../DepartementImport.service';
import { ImporterDepartements } from './ImporterDepartements';
import { DepartementGateway } from '../../Departement.gateway';
import { DEPARTEMENT_COLUMN_NAMES } from '../../Departement.model';
import { DepartementMapper } from '../../DepartementMapper';
import { DepartementImportMapper } from '../../DepartementMapper';

describe('ImporterDepartements', () => {
let useCase: ImporterDepartements;
let departementImportService: DepartementImportService;
let departementGateway: DepartementGateway;
let fileGateway: FileGateway;
let clockGateway: ClockGateway;

const mockDate = "31/07/2024";
const mockDatePlus1 = "01/08/2024";
Expand All @@ -30,7 +31,7 @@ describe('ImporterDepartements', () => {
[DEPARTEMENT_COLUMN_NAMES.dateDerniereModificationSI]: mockDate
}

const importDepartementModel = DepartementMapper.fromRecord(departementRecord);
const importDepartementModel = DepartementImportMapper.fromRecord(departementRecord);

let mockDepartementDb = {
...importDepartementModel,
Expand Down Expand Up @@ -61,7 +62,8 @@ describe('ImporterDepartements', () => {
{
provide: ClockGateway,
useValue: {
getNowSafeIsoDate: jest.fn().mockReturnValue(mockDate)
getNowSafeIsoDate: jest.fn().mockReturnValue(mockDate),
isValidDate: jest.fn().mockReturnValue(true)
}
},
{
Expand Down Expand Up @@ -92,6 +94,7 @@ describe('ImporterDepartements', () => {
departementImportService = module.get<DepartementImportService>(DepartementImportService);
departementGateway = module.get<DepartementGateway>(DepartementGateway);
fileGateway = module.get<FileGateway>(FileGateway);
clockGateway = module.get<ClockGateway>(ClockGateway);
});

describe('execute', () => {
Expand Down Expand Up @@ -236,6 +239,7 @@ describe('ImporterDepartements', () => {
[DEPARTEMENT_COLUMN_NAMES.dateDerniereModificationSI]: ""
};

jest.spyOn(clockGateway, 'isValidDate').mockRestore();
jest.spyOn(fileGateway, 'parseXLS').mockResolvedValue([departementRecordEmpty]);

const result = await useCase.execute(mockParams);
Expand All @@ -260,6 +264,7 @@ describe('ImporterDepartements', () => {
...departementRecord,
[DEPARTEMENT_COLUMN_NAMES.dateDerniereModificationSI]: "jeudi dernier",
}]);
jest.spyOn(clockGateway, 'isValidDate').mockRestore();

const result = await useCase.execute(mockParams);

Expand Down
Original file line number Diff line number Diff line change
@@ -1,17 +1,18 @@
import { Inject, Injectable, Logger } from "@nestjs/common";
import { FileGateway } from "@shared/core/File.gateway";
import { UseCase } from "@shared/core/UseCase";
import { isValidDate } from "snu-lib";
import { ReferentielImportTaskParameters } from "@admin/core/referentiel/ReferentielImportTask.model";
import { DepartementImportRapport, ImportDepartementModel } from "../../Departement.model";
import { DepartementImportService } from "../../DepartementImport.service";
import { DepartementValidationError } from "./DepartementValidationError";
import { DepartementMapper } from "../../DepartementMapper";
import { DepartementImportMapper } from "../../DepartementMapper";
import { ClockGateway } from "@shared/core/Clock.gateway";

@Injectable()
export class ImporterDepartements implements UseCase<DepartementImportRapport[]> {
constructor(@Inject( ) private readonly departementImportService: DepartementImportService,
@Inject(FileGateway) private readonly fileGateway: FileGateway,
@Inject(ClockGateway) private readonly clockGateway: ClockGateway,
private readonly logger: Logger,
) {}

Expand Down Expand Up @@ -60,7 +61,7 @@ export class ImporterDepartements implements UseCase<DepartementImportRapport[]>
throw new DepartementValidationError('Invalid format - academie');
}

if (!departement.dateDerniereModificationSI || !isValidDate(departement.dateDerniereModificationSI)) {
if (!departement.dateDerniereModificationSI || !this.clockGateway.isValidDate(departement.dateDerniereModificationSI)) {
throw new DepartementValidationError('Invalid format - dateDerniereModificationSI');
}
}
Expand All @@ -71,7 +72,7 @@ export class ImporterDepartements implements UseCase<DepartementImportRapport[]>
defval: "",
});

const departements = DepartementMapper.fromRecords(departementsXLSX);
const departements = DepartementImportMapper.fromRecords(departementsXLSX);

return departements;
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { ImportRegionAcademiqueModel, REGION_ACADEMIQUE_COLUMN_NAMES } from "./RegionAcademique.model";

export class RegionAcademiqueMapper {
export class RegionAcademiqueImportMapper {
static fromRecord(record: Record<string, string>): ImportRegionAcademiqueModel {
const dateDerniereModificationSI = this.parseDate(record[REGION_ACADEMIQUE_COLUMN_NAMES.date_derniere_modification_si]);
const zone = record[REGION_ACADEMIQUE_COLUMN_NAMES.zone];
Expand All @@ -14,7 +14,7 @@ export class RegionAcademiqueMapper {
}

static fromRecords(records: Record<string, string>[]): ImportRegionAcademiqueModel[] {
return records.map(record => RegionAcademiqueMapper.fromRecord(record));
return records.map(record => RegionAcademiqueImportMapper.fromRecord(record));
}

private static parseDate(dateString: string): Date {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,14 @@ import { Logger } from '@nestjs/common';
import { ClockGateway } from '@shared/core/Clock.gateway';
import { NotificationGateway } from '@notification/core/Notification.gateway';
import { REGION_ACADEMIQUE_COLUMN_NAMES } from '../../RegionAcademique.model';
import { RegionAcademiqueMapper } from '../../RegionAcademiqueMapper';
import { RegionAcademiqueImportMapper } from '../../RegionAcademiqueMapper';

describe('ImporterRegionsAcademiques', () => {
let useCase: ImporterRegionsAcademiques;
let regionAcademiqueImportService: RegionAcademiqueImportService;
let regionAcademiqueGateway: RegionAcademiqueGateway;
let fileGateway: FileGateway;
let clockGateway: ClockGateway;

const mockDate = "31/07/2024";
const mockDatePlus1 = "01/08/2024";
Expand All @@ -27,7 +28,7 @@ describe('ImporterRegionsAcademiques', () => {
[REGION_ACADEMIQUE_COLUMN_NAMES.date_derniere_modification_si]: mockDate
}

const importRegionAcademiqueModel = RegionAcademiqueMapper.fromRecord(regionAcademiqueRecord);
const importRegionAcademiqueModel = RegionAcademiqueImportMapper.fromRecord(regionAcademiqueRecord);

let mockRegionAcademiqueDb = {
...importRegionAcademiqueModel,
Expand Down Expand Up @@ -58,7 +59,8 @@ describe('ImporterRegionsAcademiques', () => {
{
provide: ClockGateway,
useValue: {
getNowSafeIsoDate: jest.fn().mockReturnValue(mockDate)
getNowSafeIsoDate: jest.fn().mockReturnValue(mockDate),
isValidDate: jest.fn().mockReturnValue(true)
}
},
{
Expand Down Expand Up @@ -90,6 +92,7 @@ describe('ImporterRegionsAcademiques', () => {
regionAcademiqueGateway = module.get<RegionAcademiqueGateway>(RegionAcademiqueGateway);

fileGateway = module.get<FileGateway>(FileGateway);
clockGateway = module.get<ClockGateway>(ClockGateway);
});

describe('execute', () => {
Expand Down Expand Up @@ -154,7 +157,7 @@ describe('ImporterRegionsAcademiques', () => {
});
});

describe('no createion, No update', () => {
describe('no creation, No update', () => {
it('Empty buffer', async () => {
jest.spyOn(fileGateway, 'parseXLS').mockResolvedValue([]);

Expand Down Expand Up @@ -266,6 +269,7 @@ describe('ImporterRegionsAcademiques', () => {
};

jest.spyOn(fileGateway, 'parseXLS').mockResolvedValue([regionAcademiqueRecordEmpty]);
jest.spyOn(clockGateway, 'isValidDate').mockRestore();

const result = await useCase.execute(mockParams);

Expand All @@ -287,6 +291,7 @@ describe('ImporterRegionsAcademiques', () => {
...regionAcademiqueRecord,
[REGION_ACADEMIQUE_COLUMN_NAMES.date_derniere_modification_si]: "jeudi dernier",
}]);
jest.spyOn(clockGateway, 'isValidDate').mockRestore();

const result = await useCase.execute(mockParams);

Expand All @@ -313,7 +318,6 @@ describe('ImporterRegionsAcademiques', () => {
};

jest.spyOn(fileGateway, 'parseXLS').mockResolvedValue([regionAcademiqueRecordLessThanMockDate]);

jest.spyOn(regionAcademiqueGateway, 'findByCode').mockResolvedValue(mockRegionAcademiqueDb);

const result = await useCase.execute(mockParams);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,18 +1,18 @@
import { Inject, Injectable, Logger } from "@nestjs/common";
import { FileGateway } from "@shared/core/File.gateway";
import { UseCase } from "@shared/core/UseCase";
import { isValidDate } from "snu-lib";
import { RegionAcademiqueImportService } from "../../RegionAcademiqueImport.service";
import { RegionAcademiqueValidationError } from "./RegionAcademiqueValidationError";
import { RegionAcademiqueMapper } from "../../RegionAcademiqueMapper";
import { RegionAcademiqueImportMapper } from "../../RegionAcademiqueMapper";
import { ImportRegionAcademiqueModel, RegionAcademiqueImportRapport } from "../../RegionAcademique.model";
import { ReferentielImportTaskParameters } from "@admin/core/referentiel/ReferentielImportTask.model";

import { ClockGateway } from "@shared/core/Clock.gateway";

@Injectable()
export class ImporterRegionsAcademiques implements UseCase<RegionAcademiqueImportRapport[]> {
constructor(@Inject( ) private readonly regionAcademiqueImportService: RegionAcademiqueImportService,
@Inject(FileGateway) private readonly fileGateway: FileGateway,
@Inject(ClockGateway) private readonly clockGateway: ClockGateway,
private readonly logger: Logger,
) {}

Expand Down Expand Up @@ -58,7 +58,7 @@ export class ImporterRegionsAcademiques implements UseCase<RegionAcademiqueImpor
throw new RegionAcademiqueValidationError('Invalid format - zone');
}

if (!regionAcademique.dateDerniereModificationSI || !isValidDate(regionAcademique.dateDerniereModificationSI)) {
if (!regionAcademique.dateDerniereModificationSI || !this.clockGateway.isValidDate(regionAcademique.dateDerniereModificationSI)) {
throw new RegionAcademiqueValidationError('Invalid format - dateDerniereModificationSI');
}
}
Expand All @@ -69,7 +69,7 @@ export class ImporterRegionsAcademiques implements UseCase<RegionAcademiqueImpor
defval: "",
});

const regionsAcademiques = RegionAcademiqueMapper.fromRecords(regionsAcademiquesXLSX);
const regionsAcademiques = RegionAcademiqueImportMapper.fromRecords(regionsAcademiquesXLSX);

return regionsAcademiques;
}
Expand Down
Loading

0 comments on commit 0137daa

Please sign in to comment.