From 8e00ad0a371030b6ebf19b69f0072d9b2e563225 Mon Sep 17 00:00:00 2001 From: 9sneha-n <9sneha.n@gmail.com> Date: Tue, 6 Aug 2024 16:26:57 +0530 Subject: [PATCH 1/4] reafctor: review comments --- src/data/repositories/OptionsD2Repository.ts | 10 +++--- .../repositories/TeamMemberD2Repository.ts | 8 ++++- .../DiseaseOutbreakEventTestRepository.ts | 6 ++-- .../utils/DiseaseOutbreakMapper.ts | 36 ++++++++++++------- src/domain/entities/Ref.ts | 1 + .../DiseaseOutbreakEvent.ts | 8 ++--- .../usecases/GetDiseaseOutbreakByIdUseCase.ts | 12 +++---- .../usecases/SaveDiseaseOutbreakUseCase.ts | 6 ++-- 8 files changed, 53 insertions(+), 34 deletions(-) diff --git a/src/data/repositories/OptionsD2Repository.ts b/src/data/repositories/OptionsD2Repository.ts index b51c3341..b1117c8c 100644 --- a/src/data/repositories/OptionsD2Repository.ts +++ b/src/data/repositories/OptionsD2Repository.ts @@ -1,5 +1,5 @@ import { D2Api } from "@eyeseetea/d2-api/2.36"; -import { Id, NamedRef } from "../../domain/entities/Ref"; +import { Code, Id, NamedRef } from "../../domain/entities/Ref"; import { apiToFuture, FutureData } from "../api-futures"; import { OptionsRepository } from "../../domain/repositories/OptionsRepository"; import { Future } from "../../domain/entities/generic/Future"; @@ -7,16 +7,16 @@ import { Future } from "../../domain/entities/generic/Future"; export class OptionsD2Repository implements OptionsRepository { constructor(private api: D2Api) {} - get(id: Id): FutureData { - if (!id) return Future.success({ id: "", name: "" }); + get(code: Code): FutureData { + if (!code) return Future.success({ id: "", name: "" }); return apiToFuture( this.api.metadata.get({ - options: { fields: { id: true, name: true }, filter: { id: { eq: id } } }, + options: { fields: { code: true, name: true }, filter: { code: { eq: code } } }, }) ).map(response => { if (!response.options[0]) throw new Error("Option not found"); const option: NamedRef = { - id: response.options[0].id, + id: response.options[0].code, name: response.options[0].name, }; return option; diff --git a/src/data/repositories/TeamMemberD2Repository.ts b/src/data/repositories/TeamMemberD2Repository.ts index cf2291ac..9ffeb483 100644 --- a/src/data/repositories/TeamMemberD2Repository.ts +++ b/src/data/repositories/TeamMemberD2Repository.ts @@ -22,7 +22,13 @@ export class TeamMemberD2Repository implements TeamMemberRepository { return apiToFuture( this.api.metadata.get({ users: { - fields: { id: true, name: true, email: true, phoneNumber: true }, + fields: { + id: true, + name: true, + email: true, + phoneNumber: true, + username: true, + }, filter: { username: { eq: id } }, }, }) diff --git a/src/data/repositories/test/DiseaseOutbreakEventTestRepository.ts b/src/data/repositories/test/DiseaseOutbreakEventTestRepository.ts index afb1425c..4fe53acc 100644 --- a/src/data/repositories/test/DiseaseOutbreakEventTestRepository.ts +++ b/src/data/repositories/test/DiseaseOutbreakEventTestRepository.ts @@ -17,9 +17,9 @@ export class DiseaseOutbreakEventTestRepository implements DiseaseOutbreakEventR lastUpdated: new Date(), createdByName: "createdByName", hazardType: "Biological:Animal", - mainSyndromeId: "1", - suspectedDiseaseId: "1", - notificationSourceId: "1", + mainSyndromeCode: "1", + suspectedDiseaseCode: "1", + notificationSourceCode: "1", areasAffectedDistrictIds: [], areasAffectedProvinceIds: [], incidentStatus: "Watch", diff --git a/src/data/repositories/utils/DiseaseOutbreakMapper.ts b/src/data/repositories/utils/DiseaseOutbreakMapper.ts index 660ce1fb..c4522fed 100644 --- a/src/data/repositories/utils/DiseaseOutbreakMapper.ts +++ b/src/data/repositories/utils/DiseaseOutbreakMapper.ts @@ -39,10 +39,10 @@ export function mapTrackedEntityAttributesToDiseaseOutbreak( lastUpdated: trackedEntity.updatedAt ? new Date(trackedEntity.updatedAt) : new Date(), createdByName: undefined, hazardType: getValueFromMap("hazardType", trackedEntity) as HazardType, - mainSyndromeId: getValueFromMap("mainSyndrome", trackedEntity), - suspectedDiseaseId: getValueFromMap("suspectedDisease", trackedEntity), + mainSyndromeCode: getValueFromMap("mainSyndrome", trackedEntity), + suspectedDiseaseCode: getValueFromMap("suspectedDisease", trackedEntity), - notificationSourceId: getValueFromMap("notificationSource", trackedEntity), + notificationSourceCode: getValueFromMap("notificationSource", trackedEntity), areasAffectedProvinceIds: [getValueFromMap("areasAffectedProvinces", trackedEntity)].filter( ou => ou !== "" @@ -82,10 +82,10 @@ export function mapTrackedEntityAttributesToDiseaseOutbreak( }, initiatePublicHealthCounterMeasures: { date: new Date( - getValueFromMap("initiatePublicHealthCounterMeasuresNA", trackedEntity) + getValueFromMap("initiatePublicHealthCounterMeasuresDate", trackedEntity) ), na: - getValueFromMap("initiatePublicHealthCounterMeasuresDate", trackedEntity) === + getValueFromMap("initiatePublicHealthCounterMeasuresNA", trackedEntity) === "true", }, initiateRiskCommunication: { @@ -183,11 +183,11 @@ function getValueFromDiseaseOutbreak( } break; case "RTSL_ZEB_TEA_MAIN_SYNDROME": - return diseaseOutbreak.mainSyndromeId; + return diseaseOutbreak.mainSyndromeCode; case "RTSL_ZEB_TEA_SUSPECTED_DISEASE": - return diseaseOutbreak.suspectedDiseaseId; + return diseaseOutbreak.suspectedDiseaseCode; case "RTSL_ZEB_TEA_NOTIFICATION_SOURCE": - return diseaseOutbreak.notificationSourceId; + return diseaseOutbreak.notificationSourceCode; case "RTSL_ZEB_TEA_AREAS_AFFECTED_PROVINCES": return diseaseOutbreak.areasAffectedProvinceIds[0] ?? ""; //TO DO : Handle multiple provinces once metadata change is done case "RTSL_ZEB_TEA_AREAS_AFFECTED_DISTRICTS": @@ -213,21 +213,33 @@ function getValueFromDiseaseOutbreak( case "RTSL_ZEB_TEA_LABORATORY_CONFIRMATION": return diseaseOutbreak.earlyResponseActions.laboratoryConfirmation.na ? "true" : ""; case "RTSL_ZEB_TEA_SPECIFY_DATE1": - return diseaseOutbreak.earlyResponseActions.laboratoryConfirmation.date.toISOString(); + return ( + diseaseOutbreak.earlyResponseActions.laboratoryConfirmation.date?.toISOString() ?? + "" + ); case "RTSL_ZEB_TEA_APPROPRIATE_CASE_MANAGEMENT": return diseaseOutbreak.earlyResponseActions.appropriateCaseManagement.na ? "true" : ""; case "RTSL_ZEB_TEA_SPECIFY_DATE2": - return diseaseOutbreak.earlyResponseActions.appropriateCaseManagement.date?.toISOString(); + return ( + diseaseOutbreak.earlyResponseActions.appropriateCaseManagement.date?.toISOString() ?? + "" + ); case "RTSL_ZEB_TEA_APPROPRIATE_PUBLIC_HEALTH": return diseaseOutbreak.earlyResponseActions.initiatePublicHealthCounterMeasures.na ? "true" : ""; case "RTSL_ZEB_TEA_SPECIFY_DATE3": - return diseaseOutbreak.earlyResponseActions.initiatePublicHealthCounterMeasures.date?.toISOString(); + return ( + diseaseOutbreak.earlyResponseActions.initiatePublicHealthCounterMeasures.date?.toISOString() ?? + "" + ); case "RTSL_ZEB_TEA_APPROPRIATE_RISK_COMMUNICATION": return diseaseOutbreak.earlyResponseActions.initiateRiskCommunication.na ? "true" : ""; case "RTSL_ZEB_TEA_SPECIFY_DATE4": - return diseaseOutbreak.earlyResponseActions.initiateRiskCommunication.date?.toISOString(); + return ( + diseaseOutbreak.earlyResponseActions.initiateRiskCommunication.date?.toISOString() ?? + "" + ); case "RTSL_ZEB_TEA_ESTABLISH_COORDINATION_MECHANISM": return diseaseOutbreak.earlyResponseActions.establishCoordination.toISOString(); case "RTSL_ZEB_TEA_RESPONSE_NARRATIVE": diff --git a/src/domain/entities/Ref.ts b/src/domain/entities/Ref.ts index f3f767a6..8b5f6f65 100644 --- a/src/domain/entities/Ref.ts +++ b/src/domain/entities/Ref.ts @@ -1,4 +1,5 @@ export type Id = string; +export type Code = string; export interface Ref { id: Id; diff --git a/src/domain/entities/disease-outbreak-event/DiseaseOutbreakEvent.ts b/src/domain/entities/disease-outbreak-event/DiseaseOutbreakEvent.ts index 20275af3..2decfa6f 100644 --- a/src/domain/entities/disease-outbreak-event/DiseaseOutbreakEvent.ts +++ b/src/domain/entities/disease-outbreak-event/DiseaseOutbreakEvent.ts @@ -22,7 +22,7 @@ type DateWithNarrative = { }; type DateWithNA = { - date: Date; + date: Maybe; na: Maybe; }; @@ -43,9 +43,9 @@ export type DiseaseOutbreakEventBaseAttrs = NamedRef & { lastUpdated: Date; createdByName: Maybe; hazardType: HazardType; - mainSyndromeId: Id; - suspectedDiseaseId: Id; - notificationSourceId: Id; + mainSyndromeCode: Id; + suspectedDiseaseCode: Id; + notificationSourceCode: Id; areasAffectedProvinceIds: Id[]; areasAffectedDistrictIds: Id[]; incidentStatus: IncidentStatusType; diff --git a/src/domain/usecases/GetDiseaseOutbreakByIdUseCase.ts b/src/domain/usecases/GetDiseaseOutbreakByIdUseCase.ts index 9fb6324f..1593c520 100644 --- a/src/domain/usecases/GetDiseaseOutbreakByIdUseCase.ts +++ b/src/domain/usecases/GetDiseaseOutbreakByIdUseCase.ts @@ -18,18 +18,18 @@ export class GetDiseaseOutbreakByIdUseCase { public execute(id: Id): FutureData { return this.diseaseOutbreakRepository.get(id).flatMap(diseaseOutbreakEventBase => { const { - mainSyndromeId, - suspectedDiseaseId, - notificationSourceId, + mainSyndromeCode, + suspectedDiseaseCode, + notificationSourceCode, incidentManagerName, areasAffectedDistrictIds, areasAffectedProvinceIds, } = diseaseOutbreakEventBase; return Future.joinObj({ - mainSyndrome: this.optionsRepository.get(mainSyndromeId), - suspectedDisease: this.optionsRepository.get(suspectedDiseaseId), - notificationSource: this.optionsRepository.get(notificationSourceId), + mainSyndrome: this.optionsRepository.get(mainSyndromeCode), + suspectedDisease: this.optionsRepository.get(suspectedDiseaseCode), + notificationSource: this.optionsRepository.get(notificationSourceCode), incidentManager: incidentManagerName ? this.teamMemberRepository.get(incidentManagerName) : Future.success(undefined), diff --git a/src/domain/usecases/SaveDiseaseOutbreakUseCase.ts b/src/domain/usecases/SaveDiseaseOutbreakUseCase.ts index 8db07889..09a4937a 100644 --- a/src/domain/usecases/SaveDiseaseOutbreakUseCase.ts +++ b/src/domain/usecases/SaveDiseaseOutbreakUseCase.ts @@ -15,9 +15,9 @@ export class SaveDiseaseOutbreakUseCase { lastUpdated: new Date(), createdByName: "createdByName", hazardType: "Biological:Animal", - mainSyndromeId: "", - suspectedDiseaseId: "", - notificationSourceId: "", + mainSyndromeCode: "", + suspectedDiseaseCode: "", + notificationSourceCode: "", areasAffectedDistrictIds: ["oEBf29y8JP8"], areasAffectedProvinceIds: ["AWn3s2RqgAN"], incidentStatus: "Watch", From 9fe938d552ce65ed152a3dcc0173479f32e639f4 Mon Sep 17 00:00:00 2001 From: 9sneha-n <9sneha.n@gmail.com> Date: Tue, 6 Aug 2024 16:27:39 +0530 Subject: [PATCH 2/4] reafactor: fix warning --- src/data/repositories/OptionsD2Repository.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/data/repositories/OptionsD2Repository.ts b/src/data/repositories/OptionsD2Repository.ts index b1117c8c..0ffc2a8b 100644 --- a/src/data/repositories/OptionsD2Repository.ts +++ b/src/data/repositories/OptionsD2Repository.ts @@ -1,5 +1,5 @@ import { D2Api } from "@eyeseetea/d2-api/2.36"; -import { Code, Id, NamedRef } from "../../domain/entities/Ref"; +import { Code, NamedRef } from "../../domain/entities/Ref"; import { apiToFuture, FutureData } from "../api-futures"; import { OptionsRepository } from "../../domain/repositories/OptionsRepository"; import { Future } from "../../domain/entities/generic/Future"; From be5ede70a4baccea7a9b88de98bfe4bb40b0ff74 Mon Sep 17 00:00:00 2001 From: 9sneha-n <9sneha.n@gmail.com> Date: Fri, 9 Aug 2024 14:28:06 +0530 Subject: [PATCH 3/4] fix: refactor, abstract patterns, avoid as usage, coding stds --- src/CompositionRoot.ts | 7 +- .../DiseaseOutbreakEventD2Repository.ts | 20 ++- src/data/repositories/OptionsD2Repository.ts | 26 +-- src/data/repositories/OrgUnitD2Repository.ts | 4 +- .../repositories/TeamMemberD2Repository.ts | 12 +- .../consts/DiseaseOutbreakConstants.ts | 118 +++++++++++++ .../DiseaseOutbreakEventTestRepository.ts | 67 +++++++- .../test/OptionsTestRepository.ts | 4 +- src/data/repositories/utils/AssertOrError.ts | 7 + src/data/repositories/utils/DateTimeHelper.ts | 3 + .../utils/DiseaseOutbreakMapper.ts | 156 +++++------------- src/data/repositories/utils/MetadataHelper.ts | 33 ++-- src/domain/entities/OrgUnit.ts | 3 +- src/domain/entities/Ref.ts | 5 +- src/domain/repositories/OptionsRepository.ts | 4 +- .../usecases/GetDiseaseOutbreakByIdUseCase.ts | 104 ++++++------ src/types/d2-api.ts | 2 + src/webapp/pages/dashboard/DashboardPage.tsx | 6 +- 18 files changed, 351 insertions(+), 230 deletions(-) create mode 100644 src/data/repositories/utils/AssertOrError.ts create mode 100644 src/data/repositories/utils/DateTimeHelper.ts diff --git a/src/CompositionRoot.ts b/src/CompositionRoot.ts index 2c33d13b..96a6681f 100644 --- a/src/CompositionRoot.ts +++ b/src/CompositionRoot.ts @@ -35,12 +35,7 @@ function getCompositionRoot(repositories: Repositories) { getCurrent: new GetCurrentUserUseCase(repositories.usersRepository), }, diseaseOutbreakEvent: { - get: new GetDiseaseOutbreakByIdUseCase( - repositories.diseaseOutbreakEventRepository, - repositories.optionsRepository, - repositories.teamMemberRepository, - repositories.orgUnitRepository - ), + get: new GetDiseaseOutbreakByIdUseCase(repositories), getAll: new GetAllDiseaseOutbreaksUseCase(repositories.diseaseOutbreakEventRepository), save: new SaveDiseaseOutbreakUseCase(repositories.diseaseOutbreakEventRepository), }, diff --git a/src/data/repositories/DiseaseOutbreakEventD2Repository.ts b/src/data/repositories/DiseaseOutbreakEventD2Repository.ts index 9c38fef9..a1d6727c 100644 --- a/src/data/repositories/DiseaseOutbreakEventD2Repository.ts +++ b/src/data/repositories/DiseaseOutbreakEventD2Repository.ts @@ -1,4 +1,4 @@ -import { D2Api } from "@eyeseetea/d2-api/2.36"; +import { D2Api } from "../../types/d2-api"; import { DiseaseOutbreakEventRepository } from "../../domain/repositories/DiseaseOutbreakEventRepository"; import { apiToFuture, FutureData } from "../api-futures"; import { DiseaseOutbreakEventBaseAttrs } from "../../domain/entities/disease-outbreak-event/DiseaseOutbreakEvent"; @@ -10,6 +10,7 @@ import { import { RTSL_ZEBRA_ORG_UNIT_ID, RTSL_ZEBRA_PROGRAM_ID } from "./consts/DiseaseOutbreakConstants"; import { D2TrackerTrackedEntity } from "@eyeseetea/d2-api/api/trackerTrackedEntities"; import { getProgramTEAsMetadata } from "./utils/MetadataHelper"; +import { assertOrError } from "./utils/AssertOrError"; export class DiseaseOutbreakEventD2Repository implements DiseaseOutbreakEventRepository { constructor(private api: D2Api) {} @@ -22,11 +23,16 @@ export class DiseaseOutbreakEventD2Repository implements DiseaseOutbreakEventRep trackedEntity: id, fields: { attributes: true, trackedEntity: true }, }) - ).map(trackedEntity => { - if (!trackedEntity.instances[0]) throw new Error("Tracked entity not found"); - return mapTrackedEntityAttributesToDiseaseOutbreak(trackedEntity.instances[0]); - }); + ) + .flatMap( + (response): FutureData => + assertOrError(response.instances[0], "Tracked entity") + ) + .map(trackedEntity => { + return mapTrackedEntityAttributesToDiseaseOutbreak(trackedEntity); + }); } + getAll(): FutureData { return apiToFuture( this.api.tracker.trackedEntities.get({ @@ -40,8 +46,9 @@ export class DiseaseOutbreakEventD2Repository implements DiseaseOutbreakEventRep }); }); } + save(diseaseOutbreak: DiseaseOutbreakEventBaseAttrs): FutureData { - return apiToFuture(getProgramTEAsMetadata(this.api, RTSL_ZEBRA_PROGRAM_ID)).flatMap( + return getProgramTEAsMetadata(this.api, RTSL_ZEBRA_PROGRAM_ID).flatMap( teasMetadataResponse => { const teasMetadata = teasMetadataResponse.objects[0]?.programTrackedEntityAttributes; @@ -64,6 +71,7 @@ export class DiseaseOutbreakEventD2Repository implements DiseaseOutbreakEventRep } ); } + getConfigStrings(): FutureData { throw new Error("Method not implemented."); } diff --git a/src/data/repositories/OptionsD2Repository.ts b/src/data/repositories/OptionsD2Repository.ts index 0ffc2a8b..93c10aeb 100644 --- a/src/data/repositories/OptionsD2Repository.ts +++ b/src/data/repositories/OptionsD2Repository.ts @@ -1,25 +1,25 @@ -import { D2Api } from "@eyeseetea/d2-api/2.36"; -import { Code, NamedRef } from "../../domain/entities/Ref"; +import { D2Api } from "../../types/d2-api"; +import { Code, Option } from "../../domain/entities/Ref"; import { apiToFuture, FutureData } from "../api-futures"; import { OptionsRepository } from "../../domain/repositories/OptionsRepository"; -import { Future } from "../../domain/entities/generic/Future"; +import { assertOrError } from "./utils/AssertOrError"; export class OptionsD2Repository implements OptionsRepository { constructor(private api: D2Api) {} - get(code: Code): FutureData { - if (!code) return Future.success({ id: "", name: "" }); + get(code: Code): FutureData