Skip to content

Commit

Permalink
fix: refactor, abstract patterns, avoid as usage, coding stds
Browse files Browse the repository at this point in the history
  • Loading branch information
9sneha-n committed Aug 9, 2024
1 parent 9fe938d commit be5ede7
Show file tree
Hide file tree
Showing 18 changed files with 351 additions and 230 deletions.
7 changes: 1 addition & 6 deletions src/CompositionRoot.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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),
},
Expand Down
20 changes: 14 additions & 6 deletions src/data/repositories/DiseaseOutbreakEventD2Repository.ts
Original file line number Diff line number Diff line change
@@ -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";
Expand All @@ -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) {}
Expand All @@ -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<D2TrackerTrackedEntity> =>
assertOrError(response.instances[0], "Tracked entity")
)
.map(trackedEntity => {
return mapTrackedEntityAttributesToDiseaseOutbreak(trackedEntity);
});
}

getAll(): FutureData<DiseaseOutbreakEventBaseAttrs[]> {
return apiToFuture(
this.api.tracker.trackedEntities.get({
Expand All @@ -40,8 +46,9 @@ export class DiseaseOutbreakEventD2Repository implements DiseaseOutbreakEventRep
});
});
}

save(diseaseOutbreak: DiseaseOutbreakEventBaseAttrs): FutureData<void> {
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;
Expand All @@ -64,6 +71,7 @@ export class DiseaseOutbreakEventD2Repository implements DiseaseOutbreakEventRep
}
);
}

getConfigStrings(): FutureData<ConfigLabel[]> {
throw new Error("Method not implemented.");
}
Expand Down
26 changes: 13 additions & 13 deletions src/data/repositories/OptionsD2Repository.ts
Original file line number Diff line number Diff line change
@@ -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<NamedRef> {
if (!code) return Future.success({ id: "", name: "" });
get(code: Code): FutureData<Option> {
return apiToFuture(
this.api.metadata.get({
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].code,
name: response.options[0].name,
};
return option;
});
)
.flatMap(response => assertOrError(response.options[0], "Option"))
.map(d2Option => {
const option: Option = {
id: d2Option.code,
name: d2Option.name,
};
return option;
});
}
}
4 changes: 2 additions & 2 deletions src/data/repositories/OrgUnitD2Repository.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { D2Api } from "@eyeseetea/d2-api/2.36";
import { D2Api } from "../../types/d2-api";
import { OrgUnit } from "../../domain/entities/OrgUnit";
import { Id } from "../../domain/entities/Ref";
import { OrgUnitRepository } from "../../domain/repositories/OrgUnitRepository";
Expand All @@ -20,7 +20,7 @@ export class OrgUnitD2Repository implements OrgUnitRepository {
},
})
).map(response => {
const orgUnits: OrgUnit[] = response.organisationUnits.map(ou => {
const orgUnits: OrgUnit[] = response.organisationUnits.map((ou): OrgUnit => {
return {
id: ou.id,
name: ou.name,
Expand Down
12 changes: 7 additions & 5 deletions src/data/repositories/TeamMemberD2Repository.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
import { D2Api, D2UserSchema } from "@eyeseetea/d2-api/2.36";
import { D2Api, D2UserSchema } from "../../types/d2-api";
import { TeamMember } from "../../domain/entities/incident-management-team/TeamMember";
import { Id } from "../../domain/entities/Ref";
import { TeamMemberRepository } from "../../domain/repositories/TeamMemberRepository";
import { apiToFuture, FutureData } from "../api-futures";
import { SelectedPick } from "@eyeseetea/d2-api/api";
import { assertOrError } from "./utils/AssertOrError";

type D2User = SelectedPick<
D2UserSchema,
Expand Down Expand Up @@ -32,10 +33,11 @@ export class TeamMemberD2Repository implements TeamMemberRepository {
filter: { username: { eq: id } },
},
})
).map(response => {
if (!response.users[0]) throw new Error("Team Member not found");
return this.mapUserToTeamMember(response.users[0]);
});
)
.flatMap(response => assertOrError(response.users[0], "Team member"))
.map(D2User => {
return this.mapUserToTeamMember(D2User);
});
}

mapUserToTeamMember(user: D2User): TeamMember {
Expand Down
118 changes: 118 additions & 0 deletions src/data/repositories/consts/DiseaseOutbreakConstants.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,9 @@
import {
DiseaseOutbreakEventBaseAttrs,
HazardType,
} from "../../../domain/entities/disease-outbreak-event/DiseaseOutbreakEvent";
import { GetValue } from "../../../utils/ts-utils";

export const RTSL_ZEBRA_PROGRAM_ID = "qkOTdxkte8V";
export const RTSL_ZEBRA_ORG_UNIT_ID = "PS5JpkoHHio";
export const RTSL_ZEBRA_TRACKED_ENTITY_TYPE_ID = "lIzNjLOUAKA";
Expand Down Expand Up @@ -33,3 +39,115 @@ export const DiseaseOutbreakCodes = {
incidentManager: "RTSL_ZEB_TEA_ASSIGN_INCIDENT_MANAGER",
notes: "RTSL_ZEB_TEA_NOTES",
} as const;

export type KeyCode = (typeof DiseaseOutbreakCodes)[keyof typeof DiseaseOutbreakCodes];

export function isStringInDiseaseOutbreakCodes(code: string): code is KeyCode {
return Object.values(DiseaseOutbreakCodes).includes(code as KeyCode);
}

export function isHazardType(hazardType: string): hazardType is HazardType {
return [
"Biological:Animal",
"Biological:Human",
"Chemical",
"Environmental",
"Unknown",
].includes(hazardType);
}

export function getValueFromDiseaseOutbreak(
key: KeyCode,
diseaseOutbreak: DiseaseOutbreakEventBaseAttrs
): string {
switch (key) {
case "RTSL_ZEB_TEA_EVENT_id":
return diseaseOutbreak.eventId.toString();
case "RTSL_ZEB_TEA_EVENT_NAME":
return diseaseOutbreak.name;
case "RTSL_ZEB_TEA_HAZARD_TYPE":
switch (diseaseOutbreak.hazardType) {
case "Biological:Animal":
return "BIOLOGICAL_ANIMAL";
case "Biological:Human":
return "BIOLOGICAL_HUMAN";
case "Chemical":
return "CHEMICAL";
case "Environmental":
return "ENVIRONMENTAL";
case "Unknown":
return "UNKNOWN";
}
break;
case "RTSL_ZEB_TEA_MAIN_SYNDROME":
return diseaseOutbreak.mainSyndromeCode;
case "RTSL_ZEB_TEA_SUSPECTED_DISEASE":
return diseaseOutbreak.suspectedDiseaseCode;
case "RTSL_ZEB_TEA_NOTIFICATION_SOURCE":
return diseaseOutbreak.notificationSourceCode;
case "RTSL_ZEB_TEA_AREAS_AFFECTED_PROVINCES":
return getOUTextFromList(diseaseOutbreak.areasAffectedProvinceIds);
case "RTSL_ZEB_TEA_AREAS_AFFECTED_DISTRICTS":
return getOUTextFromList(diseaseOutbreak.areasAffectedDistrictIds);
case "RTSL_ZEB_TEA_INCIDENT_STATUS":
return diseaseOutbreak.incidentStatus;
case "RTSL_ZEB_TEA_DATE_EMERGED":
return diseaseOutbreak.emerged.date.toISOString();
case "RTSL_ZEB_TEA_DATE_EMERGED_NARRATIVE":
return diseaseOutbreak.emerged.narrative;
case "RTSL_ZEB_TEA_DATE_DETECTED":
return diseaseOutbreak.detected.date.toISOString();
case "RTSL_ZEB_TEA_DATE_DETECTED_NARRATIVE":
return diseaseOutbreak.detected.narrative;
case "RTSL_ZEB_TEA_DATE_NOTIFIED":
return diseaseOutbreak.notified.date.toISOString();
case "RTSL_ZEB_TEA_DATE_NOTIFIED_NARRATIVE":
return diseaseOutbreak.notified.narrative;
case "RTSL_ZEB_TEA_INITIATE_INVESTIGATION":
return diseaseOutbreak.earlyResponseActions.initiateInvestigation.toISOString();
case "RTSL_ZEB_TEA_CONDUCT_EPIDEMIOLOGICAL_ANALYSIS":
return diseaseOutbreak.earlyResponseActions.conductEpidemiologicalAnalysis.toISOString();
case "RTSL_ZEB_TEA_LABORATORY_CONFIRMATION":
return diseaseOutbreak.earlyResponseActions.laboratoryConfirmation.na ? "true" : "";
case "RTSL_ZEB_TEA_SPECIFY_DATE1":
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() ??
""
);
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() ??
""
);
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() ??
""
);
case "RTSL_ZEB_TEA_ESTABLISH_COORDINATION_MECHANISM":
return diseaseOutbreak.earlyResponseActions.establishCoordination.toISOString();
case "RTSL_ZEB_TEA_RESPONSE_NARRATIVE":
return diseaseOutbreak.earlyResponseActions.responseNarrative;
case "RTSL_ZEB_TEA_ASSIGN_INCIDENT_MANAGER":
return diseaseOutbreak.incidentManagerName;
case "RTSL_ZEB_TEA_NOTES":
return diseaseOutbreak.notes ?? "";
}
}

function getOUTextFromList(OUs: string[]): string {
return OUs[0] ?? ""; //TO DO : Handle multiple provinces/districts once metadata change is done
}
67 changes: 64 additions & 3 deletions src/data/repositories/test/DiseaseOutbreakEventTestRepository.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,11 +40,72 @@ export class DiseaseOutbreakEventTestRepository implements DiseaseOutbreakEventR
notes: undefined,
});
}
getAll(): FutureData<DiseaseOutbreakEvent[]> {
throw new Error("Method not implemented.");
getAll(): FutureData<DiseaseOutbreakEventBaseAttrs[]> {
return Future.success([
{
id: "1",
eventId: 11,
name: "Disease Outbreak 1",
created: new Date(),
lastUpdated: new Date(),
createdByName: "createdByName",
hazardType: "Biological:Animal",
mainSyndromeCode: "1",
suspectedDiseaseCode: "1",
notificationSourceCode: "1",
areasAffectedDistrictIds: [],
areasAffectedProvinceIds: [],
incidentStatus: "Watch",
emerged: { date: new Date(), narrative: "emerged" },
detected: { date: new Date(), narrative: "detected" },
notified: { date: new Date(), narrative: "notified" },
earlyResponseActions: {
initiateInvestigation: new Date(),
conductEpidemiologicalAnalysis: new Date(),
laboratoryConfirmation: { date: new Date(), na: false },
appropriateCaseManagement: { date: new Date(), na: false },
initiatePublicHealthCounterMeasures: { date: new Date(), na: false },
initiateRiskCommunication: { date: new Date(), na: false },
establishCoordination: new Date(),
responseNarrative: "responseNarrative",
},
incidentManagerName: "incidentManager",
notes: undefined,
},
{
id: "2",
eventId: 22,
name: "Disease Outbreak 2",
created: new Date(),
lastUpdated: new Date(),
createdByName: "createdByName2",
hazardType: "Biological:Animal",
mainSyndromeCode: "2",
suspectedDiseaseCode: "2",
notificationSourceCode: "2",
areasAffectedDistrictIds: [],
areasAffectedProvinceIds: [],
incidentStatus: "Watch",
emerged: { date: new Date(), narrative: "emerged" },
detected: { date: new Date(), narrative: "detected" },
notified: { date: new Date(), narrative: "notified" },
earlyResponseActions: {
initiateInvestigation: new Date(),
conductEpidemiologicalAnalysis: new Date(),
laboratoryConfirmation: { date: new Date(), na: false },
appropriateCaseManagement: { date: new Date(), na: false },
initiatePublicHealthCounterMeasures: { date: new Date(), na: false },
initiateRiskCommunication: { date: new Date(), na: false },
establishCoordination: new Date(),
responseNarrative: "responseNarrative",
},
incidentManagerName: "incidentManager",
notes: undefined,
},
]);
}
save(_diseaseOutbreak: DiseaseOutbreakEvent): FutureData<void> {
throw new Error("Method not implemented.");
return Future.success(undefined);
}
delete(_id: Id): FutureData<void> {
throw new Error("Method not implemented.");
Expand Down
4 changes: 2 additions & 2 deletions src/data/repositories/test/OptionsTestRepository.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
import { Future } from "../../../domain/entities/generic/Future";
import { Id, NamedRef } from "../../../domain/entities/Ref";
import { Id, Option } from "../../../domain/entities/Ref";
import { OptionsRepository } from "../../../domain/repositories/OptionsRepository";
import { FutureData } from "../../api-futures";

export class OptionsTestRepository implements OptionsRepository {
get(id: Id): FutureData<NamedRef> {
get(id: Id): FutureData<Option> {
return Future.success({ id: id, name: "Test Option" });
}
}
7 changes: 7 additions & 0 deletions src/data/repositories/utils/AssertOrError.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import { Future } from "../../../domain/entities/generic/Future";
import { FutureData } from "../../api-futures";

export function assertOrError<T>(objects: T, name: string): FutureData<NonNullable<T>> {
if (!objects) return Future.error(new Error(`${name} not found`));
else return Future.success(objects);
}
3 changes: 3 additions & 0 deletions src/data/repositories/utils/DateTimeHelper.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export function getCurrentTimeString(): string {
return new Date().getTime().toString();
}
Loading

0 comments on commit be5ede7

Please sign in to comment.