diff --git a/src/data/Dhis2RelationshipTypes.ts b/src/data/Dhis2RelationshipTypes.ts index 02f4ef16..979ea35c 100644 --- a/src/data/Dhis2RelationshipTypes.ts +++ b/src/data/Dhis2RelationshipTypes.ts @@ -11,21 +11,30 @@ import { promiseMap } from "../utils/promises"; import { getUid } from "./dhis2-uid"; import { getTrackedEntities, TrackedEntityGetRequest } from "./Dhis2TrackedEntityInstances"; import { TrackerRelationship, RelationshipItem, TrackedEntitiesApiRequest } from "../domain/entities/TrackedEntity"; -import { buildOrgUnitsParameter } from "../domain/entities/OrgUnit"; import { EventsAPIResponse } from "../domain/entities/DhisDataPackage"; +import { getVersion, getMajorVersion } from "../utils/d2-api"; type RelationshipTypesById = Record>; export type RelationshipOrgUnitFilter = TrackedEntityOURequestApi["ouMode"]; -export function buildOrgUnitMode(ouMode: RelationshipOrgUnitFilter, orgUnits?: Ref[]) { +function buildOrgUnitsParameter(apiVersion: number, orgUnitsIds: Ref[]): string { + const separator = apiVersion >= 42 ? "," : ";"; + return orgUnitsIds.map(({ id }) => id).join(separator); +} + +export function buildOrgUnitMode(apiVersion: string, ouMode: RelationshipOrgUnitFilter, orgUnits?: Ref[]) { + const majorVersion = getMajorVersion(apiVersion); const isOuReq = ouMode === "SELECTED" || ouMode === "CHILDREN" || ouMode === "DESCENDANTS"; - //issue: v41 - orgUnitMode/ouMode; v38-40 ouMode; ouMode to be deprecated + //issue: v42 - orgUnitMode and orgUnits; v41 - orgUnitMode/ouMode and orgUnit; v38-40 ouMode; ouMode to be deprecated //can't use both orgUnitMode and ouMode in v41 if (!isOuReq) { - return { ouMode }; + if (majorVersion >= 42) return { orgUnitMode: ouMode }; + else return { ouMode }; } else if (orgUnits && orgUnits.length > 0) { - return { ouMode, orgUnit: buildOrgUnitsParameter(orgUnits) }; + const orgUnitsParam = buildOrgUnitsParameter(majorVersion, orgUnits); + if (majorVersion >= 42) return { orgUnitMode: ouMode, orgUnits: orgUnitsParam }; + else return { ouMode, orgUnit: orgUnitsParam }; } else { throw new Error(`No orgUnits selected for ouMode ${ouMode}`); } @@ -169,6 +178,8 @@ export async function getRelationshipMetadata( api: D2Api, filters?: ProgramFilters ): Promise { + const apiVersion = await getVersion(api); + const { trackedEntityTypes, relationshipTypes: allRelationshipTypes, @@ -201,8 +212,22 @@ export async function getRelationshipMetadata( if (!isProgramAssociatedWithSomeConstraint) return; - const fromConstraint = await getConstraint(api, trackedEntityTypes, programs, type.fromConstraint, filters); - const toConstraint = await getConstraint(api, trackedEntityTypes, programs, type.toConstraint, filters); + const fromConstraint = await getConstraint( + api, + apiVersion, + trackedEntityTypes, + programs, + type.fromConstraint, + filters + ); + const toConstraint = await getConstraint( + api, + apiVersion, + trackedEntityTypes, + programs, + type.toConstraint, + filters + ); return fromConstraint && toConstraint ? { id: relType.id, name: relType.name, constraints: { from: fromConstraint, to: toConstraint } } @@ -217,6 +242,7 @@ type ProgramInfo = NamedRef & { programStages: NamedRef[] }; const getConstraint = memoizeAsync( async ( api: D2Api, + apiVersion: string, trackedEntityTypes: NamedRef[], programs: ProgramInfo[], constraint: D2RelationshipConstraint, @@ -235,7 +261,7 @@ const getConstraint = memoizeAsync( switch (constraint.relationshipEntity) { case "TRACKED_ENTITY_INSTANCE": - return getConstraintForTypeTei(api, filters, trackedEntityTypes, constraint); + return getConstraintForTypeTei(api, apiVersion, filters, trackedEntityTypes, constraint); case "PROGRAM_STAGE_INSTANCE": { if (constraint.program) { const program = programsById[constraint.program.id]; @@ -251,6 +277,7 @@ const getConstraint = memoizeAsync( async function getConstraintForTypeTei( api: D2Api, + apiVersion: string, filters: ProgramFilters | undefined, trackedEntityTypes: NamedRef[], constraint: D2RelationshipConstraint @@ -258,7 +285,7 @@ async function getConstraintForTypeTei( const { ouMode = "CAPTURE", organisationUnits = [] } = filters || {}; const trackedEntityTypesById = _.keyBy(trackedEntityTypes, obj => obj.id); - const ouModeQuery = buildOrgUnitMode(ouMode, organisationUnits); + const ouModeQuery = buildOrgUnitMode(apiVersion, ouMode, organisationUnits); const query = { ...ouModeQuery, diff --git a/src/data/Dhis2TrackedEntityInstances.ts b/src/data/Dhis2TrackedEntityInstances.ts index 441bbaa2..34c50793 100644 --- a/src/data/Dhis2TrackedEntityInstances.ts +++ b/src/data/Dhis2TrackedEntityInstances.ts @@ -28,8 +28,9 @@ import { TrackedEntitiesApiRequest, TrackedEntitiesResponse, TrackedEntity } fro import { Params } from "@eyeseetea/d2-api/api/common"; import { ImportDataPackageOptions } from "../domain/repositories/InstanceRepository"; import { MULTI_TEXT_OPTION_DELIMITER } from "../domain/helpers/ExcelBuilder"; +import { getVersion } from "../utils/d2-api"; -export interface GetOptions { +type GetOptions = { api: D2Api; program: Ref; orgUnits: Ref[]; @@ -37,7 +38,7 @@ export interface GetOptions { enrollmentStartDate?: Moment; enrollmentEndDate?: Moment; relationshipsOuFilter?: RelationshipOrgUnitFilter; -} +}; type TrackerParams = Params & Omit; @@ -60,6 +61,8 @@ export async function getTrackedEntityInstances(options: GetOptions): Promise { + const apiVersion = await getVersion(api); + const ouModeParam = buildOrgUnitMode(apiVersion, "CAPTURE"); + const query = { - ouMode: "CAPTURE", + ...ouModeParam, pageSize: 1000, totalPages: true, fields: "trackedEntity", @@ -504,6 +511,7 @@ type TeiKey = KeysOfUnion; async function getTeisFromApi(options: { api: D2Api; + apiVersion: string; program: Program; orgUnits: Ref[]; page: number; @@ -512,7 +520,8 @@ async function getTeisFromApi(options: { enrollmentEndDate?: Moment; ouMode: RelationshipOrgUnitFilter; }): Promise { - const { api, program, orgUnits, page, pageSize, enrollmentStartDate, enrollmentEndDate, ouMode } = options; + const { api, apiVersion, program, orgUnits, page, pageSize, enrollmentStartDate, enrollmentEndDate, ouMode } = + options; const fields: TeiKey[] = [ "trackedEntity", @@ -524,7 +533,7 @@ async function getTeisFromApi(options: { "geometry", ]; - const ouModeQuery = buildOrgUnitMode(ouMode, orgUnits); + const ouModeQuery = buildOrgUnitMode(apiVersion, ouMode, orgUnits); const filters: TrackedEntityGetRequest = { ...ouModeQuery, diff --git a/src/domain/entities/OrgUnit.ts b/src/domain/entities/OrgUnit.ts index 3e7d8f35..17ac2128 100644 --- a/src/domain/entities/OrgUnit.ts +++ b/src/domain/entities/OrgUnit.ts @@ -1,4 +1,4 @@ -import { Id, Ref } from "./ReferenceObject"; +import { Id } from "./ReferenceObject"; export interface OrgUnit { id: Id; @@ -6,7 +6,3 @@ export interface OrgUnit { name: string; level: number; } - -export function buildOrgUnitsParameter(orgUnitsIds: Ref[]): string { - return orgUnitsIds.map(({ id }) => id).join(";"); -} diff --git a/src/utils/d2-api.ts b/src/utils/d2-api.ts index d9eec508..992baaab 100644 --- a/src/utils/d2-api.ts +++ b/src/utils/d2-api.ts @@ -1,6 +1,7 @@ import _ from "lodash"; import { DhisInstance } from "../domain/entities/DhisInstance"; import { D2Api } from "../types/d2-api"; +import { memoizeAsync } from "./cache"; export function getMajorVersion(version: string): number { const apiVersion = _.get(version.split("."), 1); @@ -15,3 +16,8 @@ export function getD2APiFromInstance(instance: DhisInstance) { backend: "fetch", }); } + +export const getVersion = memoizeAsync(async (api: D2Api): Promise => { + const { version } = await api.system.info.getData(); + return version; +});