From 9a8e80f53bf278c816f97a3276d59bfd33f163d1 Mon Sep 17 00:00:00 2001 From: Volker Schmidt Date: Thu, 25 Jul 2019 13:22:42 +0200 Subject: [PATCH 01/21] Fixes issue with forbidden request when creating tracked entity. --- .../fhir/server/AbstractBatchBundleFhirRestAppTest.java | 6 +++--- .../server/dstu3/Dstu3ProgramStageFhirRestAppTest.java | 7 +++---- .../dstu3/Dstu3TrackedEntityInstanceFhirRestAppTest.java | 5 ++--- .../fhir/server/r4/R4ProgramStageFhirRestAppTest.java | 7 +++---- .../r4/R4TrackedEntityInstanceFhirRestAppTest.java | 5 ++--- ...bstractCarePlanToEnrollmentTransformationAppTest.java | 3 +-- ...ctObservationToProgramStageTransformationAppTest.java | 9 ++++----- ...naireResponseToProgramStageTransformationAppTest.java | 3 +-- ...ientToTrackedEntityInstanceTransformationAppTest.java | 5 ++--- .../dhis/tracker/program/impl/EnrollmentServiceImpl.java | 5 ++--- .../dhis/tracker/program/impl/EventServiceImpl.java | 5 ++--- .../trackedentity/impl/TrackedEntityServiceImpl.java | 4 ++-- .../tracker/program/impl/EnrollmentServiceImplTest.java | 4 ++-- .../dhis/tracker/program/impl/EventServiceImplTest.java | 4 ++-- .../trackedentity/impl/TrackedEntityServiceImplTest.java | 2 +- 15 files changed, 32 insertions(+), 42 deletions(-) diff --git a/app/src/test/java/org/dhis2/fhir/adapter/fhir/server/AbstractBatchBundleFhirRestAppTest.java b/app/src/test/java/org/dhis2/fhir/adapter/fhir/server/AbstractBatchBundleFhirRestAppTest.java index 04cf7e20..97be9aae 100644 --- a/app/src/test/java/org/dhis2/fhir/adapter/fhir/server/AbstractBatchBundleFhirRestAppTest.java +++ b/app/src/test/java/org/dhis2/fhir/adapter/fhir/server/AbstractBatchBundleFhirRestAppTest.java @@ -76,17 +76,17 @@ protected void prepareCreate() throws Exception .andExpect( method( HttpMethod.GET ) ).andExpect( header( "Authorization", "Basic Zmhpcl9jbGllbnQ6Zmhpcl9jbGllbnRfMQ==" ) ) .andRespond( withSuccess( IOUtils.resourceToString( "/org/dhis2/fhir/adapter/dhis/test/default-tei-empty.json", StandardCharsets.UTF_8 ), MediaType.APPLICATION_JSON ) ); - userDhis2Server.expect( ExpectedCount.once(), requestTo( matchesPattern( dhis2BaseUrl + "/api/" + dhis2ApiVersion + "/trackedEntityInstances.json?strategy=CREATE" ) ) ).andExpect( method( HttpMethod.POST ) ) + userDhis2Server.expect( ExpectedCount.once(), requestTo( dhis2BaseUrl + "/api/" + dhis2ApiVersion + "/trackedEntityInstances.json?strategy=CREATE" ) ).andExpect( method( HttpMethod.POST ) ) .andExpect( header( "Authorization", "Basic Zmhpcl9jbGllbnQ6Zmhpcl9jbGllbnRfMQ==" ) ).andExpect( content().contentTypeCompatibleWith( MediaType.APPLICATION_JSON ) ) .andExpect( content().json( IOUtils.resourceToString( "/org/dhis2/fhir/adapter/dhis/test/default-bundle-tei-create.json", StandardCharsets.UTF_8 ) ) ) .andRespond( withSuccess( IOUtils.resourceToString( "/org/dhis2/fhir/adapter/dhis/test/default-bundle-tei-create-response.json", StandardCharsets.UTF_8 ), MediaType.APPLICATION_JSON ) .headers( createDefaultHeaders() ) ); - userDhis2Server.expect( ExpectedCount.once(), requestTo( matchesPattern( dhis2BaseUrl + "/api/" + dhis2ApiVersion + "/enrollments.json?strategy=CREATE" ) ) ).andExpect( method( HttpMethod.POST ) ) + userDhis2Server.expect( ExpectedCount.once(), requestTo( dhis2BaseUrl + "/api/" + dhis2ApiVersion + "/enrollments.json?strategy=CREATE" ) ).andExpect( method( HttpMethod.POST ) ) .andExpect( header( "Authorization", "Basic Zmhpcl9jbGllbnQ6Zmhpcl9jbGllbnRfMQ==" ) ).andExpect( content().contentTypeCompatibleWith( MediaType.APPLICATION_JSON ) ) .andExpect( content().json( IOUtils.resourceToString( "/org/dhis2/fhir/adapter/dhis/test/default-bundle-enrollment-create.json", StandardCharsets.UTF_8 ) ) ) .andRespond( withSuccess( IOUtils.resourceToString( "/org/dhis2/fhir/adapter/dhis/test/default-bundle-enrollment-create-response.json", StandardCharsets.UTF_8 ), MediaType.APPLICATION_JSON ) .headers( createDefaultHeaders() ) ); - userDhis2Server.expect( ExpectedCount.once(), requestTo( matchesPattern( dhis2BaseUrl + "/api/" + dhis2ApiVersion + "/events.json?strategy=CREATE" ) ) ).andExpect( method( HttpMethod.POST ) ) + userDhis2Server.expect( ExpectedCount.once(), requestTo( dhis2BaseUrl + "/api/" + dhis2ApiVersion + "/events.json?strategy=CREATE" ) ).andExpect( method( HttpMethod.POST ) ) .andExpect( header( "Authorization", "Basic Zmhpcl9jbGllbnQ6Zmhpcl9jbGllbnRfMQ==" ) ).andExpect( content().contentTypeCompatibleWith( MediaType.APPLICATION_JSON ) ) .andExpect( content().json( IOUtils.resourceToString( "/org/dhis2/fhir/adapter/dhis/test/default-bundle-event-create.json", StandardCharsets.UTF_8 ) ) ) .andRespond( withSuccess( IOUtils.resourceToString( "/org/dhis2/fhir/adapter/dhis/test/default-bundle-event-create-response.json", StandardCharsets.UTF_8 ), MediaType.APPLICATION_JSON ) diff --git a/app/src/test/java/org/dhis2/fhir/adapter/fhir/server/dstu3/Dstu3ProgramStageFhirRestAppTest.java b/app/src/test/java/org/dhis2/fhir/adapter/fhir/server/dstu3/Dstu3ProgramStageFhirRestAppTest.java index b5946776..53e98feb 100644 --- a/app/src/test/java/org/dhis2/fhir/adapter/fhir/server/dstu3/Dstu3ProgramStageFhirRestAppTest.java +++ b/app/src/test/java/org/dhis2/fhir/adapter/fhir/server/dstu3/Dstu3ProgramStageFhirRestAppTest.java @@ -51,7 +51,6 @@ import javax.annotation.Nonnull; import java.nio.charset.StandardCharsets; -import static org.dhis2.fhir.adapter.test.PatternMatcher.matchesPattern; import static org.springframework.test.web.client.match.MockRestRequestMatchers.*; import static org.springframework.test.web.client.response.MockRestResponseCreators.withStatus; import static org.springframework.test.web.client.response.MockRestResponseCreators.withSuccess; @@ -284,7 +283,7 @@ public void createObservation() throws Exception systemDhis2Server.expect( ExpectedCount.between( 0, 1 ), method( HttpMethod.GET ) ).andExpect( header( "Authorization", testConfiguration.getDhis2SystemAuthorization() ) ) .andExpect( requestTo( dhis2BaseUrl + "/api/" + dhis2ApiVersion + "/organisationUnits/ldXIdLNUNEp.json?fields=lastUpdated,id,code,name,shortName,displayName,level,openingDate,closedDate,coordinates,leaf,parent%5Bid%5D" ) ) .andRespond( withSuccess( IOUtils.resourceToString( "/org/dhis2/fhir/adapter/dhis/test/single-org-unit-OU_4567.json", StandardCharsets.UTF_8 ), MediaType.APPLICATION_JSON ) ); - userDhis2Server.expect( ExpectedCount.once(), requestTo( matchesPattern( dhis2BaseUrl + "/api/" + dhis2ApiVersion + "/enrollments/*.json?importStrategy=CREATE" ) ) ).andExpect( method( HttpMethod.PUT ) ) + userDhis2Server.expect( ExpectedCount.once(), requestTo( dhis2BaseUrl + "/api/" + dhis2ApiVersion + "/enrollments.json?strategy=CREATE" ) ).andExpect( method( HttpMethod.POST ) ) .andExpect( header( "Authorization", "Basic Zmhpcl9jbGllbnQ6Zmhpcl9jbGllbnRfMQ==" ) ) .andExpect( content().contentTypeCompatibleWith( MediaType.APPLICATION_JSON ) ) .andExpect( content().json( IOUtils.resourceToString( "/org/dhis2/fhir/adapter/dhis/test/default-enrollment-70-create.json", StandardCharsets.UTF_8 ) ) ) @@ -318,7 +317,7 @@ public void createObservationByCodeSetCode() throws Exception systemDhis2Server.expect( ExpectedCount.between( 0, 1 ), method( HttpMethod.GET ) ).andExpect( header( "Authorization", testConfiguration.getDhis2SystemAuthorization() ) ) .andExpect( requestTo( dhis2BaseUrl + "/api/" + dhis2ApiVersion + "/organisationUnits/ldXIdLNUNEp.json?fields=lastUpdated,id,code,name,shortName,displayName,level,openingDate,closedDate,coordinates,leaf,parent%5Bid%5D" ) ) .andRespond( withSuccess( IOUtils.resourceToString( "/org/dhis2/fhir/adapter/dhis/test/single-org-unit-OU_4567.json", StandardCharsets.UTF_8 ), MediaType.APPLICATION_JSON ) ); - userDhis2Server.expect( ExpectedCount.once(), requestTo( matchesPattern( dhis2BaseUrl + "/api/" + dhis2ApiVersion + "/enrollments/*.json?importStrategy=CREATE" ) ) ).andExpect( method( HttpMethod.PUT ) ) + userDhis2Server.expect( ExpectedCount.once(), requestTo( dhis2BaseUrl + "/api/" + dhis2ApiVersion + "/enrollments.json?strategy=CREATE" ) ).andExpect( method( HttpMethod.POST ) ) .andExpect( header( "Authorization", "Basic Zmhpcl9jbGllbnQ6Zmhpcl9jbGllbnRfMQ==" ) ) .andExpect( content().contentTypeCompatibleWith( MediaType.APPLICATION_JSON ) ) .andExpect( content().json( IOUtils.resourceToString( "/org/dhis2/fhir/adapter/dhis/test/default-enrollment-70-create.json", StandardCharsets.UTF_8 ) ) ) @@ -352,7 +351,7 @@ public void createObservationIdentifierReference() throws Exception systemDhis2Server.expect( ExpectedCount.between( 0, 1 ), method( HttpMethod.GET ) ).andExpect( header( "Authorization", testConfiguration.getDhis2SystemAuthorization() ) ) .andExpect( requestTo( dhis2BaseUrl + "/api/" + dhis2ApiVersion + "/organisationUnits/ldXIdLNUNEp.json?fields=lastUpdated,id,code,name,shortName,displayName,level,openingDate,closedDate,coordinates,leaf,parent%5Bid%5D" ) ) .andRespond( withSuccess( IOUtils.resourceToString( "/org/dhis2/fhir/adapter/dhis/test/single-org-unit-OU_4567.json", StandardCharsets.UTF_8 ), MediaType.APPLICATION_JSON ) ); - userDhis2Server.expect( ExpectedCount.once(), requestTo( matchesPattern( dhis2BaseUrl + "/api/" + dhis2ApiVersion + "/enrollments/*.json?importStrategy=CREATE" ) ) ).andExpect( method( HttpMethod.PUT ) ) + userDhis2Server.expect( ExpectedCount.once(), requestTo( dhis2BaseUrl + "/api/" + dhis2ApiVersion + "/enrollments.json?strategy=CREATE" ) ).andExpect( method( HttpMethod.POST ) ) .andExpect( header( "Authorization", "Basic Zmhpcl9jbGllbnQ6Zmhpcl9jbGllbnRfMQ==" ) ) .andExpect( content().contentTypeCompatibleWith( MediaType.APPLICATION_JSON ) ) .andExpect( content().json( IOUtils.resourceToString( "/org/dhis2/fhir/adapter/dhis/test/default-enrollment-70-create.json", StandardCharsets.UTF_8 ) ) ) diff --git a/app/src/test/java/org/dhis2/fhir/adapter/fhir/server/dstu3/Dstu3TrackedEntityInstanceFhirRestAppTest.java b/app/src/test/java/org/dhis2/fhir/adapter/fhir/server/dstu3/Dstu3TrackedEntityInstanceFhirRestAppTest.java index ecc49753..454b20f0 100644 --- a/app/src/test/java/org/dhis2/fhir/adapter/fhir/server/dstu3/Dstu3TrackedEntityInstanceFhirRestAppTest.java +++ b/app/src/test/java/org/dhis2/fhir/adapter/fhir/server/dstu3/Dstu3TrackedEntityInstanceFhirRestAppTest.java @@ -50,7 +50,6 @@ import java.nio.charset.StandardCharsets; import java.util.ArrayList; -import static org.dhis2.fhir.adapter.test.PatternMatcher.matchesPattern; import static org.springframework.test.web.client.match.MockRestRequestMatchers.*; import static org.springframework.test.web.client.response.MockRestResponseCreators.withStatus; import static org.springframework.test.web.client.response.MockRestResponseCreators.withSuccess; @@ -315,7 +314,7 @@ public void createPatientWithoutAuthorization() throws Exception public void createPatientInvalidAuthorization() throws Exception { expectTrackedEntityMetadataRequests(); - userDhis2Server.expect( ExpectedCount.once(), requestTo( matchesPattern( dhis2BaseUrl + "/api/" + dhis2ApiVersion + "/trackedEntityInstances/*.json?strategy=CREATE" ) ) ) + userDhis2Server.expect( ExpectedCount.once(), requestTo( dhis2BaseUrl + "/api/" + dhis2ApiVersion + "/trackedEntityInstances.json?strategy=CREATE" ) ) .andExpect( method( HttpMethod.POST ) ) .andExpect( header( "Authorization", "Basic Zmhpcl9jbGllbnQ6aW52YWxpZF8x" ) ).andExpect( content().contentTypeCompatibleWith( MediaType.APPLICATION_JSON ) ) .andExpect( content().json( IOUtils.resourceToString( "/org/dhis2/fhir/adapter/dhis/test/default-tei-15-create.json", StandardCharsets.UTF_8 ) ) ) @@ -336,7 +335,7 @@ public void createPatientInvalidAuthorization() throws Exception public void createPatient() throws Exception { expectTrackedEntityMetadataRequests(); - userDhis2Server.expect( ExpectedCount.once(), requestTo( matchesPattern( dhis2BaseUrl + "/api/" + dhis2ApiVersion + "/trackedEntityInstances/*.json?strategy=CREATE" ) ) ).andExpect( method( HttpMethod.POST ) ) + userDhis2Server.expect( ExpectedCount.once(), requestTo( dhis2BaseUrl + "/api/" + dhis2ApiVersion + "/trackedEntityInstances.json?strategy=CREATE" ) ).andExpect( method( HttpMethod.POST ) ) .andExpect( header( "Authorization", "Basic Zmhpcl9jbGllbnQ6Zmhpcl9jbGllbnRfMQ==" ) ).andExpect( content().contentTypeCompatibleWith( MediaType.APPLICATION_JSON ) ) .andExpect( content().json( IOUtils.resourceToString( "/org/dhis2/fhir/adapter/dhis/test/default-tei-15-create.json", StandardCharsets.UTF_8 ) ) ) .andRespond( withSuccess( IOUtils.resourceToString( "/org/dhis2/fhir/adapter/dhis/test/default-tei-15-create-response.json", StandardCharsets.UTF_8 ), MediaType.APPLICATION_JSON ) diff --git a/app/src/test/java/org/dhis2/fhir/adapter/fhir/server/r4/R4ProgramStageFhirRestAppTest.java b/app/src/test/java/org/dhis2/fhir/adapter/fhir/server/r4/R4ProgramStageFhirRestAppTest.java index 308f7a25..953b9efa 100644 --- a/app/src/test/java/org/dhis2/fhir/adapter/fhir/server/r4/R4ProgramStageFhirRestAppTest.java +++ b/app/src/test/java/org/dhis2/fhir/adapter/fhir/server/r4/R4ProgramStageFhirRestAppTest.java @@ -51,7 +51,6 @@ import javax.annotation.Nonnull; import java.nio.charset.StandardCharsets; -import static org.dhis2.fhir.adapter.test.PatternMatcher.matchesPattern; import static org.springframework.test.web.client.match.MockRestRequestMatchers.*; import static org.springframework.test.web.client.response.MockRestResponseCreators.withStatus; import static org.springframework.test.web.client.response.MockRestResponseCreators.withSuccess; @@ -317,7 +316,7 @@ public void createObservation() throws Exception systemDhis2Server.expect( ExpectedCount.between( 0, 1 ), method( HttpMethod.GET ) ).andExpect( header( "Authorization", testConfiguration.getDhis2SystemAuthorization() ) ) .andExpect( requestTo( dhis2BaseUrl + "/api/" + dhis2ApiVersion + "/organisationUnits/ldXIdLNUNEp.json?fields=lastUpdated,id,code,name,shortName,displayName,level,openingDate,closedDate,coordinates,leaf,parent%5Bid%5D" ) ) .andRespond( withSuccess( IOUtils.resourceToString( "/org/dhis2/fhir/adapter/dhis/test/single-org-unit-OU_4567.json", StandardCharsets.UTF_8 ), MediaType.APPLICATION_JSON ) ); - userDhis2Server.expect( ExpectedCount.once(), requestTo( matchesPattern( dhis2BaseUrl + "/api/" + dhis2ApiVersion + "/enrollments/*.json?importStrategy=CREATE" ) ) ).andExpect( method( HttpMethod.PUT ) ) + userDhis2Server.expect( ExpectedCount.once(), requestTo( dhis2BaseUrl + "/api/" + dhis2ApiVersion + "/enrollments.json?strategy=CREATE" ) ).andExpect( method( HttpMethod.POST ) ) .andExpect( header( "Authorization", "Basic Zmhpcl9jbGllbnQ6Zmhpcl9jbGllbnRfMQ==" ) ) .andExpect( content().contentTypeCompatibleWith( MediaType.APPLICATION_JSON ) ) .andExpect( content().json( IOUtils.resourceToString( "/org/dhis2/fhir/adapter/dhis/test/default-enrollment-70-create.json", StandardCharsets.UTF_8 ) ) ) @@ -351,7 +350,7 @@ public void createObservationCodeSetCode() throws Exception systemDhis2Server.expect( ExpectedCount.between( 0, 1 ), method( HttpMethod.GET ) ).andExpect( header( "Authorization", testConfiguration.getDhis2SystemAuthorization() ) ) .andExpect( requestTo( dhis2BaseUrl + "/api/" + dhis2ApiVersion + "/organisationUnits/ldXIdLNUNEp.json?fields=lastUpdated,id,code,name,shortName,displayName,level,openingDate,closedDate,coordinates,leaf,parent%5Bid%5D" ) ) .andRespond( withSuccess( IOUtils.resourceToString( "/org/dhis2/fhir/adapter/dhis/test/single-org-unit-OU_4567.json", StandardCharsets.UTF_8 ), MediaType.APPLICATION_JSON ) ); - userDhis2Server.expect( ExpectedCount.once(), requestTo( matchesPattern( dhis2BaseUrl + "/api/" + dhis2ApiVersion + "/enrollments/*.json?importStrategy=CREATE" ) ) ).andExpect( method( HttpMethod.PUT ) ) + userDhis2Server.expect( ExpectedCount.once(), requestTo( dhis2BaseUrl + "/api/" + dhis2ApiVersion + "/enrollments.json?strategy=CREATE" ) ).andExpect( method( HttpMethod.POST ) ) .andExpect( header( "Authorization", "Basic Zmhpcl9jbGllbnQ6Zmhpcl9jbGllbnRfMQ==" ) ) .andExpect( content().contentTypeCompatibleWith( MediaType.APPLICATION_JSON ) ) .andExpect( content().json( IOUtils.resourceToString( "/org/dhis2/fhir/adapter/dhis/test/default-enrollment-70-create.json", StandardCharsets.UTF_8 ) ) ) @@ -385,7 +384,7 @@ public void createObservationIdentifierReference() throws Exception systemDhis2Server.expect( ExpectedCount.between( 0, 1 ), method( HttpMethod.GET ) ).andExpect( header( "Authorization", testConfiguration.getDhis2SystemAuthorization() ) ) .andExpect( requestTo( dhis2BaseUrl + "/api/" + dhis2ApiVersion + "/organisationUnits/ldXIdLNUNEp.json?fields=lastUpdated,id,code,name,shortName,displayName,level,openingDate,closedDate,coordinates,leaf,parent%5Bid%5D" ) ) .andRespond( withSuccess( IOUtils.resourceToString( "/org/dhis2/fhir/adapter/dhis/test/single-org-unit-OU_4567.json", StandardCharsets.UTF_8 ), MediaType.APPLICATION_JSON ) ); - userDhis2Server.expect( ExpectedCount.once(), requestTo( matchesPattern( dhis2BaseUrl + "/api/" + dhis2ApiVersion + "/enrollments/*.json?importStrategy=CREATE" ) ) ).andExpect( method( HttpMethod.PUT ) ) + userDhis2Server.expect( ExpectedCount.once(), requestTo( dhis2BaseUrl + "/api/" + dhis2ApiVersion + "/enrollments.json?strategy=CREATE" ) ).andExpect( method( HttpMethod.POST ) ) .andExpect( header( "Authorization", "Basic Zmhpcl9jbGllbnQ6Zmhpcl9jbGllbnRfMQ==" ) ) .andExpect( content().contentTypeCompatibleWith( MediaType.APPLICATION_JSON ) ) .andExpect( content().json( IOUtils.resourceToString( "/org/dhis2/fhir/adapter/dhis/test/default-enrollment-70-create.json", StandardCharsets.UTF_8 ) ) ) diff --git a/app/src/test/java/org/dhis2/fhir/adapter/fhir/server/r4/R4TrackedEntityInstanceFhirRestAppTest.java b/app/src/test/java/org/dhis2/fhir/adapter/fhir/server/r4/R4TrackedEntityInstanceFhirRestAppTest.java index 236f2580..04cc4bdf 100644 --- a/app/src/test/java/org/dhis2/fhir/adapter/fhir/server/r4/R4TrackedEntityInstanceFhirRestAppTest.java +++ b/app/src/test/java/org/dhis2/fhir/adapter/fhir/server/r4/R4TrackedEntityInstanceFhirRestAppTest.java @@ -50,7 +50,6 @@ import java.nio.charset.StandardCharsets; import java.util.ArrayList; -import static org.dhis2.fhir.adapter.test.PatternMatcher.matchesPattern; import static org.springframework.test.web.client.match.MockRestRequestMatchers.*; import static org.springframework.test.web.client.response.MockRestResponseCreators.withStatus; import static org.springframework.test.web.client.response.MockRestResponseCreators.withSuccess; @@ -315,7 +314,7 @@ public void createPatientWithoutAuthorization() throws Exception public void createPatientInvalidAuthorization() throws Exception { expectTrackedEntityMetadataRequests(); - userDhis2Server.expect( ExpectedCount.once(), requestTo( matchesPattern( dhis2BaseUrl + "/api/" + dhis2ApiVersion + "/trackedEntityInstances/*.json?strategy=CREATE" ) ) ).andExpect( method( HttpMethod.POST ) ) + userDhis2Server.expect( ExpectedCount.once(), requestTo( dhis2BaseUrl + "/api/" + dhis2ApiVersion + "/trackedEntityInstances.json?strategy=CREATE" ) ).andExpect( method( HttpMethod.POST ) ) .andExpect( header( "Authorization", "Basic Zmhpcl9jbGllbnQ6aW52YWxpZF8x" ) ).andExpect( content().contentTypeCompatibleWith( MediaType.APPLICATION_JSON ) ) .andExpect( content().json( IOUtils.resourceToString( "/org/dhis2/fhir/adapter/dhis/test/default-tei-15-create.json", StandardCharsets.UTF_8 ) ) ) .andRespond( withStatus( HttpStatus.UNAUTHORIZED ) ); @@ -335,7 +334,7 @@ public void createPatientInvalidAuthorization() throws Exception public void createPatient() throws Exception { expectTrackedEntityMetadataRequests(); - userDhis2Server.expect( ExpectedCount.once(), requestTo( matchesPattern( dhis2BaseUrl + "/api/" + dhis2ApiVersion + "/trackedEntityInstances/*.json?strategy=CREATE" ) ) ).andExpect( method( HttpMethod.POST ) ) + userDhis2Server.expect( ExpectedCount.once(), requestTo( dhis2BaseUrl + "/api/" + dhis2ApiVersion + "/trackedEntityInstances.json?strategy=CREATE" ) ).andExpect( method( HttpMethod.POST ) ) .andExpect( header( "Authorization", "Basic Zmhpcl9jbGllbnQ6Zmhpcl9jbGllbnRfMQ==" ) ).andExpect( content().contentTypeCompatibleWith( MediaType.APPLICATION_JSON ) ) .andExpect( content().json( IOUtils.resourceToString( "/org/dhis2/fhir/adapter/dhis/test/default-tei-15-create.json", StandardCharsets.UTF_8 ) ) ) .andRespond( withSuccess( IOUtils.resourceToString( "/org/dhis2/fhir/adapter/dhis/test/default-tei-15-create-response.json", StandardCharsets.UTF_8 ), MediaType.APPLICATION_JSON ) diff --git a/app/src/test/java/org/dhis2/fhir/adapter/fhir/transform/fhir/impl/enrollment/AbstractCarePlanToEnrollmentTransformationAppTest.java b/app/src/test/java/org/dhis2/fhir/adapter/fhir/transform/fhir/impl/enrollment/AbstractCarePlanToEnrollmentTransformationAppTest.java index da42800d..8e241f39 100644 --- a/app/src/test/java/org/dhis2/fhir/adapter/fhir/transform/fhir/impl/enrollment/AbstractCarePlanToEnrollmentTransformationAppTest.java +++ b/app/src/test/java/org/dhis2/fhir/adapter/fhir/transform/fhir/impl/enrollment/AbstractCarePlanToEnrollmentTransformationAppTest.java @@ -40,7 +40,6 @@ import static com.github.tomakehurst.wiremock.client.WireMock.aResponse; import static com.github.tomakehurst.wiremock.client.WireMock.urlPathEqualTo; -import static org.dhis2.fhir.adapter.test.PatternMatcher.matchesPattern; import static org.springframework.test.web.client.match.MockRestRequestMatchers.*; import static org.springframework.test.web.client.response.MockRestResponseCreators.withSuccess; @@ -78,7 +77,7 @@ public void createEnrollment() throws Exception userDhis2Server.expect( ExpectedCount.once(), requestTo( dhis2BaseUrl + "/api/" + dhis2ApiVersion + "/enrollments.json?program=EPDyQuoRnXk&programStatus=ACTIVE&trackedEntityInstance=JeR2Ul4mZfx&ouMode=ACCESSIBLE&fields=:all&" + "order=lastUpdated:desc&pageSize=1" ) ).andExpect( method( HttpMethod.GET ) ).andExpect( header( "Authorization", testConfiguration.getDhis2UserAuthorization() ) ) .andRespond( withSuccess( IOUtils.resourceToString( "/org/dhis2/fhir/adapter/dhis/test/default-enrollment-empty.json", StandardCharsets.UTF_8 ), MediaType.APPLICATION_JSON ) ); - userDhis2Server.expect( ExpectedCount.once(), requestTo( matchesPattern( dhis2BaseUrl + "/api/" + dhis2ApiVersion + "/enrollments/*.json?importStrategy=CREATE" ) ) ).andExpect( method( HttpMethod.PUT ) ) + userDhis2Server.expect( ExpectedCount.once(), requestTo( dhis2BaseUrl + "/api/" + dhis2ApiVersion + "/enrollments.json?strategy=CREATE" ) ).andExpect( method( HttpMethod.POST ) ) .andExpect( header( "Authorization", testConfiguration.getDhis2UserAuthorization() ) ) .andExpect( content().contentTypeCompatibleWith( MediaType.APPLICATION_JSON ) ) .andExpect( content().json( IOUtils.resourceToString( "/org/dhis2/fhir/adapter/dhis/test/default-enrollment-90-create.json", StandardCharsets.UTF_8 ) ) ) diff --git a/app/src/test/java/org/dhis2/fhir/adapter/fhir/transform/fhir/impl/programstage/AbstractObservationToProgramStageTransformationAppTest.java b/app/src/test/java/org/dhis2/fhir/adapter/fhir/transform/fhir/impl/programstage/AbstractObservationToProgramStageTransformationAppTest.java index ebea222c..2be790af 100644 --- a/app/src/test/java/org/dhis2/fhir/adapter/fhir/transform/fhir/impl/programstage/AbstractObservationToProgramStageTransformationAppTest.java +++ b/app/src/test/java/org/dhis2/fhir/adapter/fhir/transform/fhir/impl/programstage/AbstractObservationToProgramStageTransformationAppTest.java @@ -40,7 +40,6 @@ import static com.github.tomakehurst.wiremock.client.WireMock.aResponse; import static com.github.tomakehurst.wiremock.client.WireMock.urlPathEqualTo; -import static org.dhis2.fhir.adapter.test.PatternMatcher.matchesPattern; import static org.springframework.test.web.client.match.MockRestRequestMatchers.*; import static org.springframework.test.web.client.response.MockRestResponseCreators.withSuccess; @@ -82,7 +81,7 @@ public void createEnrollment() throws Exception userDhis2Server.expect( ExpectedCount.between( 2, 4 ), requestTo( dhis2BaseUrl + "/api/" + dhis2ApiVersion + "/enrollments.json?program=EPDyQuoRnXk&programStatus=ACTIVE&trackedEntityInstance=JeR2Ul4mZfx&ouMode=ACCESSIBLE&fields=:all&" + "order=lastUpdated:desc&pageSize=1" ) ).andExpect( method( HttpMethod.GET ) ).andExpect( header( "Authorization", testConfiguration.getDhis2UserAuthorization() ) ) .andRespond( withSuccess( IOUtils.resourceToString( "/org/dhis2/fhir/adapter/dhis/test/default-enrollment-empty.json", StandardCharsets.UTF_8 ), MediaType.APPLICATION_JSON ) ); - userDhis2Server.expect( ExpectedCount.once(), requestTo( matchesPattern( dhis2BaseUrl + "/api/" + dhis2ApiVersion + "/enrollments/*.json?importStrategy=CREATE" ) ) ).andExpect( method( HttpMethod.PUT ) ) + userDhis2Server.expect( ExpectedCount.once(), requestTo( dhis2BaseUrl + "/api/" + dhis2ApiVersion + "/enrollments.json?strategy=CREATE" ) ).andExpect( method( HttpMethod.POST ) ) .andExpect( header( "Authorization", testConfiguration.getDhis2UserAuthorization() ) ) .andExpect( content().contentTypeCompatibleWith( MediaType.APPLICATION_JSON ) ) .andExpect( content().json( IOUtils.resourceToString( "/org/dhis2/fhir/adapter/dhis/test/default-enrollment-70-create.json", StandardCharsets.UTF_8 ) ) ) @@ -131,7 +130,7 @@ public void createEnrollmentMissingTei() throws Exception .andExpect( requestTo( dhis2BaseUrl + "/api/" + dhis2ApiVersion + "/organisationUnits/ldXIdLNUNEn.json?fields=lastUpdated,id,code,name,shortName,displayName,level,openingDate,closedDate,coordinates,leaf,parent%5Bid%5D" ) ) .andRespond( withSuccess( IOUtils.resourceToString( "/org/dhis2/fhir/adapter/dhis/test/default-org-unit-OU_1234.json", StandardCharsets.UTF_8 ), MediaType.APPLICATION_JSON ) ); - userDhis2Server.expect( ExpectedCount.once(), requestTo( matchesPattern( dhis2BaseUrl + "/api/" + dhis2ApiVersion + "/trackedEntityInstances/*.json?strategy=CREATE" ) ) ) + userDhis2Server.expect( ExpectedCount.once(), requestTo( dhis2BaseUrl + "/api/" + dhis2ApiVersion + "/trackedEntityInstances.json?strategy=CREATE" ) ) .andExpect( header( "Authorization", testConfiguration.getDhis2UserAuthorization() ) ).andExpect( method( HttpMethod.POST ) ) .andExpect( content().contentTypeCompatibleWith( MediaType.APPLICATION_JSON ) ) .andExpect( content().json( IOUtils.resourceToString( "/org/dhis2/fhir/adapter/dhis/test/default-tei-15-create.json", StandardCharsets.UTF_8 ) ) ) @@ -146,7 +145,7 @@ public void createEnrollmentMissingTei() throws Exception userDhis2Server.expect( ExpectedCount.between( 2, 4 ), requestTo( dhis2BaseUrl + "/api/" + dhis2ApiVersion + "/enrollments.json?program=EPDyQuoRnXk&programStatus=ACTIVE&trackedEntityInstance=JeR2Ul4mZfx&ouMode=ACCESSIBLE&fields=:all&" + "order=lastUpdated:desc&pageSize=1" ) ).andExpect( method( HttpMethod.GET ) ).andExpect( header( "Authorization", testConfiguration.getDhis2UserAuthorization() ) ) .andRespond( withSuccess( IOUtils.resourceToString( "/org/dhis2/fhir/adapter/dhis/test/default-enrollment-empty.json", StandardCharsets.UTF_8 ), MediaType.APPLICATION_JSON ) ); - userDhis2Server.expect( ExpectedCount.once(), requestTo( matchesPattern( dhis2BaseUrl + "/api/" + dhis2ApiVersion + "/enrollments/*.json?importStrategy=CREATE" ) ) ).andExpect( method( HttpMethod.PUT ) ) + userDhis2Server.expect( ExpectedCount.once(), requestTo( dhis2BaseUrl + "/api/" + dhis2ApiVersion + "/enrollments.json?strategy=CREATE" ) ).andExpect( method( HttpMethod.POST ) ) .andExpect( header( "Authorization", testConfiguration.getDhis2UserAuthorization() ) ) .andExpect( content().contentTypeCompatibleWith( MediaType.APPLICATION_JSON ) ) .andExpect( content().json( IOUtils.resourceToString( "/org/dhis2/fhir/adapter/dhis/test/default-enrollment-70-create.json", StandardCharsets.UTF_8 ) ) ) @@ -196,7 +195,7 @@ public void createEvent() throws Exception "fields=deleted,event,orgUnit,program,enrollment,trackedEntityInstance,programStage,status,eventDate,dueDate,coordinate,lastUpdated,dataValues%5BdataElement,value,providedElsewhere,lastUpdated,storedBy%5D&skipPaging=true" ) ) .andExpect( method( HttpMethod.GET ) ).andExpect( header( "Authorization", testConfiguration.getDhis2UserAuthorization() ) ) .andRespond( withSuccess( IOUtils.resourceToString( "/org/dhis2/fhir/adapter/dhis/test/default-event-70-get.json", StandardCharsets.UTF_8 ), MediaType.APPLICATION_JSON ) ); - userDhis2Server.expect( ExpectedCount.once(), requestTo( matchesPattern( dhis2BaseUrl + "/api/" + dhis2ApiVersion + "/events/*.json?importStrategy=CREATE" ) ) ).andExpect( method( HttpMethod.PUT ) ) + userDhis2Server.expect( ExpectedCount.once(), requestTo( dhis2BaseUrl + "/api/" + dhis2ApiVersion + "/events.json?strategy=CREATE" ) ).andExpect( method( HttpMethod.POST ) ) .andExpect( header( "Authorization", testConfiguration.getDhis2UserAuthorization() ) ) .andExpect( content().contentTypeCompatibleWith( MediaType.APPLICATION_JSON ) ) .andExpect( content().json( IOUtils.resourceToString( "/org/dhis2/fhir/adapter/dhis/test/default-event-71-create.json", StandardCharsets.UTF_8 ) ) ) diff --git a/app/src/test/java/org/dhis2/fhir/adapter/fhir/transform/fhir/impl/programstage/AbstractQuestionnaireResponseToProgramStageTransformationAppTest.java b/app/src/test/java/org/dhis2/fhir/adapter/fhir/transform/fhir/impl/programstage/AbstractQuestionnaireResponseToProgramStageTransformationAppTest.java index ad47daac..36f6e54f 100644 --- a/app/src/test/java/org/dhis2/fhir/adapter/fhir/transform/fhir/impl/programstage/AbstractQuestionnaireResponseToProgramStageTransformationAppTest.java +++ b/app/src/test/java/org/dhis2/fhir/adapter/fhir/transform/fhir/impl/programstage/AbstractQuestionnaireResponseToProgramStageTransformationAppTest.java @@ -40,7 +40,6 @@ import static com.github.tomakehurst.wiremock.client.WireMock.aResponse; import static com.github.tomakehurst.wiremock.client.WireMock.urlPathEqualTo; -import static org.dhis2.fhir.adapter.test.PatternMatcher.matchesPattern; import static org.springframework.test.web.client.match.MockRestRequestMatchers.*; import static org.springframework.test.web.client.response.MockRestResponseCreators.withSuccess; @@ -79,7 +78,7 @@ public void createEnrollment() throws Exception userDhis2Server.expect( ExpectedCount.between( 2, 4 ), requestTo( dhis2BaseUrl + "/api/" + dhis2ApiVersion + "/enrollments.json?program=EPDyQuoRnXk&programStatus=ACTIVE&trackedEntityInstance=JeR2Ul4mZfx&ouMode=ACCESSIBLE&fields=:all&" + "order=lastUpdated:desc&pageSize=1" ) ).andExpect( method( HttpMethod.GET ) ).andExpect( header( "Authorization", testConfiguration.getDhis2UserAuthorization() ) ) .andRespond( withSuccess( IOUtils.resourceToString( "/org/dhis2/fhir/adapter/dhis/test/default-enrollment-empty.json", StandardCharsets.UTF_8 ), MediaType.APPLICATION_JSON ) ); - userDhis2Server.expect( ExpectedCount.once(), requestTo( matchesPattern( dhis2BaseUrl + "/api/" + dhis2ApiVersion + "/enrollments/*.json?importStrategy=CREATE" ) ) ).andExpect( method( HttpMethod.PUT ) ) + userDhis2Server.expect( ExpectedCount.once(), requestTo( dhis2BaseUrl + "/api/" + dhis2ApiVersion + "/enrollments.json?strategy=CREATE" ) ).andExpect( method( HttpMethod.POST ) ) .andExpect( header( "Authorization", testConfiguration.getDhis2UserAuthorization() ) ) .andExpect( content().contentTypeCompatibleWith( MediaType.APPLICATION_JSON ) ) .andExpect( content().json( IOUtils.resourceToString( "/org/dhis2/fhir/adapter/dhis/test/default-enrollment-91-create.json", StandardCharsets.UTF_8 ) ) ) diff --git a/app/src/test/java/org/dhis2/fhir/adapter/fhir/transform/fhir/impl/trackedentity/AbstractPatientToTrackedEntityInstanceTransformationAppTest.java b/app/src/test/java/org/dhis2/fhir/adapter/fhir/transform/fhir/impl/trackedentity/AbstractPatientToTrackedEntityInstanceTransformationAppTest.java index 15d11641..2868532c 100644 --- a/app/src/test/java/org/dhis2/fhir/adapter/fhir/transform/fhir/impl/trackedentity/AbstractPatientToTrackedEntityInstanceTransformationAppTest.java +++ b/app/src/test/java/org/dhis2/fhir/adapter/fhir/transform/fhir/impl/trackedentity/AbstractPatientToTrackedEntityInstanceTransformationAppTest.java @@ -40,7 +40,6 @@ import static com.github.tomakehurst.wiremock.client.WireMock.aResponse; import static com.github.tomakehurst.wiremock.client.WireMock.urlPathEqualTo; -import static org.dhis2.fhir.adapter.test.PatternMatcher.matchesPattern; import static org.springframework.test.web.client.match.MockRestRequestMatchers.*; import static org.springframework.test.web.client.response.MockRestResponseCreators.withSuccess; @@ -73,7 +72,7 @@ public void createPatient() throws Exception "deleted,trackedEntityInstance,trackedEntityType,orgUnit,coordinates,lastUpdated,attributes%5Battribute,value,lastUpdated,storedBy%5D" ) ) .andExpect( method( HttpMethod.GET ) ).andExpect( header( "Authorization", testConfiguration.getDhis2UserAuthorization() ) ) .andRespond( withSuccess( IOUtils.resourceToString( "/org/dhis2/fhir/adapter/dhis/test/default-tei-empty.json", StandardCharsets.UTF_8 ), MediaType.APPLICATION_JSON ) ); - userDhis2Server.expect( ExpectedCount.once(), requestTo( matchesPattern( dhis2BaseUrl + "/api/" + dhis2ApiVersion + "/trackedEntityInstances/*.json?strategy=CREATE" ) ) ) + userDhis2Server.expect( ExpectedCount.once(), requestTo( dhis2BaseUrl + "/api/" + dhis2ApiVersion + "/trackedEntityInstances.json?strategy=CREATE" ) ) .andExpect( header( "Authorization", testConfiguration.getDhis2UserAuthorization() ) ).andExpect( method( HttpMethod.POST ) ) .andExpect( content().contentTypeCompatibleWith( MediaType.APPLICATION_JSON ) ) .andExpect( content().json( IOUtils.resourceToString( "/org/dhis2/fhir/adapter/dhis/test/default-tei-15-create.json", StandardCharsets.UTF_8 ) ) ) @@ -129,7 +128,7 @@ public void createRelatedPersonFirst() throws Exception "deleted,trackedEntityInstance,trackedEntityType,orgUnit,coordinates,lastUpdated,attributes%5Battribute,value,lastUpdated,storedBy%5D" ) ) .andExpect( method( HttpMethod.GET ) ).andExpect( header( "Authorization", testConfiguration.getDhis2UserAuthorization() ) ) .andRespond( withSuccess( IOUtils.resourceToString( "/org/dhis2/fhir/adapter/dhis/test/default-tei-empty.json", StandardCharsets.UTF_8 ), MediaType.APPLICATION_JSON ) ); - userDhis2Server.expect( ExpectedCount.once(), requestTo( matchesPattern( dhis2BaseUrl + "/api/" + dhis2ApiVersion + "/trackedEntityInstances/*.json?strategy=CREATE" ) ) ) + userDhis2Server.expect( ExpectedCount.once(), requestTo( dhis2BaseUrl + "/api/" + dhis2ApiVersion + "/trackedEntityInstances.json?strategy=CREATE" ) ) .andExpect( header( "Authorization", testConfiguration.getDhis2UserAuthorization() ) ).andExpect( method( HttpMethod.POST ) ) .andExpect( content().contentTypeCompatibleWith( MediaType.APPLICATION_JSON ) ) .andExpect( content().json( IOUtils.resourceToString( "/org/dhis2/fhir/adapter/dhis/test/default-tei-16-create.json", StandardCharsets.UTF_8 ) ) ) diff --git a/dhis/src/main/java/org/dhis2/fhir/adapter/dhis/tracker/program/impl/EnrollmentServiceImpl.java b/dhis/src/main/java/org/dhis2/fhir/adapter/dhis/tracker/program/impl/EnrollmentServiceImpl.java index b7c2977a..1e33b3a5 100644 --- a/dhis/src/main/java/org/dhis2/fhir/adapter/dhis/tracker/program/impl/EnrollmentServiceImpl.java +++ b/dhis/src/main/java/org/dhis2/fhir/adapter/dhis/tracker/program/impl/EnrollmentServiceImpl.java @@ -88,7 +88,7 @@ public class EnrollmentServiceImpl implements EnrollmentService, LocalDhisReposi protected static final String ENROLLMENT_ID_URI = "/enrollments/{id}.json"; - protected static final String ENROLLMENT_CREATE_URI = "/enrollments/{id}.json?importStrategy=CREATE"; + protected static final String ENROLLMENT_CREATE_URI = "/enrollments.json?strategy=CREATE"; protected static final String ENROLLMENT_CREATES_URI = "/enrollments.json?strategy=CREATE"; @@ -229,8 +229,7 @@ protected Enrollment _create( @Nonnull Enrollment enrollment ) try { - response = restTemplate.exchange( ENROLLMENT_CREATE_URI, HttpMethod.PUT, new HttpEntity<>( enrollment ), - ImportSummaryWebMessage.class, enrollment.getId() ); + response = restTemplate.exchange( ENROLLMENT_CREATE_URI, HttpMethod.POST, new HttpEntity<>( enrollment ), ImportSummaryWebMessage.class ); } catch ( HttpClientErrorException e ) { diff --git a/dhis/src/main/java/org/dhis2/fhir/adapter/dhis/tracker/program/impl/EventServiceImpl.java b/dhis/src/main/java/org/dhis2/fhir/adapter/dhis/tracker/program/impl/EventServiceImpl.java index b1f6bea6..fc9b8fc7 100644 --- a/dhis/src/main/java/org/dhis2/fhir/adapter/dhis/tracker/program/impl/EventServiceImpl.java +++ b/dhis/src/main/java/org/dhis2/fhir/adapter/dhis/tracker/program/impl/EventServiceImpl.java @@ -100,7 +100,7 @@ public class EventServiceImpl implements EventService, LocalDhisRepositoryPersis protected static final String FIND_ID_URI = "/events/{id}.json?fields=" + FIELDS; - protected static final String CREATE_URI = ID_URI + "?importStrategy=CREATE"; + protected static final String CREATE_URI = "/events.json?strategy=CREATE"; protected static final String CREATES_URI = "/events.json?strategy=CREATE"; @@ -382,8 +382,7 @@ protected Event create( @Nonnull Event event ) try { - response = restTemplate.exchange( CREATE_URI, HttpMethod.PUT, new HttpEntity<>( event ), - ImportSummaryWebMessage.class, event.getId() ); + response = restTemplate.exchange( CREATE_URI, HttpMethod.POST, new HttpEntity<>( event ), ImportSummaryWebMessage.class ); } catch ( HttpClientErrorException e ) { diff --git a/dhis/src/main/java/org/dhis2/fhir/adapter/dhis/tracker/trackedentity/impl/TrackedEntityServiceImpl.java b/dhis/src/main/java/org/dhis2/fhir/adapter/dhis/tracker/trackedentity/impl/TrackedEntityServiceImpl.java index 184d2674..e989cd56 100644 --- a/dhis/src/main/java/org/dhis2/fhir/adapter/dhis/tracker/trackedentity/impl/TrackedEntityServiceImpl.java +++ b/dhis/src/main/java/org/dhis2/fhir/adapter/dhis/tracker/trackedentity/impl/TrackedEntityServiceImpl.java @@ -111,7 +111,7 @@ public class TrackedEntityServiceImpl implements TrackedEntityService, LocalDhis protected static final String GENERATE_URI = "/trackedEntityAttributes/{attributeId}/generate.json"; - protected static final String CREATE_URI = "/trackedEntityInstances/{id}.json?strategy=CREATE"; + protected static final String CREATE_URI = "/trackedEntityInstances.json?strategy=CREATE"; protected static final String CREATES_URI = "/trackedEntityInstances.json?strategy=CREATE"; @@ -467,7 +467,7 @@ protected TrackedEntityInstance create( @Nonnull TrackedEntityInstance trackedEn try { clear( trackedEntityInstance ); - response = restTemplate.postForEntity( CREATE_URI, trackedEntityInstance, ImportSummaryWebMessage.class, trackedEntityInstance.getId() ); + response = restTemplate.exchange( CREATE_URI, HttpMethod.POST, new HttpEntity<>( trackedEntityInstance ), ImportSummaryWebMessage.class ); } catch ( HttpClientErrorException e ) { diff --git a/dhis/src/test/java/org/dhis2/fhir/adapter/dhis/tracker/program/impl/EnrollmentServiceImplTest.java b/dhis/src/test/java/org/dhis2/fhir/adapter/dhis/tracker/program/impl/EnrollmentServiceImplTest.java index 4b277d62..f0b149e7 100644 --- a/dhis/src/test/java/org/dhis2/fhir/adapter/dhis/tracker/program/impl/EnrollmentServiceImplTest.java +++ b/dhis/src/test/java/org/dhis2/fhir/adapter/dhis/tracker/program/impl/EnrollmentServiceImplTest.java @@ -260,10 +260,10 @@ public void findOneByIdNotFound() @Test public void create() throws IOException { - mockServer.expect( ExpectedCount.once(), requestTo( "http://localhost:8080/api/enrollments/Jskdsjeua1s.json?importStrategy=CREATE" ) ) + mockServer.expect( ExpectedCount.once(), requestTo( "http://localhost:8080/api/enrollments.json?strategy=CREATE" ) ) .andExpect( content().contentTypeCompatibleWith( MediaType.APPLICATION_JSON ) ) .andExpect( content().json( IOUtils.resourceToString( "/org/dhis2/fhir/adapter/dhis/tracker/program/impl/createEnrollment.json", StandardCharsets.UTF_8 ) ) ) - .andExpect( method( HttpMethod.PUT ) ).andRespond( withSuccess( IOUtils.resourceToByteArray( "/org/dhis2/fhir/adapter/dhis/tracker/program/impl/createEnrollment-response.json" ), MediaType.APPLICATION_JSON ) ); + .andExpect( method( HttpMethod.POST ) ).andRespond( withSuccess( IOUtils.resourceToByteArray( "/org/dhis2/fhir/adapter/dhis/tracker/program/impl/createEnrollment-response.json" ), MediaType.APPLICATION_JSON ) ); final WritableDataValue dataValue1 = new WritableDataValue(); dataValue1.setDataElementId( "dsf84sfsdf" ); diff --git a/dhis/src/test/java/org/dhis2/fhir/adapter/dhis/tracker/program/impl/EventServiceImplTest.java b/dhis/src/test/java/org/dhis2/fhir/adapter/dhis/tracker/program/impl/EventServiceImplTest.java index 673f01a7..ab114dd4 100644 --- a/dhis/src/test/java/org/dhis2/fhir/adapter/dhis/tracker/program/impl/EventServiceImplTest.java +++ b/dhis/src/test/java/org/dhis2/fhir/adapter/dhis/tracker/program/impl/EventServiceImplTest.java @@ -263,10 +263,10 @@ public void findOneByIdNotFound() @Test public void create() throws IOException { - mockServer.expect( ExpectedCount.once(), requestTo( "http://localhost:8080/api/events/jShdkweusi2.json?importStrategy=CREATE" ) ) + mockServer.expect( ExpectedCount.once(), requestTo( "http://localhost:8080/api/events.json?strategy=CREATE" ) ) .andExpect( content().contentTypeCompatibleWith( MediaType.APPLICATION_JSON ) ) .andExpect( content().json( IOUtils.resourceToString( "/org/dhis2/fhir/adapter/dhis/tracker/program/impl/createEvent.json", StandardCharsets.UTF_8 ) ) ) - .andExpect( method( HttpMethod.PUT ) ).andRespond( withSuccess( IOUtils.resourceToByteArray( "/org/dhis2/fhir/adapter/dhis/tracker/program/impl/createEvent-response.json" ), MediaType.APPLICATION_JSON ) ); + .andExpect( method( HttpMethod.POST ) ).andRespond( withSuccess( IOUtils.resourceToByteArray( "/org/dhis2/fhir/adapter/dhis/tracker/program/impl/createEvent-response.json" ), MediaType.APPLICATION_JSON ) ); final WritableDataValue dataValue1 = new WritableDataValue(); dataValue1.setDataElementId( "dsf84sfsdf" ); diff --git a/dhis/src/test/java/org/dhis2/fhir/adapter/dhis/tracker/trackedentity/impl/TrackedEntityServiceImplTest.java b/dhis/src/test/java/org/dhis2/fhir/adapter/dhis/tracker/trackedentity/impl/TrackedEntityServiceImplTest.java index 69c47647..185756ec 100644 --- a/dhis/src/test/java/org/dhis2/fhir/adapter/dhis/tracker/trackedentity/impl/TrackedEntityServiceImplTest.java +++ b/dhis/src/test/java/org/dhis2/fhir/adapter/dhis/tracker/trackedentity/impl/TrackedEntityServiceImplTest.java @@ -180,7 +180,7 @@ public void findOneByIdNotFound() @Test public void create() throws IOException { - mockServer.expect( ExpectedCount.once(), requestTo( "http://localhost:8080/api/trackedEntityInstances/Jskdsjeua1s.json?strategy=CREATE" ) ) + mockServer.expect( ExpectedCount.once(), requestTo( "http://localhost:8080/api/trackedEntityInstances.json?strategy=CREATE" ) ) .andExpect( content().contentTypeCompatibleWith( MediaType.APPLICATION_JSON ) ) .andExpect( content().json( IOUtils.resourceToString( "/org/dhis2/fhir/adapter/dhis/tracker/program/impl/createTrackedEntityInstance.json", StandardCharsets.UTF_8 ) ) ) .andExpect( method( HttpMethod.POST ) ).andRespond( withSuccess( IOUtils.resourceToByteArray( "/org/dhis2/fhir/adapter/dhis/tracker/program/impl/createTrackedEntityInstance-response.json" ), MediaType.APPLICATION_JSON ) ); From b7bf8fcd5b8b526bde9db48c17573e34703d72c7 Mon Sep 17 00:00:00 2001 From: Volker Schmidt Date: Mon, 29 Jul 2019 19:30:57 +0200 Subject: [PATCH 02/21] Fixes issue with serialized polled items (stack overflow). --- .../service/impl/DhisMetadataPolledItemDeserializer.java | 8 +++++++- .../dhis/service/impl/DhisMetadataPolledItems.java | 6 ++++++ 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/dhis/src/main/java/org/dhis2/fhir/adapter/dhis/service/impl/DhisMetadataPolledItemDeserializer.java b/dhis/src/main/java/org/dhis2/fhir/adapter/dhis/service/impl/DhisMetadataPolledItemDeserializer.java index 01126e98..1b90c21f 100644 --- a/dhis/src/main/java/org/dhis2/fhir/adapter/dhis/service/impl/DhisMetadataPolledItemDeserializer.java +++ b/dhis/src/main/java/org/dhis2/fhir/adapter/dhis/service/impl/DhisMetadataPolledItemDeserializer.java @@ -33,6 +33,7 @@ import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.deser.std.StdDeserializer; import com.fasterxml.jackson.databind.node.ObjectNode; +import org.dhis2.fhir.adapter.dhis.poll.PolledItems; import java.io.IOException; import java.util.Iterator; @@ -72,6 +73,11 @@ public DhisMetadataPolledItems deserialize( JsonParser p, DeserializationContext } } - return p.getCodec().treeToValue( rootNode, DhisMetadataPolledItems.class ); + return new DhisMetadataPolledItems( p.getCodec().treeToValue( rootNode, InternalDhisMetadataPolledItems.class ) ); + } + + public static class InternalDhisMetadataPolledItems extends PolledItems + { + private static final long serialVersionUID = -3139366174834526658L; } } diff --git a/dhis/src/main/java/org/dhis2/fhir/adapter/dhis/service/impl/DhisMetadataPolledItems.java b/dhis/src/main/java/org/dhis2/fhir/adapter/dhis/service/impl/DhisMetadataPolledItems.java index 9b2bbabe..1cde5220 100644 --- a/dhis/src/main/java/org/dhis2/fhir/adapter/dhis/service/impl/DhisMetadataPolledItems.java +++ b/dhis/src/main/java/org/dhis2/fhir/adapter/dhis/service/impl/DhisMetadataPolledItems.java @@ -53,4 +53,10 @@ public DhisMetadataPolledItems( @Nonnull List items ) { setItems( items ); } + + public DhisMetadataPolledItems( @Nonnull PolledItems polledItems ) + { + setItems( polledItems.getItems() ); + setPager( polledItems.getPager() ); + } } From 3f44805aa490f74434ef2bb7dade85f483bc1955 Mon Sep 17 00:00:00 2001 From: Volker Schmidt Date: Thu, 1 Aug 2019 13:42:42 +0200 Subject: [PATCH 03/21] Adds support for lazy loaded tracked entity instances. --- .../AbstractBatchBundleFhirRestAppTest.java | 2 - .../Dstu3ProgramStageFhirRestAppTest.java | 73 ++---- .../r4/R4ProgramStageFhirRestAppTest.java | 73 ++---- .../LocalDhisResourceRepositoryTemplate.java | 9 + .../dhis/model/IdentifiedDhisResource.java | 77 ++++++ .../IdentifiedTrackedEntityInstance.java | 53 ++++ .../trackedentity/TrackedEntityService.java | 2 + .../impl/TrackedEntityServiceImpl.java | 6 + ...calDhisResourceRepositoryTemplateTest.java | 62 +++++ .../impl/TrackedEntityServiceImplTest.java | 39 +++ .../DhisToFhirTransformerServiceImpl.java | 3 +- .../ProgramStageToFhirRequestResolver.java | 31 +-- .../ProgramStageToFhirTransformer.java | 10 +- .../TrackedEntityToFhirRequestResolver.java | 19 +- .../impl/AbstractFhirToDhisTransformer.java | 82 +++++- .../program/FhirToEnrollmentTransformer.java | 46 ++-- .../FhirToProgramStageTransformer.java | 82 ++++-- .../FhirToTrackedEntityTransformer.java | 12 +- ...tractUnsupportedFhirToDhisTransformer.java | 10 +- ...OrganizationUnitFhirToDhisTransformer.java | 10 +- .../ProgramMetadataFhirToDhisTransformer.java | 10 +- ...ramStageMetadataFhirToDhisTransformer.java | 10 +- .../AbstractWritableScriptedDhisResource.java | 160 ++++++++++++ ...sibleWritableScriptedLazyDhisResource.java | 49 ++++ .../LazyImmutableTrackedEntityType.java | 187 ++++++++++++++ .../WritableScriptedDhisResource.java | 85 +----- .../WritableScriptedLazyDhisResource.java | 72 ++++++ ...WritableScriptedTrackedEntityInstance.java | 180 ++++++++----- .../fhir/transform/util/TransformerUtils.java | 6 + .../FhirToEnrollmentTransformerTest.java | 1 + ...tractWritableScriptedDhisResourceTest.java | 242 ++++++++++++++++++ .../WritableScriptedDhisResourceTest.java | 72 ++++++ .../WritableScriptedLazyDhisResourceTest.java | 89 +++++++ .../transform/util/TransformerUtilsTest.java | 61 +++++ 34 files changed, 1562 insertions(+), 363 deletions(-) create mode 100644 dhis/src/main/java/org/dhis2/fhir/adapter/dhis/model/IdentifiedDhisResource.java create mode 100644 dhis/src/main/java/org/dhis2/fhir/adapter/dhis/tracker/trackedentity/IdentifiedTrackedEntityInstance.java create mode 100644 fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/scripted/AbstractWritableScriptedDhisResource.java create mode 100644 fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/scripted/AccessibleWritableScriptedLazyDhisResource.java create mode 100644 fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/scripted/LazyImmutableTrackedEntityType.java create mode 100644 fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/scripted/WritableScriptedLazyDhisResource.java create mode 100644 fhir/src/test/java/org/dhis2/fhir/adapter/fhir/transform/scripted/AbstractWritableScriptedDhisResourceTest.java create mode 100644 fhir/src/test/java/org/dhis2/fhir/adapter/fhir/transform/scripted/WritableScriptedDhisResourceTest.java create mode 100644 fhir/src/test/java/org/dhis2/fhir/adapter/fhir/transform/scripted/WritableScriptedLazyDhisResourceTest.java create mode 100644 fhir/src/test/java/org/dhis2/fhir/adapter/fhir/transform/util/TransformerUtilsTest.java diff --git a/app/src/test/java/org/dhis2/fhir/adapter/fhir/server/AbstractBatchBundleFhirRestAppTest.java b/app/src/test/java/org/dhis2/fhir/adapter/fhir/server/AbstractBatchBundleFhirRestAppTest.java index 97be9aae..09c74d5e 100644 --- a/app/src/test/java/org/dhis2/fhir/adapter/fhir/server/AbstractBatchBundleFhirRestAppTest.java +++ b/app/src/test/java/org/dhis2/fhir/adapter/fhir/server/AbstractBatchBundleFhirRestAppTest.java @@ -101,12 +101,10 @@ protected void prepareUpdate() throws Exception "deleted,trackedEntityInstance,trackedEntityType,orgUnit,coordinates,lastUpdated,attributes%5Battribute,value,lastUpdated,storedBy%5D" ) ) .andExpect( method( HttpMethod.GET ) ).andExpect( header( "Authorization", "Basic Zmhpcl9jbGllbnQ6Zmhpcl9jbGllbnRfMQ==" ) ) .andRespond( withSuccess( IOUtils.resourceToString( "/org/dhis2/fhir/adapter/dhis/test/default-tei-15-get.json", StandardCharsets.UTF_8 ), MediaType.APPLICATION_JSON ) ); - userDhis2Server.expect( ExpectedCount.once(), requestTo( matchesPattern( dhis2BaseUrl + "/api/" + dhis2ApiVersion + "/enrollments.json?program=EPDyQuoRnXk&programStatus=ACTIVE&trackedEntityInstance=*&ouMode=ACCESSIBLE&fields=:all&" + "order=lastUpdated:desc&pageSize=1" ) ) ) .andExpect( method( HttpMethod.GET ) ).andExpect( header( "Authorization", "Basic Zmhpcl9jbGllbnQ6Zmhpcl9jbGllbnRfMQ==" ) ) .andRespond( withSuccess( IOUtils.resourceToString( "/org/dhis2/fhir/adapter/dhis/test/default-bundle-enrollment-get.json", StandardCharsets.UTF_8 ), MediaType.APPLICATION_JSON ) ); - userDhis2Server.expect( ExpectedCount.between( 1, 2 ), requestTo( dhis2BaseUrl + "/api/" + dhis2ApiVersion + "/events.json?program=EPDyQuoRnXk&trackedEntityInstance=JeR2Ul4mZfx&ouMode=ACCESSIBLE&fields=deleted,event,orgUnit,program," + "enrollment,trackedEntityInstance,programStage,status,eventDate,dueDate,coordinate,lastUpdated,dataValues%5BdataElement,value,providedElsewhere,lastUpdated,storedBy%5D&skipPaging=true" ) ) .andExpect( method( HttpMethod.GET ) ).andExpect( header( "Authorization", "Basic Zmhpcl9jbGllbnQ6Zmhpcl9jbGllbnRfMQ==" ) ) diff --git a/app/src/test/java/org/dhis2/fhir/adapter/fhir/server/dstu3/Dstu3ProgramStageFhirRestAppTest.java b/app/src/test/java/org/dhis2/fhir/adapter/fhir/server/dstu3/Dstu3ProgramStageFhirRestAppTest.java index 53e98feb..0e9b8d48 100644 --- a/app/src/test/java/org/dhis2/fhir/adapter/fhir/server/dstu3/Dstu3ProgramStageFhirRestAppTest.java +++ b/app/src/test/java/org/dhis2/fhir/adapter/fhir/server/dstu3/Dstu3ProgramStageFhirRestAppTest.java @@ -95,11 +95,11 @@ public void getObservationWithInvalidAuthorization() throws Exception @Test public void getObservationRepeated() throws Exception { - getObservation( false ); - getObservation( true ); + getObservation(); + getObservation(); } - private void getObservation( boolean skipCached ) throws Exception + private void getObservation() throws Exception { systemDhis2Server.reset(); userDhis2Server.reset(); @@ -109,15 +109,6 @@ private void getObservation( boolean skipCached ) throws Exception .andExpect( requestTo( dhis2BaseUrl + "/api/" + dhis2ApiVersion + "/events/deR4kl4mnf7.json?" + "fields=deleted,event,orgUnit,program,enrollment,trackedEntityInstance,programStage,status,eventDate,dueDate,coordinate,lastUpdated,dataValues%5BdataElement,value,providedElsewhere,lastUpdated,storedBy%5D" ) ) .andRespond( withSuccess( IOUtils.resourceToString( "/org/dhis2/fhir/adapter/dhis/test/single-event-70-get.json", StandardCharsets.UTF_8 ), MediaType.APPLICATION_JSON ) ); - - if ( !skipCached ) - { - userDhis2Server.expect( ExpectedCount.once(), method( HttpMethod.GET ) ).andExpect( header( "Authorization", "Basic Zmhpcl9jbGllbnQ6Zmhpcl9jbGllbnRfMQ==" ) ) - .andExpect( requestTo( dhis2BaseUrl + "/api/" + dhis2ApiVersion + "/trackedEntityInstances/JeR2Ul4mZfx.json?" + - "fields=deleted,trackedEntityInstance,trackedEntityType,orgUnit,coordinates,lastUpdated,attributes%5Battribute,value,lastUpdated,storedBy%5D" ) ) - .andRespond( withSuccess( IOUtils.resourceToString( "/org/dhis2/fhir/adapter/dhis/test/single-tei-15-get.json", StandardCharsets.UTF_8 ), MediaType.APPLICATION_JSON ) ); - } - systemDhis2Server.expect( ExpectedCount.between( 0, 1 ), method( HttpMethod.GET ) ).andExpect( header( "Authorization", testConfiguration.getDhis2SystemAuthorization() ) ) .andExpect( requestTo( dhis2BaseUrl + "/api/" + dhis2ApiVersion + "/organisationUnits/ldXIdLNUNEp.json?fields=lastUpdated,id,code,name,shortName,displayName,level,openingDate,closedDate,coordinates,leaf,parent%5Bid%5D" ) ) .andRespond( withSuccess( IOUtils.resourceToString( "/org/dhis2/fhir/adapter/dhis/test/single-org-unit-OU_4567.json", StandardCharsets.UTF_8 ), MediaType.APPLICATION_JSON ) ); @@ -158,11 +149,11 @@ public void searchObservationInvalidAuthorization() throws Exception @Test public void searchObservationRepeated() throws Exception { - searchObservation( false ); - searchObservation( true ); + searchObservation(); + searchObservation(); } - private void searchObservation( boolean skipCached ) throws Exception + private void searchObservation() throws Exception { systemDhis2Server.reset(); userDhis2Server.reset(); @@ -172,15 +163,6 @@ private void searchObservation( boolean skipCached ) throws Exception .andExpect( requestTo( dhis2BaseUrl + "/api/" + dhis2ApiVersion + "/events.json?skipPaging=false&page=1&pageSize=10&program=EPDyQuoRnXk&programStage=qowTSevVSkd&ouMode=ACCESSIBLE&fields=deleted,event,orgUnit,program,enrollment," + "trackedEntityInstance,programStage,status,eventDate,dueDate,coordinate,lastUpdated,dataValues%5BdataElement,value,providedElsewhere,lastUpdated,storedBy%5D" ) ) .andRespond( withSuccess( IOUtils.resourceToString( "/org/dhis2/fhir/adapter/dhis/test/default-event-70-get.json", StandardCharsets.UTF_8 ), MediaType.APPLICATION_JSON ) ); - - if ( !skipCached ) - { - userDhis2Server.expect( ExpectedCount.once(), method( HttpMethod.GET ) ).andExpect( header( "Authorization", "Basic Zmhpcl9jbGllbnQ6Zmhpcl9jbGllbnRfMQ==" ) ) - .andExpect( requestTo( dhis2BaseUrl + "/api/" + dhis2ApiVersion + "/trackedEntityInstances/JeR2Ul4mZfx.json?" + - "fields=deleted,trackedEntityInstance,trackedEntityType,orgUnit,coordinates,lastUpdated,attributes%5Battribute,value,lastUpdated,storedBy%5D" ) ) - .andRespond( withSuccess( IOUtils.resourceToString( "/org/dhis2/fhir/adapter/dhis/test/single-tei-15-get.json", StandardCharsets.UTF_8 ), MediaType.APPLICATION_JSON ) ); - } - userDhis2Server.expect( ExpectedCount.once(), method( HttpMethod.GET ) ).andExpect( header( "Authorization", "Basic Zmhpcl9jbGllbnQ6Zmhpcl9jbGllbnRfMQ==" ) ) .andExpect( requestTo( dhis2BaseUrl + "/api/" + dhis2ApiVersion + "/events.json?skipPaging=false&page=1&pageSize=9&program=EPDyQuoRnXk&programStage=MsWxkiY6tMS&ouMode=ACCESSIBLE&fields=deleted,event,orgUnit,program,enrollment," + "trackedEntityInstance,programStage,status,eventDate,dueDate,coordinate,lastUpdated,dataValues%5BdataElement,value,providedElsewhere,lastUpdated,storedBy%5D" ) ) @@ -210,14 +192,9 @@ public void searchObservationPatient() throws Exception .andExpect( requestTo( dhis2BaseUrl + "/api/" + dhis2ApiVersion + "/events.json?trackedEntityInstance=JeR2Ul4mZfx&skipPaging=false&page=1&pageSize=10&program=EPDyQuoRnXk&programStage=qowTSevVSkd&ouMode=ACCESSIBLE&fields=deleted,event,orgUnit," + "program,enrollment,trackedEntityInstance,programStage,status,eventDate,dueDate,coordinate,lastUpdated,dataValues%5BdataElement,value,providedElsewhere,lastUpdated,storedBy%5D" ) ) .andRespond( withSuccess( IOUtils.resourceToString( "/org/dhis2/fhir/adapter/dhis/test/default-event-70-get.json", StandardCharsets.UTF_8 ), MediaType.APPLICATION_JSON ) ); - userDhis2Server.expect( ExpectedCount.once(), method( HttpMethod.GET ) ).andExpect( header( "Authorization", "Basic Zmhpcl9jbGllbnQ6Zmhpcl9jbGllbnRfMQ==" ) ) - .andExpect( requestTo( dhis2BaseUrl + "/api/" + dhis2ApiVersion + "/trackedEntityInstances/JeR2Ul4mZfx.json?" + - "fields=deleted,trackedEntityInstance,trackedEntityType,orgUnit,coordinates,lastUpdated,attributes%5Battribute,value,lastUpdated,storedBy%5D" ) ) - .andRespond( withSuccess( IOUtils.resourceToString( "/org/dhis2/fhir/adapter/dhis/test/single-tei-15-get.json", StandardCharsets.UTF_8 ), MediaType.APPLICATION_JSON ) ); userDhis2Server.expect( ExpectedCount.once(), method( HttpMethod.GET ) ).andExpect( header( "Authorization", "Basic Zmhpcl9jbGllbnQ6Zmhpcl9jbGllbnRfMQ==" ) ) .andExpect( requestTo( dhis2BaseUrl + "/api/" + dhis2ApiVersion + "/events.json?trackedEntityInstance=JeR2Ul4mZfx&skipPaging=false&page=1&pageSize=9&program=EPDyQuoRnXk&programStage=MsWxkiY6tMS&ouMode=ACCESSIBLE&fields=deleted,event,orgUnit," + - "program,enrollment," + - "trackedEntityInstance,programStage,status,eventDate,dueDate,coordinate,lastUpdated,dataValues%5BdataElement,value,providedElsewhere,lastUpdated,storedBy%5D" ) ) + "program,enrollment,trackedEntityInstance,programStage,status,eventDate,dueDate,coordinate,lastUpdated,dataValues%5BdataElement,value,providedElsewhere,lastUpdated,storedBy%5D" ) ) .andRespond( withSuccess( IOUtils.resourceToString( "/org/dhis2/fhir/adapter/dhis/test/default-event-71-only-get.json", StandardCharsets.UTF_8 ), MediaType.APPLICATION_JSON ) ); systemDhis2Server.expect( ExpectedCount.between( 0, 1 ), method( HttpMethod.GET ) ).andExpect( header( "Authorization", testConfiguration.getDhis2SystemAuthorization() ) ) .andExpect( requestTo( dhis2BaseUrl + "/api/" + dhis2ApiVersion + "/organisationUnits/ldXIdLNUNEp.json?fields=lastUpdated,id,code,name,shortName,displayName,level,openingDate,closedDate,coordinates,leaf,parent%5Bid%5D" ) ) @@ -255,8 +232,7 @@ public void createObservationInvalidAuthorization() throws Exception { expectProgramStageMetadataRequests(); userDhis2Server.expect( ExpectedCount.once(), method( HttpMethod.GET ) ).andExpect( header( "Authorization", "Basic Zmhpcl9jbGllbnQ6aW52YWxpZF8x" ) ) - .andExpect( requestTo( dhis2BaseUrl + "/api/" + dhis2ApiVersion + "/trackedEntityInstances/JeR2Ul4mZfx.json?" + - "fields=deleted,trackedEntityInstance,trackedEntityType,orgUnit,coordinates,lastUpdated,attributes%5Battribute,value,lastUpdated,storedBy%5D" ) ) + .andExpect( requestTo( dhis2BaseUrl + "/api/" + dhis2ApiVersion + "/enrollments.json?program=EPDyQuoRnXk&programStatus=ACTIVE&trackedEntityInstance=JeR2Ul4mZfx&ouMode=ACCESSIBLE&fields=:all&order=lastUpdated:desc&pageSize=1" ) ) .andRespond( withStatus( HttpStatus.UNAUTHORIZED ) ); Observation observation = (Observation) getFhirContext().newJsonParser().parseResource( @@ -273,16 +249,16 @@ public void createObservationInvalidAuthorization() throws Exception public void createObservation() throws Exception { expectProgramStageMetadataRequests(); - userDhis2Server.expect( ExpectedCount.between( 1, 2 ), method( HttpMethod.GET ) ).andExpect( header( "Authorization", "Basic Zmhpcl9jbGllbnQ6Zmhpcl9jbGllbnRfMQ==" ) ) - .andExpect( requestTo( dhis2BaseUrl + "/api/" + dhis2ApiVersion + "/trackedEntityInstances/JeR2Ul4mZfx.json?" + - "fields=deleted,trackedEntityInstance,trackedEntityType,orgUnit,coordinates,lastUpdated,attributes%5Battribute,value,lastUpdated,storedBy%5D" ) ) - .andRespond( withSuccess( IOUtils.resourceToString( "/org/dhis2/fhir/adapter/dhis/test/single-tei-15-get.json", StandardCharsets.UTF_8 ), MediaType.APPLICATION_JSON ) ); userDhis2Server.expect( ExpectedCount.between( 2, 4 ), requestTo( dhis2BaseUrl + "/api/" + dhis2ApiVersion + "/enrollments.json?program=EPDyQuoRnXk&programStatus=ACTIVE&trackedEntityInstance=JeR2Ul4mZfx&ouMode=ACCESSIBLE&fields=:all&" + "order=lastUpdated:desc&pageSize=1" ) ).andExpect( method( HttpMethod.GET ) ).andExpect( header( "Authorization", "Basic Zmhpcl9jbGllbnQ6Zmhpcl9jbGllbnRfMQ==" ) ) .andRespond( withSuccess( IOUtils.resourceToString( "/org/dhis2/fhir/adapter/dhis/test/default-enrollment-empty.json", StandardCharsets.UTF_8 ), MediaType.APPLICATION_JSON ) ); systemDhis2Server.expect( ExpectedCount.between( 0, 1 ), method( HttpMethod.GET ) ).andExpect( header( "Authorization", testConfiguration.getDhis2SystemAuthorization() ) ) .andExpect( requestTo( dhis2BaseUrl + "/api/" + dhis2ApiVersion + "/organisationUnits/ldXIdLNUNEp.json?fields=lastUpdated,id,code,name,shortName,displayName,level,openingDate,closedDate,coordinates,leaf,parent%5Bid%5D" ) ) .andRespond( withSuccess( IOUtils.resourceToString( "/org/dhis2/fhir/adapter/dhis/test/single-org-unit-OU_4567.json", StandardCharsets.UTF_8 ), MediaType.APPLICATION_JSON ) ); + userDhis2Server.expect( ExpectedCount.between( 1, 1 ), method( HttpMethod.GET ) ).andExpect( header( "Authorization", "Basic Zmhpcl9jbGllbnQ6Zmhpcl9jbGllbnRfMQ==" ) ) + .andExpect( requestTo( dhis2BaseUrl + "/api/" + dhis2ApiVersion + "/trackedEntityInstances/JeR2Ul4mZfx.json?" + + "fields=deleted,trackedEntityInstance,trackedEntityType,orgUnit,coordinates,lastUpdated,attributes%5Battribute,value,lastUpdated,storedBy%5D" ) ) + .andRespond( withSuccess( IOUtils.resourceToString( "/org/dhis2/fhir/adapter/dhis/test/single-tei-15-get.json", StandardCharsets.UTF_8 ), MediaType.APPLICATION_JSON ) ); userDhis2Server.expect( ExpectedCount.once(), requestTo( dhis2BaseUrl + "/api/" + dhis2ApiVersion + "/enrollments.json?strategy=CREATE" ) ).andExpect( method( HttpMethod.POST ) ) .andExpect( header( "Authorization", "Basic Zmhpcl9jbGllbnQ6Zmhpcl9jbGllbnRfMQ==" ) ) .andExpect( content().contentTypeCompatibleWith( MediaType.APPLICATION_JSON ) ) @@ -307,16 +283,16 @@ public void createObservation() throws Exception public void createObservationByCodeSetCode() throws Exception { expectProgramStageMetadataRequests(); - userDhis2Server.expect( ExpectedCount.between( 1, 2 ), method( HttpMethod.GET ) ).andExpect( header( "Authorization", "Basic Zmhpcl9jbGllbnQ6Zmhpcl9jbGllbnRfMQ==" ) ) - .andExpect( requestTo( dhis2BaseUrl + "/api/" + dhis2ApiVersion + "/trackedEntityInstances/JeR2Ul4mZfx.json?" + - "fields=deleted,trackedEntityInstance,trackedEntityType,orgUnit,coordinates,lastUpdated,attributes%5Battribute,value,lastUpdated,storedBy%5D" ) ) - .andRespond( withSuccess( IOUtils.resourceToString( "/org/dhis2/fhir/adapter/dhis/test/single-tei-15-get.json", StandardCharsets.UTF_8 ), MediaType.APPLICATION_JSON ) ); userDhis2Server.expect( ExpectedCount.between( 2, 4 ), requestTo( dhis2BaseUrl + "/api/" + dhis2ApiVersion + "/enrollments.json?program=EPDyQuoRnXk&programStatus=ACTIVE&trackedEntityInstance=JeR2Ul4mZfx&ouMode=ACCESSIBLE&fields=:all&" + "order=lastUpdated:desc&pageSize=1" ) ).andExpect( method( HttpMethod.GET ) ).andExpect( header( "Authorization", "Basic Zmhpcl9jbGllbnQ6Zmhpcl9jbGllbnRfMQ==" ) ) .andRespond( withSuccess( IOUtils.resourceToString( "/org/dhis2/fhir/adapter/dhis/test/default-enrollment-empty.json", StandardCharsets.UTF_8 ), MediaType.APPLICATION_JSON ) ); systemDhis2Server.expect( ExpectedCount.between( 0, 1 ), method( HttpMethod.GET ) ).andExpect( header( "Authorization", testConfiguration.getDhis2SystemAuthorization() ) ) .andExpect( requestTo( dhis2BaseUrl + "/api/" + dhis2ApiVersion + "/organisationUnits/ldXIdLNUNEp.json?fields=lastUpdated,id,code,name,shortName,displayName,level,openingDate,closedDate,coordinates,leaf,parent%5Bid%5D" ) ) .andRespond( withSuccess( IOUtils.resourceToString( "/org/dhis2/fhir/adapter/dhis/test/single-org-unit-OU_4567.json", StandardCharsets.UTF_8 ), MediaType.APPLICATION_JSON ) ); + userDhis2Server.expect( ExpectedCount.between( 1, 1 ), method( HttpMethod.GET ) ).andExpect( header( "Authorization", "Basic Zmhpcl9jbGllbnQ6Zmhpcl9jbGllbnRfMQ==" ) ) + .andExpect( requestTo( dhis2BaseUrl + "/api/" + dhis2ApiVersion + "/trackedEntityInstances/JeR2Ul4mZfx.json?" + + "fields=deleted,trackedEntityInstance,trackedEntityType,orgUnit,coordinates,lastUpdated,attributes%5Battribute,value,lastUpdated,storedBy%5D" ) ) + .andRespond( withSuccess( IOUtils.resourceToString( "/org/dhis2/fhir/adapter/dhis/test/single-tei-15-get.json", StandardCharsets.UTF_8 ), MediaType.APPLICATION_JSON ) ); userDhis2Server.expect( ExpectedCount.once(), requestTo( dhis2BaseUrl + "/api/" + dhis2ApiVersion + "/enrollments.json?strategy=CREATE" ) ).andExpect( method( HttpMethod.POST ) ) .andExpect( header( "Authorization", "Basic Zmhpcl9jbGllbnQ6Zmhpcl9jbGllbnRfMQ==" ) ) .andExpect( content().contentTypeCompatibleWith( MediaType.APPLICATION_JSON ) ) @@ -341,16 +317,17 @@ public void createObservationByCodeSetCode() throws Exception public void createObservationIdentifierReference() throws Exception { expectProgramStageMetadataRequests(); - userDhis2Server.expect( ExpectedCount.between( 1, 2 ), method( HttpMethod.GET ) ).andExpect( header( "Authorization", "Basic Zmhpcl9jbGllbnQ6Zmhpcl9jbGllbnRfMQ==" ) ) - .andExpect( requestTo( dhis2BaseUrl + "/api/" + dhis2ApiVersion + "/trackedEntityInstances/JeR2Ul4mZfx.json?" + - "fields=deleted,trackedEntityInstance,trackedEntityType,orgUnit,coordinates,lastUpdated,attributes%5Battribute,value,lastUpdated,storedBy%5D" ) ) - .andRespond( withSuccess( IOUtils.resourceToString( "/org/dhis2/fhir/adapter/dhis/test/single-tei-15-get.json", StandardCharsets.UTF_8 ), MediaType.APPLICATION_JSON ) ); + userDhis2Server.expect( ExpectedCount.between( 2, 4 ), requestTo( dhis2BaseUrl + "/api/" + dhis2ApiVersion + "/enrollments.json?program=EPDyQuoRnXk&programStatus=ACTIVE&trackedEntityInstance=JeR2Ul4mZfx&ouMode=ACCESSIBLE&fields=:all&" + "order=lastUpdated:desc&pageSize=1" ) ).andExpect( method( HttpMethod.GET ) ).andExpect( header( "Authorization", "Basic Zmhpcl9jbGllbnQ6Zmhpcl9jbGllbnRfMQ==" ) ) .andRespond( withSuccess( IOUtils.resourceToString( "/org/dhis2/fhir/adapter/dhis/test/default-enrollment-empty.json", StandardCharsets.UTF_8 ), MediaType.APPLICATION_JSON ) ); systemDhis2Server.expect( ExpectedCount.between( 0, 1 ), method( HttpMethod.GET ) ).andExpect( header( "Authorization", testConfiguration.getDhis2SystemAuthorization() ) ) .andExpect( requestTo( dhis2BaseUrl + "/api/" + dhis2ApiVersion + "/organisationUnits/ldXIdLNUNEp.json?fields=lastUpdated,id,code,name,shortName,displayName,level,openingDate,closedDate,coordinates,leaf,parent%5Bid%5D" ) ) .andRespond( withSuccess( IOUtils.resourceToString( "/org/dhis2/fhir/adapter/dhis/test/single-org-unit-OU_4567.json", StandardCharsets.UTF_8 ), MediaType.APPLICATION_JSON ) ); + userDhis2Server.expect( ExpectedCount.between( 1, 1 ), method( HttpMethod.GET ) ).andExpect( header( "Authorization", "Basic Zmhpcl9jbGllbnQ6Zmhpcl9jbGllbnRfMQ==" ) ) + .andExpect( requestTo( dhis2BaseUrl + "/api/" + dhis2ApiVersion + "/trackedEntityInstances/JeR2Ul4mZfx.json?" + + "fields=deleted,trackedEntityInstance,trackedEntityType,orgUnit,coordinates,lastUpdated,attributes%5Battribute,value,lastUpdated,storedBy%5D" ) ) + .andRespond( withSuccess( IOUtils.resourceToString( "/org/dhis2/fhir/adapter/dhis/test/single-tei-15-get.json", StandardCharsets.UTF_8 ), MediaType.APPLICATION_JSON ) ); userDhis2Server.expect( ExpectedCount.once(), requestTo( dhis2BaseUrl + "/api/" + dhis2ApiVersion + "/enrollments.json?strategy=CREATE" ) ).andExpect( method( HttpMethod.POST ) ) .andExpect( header( "Authorization", "Basic Zmhpcl9jbGllbnQ6Zmhpcl9jbGllbnRfMQ==" ) ) .andExpect( content().contentTypeCompatibleWith( MediaType.APPLICATION_JSON ) ) @@ -394,8 +371,8 @@ public void updateObservationInvalidAuthorization() throws Exception { expectProgramStageMetadataRequests(); userDhis2Server.expect( ExpectedCount.between( 1, 2 ), method( HttpMethod.GET ) ).andExpect( header( "Authorization", "Basic Zmhpcl9jbGllbnQ6aW52YWxpZF8x" ) ) - .andExpect( requestTo( dhis2BaseUrl + "/api/" + dhis2ApiVersion + "/trackedEntityInstances/JeR2Ul4mZfx.json?" + - "fields=deleted,trackedEntityInstance,trackedEntityType,orgUnit,coordinates,lastUpdated,attributes%5Battribute,value,lastUpdated,storedBy%5D" ) ) + .andExpect( requestTo( dhis2BaseUrl + "/api/" + dhis2ApiVersion + "/events/deR4kl4mnf7.json?fields=deleted,event,orgUnit,program,enrollment,trackedEntityInstance,programStage,status,eventDate,dueDate,coordinate,lastUpdated," + + "dataValues%5BdataElement,value,providedElsewhere,lastUpdated,storedBy%5D" ) ) .andRespond( withStatus( HttpStatus.UNAUTHORIZED ) ); Observation observation = (Observation) getFhirContext().newJsonParser().parseResource( @@ -416,10 +393,6 @@ public void updateObservation() throws Exception systemDhis2Server.expect( ExpectedCount.between( 0, 1 ), method( HttpMethod.GET ) ).andExpect( header( "Authorization", testConfiguration.getDhis2SystemAuthorization() ) ) .andExpect( requestTo( dhis2BaseUrl + "/api/" + dhis2ApiVersion + "/organisationUnits.json?paging=false&fields=lastUpdated,id,code,name,shortName,displayName,level,openingDate,closedDate,coordinates,leaf,parent%5Bid%5D&filter=code:eq:OU_4567" ) ) .andRespond( withSuccess( IOUtils.resourceToString( "/org/dhis2/fhir/adapter/dhis/test/default-org-unit-OU_4567.json", StandardCharsets.UTF_8 ), MediaType.APPLICATION_JSON ) ); - userDhis2Server.expect( ExpectedCount.between( 1, 2 ), method( HttpMethod.GET ) ).andExpect( header( "Authorization", "Basic Zmhpcl9jbGllbnQ6Zmhpcl9jbGllbnRfMQ==" ) ) - .andExpect( requestTo( dhis2BaseUrl + "/api/" + dhis2ApiVersion + "/trackedEntityInstances/JeR2Ul4mZfx.json?" + - "fields=deleted,trackedEntityInstance,trackedEntityType,orgUnit,coordinates,lastUpdated,attributes%5Battribute,value,lastUpdated,storedBy%5D" ) ) - .andRespond( withSuccess( IOUtils.resourceToString( "/org/dhis2/fhir/adapter/dhis/test/single-tei-15-get.json", StandardCharsets.UTF_8 ), MediaType.APPLICATION_JSON ) ); systemDhis2Server.expect( ExpectedCount.between( 0, 1 ), method( HttpMethod.GET ) ).andExpect( header( "Authorization", testConfiguration.getDhis2SystemAuthorization() ) ) .andExpect( requestTo( dhis2BaseUrl + "/api/" + dhis2ApiVersion + "/organisationUnits/ldXIdLNUNEn.json?fields=lastUpdated,id,code,name,shortName,displayName,level,openingDate,closedDate,coordinates,leaf,parent%5Bid%5D" ) ) .andRespond( withSuccess( IOUtils.resourceToString( "/org/dhis2/fhir/adapter/dhis/test/default-org-unit-OU_1234.json", StandardCharsets.UTF_8 ), MediaType.APPLICATION_JSON ) ); diff --git a/app/src/test/java/org/dhis2/fhir/adapter/fhir/server/r4/R4ProgramStageFhirRestAppTest.java b/app/src/test/java/org/dhis2/fhir/adapter/fhir/server/r4/R4ProgramStageFhirRestAppTest.java index 953b9efa..4a0a1801 100644 --- a/app/src/test/java/org/dhis2/fhir/adapter/fhir/server/r4/R4ProgramStageFhirRestAppTest.java +++ b/app/src/test/java/org/dhis2/fhir/adapter/fhir/server/r4/R4ProgramStageFhirRestAppTest.java @@ -95,11 +95,11 @@ public void getObservationWithInvalidAuthorization() throws Exception @Test public void getObservationRepeated() throws Exception { - getObservation( false ); - getObservation( true ); + getObservation(); + getObservation(); } - private void getObservation( boolean skipCached ) throws Exception + private void getObservation() throws Exception { systemDhis2Server.reset(); userDhis2Server.reset(); @@ -109,15 +109,6 @@ private void getObservation( boolean skipCached ) throws Exception .andExpect( requestTo( dhis2BaseUrl + "/api/" + dhis2ApiVersion + "/events/deR4kl4mnf7.json?" + "fields=deleted,event,orgUnit,program,enrollment,trackedEntityInstance,programStage,status,eventDate,dueDate,coordinate,lastUpdated,dataValues%5BdataElement,value,providedElsewhere,lastUpdated,storedBy%5D" ) ) .andRespond( withSuccess( IOUtils.resourceToString( "/org/dhis2/fhir/adapter/dhis/test/single-event-70-get.json", StandardCharsets.UTF_8 ), MediaType.APPLICATION_JSON ) ); - - if ( !skipCached ) - { - userDhis2Server.expect( ExpectedCount.once(), method( HttpMethod.GET ) ).andExpect( header( "Authorization", "Basic Zmhpcl9jbGllbnQ6Zmhpcl9jbGllbnRfMQ==" ) ) - .andExpect( requestTo( dhis2BaseUrl + "/api/" + dhis2ApiVersion + "/trackedEntityInstances/JeR2Ul4mZfx.json?" + - "fields=deleted,trackedEntityInstance,trackedEntityType,orgUnit,coordinates,lastUpdated,attributes%5Battribute,value,lastUpdated,storedBy%5D" ) ) - .andRespond( withSuccess( IOUtils.resourceToString( "/org/dhis2/fhir/adapter/dhis/test/single-tei-15-get.json", StandardCharsets.UTF_8 ), MediaType.APPLICATION_JSON ) ); - } - systemDhis2Server.expect( ExpectedCount.between( 0, 1 ), method( HttpMethod.GET ) ).andExpect( header( "Authorization", testConfiguration.getDhis2SystemAuthorization() ) ) .andExpect( requestTo( dhis2BaseUrl + "/api/" + dhis2ApiVersion + "/organisationUnits/ldXIdLNUNEp.json?fields=lastUpdated,id,code,name,shortName,displayName,level,openingDate,closedDate,coordinates,leaf,parent%5Bid%5D" ) ) .andRespond( withSuccess( IOUtils.resourceToString( "/org/dhis2/fhir/adapter/dhis/test/single-org-unit-OU_4567.json", StandardCharsets.UTF_8 ), MediaType.APPLICATION_JSON ) ); @@ -159,11 +150,11 @@ public void searchObservationInvalidAuthorization() throws Exception @Test public void searchObservationRepeated() throws Exception { - searchObservation( false ); - searchObservation( true ); + searchObservation(); + searchObservation(); } - private void searchObservation( boolean skipCached ) throws Exception + private void searchObservation() throws Exception { systemDhis2Server.reset(); userDhis2Server.reset(); @@ -173,15 +164,6 @@ private void searchObservation( boolean skipCached ) throws Exception .andExpect( requestTo( dhis2BaseUrl + "/api/" + dhis2ApiVersion + "/events.json?skipPaging=false&page=1&pageSize=10&program=EPDyQuoRnXk&programStage=qowTSevVSkd&ouMode=ACCESSIBLE&fields=deleted,event,orgUnit,program,enrollment," + "trackedEntityInstance,programStage,status,eventDate,dueDate,coordinate,lastUpdated,dataValues%5BdataElement,value,providedElsewhere,lastUpdated,storedBy%5D" ) ) .andRespond( withSuccess( IOUtils.resourceToString( "/org/dhis2/fhir/adapter/dhis/test/default-event-70-get.json", StandardCharsets.UTF_8 ), MediaType.APPLICATION_JSON ) ); - - if ( !skipCached ) - { - userDhis2Server.expect( ExpectedCount.once(), method( HttpMethod.GET ) ).andExpect( header( "Authorization", "Basic Zmhpcl9jbGllbnQ6Zmhpcl9jbGllbnRfMQ==" ) ) - .andExpect( requestTo( dhis2BaseUrl + "/api/" + dhis2ApiVersion + "/trackedEntityInstances/JeR2Ul4mZfx.json?" + - "fields=deleted,trackedEntityInstance,trackedEntityType,orgUnit,coordinates,lastUpdated,attributes%5Battribute,value,lastUpdated,storedBy%5D" ) ) - .andRespond( withSuccess( IOUtils.resourceToString( "/org/dhis2/fhir/adapter/dhis/test/single-tei-15-get.json", StandardCharsets.UTF_8 ), MediaType.APPLICATION_JSON ) ); - } - userDhis2Server.expect( ExpectedCount.once(), method( HttpMethod.GET ) ).andExpect( header( "Authorization", "Basic Zmhpcl9jbGllbnQ6Zmhpcl9jbGllbnRfMQ==" ) ) .andExpect( requestTo( dhis2BaseUrl + "/api/" + dhis2ApiVersion + "/events.json?skipPaging=false&page=1&pageSize=9&program=EPDyQuoRnXk&programStage=MsWxkiY6tMS&ouMode=ACCESSIBLE&fields=deleted,event,orgUnit,program,enrollment," + "trackedEntityInstance,programStage,status,eventDate,dueDate,coordinate,lastUpdated,dataValues%5BdataElement,value,providedElsewhere,lastUpdated,storedBy%5D" ) ) @@ -211,10 +193,6 @@ public void searchObservationPatient() throws Exception .andExpect( requestTo( dhis2BaseUrl + "/api/" + dhis2ApiVersion + "/events.json?trackedEntityInstance=JeR2Ul4mZfx&skipPaging=false&page=1&pageSize=10&program=EPDyQuoRnXk&programStage=qowTSevVSkd&ouMode=ACCESSIBLE&fields=deleted,event,orgUnit," + "program,enrollment,trackedEntityInstance,programStage,status,eventDate,dueDate,coordinate,lastUpdated,dataValues%5BdataElement,value,providedElsewhere,lastUpdated,storedBy%5D" ) ) .andRespond( withSuccess( IOUtils.resourceToString( "/org/dhis2/fhir/adapter/dhis/test/default-event-70-get.json", StandardCharsets.UTF_8 ), MediaType.APPLICATION_JSON ) ); - userDhis2Server.expect( ExpectedCount.once(), method( HttpMethod.GET ) ).andExpect( header( "Authorization", "Basic Zmhpcl9jbGllbnQ6Zmhpcl9jbGllbnRfMQ==" ) ) - .andExpect( requestTo( dhis2BaseUrl + "/api/" + dhis2ApiVersion + "/trackedEntityInstances/JeR2Ul4mZfx.json?" + - "fields=deleted,trackedEntityInstance,trackedEntityType,orgUnit,coordinates,lastUpdated,attributes%5Battribute,value,lastUpdated,storedBy%5D" ) ) - .andRespond( withSuccess( IOUtils.resourceToString( "/org/dhis2/fhir/adapter/dhis/test/single-tei-15-get.json", StandardCharsets.UTF_8 ), MediaType.APPLICATION_JSON ) ); userDhis2Server.expect( ExpectedCount.once(), method( HttpMethod.GET ) ).andExpect( header( "Authorization", "Basic Zmhpcl9jbGllbnQ6Zmhpcl9jbGllbnRfMQ==" ) ) .andExpect( requestTo( dhis2BaseUrl + "/api/" + dhis2ApiVersion + "/events.json?trackedEntityInstance=JeR2Ul4mZfx&skipPaging=false&page=1&pageSize=9&program=EPDyQuoRnXk&programStage=MsWxkiY6tMS&ouMode=ACCESSIBLE&fields=deleted,event,orgUnit," + "program,enrollment,trackedEntityInstance,programStage,status,eventDate,dueDate,coordinate,lastUpdated,dataValues%5BdataElement,value,providedElsewhere,lastUpdated,storedBy%5D" ) ) @@ -244,10 +222,6 @@ public void searchObservationEncounter() throws Exception .andExpect( requestTo( dhis2BaseUrl + "/api/" + dhis2ApiVersion + "/events.json?event=deR4kl4mnf7&skipPaging=false&page=1&pageSize=10&program=EPDyQuoRnXk&programStage=qowTSevVSkd&ouMode=ACCESSIBLE&fields=deleted,event,orgUnit," + "program,enrollment,trackedEntityInstance,programStage,status,eventDate,dueDate,coordinate,lastUpdated,dataValues%5BdataElement,value,providedElsewhere,lastUpdated,storedBy%5D" ) ) .andRespond( withSuccess( IOUtils.resourceToString( "/org/dhis2/fhir/adapter/dhis/test/default-event-70-get.json", StandardCharsets.UTF_8 ), MediaType.APPLICATION_JSON ) ); - userDhis2Server.expect( ExpectedCount.once(), method( HttpMethod.GET ) ).andExpect( header( "Authorization", "Basic Zmhpcl9jbGllbnQ6Zmhpcl9jbGllbnRfMQ==" ) ) - .andExpect( requestTo( dhis2BaseUrl + "/api/" + dhis2ApiVersion + "/trackedEntityInstances/JeR2Ul4mZfx.json?" + - "fields=deleted,trackedEntityInstance,trackedEntityType,orgUnit,coordinates,lastUpdated,attributes%5Battribute,value,lastUpdated,storedBy%5D" ) ) - .andRespond( withSuccess( IOUtils.resourceToString( "/org/dhis2/fhir/adapter/dhis/test/single-tei-15-get.json", StandardCharsets.UTF_8 ), MediaType.APPLICATION_JSON ) ); userDhis2Server.expect( ExpectedCount.once(), method( HttpMethod.GET ) ).andExpect( header( "Authorization", "Basic Zmhpcl9jbGllbnQ6Zmhpcl9jbGllbnRfMQ==" ) ) .andExpect( requestTo( dhis2BaseUrl + "/api/" + dhis2ApiVersion + "/events.json?event=deR4kl4mnf7&skipPaging=false&page=1&pageSize=9&program=EPDyQuoRnXk&programStage=MsWxkiY6tMS&ouMode=ACCESSIBLE&fields=deleted,event,orgUnit,program,enrollment," + "trackedEntityInstance,programStage,status,eventDate,dueDate,coordinate,lastUpdated,dataValues%5BdataElement,value,providedElsewhere,lastUpdated,storedBy%5D" ) ) @@ -288,8 +262,7 @@ public void createObservationInvalidAuthorization() throws Exception { expectProgramStageMetadataRequests(); userDhis2Server.expect( ExpectedCount.once(), method( HttpMethod.GET ) ).andExpect( header( "Authorization", "Basic Zmhpcl9jbGllbnQ6aW52YWxpZF8x" ) ) - .andExpect( requestTo( dhis2BaseUrl + "/api/" + dhis2ApiVersion + "/trackedEntityInstances/JeR2Ul4mZfx.json?" + - "fields=deleted,trackedEntityInstance,trackedEntityType,orgUnit,coordinates,lastUpdated,attributes%5Battribute,value,lastUpdated,storedBy%5D" ) ) + .andExpect( requestTo( dhis2BaseUrl + "/api/" + dhis2ApiVersion + "/enrollments.json?program=EPDyQuoRnXk&programStatus=ACTIVE&trackedEntityInstance=JeR2Ul4mZfx&ouMode=ACCESSIBLE&fields=:all&order=lastUpdated:desc&pageSize=1" ) ) .andRespond( withStatus( HttpStatus.UNAUTHORIZED ) ); Observation observation = (Observation) getFhirContext().newJsonParser().parseResource( @@ -306,16 +279,16 @@ public void createObservationInvalidAuthorization() throws Exception public void createObservation() throws Exception { expectProgramStageMetadataRequests(); - userDhis2Server.expect( ExpectedCount.between( 1, 2 ), method( HttpMethod.GET ) ).andExpect( header( "Authorization", "Basic Zmhpcl9jbGllbnQ6Zmhpcl9jbGllbnRfMQ==" ) ) - .andExpect( requestTo( dhis2BaseUrl + "/api/" + dhis2ApiVersion + "/trackedEntityInstances/JeR2Ul4mZfx.json?" + - "fields=deleted,trackedEntityInstance,trackedEntityType,orgUnit,coordinates,lastUpdated,attributes%5Battribute,value,lastUpdated,storedBy%5D" ) ) - .andRespond( withSuccess( IOUtils.resourceToString( "/org/dhis2/fhir/adapter/dhis/test/single-tei-15-get.json", StandardCharsets.UTF_8 ), MediaType.APPLICATION_JSON ) ); userDhis2Server.expect( ExpectedCount.between( 2, 4 ), requestTo( dhis2BaseUrl + "/api/" + dhis2ApiVersion + "/enrollments.json?program=EPDyQuoRnXk&programStatus=ACTIVE&trackedEntityInstance=JeR2Ul4mZfx&ouMode=ACCESSIBLE&fields=:all&" + "order=lastUpdated:desc&pageSize=1" ) ).andExpect( method( HttpMethod.GET ) ).andExpect( header( "Authorization", "Basic Zmhpcl9jbGllbnQ6Zmhpcl9jbGllbnRfMQ==" ) ) .andRespond( withSuccess( IOUtils.resourceToString( "/org/dhis2/fhir/adapter/dhis/test/default-enrollment-empty.json", StandardCharsets.UTF_8 ), MediaType.APPLICATION_JSON ) ); systemDhis2Server.expect( ExpectedCount.between( 0, 1 ), method( HttpMethod.GET ) ).andExpect( header( "Authorization", testConfiguration.getDhis2SystemAuthorization() ) ) .andExpect( requestTo( dhis2BaseUrl + "/api/" + dhis2ApiVersion + "/organisationUnits/ldXIdLNUNEp.json?fields=lastUpdated,id,code,name,shortName,displayName,level,openingDate,closedDate,coordinates,leaf,parent%5Bid%5D" ) ) .andRespond( withSuccess( IOUtils.resourceToString( "/org/dhis2/fhir/adapter/dhis/test/single-org-unit-OU_4567.json", StandardCharsets.UTF_8 ), MediaType.APPLICATION_JSON ) ); + userDhis2Server.expect( ExpectedCount.between( 1, 1 ), method( HttpMethod.GET ) ).andExpect( header( "Authorization", "Basic Zmhpcl9jbGllbnQ6Zmhpcl9jbGllbnRfMQ==" ) ) + .andExpect( requestTo( dhis2BaseUrl + "/api/" + dhis2ApiVersion + "/trackedEntityInstances/JeR2Ul4mZfx.json?" + + "fields=deleted,trackedEntityInstance,trackedEntityType,orgUnit,coordinates,lastUpdated,attributes%5Battribute,value,lastUpdated,storedBy%5D" ) ) + .andRespond( withSuccess( IOUtils.resourceToString( "/org/dhis2/fhir/adapter/dhis/test/single-tei-15-get.json", StandardCharsets.UTF_8 ), MediaType.APPLICATION_JSON ) ); userDhis2Server.expect( ExpectedCount.once(), requestTo( dhis2BaseUrl + "/api/" + dhis2ApiVersion + "/enrollments.json?strategy=CREATE" ) ).andExpect( method( HttpMethod.POST ) ) .andExpect( header( "Authorization", "Basic Zmhpcl9jbGllbnQ6Zmhpcl9jbGllbnRfMQ==" ) ) .andExpect( content().contentTypeCompatibleWith( MediaType.APPLICATION_JSON ) ) @@ -340,16 +313,16 @@ public void createObservation() throws Exception public void createObservationCodeSetCode() throws Exception { expectProgramStageMetadataRequests(); - userDhis2Server.expect( ExpectedCount.between( 1, 2 ), method( HttpMethod.GET ) ).andExpect( header( "Authorization", "Basic Zmhpcl9jbGllbnQ6Zmhpcl9jbGllbnRfMQ==" ) ) - .andExpect( requestTo( dhis2BaseUrl + "/api/" + dhis2ApiVersion + "/trackedEntityInstances/JeR2Ul4mZfx.json?" + - "fields=deleted,trackedEntityInstance,trackedEntityType,orgUnit,coordinates,lastUpdated,attributes%5Battribute,value,lastUpdated,storedBy%5D" ) ) - .andRespond( withSuccess( IOUtils.resourceToString( "/org/dhis2/fhir/adapter/dhis/test/single-tei-15-get.json", StandardCharsets.UTF_8 ), MediaType.APPLICATION_JSON ) ); userDhis2Server.expect( ExpectedCount.between( 2, 4 ), requestTo( dhis2BaseUrl + "/api/" + dhis2ApiVersion + "/enrollments.json?program=EPDyQuoRnXk&programStatus=ACTIVE&trackedEntityInstance=JeR2Ul4mZfx&ouMode=ACCESSIBLE&fields=:all&" + "order=lastUpdated:desc&pageSize=1" ) ).andExpect( method( HttpMethod.GET ) ).andExpect( header( "Authorization", "Basic Zmhpcl9jbGllbnQ6Zmhpcl9jbGllbnRfMQ==" ) ) .andRespond( withSuccess( IOUtils.resourceToString( "/org/dhis2/fhir/adapter/dhis/test/default-enrollment-empty.json", StandardCharsets.UTF_8 ), MediaType.APPLICATION_JSON ) ); systemDhis2Server.expect( ExpectedCount.between( 0, 1 ), method( HttpMethod.GET ) ).andExpect( header( "Authorization", testConfiguration.getDhis2SystemAuthorization() ) ) .andExpect( requestTo( dhis2BaseUrl + "/api/" + dhis2ApiVersion + "/organisationUnits/ldXIdLNUNEp.json?fields=lastUpdated,id,code,name,shortName,displayName,level,openingDate,closedDate,coordinates,leaf,parent%5Bid%5D" ) ) .andRespond( withSuccess( IOUtils.resourceToString( "/org/dhis2/fhir/adapter/dhis/test/single-org-unit-OU_4567.json", StandardCharsets.UTF_8 ), MediaType.APPLICATION_JSON ) ); + userDhis2Server.expect( ExpectedCount.between( 1, 1 ), method( HttpMethod.GET ) ).andExpect( header( "Authorization", "Basic Zmhpcl9jbGllbnQ6Zmhpcl9jbGllbnRfMQ==" ) ) + .andExpect( requestTo( dhis2BaseUrl + "/api/" + dhis2ApiVersion + "/trackedEntityInstances/JeR2Ul4mZfx.json?" + + "fields=deleted,trackedEntityInstance,trackedEntityType,orgUnit,coordinates,lastUpdated,attributes%5Battribute,value,lastUpdated,storedBy%5D" ) ) + .andRespond( withSuccess( IOUtils.resourceToString( "/org/dhis2/fhir/adapter/dhis/test/single-tei-15-get.json", StandardCharsets.UTF_8 ), MediaType.APPLICATION_JSON ) ); userDhis2Server.expect( ExpectedCount.once(), requestTo( dhis2BaseUrl + "/api/" + dhis2ApiVersion + "/enrollments.json?strategy=CREATE" ) ).andExpect( method( HttpMethod.POST ) ) .andExpect( header( "Authorization", "Basic Zmhpcl9jbGllbnQ6Zmhpcl9jbGllbnRfMQ==" ) ) .andExpect( content().contentTypeCompatibleWith( MediaType.APPLICATION_JSON ) ) @@ -374,16 +347,16 @@ public void createObservationCodeSetCode() throws Exception public void createObservationIdentifierReference() throws Exception { expectProgramStageMetadataRequests(); - userDhis2Server.expect( ExpectedCount.between( 1, 2 ), method( HttpMethod.GET ) ).andExpect( header( "Authorization", "Basic Zmhpcl9jbGllbnQ6Zmhpcl9jbGllbnRfMQ==" ) ) - .andExpect( requestTo( dhis2BaseUrl + "/api/" + dhis2ApiVersion + "/trackedEntityInstances/JeR2Ul4mZfx.json?" + - "fields=deleted,trackedEntityInstance,trackedEntityType,orgUnit,coordinates,lastUpdated,attributes%5Battribute,value,lastUpdated,storedBy%5D" ) ) - .andRespond( withSuccess( IOUtils.resourceToString( "/org/dhis2/fhir/adapter/dhis/test/single-tei-15-get.json", StandardCharsets.UTF_8 ), MediaType.APPLICATION_JSON ) ); userDhis2Server.expect( ExpectedCount.between( 2, 4 ), requestTo( dhis2BaseUrl + "/api/" + dhis2ApiVersion + "/enrollments.json?program=EPDyQuoRnXk&programStatus=ACTIVE&trackedEntityInstance=JeR2Ul4mZfx&ouMode=ACCESSIBLE&fields=:all&" + "order=lastUpdated:desc&pageSize=1" ) ).andExpect( method( HttpMethod.GET ) ).andExpect( header( "Authorization", "Basic Zmhpcl9jbGllbnQ6Zmhpcl9jbGllbnRfMQ==" ) ) .andRespond( withSuccess( IOUtils.resourceToString( "/org/dhis2/fhir/adapter/dhis/test/default-enrollment-empty.json", StandardCharsets.UTF_8 ), MediaType.APPLICATION_JSON ) ); systemDhis2Server.expect( ExpectedCount.between( 0, 1 ), method( HttpMethod.GET ) ).andExpect( header( "Authorization", testConfiguration.getDhis2SystemAuthorization() ) ) .andExpect( requestTo( dhis2BaseUrl + "/api/" + dhis2ApiVersion + "/organisationUnits/ldXIdLNUNEp.json?fields=lastUpdated,id,code,name,shortName,displayName,level,openingDate,closedDate,coordinates,leaf,parent%5Bid%5D" ) ) .andRespond( withSuccess( IOUtils.resourceToString( "/org/dhis2/fhir/adapter/dhis/test/single-org-unit-OU_4567.json", StandardCharsets.UTF_8 ), MediaType.APPLICATION_JSON ) ); + userDhis2Server.expect( ExpectedCount.between( 1, 1 ), method( HttpMethod.GET ) ).andExpect( header( "Authorization", "Basic Zmhpcl9jbGllbnQ6Zmhpcl9jbGllbnRfMQ==" ) ) + .andExpect( requestTo( dhis2BaseUrl + "/api/" + dhis2ApiVersion + "/trackedEntityInstances/JeR2Ul4mZfx.json?" + + "fields=deleted,trackedEntityInstance,trackedEntityType,orgUnit,coordinates,lastUpdated,attributes%5Battribute,value,lastUpdated,storedBy%5D" ) ) + .andRespond( withSuccess( IOUtils.resourceToString( "/org/dhis2/fhir/adapter/dhis/test/single-tei-15-get.json", StandardCharsets.UTF_8 ), MediaType.APPLICATION_JSON ) ); userDhis2Server.expect( ExpectedCount.once(), requestTo( dhis2BaseUrl + "/api/" + dhis2ApiVersion + "/enrollments.json?strategy=CREATE" ) ).andExpect( method( HttpMethod.POST ) ) .andExpect( header( "Authorization", "Basic Zmhpcl9jbGllbnQ6Zmhpcl9jbGllbnRfMQ==" ) ) .andExpect( content().contentTypeCompatibleWith( MediaType.APPLICATION_JSON ) ) @@ -427,8 +400,8 @@ public void updateObservationInvalidAuthorization() throws Exception { expectProgramStageMetadataRequests(); userDhis2Server.expect( ExpectedCount.between( 1, 2 ), method( HttpMethod.GET ) ).andExpect( header( "Authorization", "Basic Zmhpcl9jbGllbnQ6aW52YWxpZF8x" ) ) - .andExpect( requestTo( dhis2BaseUrl + "/api/" + dhis2ApiVersion + "/trackedEntityInstances/JeR2Ul4mZfx.json?" + - "fields=deleted,trackedEntityInstance,trackedEntityType,orgUnit,coordinates,lastUpdated,attributes%5Battribute,value,lastUpdated,storedBy%5D" ) ) + .andExpect( requestTo( dhis2BaseUrl + "/api/" + dhis2ApiVersion + "/events/deR4kl4mnf7.json?fields=deleted,event,orgUnit,program,enrollment,trackedEntityInstance,programStage,status,eventDate,dueDate,coordinate,lastUpdated," + + "dataValues%5BdataElement,value,providedElsewhere,lastUpdated,storedBy%5D" ) ) .andRespond( withStatus( HttpStatus.UNAUTHORIZED ) ); Observation observation = (Observation) getFhirContext().newJsonParser().parseResource( @@ -449,10 +422,6 @@ public void updateObservation() throws Exception systemDhis2Server.expect( ExpectedCount.between( 0, 1 ), method( HttpMethod.GET ) ).andExpect( header( "Authorization", testConfiguration.getDhis2SystemAuthorization() ) ) .andExpect( requestTo( dhis2BaseUrl + "/api/" + dhis2ApiVersion + "/organisationUnits.json?paging=false&fields=lastUpdated,id,code,name,shortName,displayName,level,openingDate,closedDate,coordinates,leaf,parent%5Bid%5D&filter=code:eq:OU_4567" ) ) .andRespond( withSuccess( IOUtils.resourceToString( "/org/dhis2/fhir/adapter/dhis/test/default-org-unit-OU_4567.json", StandardCharsets.UTF_8 ), MediaType.APPLICATION_JSON ) ); - userDhis2Server.expect( ExpectedCount.between( 1, 2 ), method( HttpMethod.GET ) ).andExpect( header( "Authorization", "Basic Zmhpcl9jbGllbnQ6Zmhpcl9jbGllbnRfMQ==" ) ) - .andExpect( requestTo( dhis2BaseUrl + "/api/" + dhis2ApiVersion + "/trackedEntityInstances/JeR2Ul4mZfx.json?" + - "fields=deleted,trackedEntityInstance,trackedEntityType,orgUnit,coordinates,lastUpdated,attributes%5Battribute,value,lastUpdated,storedBy%5D" ) ) - .andRespond( withSuccess( IOUtils.resourceToString( "/org/dhis2/fhir/adapter/dhis/test/single-tei-15-get.json", StandardCharsets.UTF_8 ), MediaType.APPLICATION_JSON ) ); systemDhis2Server.expect( ExpectedCount.between( 0, 1 ), method( HttpMethod.GET ) ).andExpect( header( "Authorization", testConfiguration.getDhis2SystemAuthorization() ) ) .andExpect( requestTo( dhis2BaseUrl + "/api/" + dhis2ApiVersion + "/organisationUnits/ldXIdLNUNEn.json?fields=lastUpdated,id,code,name,shortName,displayName,level,openingDate,closedDate,coordinates,leaf,parent%5Bid%5D" ) ) .andRespond( withSuccess( IOUtils.resourceToString( "/org/dhis2/fhir/adapter/dhis/test/default-org-unit-OU_1234.json", StandardCharsets.UTF_8 ), MediaType.APPLICATION_JSON ) ); diff --git a/dhis/src/main/java/org/dhis2/fhir/adapter/dhis/local/LocalDhisResourceRepositoryTemplate.java b/dhis/src/main/java/org/dhis2/fhir/adapter/dhis/local/LocalDhisResourceRepositoryTemplate.java index 28178635..6b28f3d1 100644 --- a/dhis/src/main/java/org/dhis2/fhir/adapter/dhis/local/LocalDhisResourceRepositoryTemplate.java +++ b/dhis/src/main/java/org/dhis2/fhir/adapter/dhis/local/LocalDhisResourceRepositoryTemplate.java @@ -124,6 +124,15 @@ public Optional findOneById( @Nonnull String id, @Nonnull Function return Optional.ofNullable( result ); } + public boolean isLocal( @Nonnull String id ) + { + final RequestCacheContext context = requestCacheService.getCurrentRequestCacheContext(); + final Optional> repository = getRepository( context ); + + return repository.map( r -> r.findOneById( id ).map( DhisResource::isLocal ).orElse( false ) ).orElse( false ); + + } + @Nonnull public Collection find( @Nonnull Predicate filter, @Nonnull Supplier> collectionSupplier, boolean localOnly, @Nonnull String methodName, @Nonnull Object... args ) { diff --git a/dhis/src/main/java/org/dhis2/fhir/adapter/dhis/model/IdentifiedDhisResource.java b/dhis/src/main/java/org/dhis2/fhir/adapter/dhis/model/IdentifiedDhisResource.java new file mode 100644 index 00000000..c14e8078 --- /dev/null +++ b/dhis/src/main/java/org/dhis2/fhir/adapter/dhis/model/IdentifiedDhisResource.java @@ -0,0 +1,77 @@ +package org.dhis2.fhir.adapter.dhis.model; + +/* + * Copyright (c) 2004-2019, University of Oslo + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * Neither the name of the HISP project nor the names of its contributors may + * be used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +import javax.annotation.Nonnull; +import javax.annotation.Nullable; +import java.io.Serializable; + +/** + * Contains either the ID of the resource or the resource itself. + * + * @param the concrete type of the DHIS2 resource. + * @author volsch + */ +public class IdentifiedDhisResource implements Serializable +{ + private static final long serialVersionUID = -5373285083896395201L; + + private final String id; + + private final T resource; + + public IdentifiedDhisResource( @Nonnull String id ) + { + this.id = id; + this.resource = null; + } + + public IdentifiedDhisResource( @Nonnull T resource ) + { + this.resource = resource; + this.id = null; + } + + @Nullable + public String getId() + { + return id; + } + + @Nullable + public T getResource() + { + return resource; + } + + public boolean hasResource() + { + return resource != null; + } +} diff --git a/dhis/src/main/java/org/dhis2/fhir/adapter/dhis/tracker/trackedentity/IdentifiedTrackedEntityInstance.java b/dhis/src/main/java/org/dhis2/fhir/adapter/dhis/tracker/trackedentity/IdentifiedTrackedEntityInstance.java new file mode 100644 index 00000000..b2168fd4 --- /dev/null +++ b/dhis/src/main/java/org/dhis2/fhir/adapter/dhis/tracker/trackedentity/IdentifiedTrackedEntityInstance.java @@ -0,0 +1,53 @@ +package org.dhis2.fhir.adapter.dhis.tracker.trackedentity; + +/* + * Copyright (c) 2004-2019, University of Oslo + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * Neither the name of the HISP project nor the names of its contributors may + * be used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +import org.dhis2.fhir.adapter.dhis.model.IdentifiedDhisResource; + +import javax.annotation.Nonnull; + +/** + * Identified tracked entity instance. + * + * @author volsch + */ +public class IdentifiedTrackedEntityInstance extends IdentifiedDhisResource +{ + private static final long serialVersionUID = 7975425868929861631L; + + public IdentifiedTrackedEntityInstance( @Nonnull String id ) + { + super( id ); + } + + public IdentifiedTrackedEntityInstance( @Nonnull TrackedEntityInstance resource ) + { + super( resource ); + } +} diff --git a/dhis/src/main/java/org/dhis2/fhir/adapter/dhis/tracker/trackedentity/TrackedEntityService.java b/dhis/src/main/java/org/dhis2/fhir/adapter/dhis/tracker/trackedentity/TrackedEntityService.java index 5f2e8f28..34688f99 100644 --- a/dhis/src/main/java/org/dhis2/fhir/adapter/dhis/tracker/trackedentity/TrackedEntityService.java +++ b/dhis/src/main/java/org/dhis2/fhir/adapter/dhis/tracker/trackedentity/TrackedEntityService.java @@ -57,6 +57,8 @@ void updateGeneratedValues( @Nonnull TrackedEntityInstance trackedEntityInstance @Nonnull Optional findOneById( @Nonnull String id ); + boolean isLocal( @Nonnull String id ); + @Nonnull Collection findByAttrValueRefreshed( @Nonnull String typeId, @Nonnull String attributeId, @Nonnull String value, int maxResult ); diff --git a/dhis/src/main/java/org/dhis2/fhir/adapter/dhis/tracker/trackedentity/impl/TrackedEntityServiceImpl.java b/dhis/src/main/java/org/dhis2/fhir/adapter/dhis/tracker/trackedentity/impl/TrackedEntityServiceImpl.java index e989cd56..beda50e6 100644 --- a/dhis/src/main/java/org/dhis2/fhir/adapter/dhis/tracker/trackedentity/impl/TrackedEntityServiceImpl.java +++ b/dhis/src/main/java/org/dhis2/fhir/adapter/dhis/tracker/trackedentity/impl/TrackedEntityServiceImpl.java @@ -225,6 +225,12 @@ public Optional findOneById( @Nonnull String id ) return findOneByIdRefreshed( id ); } + @Override + public boolean isLocal( @Nonnull String id ) + { + return resourceRepositoryTemplate.isLocal( id ); + } + @HystrixCommand( ignoreExceptions = UnauthorizedException.class ) @Nonnull @Override diff --git a/dhis/src/test/java/org/dhis2/fhir/adapter/dhis/local/LocalDhisResourceRepositoryTemplateTest.java b/dhis/src/test/java/org/dhis2/fhir/adapter/dhis/local/LocalDhisResourceRepositoryTemplateTest.java index 30f95bff..89b3c12b 100644 --- a/dhis/src/test/java/org/dhis2/fhir/adapter/dhis/local/LocalDhisResourceRepositoryTemplateTest.java +++ b/dhis/src/test/java/org/dhis2/fhir/adapter/dhis/local/LocalDhisResourceRepositoryTemplateTest.java @@ -174,6 +174,68 @@ public void deleteByIdLocal() Mockito.verifyZeroInteractions( persistCallback ); } + @Test + public void isLocal() + { + final TrackedEntityInstance tei = new TrackedEntityInstance(); + final Object resourceKey = new Object(); + + tei.setLocal( true ); + + Mockito.doReturn( requestCacheContext ).when( requestCacheService ).getCurrentRequestCacheContext(); + Mockito.doReturn( resourceRepositoryContainer ).when( requestCacheContext ).getAttribute( + Mockito.eq( LocalDhisResourceRepositoryTemplate.CONTAINER_REQUEST_CACHE_ATTRIBUTE_NAME ), + Mockito.eq( LocalDhisResourceRepositoryContainer.class ) ); + Mockito.doReturn( resourceKey ).when( requestCacheContext ).getAttribute( + Mockito.eq( LocalDhisResourceRepositoryTemplate.RESOURCE_KEY_REQUEST_CACHE_ATTRIBUTE_NAME ), Mockito.eq( Object.class ) ); + Mockito.doReturn( resourceRepository ).when( resourceRepositoryContainer ).getRepository( Mockito.eq( TrackedEntityInstance.class ), Mockito.same( persistCallback ) ); + Mockito.doReturn( Optional.of( tei ) ).when( resourceRepository ).findOneById( Mockito.eq( "h1234567890" ) ); + + Assert.assertTrue( template.isLocal( "h1234567890" ) ); + + Mockito.verifyZeroInteractions( persistCallback ); + } + + @Test + public void isLocalIncludedNot() + { + final TrackedEntityInstance tei = new TrackedEntityInstance(); + final Object resourceKey = new Object(); + + Mockito.doReturn( requestCacheContext ).when( requestCacheService ).getCurrentRequestCacheContext(); + Mockito.doReturn( resourceRepositoryContainer ).when( requestCacheContext ).getAttribute( + Mockito.eq( LocalDhisResourceRepositoryTemplate.CONTAINER_REQUEST_CACHE_ATTRIBUTE_NAME ), + Mockito.eq( LocalDhisResourceRepositoryContainer.class ) ); + Mockito.doReturn( resourceKey ).when( requestCacheContext ).getAttribute( + Mockito.eq( LocalDhisResourceRepositoryTemplate.RESOURCE_KEY_REQUEST_CACHE_ATTRIBUTE_NAME ), Mockito.eq( Object.class ) ); + Mockito.doReturn( resourceRepository ).when( resourceRepositoryContainer ).getRepository( Mockito.eq( TrackedEntityInstance.class ), Mockito.same( persistCallback ) ); + Mockito.doReturn( Optional.of( tei ) ).when( resourceRepository ).findOneById( Mockito.eq( "h1234567890" ) ); + + Assert.assertFalse( template.isLocal( "h1234567890" ) ); + + Mockito.verifyZeroInteractions( persistCallback ); + } + + @Test + public void isLocalNotIncluded() + { + final TrackedEntityInstance tei = new TrackedEntityInstance(); + final Object resourceKey = new Object(); + + Mockito.doReturn( requestCacheContext ).when( requestCacheService ).getCurrentRequestCacheContext(); + Mockito.doReturn( resourceRepositoryContainer ).when( requestCacheContext ).getAttribute( + Mockito.eq( LocalDhisResourceRepositoryTemplate.CONTAINER_REQUEST_CACHE_ATTRIBUTE_NAME ), + Mockito.eq( LocalDhisResourceRepositoryContainer.class ) ); + Mockito.doReturn( resourceKey ).when( requestCacheContext ).getAttribute( + Mockito.eq( LocalDhisResourceRepositoryTemplate.RESOURCE_KEY_REQUEST_CACHE_ATTRIBUTE_NAME ), Mockito.eq( Object.class ) ); + Mockito.doReturn( resourceRepository ).when( resourceRepositoryContainer ).getRepository( Mockito.eq( TrackedEntityInstance.class ), Mockito.same( persistCallback ) ); + Mockito.doReturn( Optional.empty() ).when( resourceRepository ).findOneById( Mockito.eq( "h1234567890" ) ); + + Assert.assertFalse( template.isLocal( "h1234567890" ) ); + + Mockito.verifyZeroInteractions( persistCallback ); + } + @Test public void findOneByIdNoLocal() { diff --git a/dhis/src/test/java/org/dhis2/fhir/adapter/dhis/tracker/trackedentity/impl/TrackedEntityServiceImplTest.java b/dhis/src/test/java/org/dhis2/fhir/adapter/dhis/tracker/trackedentity/impl/TrackedEntityServiceImplTest.java index 185756ec..5f8b6dfe 100644 --- a/dhis/src/test/java/org/dhis2/fhir/adapter/dhis/tracker/trackedentity/impl/TrackedEntityServiceImplTest.java +++ b/dhis/src/test/java/org/dhis2/fhir/adapter/dhis/tracker/trackedentity/impl/TrackedEntityServiceImplTest.java @@ -154,6 +154,45 @@ public void findLocal() throws IOException mockServer.verify(); } + @Test + public void isLocal() throws IOException + { + mockServer.expect( ExpectedCount.once(), requestTo( "http://localhost:8080/api/trackedEntityInstances.json?strategy=CREATE" ) ) + .andExpect( content().contentTypeCompatibleWith( MediaType.APPLICATION_JSON ) ) + .andExpect( content().json( IOUtils.resourceToString( "/org/dhis2/fhir/adapter/dhis/tracker/program/impl/createTrackedEntityInstance.json", StandardCharsets.UTF_8 ) ) ) + .andExpect( method( HttpMethod.POST ) ).andRespond( withSuccess( IOUtils.resourceToByteArray( "/org/dhis2/fhir/adapter/dhis/tracker/program/impl/createTrackedEntityInstance-response.json" ), MediaType.APPLICATION_JSON ) ); + + final TrackedEntityInstance trackedEntityInstance = new TrackedEntityInstance( trackedEntityType, "Jskdsjeua1s", true ); + trackedEntityInstance.setOrgUnitId( "pMEnu7BjqMz" ); + + try ( final RequestCacheContext cacheContext = requestCacheService.createRequestCacheContext() ) + { + cacheContext.setAttribute( LocalDhisResourceRepositoryTemplate.CONTAINER_REQUEST_CACHE_ATTRIBUTE_NAME, + new LocalDhisResourceRepositoryContainerImpl( Collections.singleton( TrackedEntityInstance.class ) ) ); + + final TrackedEntityInstance createdTrackedEntityInstance = service.createOrUpdate( trackedEntityInstance ); + Assert.assertTrue( service.isLocal( "Jskdsjeua1s" ) ); + } + } + + @Test + public void isLocalNot() + { + try ( final RequestCacheContext cacheContext = requestCacheService.createRequestCacheContext() ) + { + Assert.assertFalse( service.isLocal( "Jskdsjeua1s" ) ); + } + } + + @Test + public void isLocalNoContextNot() + { + try ( final RequestCacheContext cacheContext = requestCacheService.createRequestCacheContext() ) + { + Assert.assertFalse( service.isLocal( "Jskdsjeua1s" ) ); + } + } + @Test public void findOneById() throws IOException { diff --git a/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/DhisToFhirTransformerServiceImpl.java b/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/DhisToFhirTransformerServiceImpl.java index ef9c1666..64e391ac 100644 --- a/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/DhisToFhirTransformerServiceImpl.java +++ b/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/DhisToFhirTransformerServiceImpl.java @@ -220,7 +220,6 @@ public List> findAllRules( @Nonnull FhirClient public List resolveFhirReferences( @Nonnull FhirClient fhirClient, @Nonnull ScriptedDhisResource dhisResource, @Nullable Set fhirResourceTypes, int max ) { final WritableDhisRequest dhisRequest = new WritableDhisRequest( true, false, false ); - dhisRequest.setLastUpdated( dhisResource.getLastUpdated() ); dhisRequest.setResourceType( dhisResource.getResourceType() ); DhisToFhirTransformerRequest transformerRequest = createTransformerRequest( @@ -240,6 +239,8 @@ public List resolveFhirReferences( @Nonnull FhirClient fhirClien } else { + // retrieving last updated may initialize resource event if not required for simple FHIR ID + dhisRequest.setLastUpdated( dhisResource.getLastUpdated() ); final AbstractFhirResourceDhisToFhirTransformerUtils fhirResourceTransformerUtils = getFhirResourceTransformerUtils( fhirClient.getFhirVersion() ); do diff --git a/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/program/ProgramStageToFhirRequestResolver.java b/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/program/ProgramStageToFhirRequestResolver.java index 532e6a2c..d6fe3ea1 100644 --- a/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/program/ProgramStageToFhirRequestResolver.java +++ b/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/program/ProgramStageToFhirRequestResolver.java @@ -38,7 +38,6 @@ import org.dhis2.fhir.adapter.dhis.tracker.program.ProgramMetadataService; import org.dhis2.fhir.adapter.dhis.tracker.program.ProgramStage; import org.dhis2.fhir.adapter.dhis.tracker.trackedentity.TrackedEntityAttributes; -import org.dhis2.fhir.adapter.dhis.tracker.trackedentity.TrackedEntityInstance; import org.dhis2.fhir.adapter.dhis.tracker.trackedentity.TrackedEntityMetadataService; import org.dhis2.fhir.adapter.dhis.tracker.trackedentity.TrackedEntityService; import org.dhis2.fhir.adapter.dhis.tracker.trackedentity.TrackedEntityType; @@ -47,6 +46,7 @@ import org.dhis2.fhir.adapter.fhir.metadata.model.RuleInfo; import org.dhis2.fhir.adapter.fhir.metadata.repository.FhirClientRepository; import org.dhis2.fhir.adapter.fhir.metadata.repository.ProgramStageRuleRepository; +import org.dhis2.fhir.adapter.fhir.script.ScriptExecutionContext; import org.dhis2.fhir.adapter.fhir.transform.TransformerDataException; import org.dhis2.fhir.adapter.fhir.transform.dhis.impl.AbstractDhisToFhirRequestResolver; import org.dhis2.fhir.adapter.fhir.transform.dhis.impl.DhisToFhirRequestResolver; @@ -80,6 +80,8 @@ public class ProgramStageToFhirRequestResolver extends AbstractDhisToFhirRequest private final TrackedEntityMetadataService trackedEntityMetadataService; + private final ScriptExecutionContext scriptExecutionContext; + private final ValueConverter valueConverter; public ProgramStageToFhirRequestResolver( @@ -88,13 +90,16 @@ public ProgramStageToFhirRequestResolver( @Nonnull ProgramStageRuleRepository ruleRepository, @Nonnull TrackedEntityService trackedEntityService, @Nonnull TrackedEntityMetadataService trackedEntityMetadataService, + @Nonnull ScriptExecutionContext scriptExecutionContext, @Nonnull ValueConverter valueConverter ) { super( fhirClientRepository ); + this.programMetadataService = programMetadataService; this.ruleRepository = ruleRepository; this.trackedEntityService = trackedEntityService; this.trackedEntityMetadataService = trackedEntityMetadataService; + this.scriptExecutionContext = scriptExecutionContext; this.valueConverter = valueConverter; } @@ -110,8 +115,8 @@ public DhisResourceType getDhisResourceType() public List> resolveRules( @Nonnull ScriptedDhisResource dhisResource ) { final ScriptedEvent event = (ScriptedEvent) dhisResource; - return ruleRepository.findAllExp( event.getProgram().getAllReferences(), event.getProgramStage().getAllReferences(), null ).stream() - .sorted().collect( Collectors.toList() ); + return ruleRepository.findAllExp( event.getProgram().getAllReferences(), event.getProgramStage().getAllReferences(), null ) + .stream().sorted().collect( Collectors.toList() ); } @Nonnull @@ -135,22 +140,18 @@ public ScriptedDhisResource convert( @Nonnull DhisResource dhisResource, @Nonnul { final Event event = (Event) dhisResource; - final TrackedEntityInstance tei = trackedEntityService.findOneById( event.getTrackedEntityInstanceId() ) - .orElseThrow( () -> new TransformerDataException( "Tracked entity instance " + event.getTrackedEntityInstanceId() + - " of event " + event.getId() + " could not be found." ) ); - final TrackedEntityType trackedEntityType = trackedEntityMetadataService.findTypeByReference( new Reference( tei.getTypeId(), ReferenceType.ID ) ) - .orElseThrow( () -> new TransformerDataException( "Tracked entity type " + tei.getTypeId() + " of tracked entity instance " + - tei.getId() + " could not be found." ) ); - final TrackedEntityAttributes trackedEntityAttributes = trackedEntityMetadataService.getAttributes(); - final ScriptedTrackedEntityInstance scriptedTrackedEntityInstance = new ImmutableScriptedTrackedEntityInstance( - new WritableScriptedTrackedEntityInstance( trackedEntityAttributes, trackedEntityType, tei, valueConverter ) ); - final Program program = programMetadataService.findMetadataByReference( new Reference( event.getProgramId(), ReferenceType.ID ) ) .orElseThrow( () -> new TransformerDataException( "Program " + event.getProgramId() + " of event " + event.getId() + " could not be found." ) ); final ProgramStage programStage = program.getOptionalStage( new Reference( event.getProgramStageId(), ReferenceType.ID ) ) .orElseThrow( () -> new TransformerDataException( "Program stage " + event.getProgramStageId() + " of event " + event.getId() + " could not be found as part of program " + event.getProgramId() + "." ) ); - return new ImmutableScriptedEvent( new WritableScriptedEvent( program, programStage, event, - scriptedTrackedEntityInstance, valueConverter ) ); + final TrackedEntityType trackedEntityType = trackedEntityMetadataService.findTypeByReference( Reference.createIdReference( program.getTrackedEntityTypeId() ) ) + .orElseThrow( () -> new TransformerDataException( "Tracked entity type " + program.getTrackedEntityTypeId() + " of program " + program.getId() + "." ) ); + + final TrackedEntityAttributes trackedEntityAttributes = trackedEntityMetadataService.getAttributes(); + final ScriptedTrackedEntityInstance scriptedTrackedEntityInstance = new ImmutableScriptedTrackedEntityInstance( new WritableScriptedTrackedEntityInstance( + trackedEntityMetadataService, trackedEntityService, trackedEntityAttributes, trackedEntityType, event.getTrackedEntityInstanceId(), scriptExecutionContext, valueConverter ) ); + + return new ImmutableScriptedEvent( new WritableScriptedEvent( program, programStage, event, scriptedTrackedEntityInstance, valueConverter ) ); } } diff --git a/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/program/ProgramStageToFhirTransformer.java b/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/program/ProgramStageToFhirTransformer.java index 5754fc6f..980401ed 100644 --- a/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/program/ProgramStageToFhirTransformer.java +++ b/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/program/ProgramStageToFhirTransformer.java @@ -125,12 +125,14 @@ public DhisToFhirTransformOutcome transform( @Nonnull RuleInfo ruleInfo, @Nonnull Map scriptVariables ) throws TransformerException { final Map variables = new HashMap<>( scriptVariables ); + if ( !addScriptVariables( variables, input ) ) { return null; } final FhirResourceMapping resourceMapping = getResourceMapping( ruleInfo ); + if ( isDataAbsent( context, input, ruleInfo ) ) { return handleDataAbsent( fhirClient, context, ruleInfo, resourceMapping, variables ); @@ -139,20 +141,24 @@ public DhisToFhirTransformOutcome transform( final IBaseResource trackedEntityFhirResource = getTrackedEntityFhirResource( fhirClient, context, new RuleInfo<>( ruleInfo.getRule().getProgramStage().getProgram().getTrackedEntityRule(), Collections.emptyList() ), ruleInfo.getRule().getProgramStage().getProgram().getTrackedEntityFhirResourceType(), - Objects.requireNonNull( input.getTrackedEntityInstance() ), variables ) - .orElseThrow( () -> new MissingDhisResourceException( Objects.requireNonNull( input.getTrackedEntityInstance().getResourceId() ) ) ); + Objects.requireNonNull( input.getTrackedEntityInstance() ), variables ).orElseThrow( + () -> new MissingDhisResourceException( Objects.requireNonNull( input.getTrackedEntityInstance().getResourceId() ) ) ); variables.put( ScriptVariable.TEI_FHIR_RESOURCE.getVariableName(), trackedEntityFhirResource ); final IBaseResource resource = getResource( fhirClient, context, ruleInfo, variables ).orElse( null ); + if ( resource == null ) { return null; } + final IBaseResource modifiedResource = cloneToModified( context, ruleInfo, resource, variables ); + if ( modifiedResource == null ) { return null; } + variables.put( ScriptVariable.OUTPUT.getVariableName(), modifiedResource ); if ( isDataDelete( context, ruleInfo, resourceMapping, variables ) ) diff --git a/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/trackedentity/TrackedEntityToFhirRequestResolver.java b/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/trackedentity/TrackedEntityToFhirRequestResolver.java index 371b33c0..d4a5142c 100644 --- a/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/trackedentity/TrackedEntityToFhirRequestResolver.java +++ b/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/trackedentity/TrackedEntityToFhirRequestResolver.java @@ -42,6 +42,7 @@ import org.dhis2.fhir.adapter.fhir.metadata.model.TrackedEntityRule; import org.dhis2.fhir.adapter.fhir.metadata.repository.FhirClientRepository; import org.dhis2.fhir.adapter.fhir.metadata.repository.TrackedEntityRuleRepository; +import org.dhis2.fhir.adapter.fhir.script.ScriptExecutionContext; import org.dhis2.fhir.adapter.fhir.transform.TransformerDataException; import org.dhis2.fhir.adapter.fhir.transform.dhis.impl.AbstractDhisToFhirRequestResolver; import org.dhis2.fhir.adapter.fhir.transform.dhis.impl.DhisToFhirRequestResolver; @@ -69,17 +70,22 @@ public class TrackedEntityToFhirRequestResolver extends AbstractDhisToFhirReques private final TrackedEntityRuleRepository ruleRepository; + private final ScriptExecutionContext scriptExecutionContext; + private final ValueConverter valueConverter; public TrackedEntityToFhirRequestResolver( @Nonnull FhirClientRepository fhirClientRepository, @Nonnull TrackedEntityMetadataService trackedEntityMetadataService, @Nonnull TrackedEntityRuleRepository ruleRepository, + @Nonnull ScriptExecutionContext scriptExecutionContext, @Nonnull ValueConverter valueConverter ) { super( fhirClientRepository ); + this.trackedEntityMetadataService = trackedEntityMetadataService; this.ruleRepository = ruleRepository; + this.scriptExecutionContext = scriptExecutionContext; this.valueConverter = valueConverter; } @@ -95,6 +101,7 @@ public DhisResourceType getDhisResourceType() public List> resolveRules( @Nonnull ScriptedDhisResource dhisResource ) { final ScriptedTrackedEntityInstance tei = (ScriptedTrackedEntityInstance) dhisResource; + return ruleRepository.findAllByType( tei.getType().getAllReferences() ).stream() .sorted( DhisToFhirRuleComparator.INSTANCE ).collect( Collectors.toList() ); } @@ -105,6 +112,7 @@ public List> resolveRules( @Nonnull ScriptedDhi { final ScriptedTrackedEntityInstance tei = (ScriptedTrackedEntityInstance) dhisResource; final TrackedEntityType type = tei.getType(); + return rules.stream().map( ri -> new RuleInfo<>( (TrackedEntityRule) ri.getRule(), ri.getDhisDataReferences() ) ) .filter( ri -> type.isReference( ri.getRule().getTrackedEntity().getTrackedEntityReference() ) && ri.getRule().getTrackedEntity().isEnabled() && ri.getRule().getTrackedEntity().isExpEnabled() ) @@ -115,12 +123,13 @@ public List> resolveRules( @Nonnull ScriptedDhi @Override public ScriptedDhisResource convert( @Nonnull DhisResource dhisResource, @Nonnull DhisRequest dhisRequest ) { - final TrackedEntityInstance tei = (TrackedEntityInstance) dhisResource; - final TrackedEntityType trackedEntityType = trackedEntityMetadataService.findTypeByReference( new Reference( tei.getTypeId(), ReferenceType.ID ) ) - .orElseThrow( () -> new TransformerDataException( "Tracked entity type " + tei.getTypeId() + " of tracked entity instance " + - tei.getId() + " could not be found." ) ); + final TrackedEntityInstance trackedEntityInstance = (TrackedEntityInstance) dhisResource; + final TrackedEntityType trackedEntityType = trackedEntityMetadataService.findTypeByReference( new Reference( trackedEntityInstance.getTypeId(), ReferenceType.ID ) ) + .orElseThrow( () -> new TransformerDataException( "Tracked entity type " + trackedEntityInstance.getTypeId() + + " of tracked entity instance " + trackedEntityInstance.getId() + " could not be found." ) ); final TrackedEntityAttributes trackedEntityAttributes = trackedEntityMetadataService.getAttributes(); + return new ImmutableScriptedTrackedEntityInstance( new WritableScriptedTrackedEntityInstance( - trackedEntityAttributes, trackedEntityType, tei, valueConverter ) ); + trackedEntityAttributes, trackedEntityType, trackedEntityInstance, scriptExecutionContext, valueConverter ) ); } } diff --git a/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/fhir/impl/AbstractFhirToDhisTransformer.java b/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/fhir/impl/AbstractFhirToDhisTransformer.java index 1a13cf19..8b93e04a 100644 --- a/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/fhir/impl/AbstractFhirToDhisTransformer.java +++ b/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/fhir/impl/AbstractFhirToDhisTransformer.java @@ -29,13 +29,16 @@ */ import org.apache.commons.lang3.StringUtils; +import org.dhis2.fhir.adapter.dhis.converter.ValueConverter; import org.dhis2.fhir.adapter.dhis.model.DhisResource; import org.dhis2.fhir.adapter.dhis.model.Reference; import org.dhis2.fhir.adapter.dhis.orgunit.OrganizationUnit; import org.dhis2.fhir.adapter.dhis.orgunit.OrganizationUnitService; +import org.dhis2.fhir.adapter.dhis.tracker.trackedentity.IdentifiedTrackedEntityInstance; import org.dhis2.fhir.adapter.dhis.tracker.trackedentity.TrackedEntityAttribute; import org.dhis2.fhir.adapter.dhis.tracker.trackedentity.TrackedEntityAttributes; import org.dhis2.fhir.adapter.dhis.tracker.trackedentity.TrackedEntityInstance; +import org.dhis2.fhir.adapter.dhis.tracker.trackedentity.TrackedEntityMetadataService; import org.dhis2.fhir.adapter.dhis.tracker.trackedentity.TrackedEntityService; import org.dhis2.fhir.adapter.dhis.tracker.trackedentity.TrackedEntityType; import org.dhis2.fhir.adapter.fhir.data.repository.FhirDhisAssignmentRepository; @@ -48,6 +51,7 @@ import org.dhis2.fhir.adapter.fhir.metadata.model.ScriptVariable; import org.dhis2.fhir.adapter.fhir.metadata.model.TrackedEntityRule; import org.dhis2.fhir.adapter.fhir.repository.DhisFhirResourceId; +import org.dhis2.fhir.adapter.fhir.script.ScriptExecutionContext; import org.dhis2.fhir.adapter.fhir.script.ScriptExecutionException; import org.dhis2.fhir.adapter.fhir.script.ScriptExecutor; import org.dhis2.fhir.adapter.fhir.transform.FatalTransformerException; @@ -61,6 +65,7 @@ import org.dhis2.fhir.adapter.fhir.transform.fhir.impl.util.AbstractFhirResourceFhirToDhisTransformerUtils; import org.dhis2.fhir.adapter.fhir.transform.fhir.impl.util.AbstractIdentifierFhirToDhisTransformerUtils; import org.dhis2.fhir.adapter.fhir.transform.fhir.model.ResourceSystem; +import org.dhis2.fhir.adapter.fhir.transform.scripted.WritableScriptedTrackedEntityInstance; import org.dhis2.fhir.adapter.fhir.transform.util.TransformerUtils; import org.dhis2.fhir.adapter.geo.Location; import org.hl7.fhir.instance.model.api.IBaseResource; @@ -93,15 +98,25 @@ public abstract class AbstractFhirToDhisTransformer trackedEntityService, - @Nonnull FhirDhisAssignmentRepository fhirDhisAssignmentRepository ) + private final ScriptExecutionContext scriptExecutionContext; + + private final ValueConverter valueConverter; + + public AbstractFhirToDhisTransformer( @Nonnull ScriptExecutor scriptExecutor, @Nonnull OrganizationUnitService organizationUnitService, + @Nonnull ObjectProvider trackedEntityMetadataService, @Nonnull ObjectProvider trackedEntityService, + @Nonnull FhirDhisAssignmentRepository fhirDhisAssignmentRepository, @Nonnull ScriptExecutionContext scriptExecutionContext, @Nonnull ValueConverter valueConverter ) { this.scriptExecutor = scriptExecutor; this.organizationUnitService = organizationUnitService; + this.trackedEntityMetadataService = trackedEntityMetadataService.getIfAvailable(); this.trackedEntityService = trackedEntityService.getIfAvailable(); this.fhirDhisAssignmentRepository = fhirDhisAssignmentRepository; + this.scriptExecutionContext = scriptExecutionContext; + this.valueConverter = valueConverter; } @Nullable @@ -126,9 +141,21 @@ protected TrackedEntityService getTrackedEntityService() throws FatalTransformer { throw new FatalTransformerException( "Tracked entity service has not been provided." ); } + return trackedEntityService; } + @Nonnull + protected TrackedEntityMetadataService getTrackedEntityMetadataService() throws FatalTransformerException + { + if ( trackedEntityMetadataService == null ) + { + throw new FatalTransformerException( "Tracked entity metadata service has not been provided." ); + } + + return trackedEntityMetadataService; + } + protected abstract boolean isAlwaysActiveResource( @Nonnull RuleInfo ruleInfo ); @Nonnull @@ -136,9 +163,11 @@ protected Optional getResource( @Nonnull FhirClientResource fhirClientResourc @Nonnull RuleInfo ruleInfo, @Nonnull Map scriptVariables ) throws TransformerException { boolean activeResource = true; + if ( context.getFhirRequest().isDhisFhirId() ) { final String id = getDhisId( context, ruleInfo ); + if ( id == null ) { activeResource = ( context.getFhirRequest().getRequestMethod() == null ) || @@ -147,24 +176,29 @@ protected Optional getResource( @Nonnull FhirClientResource fhirClientResourc else { R resource = getResourceById( id ).orElse( null ); + if ( resource != null ) { return Optional.of( resource ); } + activeResource = false; } } else { final R resource = getResourceByAssignment( fhirClientResource, context, ruleInfo, scriptVariables ).orElse( null ); + if ( resource != null ) { return Optional.of( resource ); } } + if ( activeResource || isAlwaysActiveResource( ruleInfo ) ) { final R resource = getActiveResource( context, ruleInfo, scriptVariables, false, true ).orElse( null ); + if ( resource != null ) { return Optional.of( resource ); @@ -172,18 +206,21 @@ protected Optional getResource( @Nonnull FhirClientResource fhirClientResourc } R resource = null; - if ( ( context.getFhirRequest().getRequestMethod() == null ) || context.getFhirRequest().getRequestMethod().isCreate() ) + if ( context.getFhirRequest().getRequestMethod() == null || context.getFhirRequest().getRequestMethod().isCreate() ) { final String id = getDhisId( context, ruleInfo ); resource = createResource( context, ruleInfo, id, scriptVariables, false, false ); - if ( (resource != null) && isSyncRequired( context, ruleInfo, scriptVariables ) ) + + if ( resource != null && isSyncRequired( context, ruleInfo, scriptVariables ) ) { // the active resource may have been created in the meantime resource = getActiveResource( context, ruleInfo, scriptVariables, true, true ).orElse( null ); + if ( resource != null ) { return Optional.of( resource ); } + resource = createResource( context, ruleInfo, id, scriptVariables, true, false ); } } @@ -208,10 +245,12 @@ protected Optional getResourceByAssignment( @Nonnull FhirClientResource fhirC final IBaseResource fhirResource = TransformerUtils.getScriptVariable( scriptVariables, ScriptVariable.INPUT, IBaseResource.class ); final String dhisResourceId = fhirDhisAssignmentRepository.findFirstDhisResourceId( ruleInfo.getRule(), fhirClientResource.getFhirClient(), fhirResource.getIdElement() ); + if ( dhisResourceId == null ) { return Optional.empty(); } + return findResourceById( context, ruleInfo, dhisResourceId, scriptVariables ); } @@ -262,11 +301,10 @@ protected Optional getOrgUnit( @Nonnull FhirToDhisTransformerC } @Nonnull - protected Optional getTrackedEntityInstance( @Nonnull FhirToDhisTransformerContext context, @Nonnull RuleInfo ruleInfo, + protected Optional getTrackedEntityInstance( @Nonnull FhirToDhisTransformerContext context, @Nonnull RuleInfo ruleInfo, @Nonnull FhirResourceMapping resourceMapping, @Nonnull Map scriptVariables, boolean sync ) { final IBaseResource baseResource = getTrackedEntityInstanceResource( context, ruleInfo, resourceMapping, scriptVariables ); - TrackedEntityInstance trackedEntityInstance; if ( baseResource == null ) { @@ -288,19 +326,35 @@ protected Optional getTrackedEntityInstance( @Nonnull Fhi switch ( reference.getType() ) { case ID: - final Optional optTrackedEntityInstance = sync ? - getTrackedEntityService().findOneByIdRefreshed( reference.getValue() ) : getTrackedEntityService().findOneById( reference.getValue() ); - trackedEntityInstance = optTrackedEntityInstance.orElseThrow( () -> new TrackedEntityInstanceNotFoundException( "Tracked entity instance for FHIR Resource ID " + baseResource.getIdElement() + " could not be found.", baseResource ) ); - break; + return Optional.of( new IdentifiedTrackedEntityInstance( reference.getValue() ) ); case CODE: - trackedEntityInstance = getTrackedEntityInstanceByIdentifier( context, ruleInfo, baseResource, scriptVariables, sync ) - .orElseThrow( () -> new TrackedEntityInstanceNotFoundException( "Tracked entity instance for FHIR Resource ID " + baseResource.getIdElement() + " could not be found.", baseResource ) ); - break; + return Optional.of( new IdentifiedTrackedEntityInstance( getTrackedEntityInstanceByIdentifier( context, ruleInfo, baseResource, scriptVariables, sync ) + .orElseThrow( () -> new TrackedEntityInstanceNotFoundException( "Tracked entity instance for FHIR Resource ID " + baseResource.getIdElement() + " could not be found.", baseResource ) ) ) ); default: throw new TransformerMappingException( "Reference type " + reference.getType() + " is not supported to retrieve tracked entity." ); } + } + + protected void addTrackedEntityScriptVariables( @Nonnull FhirToDhisTransformerContext context, @Nonnull RuleInfo ruleInfo, @Nonnull IdentifiedTrackedEntityInstance trackedEntityInstance, @Nonnull Map variables ) throws TransformerException + { + final WritableScriptedTrackedEntityInstance scriptedTrackedEntityInstance; + + if ( trackedEntityInstance.hasResource() ) + { + scriptedTrackedEntityInstance = new WritableScriptedTrackedEntityInstance( + TransformerUtils.getScriptVariable( variables, ScriptVariable.TRACKED_ENTITY_ATTRIBUTES, TrackedEntityAttributes.class ), + TransformerUtils.getScriptVariable( variables, ScriptVariable.TRACKED_ENTITY_TYPE, TrackedEntityType.class ), + Objects.requireNonNull( trackedEntityInstance.getResource() ), scriptExecutionContext, valueConverter ); + } + else + { + scriptedTrackedEntityInstance = new WritableScriptedTrackedEntityInstance( getTrackedEntityMetadataService(), getTrackedEntityService(), + TransformerUtils.getScriptVariable( variables, ScriptVariable.TRACKED_ENTITY_ATTRIBUTES, TrackedEntityAttributes.class ), + TransformerUtils.getOptionalScriptVariable( variables, ScriptVariable.TRACKED_ENTITY_TYPE, TrackedEntityType.class ), + Objects.requireNonNull( trackedEntityInstance.getId() ), scriptExecutionContext, valueConverter ); + } - return Optional.of( trackedEntityInstance ); + variables.put( ScriptVariable.TRACKED_ENTITY_INSTANCE.getVariableName(), scriptedTrackedEntityInstance ); } @Nullable diff --git a/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/fhir/impl/program/FhirToEnrollmentTransformer.java b/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/fhir/impl/program/FhirToEnrollmentTransformer.java index cfa8305e..57e0148d 100644 --- a/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/fhir/impl/program/FhirToEnrollmentTransformer.java +++ b/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/fhir/impl/program/FhirToEnrollmentTransformer.java @@ -38,8 +38,8 @@ import org.dhis2.fhir.adapter.dhis.tracker.program.EnrollmentStatus; import org.dhis2.fhir.adapter.dhis.tracker.program.Program; import org.dhis2.fhir.adapter.dhis.tracker.program.ProgramMetadataService; +import org.dhis2.fhir.adapter.dhis.tracker.trackedentity.IdentifiedTrackedEntityInstance; import org.dhis2.fhir.adapter.dhis.tracker.trackedentity.TrackedEntityAttributes; -import org.dhis2.fhir.adapter.dhis.tracker.trackedentity.TrackedEntityInstance; import org.dhis2.fhir.adapter.dhis.tracker.trackedentity.TrackedEntityMetadataService; import org.dhis2.fhir.adapter.dhis.tracker.trackedentity.TrackedEntityService; import org.dhis2.fhir.adapter.dhis.tracker.trackedentity.TrackedEntityType; @@ -55,8 +55,8 @@ import org.dhis2.fhir.adapter.fhir.metadata.repository.TrackedEntityRuleRepository; import org.dhis2.fhir.adapter.fhir.model.FhirVersion; import org.dhis2.fhir.adapter.fhir.repository.DhisFhirResourceId; +import org.dhis2.fhir.adapter.fhir.script.ScriptExecutionContext; import org.dhis2.fhir.adapter.fhir.script.ScriptExecutor; -import org.dhis2.fhir.adapter.fhir.transform.FatalTransformerException; import org.dhis2.fhir.adapter.fhir.transform.TransformerDataException; import org.dhis2.fhir.adapter.fhir.transform.TransformerException; import org.dhis2.fhir.adapter.fhir.transform.TransformerMappingException; @@ -66,7 +66,6 @@ import org.dhis2.fhir.adapter.fhir.transform.fhir.impl.AbstractFhirToDhisTransformer; import org.dhis2.fhir.adapter.fhir.transform.scripted.ScriptedTrackedEntityInstance; import org.dhis2.fhir.adapter.fhir.transform.scripted.WritableScriptedEnrollment; -import org.dhis2.fhir.adapter.fhir.transform.scripted.WritableScriptedTrackedEntityInstance; import org.dhis2.fhir.adapter.fhir.transform.util.TransformerUtils; import org.dhis2.fhir.adapter.model.ValueType; import org.dhis2.fhir.adapter.spring.StaticObjectProvider; @@ -88,6 +87,7 @@ /** * @author Charles Chigoriwa (ITINORDIC) + * @author volsch */ @Component public class FhirToEnrollmentTransformer extends AbstractFhirToDhisTransformer @@ -98,6 +98,8 @@ public class FhirToEnrollmentTransformer extends AbstractFhirToDhisTransformer( trackedEntityService ), fhirDhisAssignmentRepository ); + super( scriptExecutor, organizationUnitService, new StaticObjectProvider<>( trackedEntityMetadataService ), new StaticObjectProvider<>( trackedEntityService ), + fhirDhisAssignmentRepository, scriptExecutionContext, valueConverter ); this.programMetadataService = programMetadataService; this.enrollmentService = enrollmentService; this.trackedEntityMetadataService = trackedEntityMetadataService; this.trackedEntityRuleRepository = trackedEntityRuleRepository; - this.valueConverter = valueConverter; this.resourceMappingRepository = resourceMappingRepository; + this.scriptExecutionContext = scriptExecutionContext; + this.valueConverter = valueConverter; } @Nonnull @@ -227,7 +232,7 @@ public FhirToDhisTransformOutcome transform( @Nonnull FhirClientReso TransformerUtils.getScriptVariable( variables, ScriptVariable.PROGRAM, Program.class ), TransformerUtils.getScriptVariable( variables, ScriptVariable.TRACKED_ENTITY_TYPE, TrackedEntityType.class ) ); final FhirResourceMapping resourceMapping = getResourceMapping( ruleInfo, trackedEntityRuleInfo.getRule().getFhirResourceType() ); - final TrackedEntityInstance trackedEntityInstance = getTrackedEntityInstance( context, trackedEntityRuleInfo, resourceMapping, variables, false ).orElse( null ); + final IdentifiedTrackedEntityInstance trackedEntityInstance = getTrackedEntityInstance( context, trackedEntityRuleInfo, resourceMapping, variables, false ).orElse( null ); if ( trackedEntityInstance == null ) { @@ -271,40 +276,25 @@ public FhirToDhisDeleteTransformOutcome transformDeletion( @Nonnull return new FhirToDhisDeleteTransformOutcome<>( ruleInfo.getRule(), enrollment, true ); } - @Nonnull - @Override - protected Optional getTrackedEntityInstance( @Nonnull FhirToDhisTransformerContext context, @Nonnull RuleInfo ruleInfo, @Nonnull FhirResourceMapping resourceMapping, - @Nonnull Map scriptVariables, boolean sync ) - { - return super.getTrackedEntityInstance( context, ruleInfo, resourceMapping, scriptVariables, sync ); - } - protected boolean addBasicScriptVariables( @Nonnull FhirToDhisTransformerContext context, @Nonnull RuleInfo ruleInfo, @Nonnull Map scriptVariables ) throws TransformerException { final Program program = getProgram( context, ruleInfo, scriptVariables ); scriptVariables.put( ScriptVariable.PROGRAM.getVariableName(), program ); - final TrackedEntityType trackedEntityType = getTrackedEntityType( context, ruleInfo, scriptVariables, program ); final TrackedEntityAttributes attributes = trackedEntityMetadataService.getAttributes(); scriptVariables.put( ScriptVariable.TRACKED_ENTITY_ATTRIBUTES.getVariableName(), attributes ); + + final TrackedEntityType trackedEntityType = getTrackedEntityType( context, ruleInfo, scriptVariables, program ); scriptVariables.put( ScriptVariable.TRACKED_ENTITY_TYPE.getVariableName(), trackedEntityType ); return true; } protected void addScriptVariables( @Nonnull FhirToDhisTransformerContext context, @Nonnull Map variables, @Nonnull RuleInfo ruleInfo, - @Nonnull RuleInfo trackedEntityRuleRuleInfo, @Nonnull TrackedEntityInstance trackedEntityInstance ) throws TransformerException + @Nonnull RuleInfo trackedEntityRuleRuleInfo, @Nonnull IdentifiedTrackedEntityInstance trackedEntityInstance ) throws TransformerException { - if ( !context.getFhirRequest().isDhisFhirId() && trackedEntityInstance.getIdentifier() == null ) - { - throw new FatalTransformerException( "Identifier of tracked entity instance has not yet been set." ); - } - variables.put( ScriptVariable.TRACKED_ENTITY_RESOURCE_TYPE.getVariableName(), trackedEntityRuleRuleInfo.getRule().getFhirResourceType() ); - variables.put( ScriptVariable.TRACKED_ENTITY_INSTANCE.getVariableName(), new WritableScriptedTrackedEntityInstance( - TransformerUtils.getScriptVariable( variables, ScriptVariable.TRACKED_ENTITY_ATTRIBUTES, TrackedEntityAttributes.class ), - TransformerUtils.getScriptVariable( variables, ScriptVariable.TRACKED_ENTITY_TYPE, TrackedEntityType.class ), - trackedEntityInstance, valueConverter ) ); + addTrackedEntityScriptVariables( context, ruleInfo, trackedEntityInstance, variables ); } @Nonnull diff --git a/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/fhir/impl/program/FhirToProgramStageTransformer.java b/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/fhir/impl/program/FhirToProgramStageTransformer.java index 647a66e6..39ebbcaf 100644 --- a/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/fhir/impl/program/FhirToProgramStageTransformer.java +++ b/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/fhir/impl/program/FhirToProgramStageTransformer.java @@ -48,6 +48,7 @@ import org.dhis2.fhir.adapter.dhis.tracker.program.ProgramStage; import org.dhis2.fhir.adapter.dhis.tracker.program.ProgramStageDataElement; import org.dhis2.fhir.adapter.dhis.tracker.program.ProgramTrackedEntityAttribute; +import org.dhis2.fhir.adapter.dhis.tracker.trackedentity.IdentifiedTrackedEntityInstance; import org.dhis2.fhir.adapter.dhis.tracker.trackedentity.TrackedEntityAttributes; import org.dhis2.fhir.adapter.dhis.tracker.trackedentity.TrackedEntityInstance; import org.dhis2.fhir.adapter.dhis.tracker.trackedentity.TrackedEntityMetadataService; @@ -65,6 +66,7 @@ import org.dhis2.fhir.adapter.fhir.model.EventDecisionType; import org.dhis2.fhir.adapter.fhir.model.FhirVersion; import org.dhis2.fhir.adapter.fhir.repository.DhisFhirResourceId; +import org.dhis2.fhir.adapter.fhir.script.ScriptExecutionContext; import org.dhis2.fhir.adapter.fhir.script.ScriptExecutor; import org.dhis2.fhir.adapter.fhir.transform.DhisDataExistsException; import org.dhis2.fhir.adapter.fhir.transform.FatalTransformerException; @@ -128,16 +130,20 @@ public class FhirToProgramStageTransformer extends AbstractFhirToDhisTransformer private final FhirResourceMappingRepository resourceMappingRepository; + private final ScriptExecutionContext scriptExecutionContext; + private final ValueConverter valueConverter; private final ZoneId zoneId = ZoneId.systemDefault(); public FhirToProgramStageTransformer( @Nonnull ScriptExecutor scriptExecutor, @Nonnull OrganizationUnitService organizationUnitService, @Nonnull LockManager lockManager, - @Nonnull TrackedEntityMetadataService trackedEntityMetadataService, @Nonnull TrackedEntityService trackedEntityService, + @Nonnull TrackedEntityService trackedEntityService, @Nonnull TrackedEntityMetadataService trackedEntityMetadataService, @Nonnull ProgramMetadataService programMetadataService, @Nonnull EnrollmentService enrollmentService, @Nonnull EventService eventService, - @Nonnull FhirResourceMappingRepository resourceMappingRepository, @Nonnull FhirDhisAssignmentRepository fhirDhisAssignmentRepository, @Nonnull ValueConverter valueConverter ) + @Nonnull FhirResourceMappingRepository resourceMappingRepository, @Nonnull FhirDhisAssignmentRepository fhirDhisAssignmentRepository, + @Nonnull ScriptExecutionContext scriptExecutionContext, @Nonnull ValueConverter valueConverter ) { - super( scriptExecutor, organizationUnitService, new StaticObjectProvider<>( trackedEntityService ), fhirDhisAssignmentRepository ); + super( scriptExecutor, organizationUnitService, new StaticObjectProvider<>( trackedEntityMetadataService ), new StaticObjectProvider<>( trackedEntityService ), + fhirDhisAssignmentRepository, scriptExecutionContext, valueConverter ); this.lockManager = lockManager; this.programMetadataService = programMetadataService; @@ -145,6 +151,7 @@ public FhirToProgramStageTransformer( @Nonnull ScriptExecutor scriptExecutor, @N this.enrollmentService = enrollmentService; this.eventService = eventService; this.resourceMappingRepository = resourceMappingRepository; + this.scriptExecutionContext = scriptExecutionContext; this.valueConverter = valueConverter; } @@ -196,7 +203,7 @@ public FhirToDhisTransformOutcome transform( @Nonnull FhirClientResource return null; } - final TrackedEntityInstance trackedEntityInstance = getTrackedEntityInstance( context, + final IdentifiedTrackedEntityInstance trackedEntityInstance = getTrackedEntityInstance( context, new RuleInfo<>( ruleInfo.getRule().getProgramStage().getProgram().getTrackedEntityRule(), Collections.emptyList() ), resourceMapping, variables, false ).orElse( null ); if ( trackedEntityInstance == null ) { @@ -260,21 +267,31 @@ public FhirToDhisTransformOutcome transform( @Nonnull FhirClientResource return null; } - if ( trackedEntityInstance.isModified() ) - { - TransformerUtils.getScriptVariable( variables, ScriptVariable.TRACKED_ENTITY_INSTANCE, ScriptedTrackedEntityInstance.class ).validate(); - } - event.getEnrollment().setTrackedEntityInstance( trackedEntityInstance ); if ( event.getEnrollment().isModified() ) { scriptedEnrollment.validate(); } - event.setTrackedEntityInstance( trackedEntityInstance ); + + updateTrackedEntityInstance( event, variables ); scriptedProgramStageEvents.stream().filter( se -> se.isNewResource() || se.isModified() || se.isAnyDataValueModified() ).forEach( WritableScriptedEvent::validate ); return new FhirToDhisTransformOutcome<>( ruleInfo.getRule(), event, event.isNewResource() || oldEmpty ); } + protected void updateTrackedEntityInstance( @Nonnull Event event, @Nonnull Map variables ) + { + final WritableScriptedTrackedEntityInstance scriptedTrackedEntityInstance = TransformerUtils.getScriptVariable( variables, ScriptVariable.TRACKED_ENTITY_INSTANCE, WritableScriptedTrackedEntityInstance.class ); + final TrackedEntityInstance trackedEntityInstance = (TrackedEntityInstance) scriptedTrackedEntityInstance.getDhisResource(); + + if ( trackedEntityInstance != null && trackedEntityInstance.isModified() ) + { + scriptedTrackedEntityInstance.validate(); + } + + event.getEnrollment().setTrackedEntityInstance( trackedEntityInstance ); + event.setTrackedEntityInstance( trackedEntityInstance ); + } + @Nullable @Override public FhirToDhisDeleteTransformOutcome transformDeletion( @Nonnull FhirClientResource fhirClientResource, @Nonnull RuleInfo ruleInfo, @Nonnull DhisFhirResourceId dhisFhirResourceId ) throws TransformerException @@ -350,17 +367,9 @@ protected boolean addBasicScriptVariables( @Nonnull Map variable } protected void addScriptVariables( @Nonnull FhirToDhisTransformerContext context, @Nonnull Map variables, @Nonnull RuleInfo ruleInfo, - @Nonnull TrackedEntityInstance trackedEntityInstance ) throws TransformerException + @Nonnull IdentifiedTrackedEntityInstance trackedEntityInstance ) throws TransformerException { - if ( !context.getFhirRequest().isDhisFhirId() && (trackedEntityInstance.getIdentifier() == null) ) - { - throw new FatalTransformerException( "Identifier of tracked entity instance has not yet been set." ); - } - - variables.put( ScriptVariable.TRACKED_ENTITY_INSTANCE.getVariableName(), new WritableScriptedTrackedEntityInstance( - TransformerUtils.getScriptVariable( variables, ScriptVariable.TRACKED_ENTITY_ATTRIBUTES, TrackedEntityAttributes.class ), - TransformerUtils.getScriptVariable( variables, ScriptVariable.TRACKED_ENTITY_TYPE, TrackedEntityType.class ), - trackedEntityInstance, valueConverter ) ); + addTrackedEntityScriptVariables( context, ruleInfo, trackedEntityInstance, variables ); } protected void updateCoordinates( @Nonnull FhirToDhisTransformerContext context, @Nonnull RuleInfo ruleInfo, @Nonnull FhirResourceMapping resourceMapping, @@ -448,6 +457,7 @@ private Optional getExistingResource( @Nonnull FhirToDhisTransformerConte @Nonnull BiFunction, LazyObject, Event> function, boolean sync, boolean refreshed ) { final EventInfo eventInfo = getEventInfo( scriptVariables, sync, refreshed ); + if ( eventInfo.getEvents().isEmpty() ) { return Optional.empty(); @@ -573,6 +583,7 @@ protected Event createResource( @Nonnull FhirToDhisTransformerContext context, @ variables.put( ScriptVariable.EVENT.getVariableName(), scriptedEvent ); final ZonedDateTime eventDate; + if ( ruleInfo.getRule().getProgramStage().isEventDateIsIncident() && !eventInfo.getProgramStage().isGeneratedByEnrollmentDate() ) { eventDate = enrollment.getIncidentDate().plusDays( eventInfo.getProgramStage().getMinDaysFromStart() ); @@ -580,11 +591,8 @@ protected Event createResource( @Nonnull FhirToDhisTransformerContext context, @ else { eventDate = getEventDate( context, ruleInfo, resourceMapping, enrollment, eventInfo.getProgramStage(), variables ); - if ( eventDate == null ) - { - return null; - } } + event.setEventDate( eventDate ); event.setDueDate( Stream.of( enrollment.getIncidentDate().plusDays( eventInfo.getProgramStage().getMinDaysFromStart() ), eventDate ) .max( Comparator.naturalOrder() ).get() ); @@ -639,7 +647,8 @@ protected void updateEventDate( @Nonnull FhirToDhisTransformerContext context, @ if ( ruleInfo.getRule().isUpdateEventDate() && !programStage.isGeneratedByEnrollmentDate() ) { final ZonedDateTime eventDate = getEventDate( context, ruleInfo, resourceMapping, enrollment, programStage, variables ); - if ( (eventDate != null) && !Objects.equals( event.getEventDate(), eventDate ) ) + + if ( !Objects.equals( event.getEventDate(), eventDate ) ) { event.setEventDate( eventDate ); event.setModified( true ); @@ -648,10 +657,12 @@ protected void updateEventDate( @Nonnull FhirToDhisTransformerContext context, @ } } + @Nonnull private ZonedDateTime getEventDate( @Nonnull FhirToDhisTransformerContext context, @Nonnull RuleInfo ruleInfo, @Nonnull FhirResourceMapping resourceMapping, @Nonnull Enrollment enrollment, @Nonnull ProgramStage programStage, @Nonnull Map variables ) { ZonedDateTime eventDate; + if ( programStage.isGeneratedByEnrollmentDate() ) { eventDate = enrollment.getEnrollmentDate().plusDays( programStage.getMinDaysFromStart() ); @@ -661,22 +672,26 @@ private ZonedDateTime getEventDate( @Nonnull FhirToDhisTransformerContext contex eventDate = valueConverter.convert( executeScript( context, ruleInfo, resourceMapping.getImpEventDateLookupScript(), variables, Object.class ), ValueType.DATETIME, ZonedDateTime.class ); } + if ( eventDate == null ) { final IAnyResource resource = TransformerUtils.getScriptVariable( variables, ScriptVariable.INPUT, IAnyResource.class ); - if ( (resource.getMeta() != null) && (resource.getMeta().getLastUpdated() != null) ) + + if ( resource.getMeta() != null && resource.getMeta().getLastUpdated() != null ) { logger.info( "Event date of program stage instance \"{}\" has not been returned " + "by event date script (using last updated timestamp).", programStage.getName() ); eventDate = ZonedDateTime.ofInstant( resource.getMeta().getLastUpdated().toInstant(), zoneId ); } } + if ( eventDate == null ) { logger.info( "Event date of program stage instance \"{}\" has not been returned " + "by event date script (using current timestamp).", programStage.getName() ); eventDate = ZonedDateTime.now(); } + return eventDate; } @@ -690,6 +705,7 @@ protected Enrollment createEnrollment( @Nonnull FhirToDhisTransformerContext con } final MappedTrackerProgram mappedProgram = ruleInfo.getRule().getProgramStage().getProgram(); + if ( !mappedProgram.isCreationEnabled() ) { logger.info( "Creation of program stage has not been enabled." ); @@ -697,6 +713,7 @@ protected Enrollment createEnrollment( @Nonnull FhirToDhisTransformerContext con } final Program program = TransformerUtils.getScriptVariable( scriptVariables, ScriptVariable.PROGRAM, Program.class ); + if ( !program.isRegistration() ) { throw new TransformerMappingException( "Cannot create enrollment into program \"" + program.getName() + @@ -704,6 +721,7 @@ protected Enrollment createEnrollment( @Nonnull FhirToDhisTransformerContext con } final ZonedDateTime enrollmentDate = getEnrollmentDate( context, ruleInfo, resourceMapping, program, scriptVariables ); + if ( enrollmentDate == null ) { return null; @@ -712,7 +730,8 @@ protected Enrollment createEnrollment( @Nonnull FhirToDhisTransformerContext con // creation of event may not be applicable final Map variables = new HashMap<>( scriptVariables ); variables.put( ScriptVariable.DATE_TIME.getVariableName(), enrollmentDate ); - if ( (mappedProgram.getCreationApplicableScript() != null) && + + if ( mappedProgram.getCreationApplicableScript() != null && !Boolean.TRUE.equals( executeScript( context, ruleInfo, mappedProgram.getCreationApplicableScript(), variables, Boolean.class ) ) ) { logger.info( "Creation of program instance \"{}\" is not applicable.", mappedProgram.getName() ); @@ -720,6 +739,7 @@ protected Enrollment createEnrollment( @Nonnull FhirToDhisTransformerContext con } final Optional organisationUnit = getOrgUnit( context, ruleInfo, resourceMapping.getImpEnrollmentOrgLookupScript(), variables ); + if ( !organisationUnit.isPresent() ) { return null; @@ -746,10 +766,12 @@ protected Enrollment createEnrollment( @Nonnull FhirToDhisTransformerContext con { return null; } + if ( ruleInfo.getRule().getProgramStage().getProgram().isEnrollmentDateIsIncident() ) { enrollment.setEnrollmentDate( enrollment.getIncidentDate() ); } + if ( enrollment.getStatus() == null ) { enrollment.setStatus( EnrollmentStatus.ACTIVE ); @@ -844,9 +866,11 @@ protected ZonedDateTime getEnrollmentDate( @Nonnull FhirToDhisTransformerContext { ZonedDateTime enrollmentDate = valueConverter.convert( executeScript( context, ruleInfo, resourceMapping.getImpEnrollmentDateLookupScript(), scriptVariables, Object.class ), ValueType.DATETIME, ZonedDateTime.class ); + if ( enrollmentDate == null ) { final IAnyResource resource = TransformerUtils.getScriptVariable( scriptVariables, ScriptVariable.INPUT, IAnyResource.class ); + if ( (resource.getMeta() != null) && (resource.getMeta().getLastUpdated() != null) ) { logger.info( "Enrollment date of program instance \"{}\" has not been returned by " + @@ -854,17 +878,20 @@ protected ZonedDateTime getEnrollmentDate( @Nonnull FhirToDhisTransformerContext enrollmentDate = ZonedDateTime.ofInstant( resource.getMeta().getLastUpdated().toInstant(), zoneId ); } } + if ( enrollmentDate == null ) { logger.info( "Enrollment date of program instance \"{}\" has not been returned by " + "enrollment date script (using current timestamp).", program.getName() ); enrollmentDate = ZonedDateTime.now(); } + if ( !program.isSelectEnrollmentDatesInFuture() && DateTimeUtils.isFutureDate( enrollmentDate ) ) { logger.info( "Enrollment date of of program instance \"{}\" is in the future and program does not allow dates in the future.", program.getName() ); return null; } + return enrollmentDate; } @@ -919,6 +946,7 @@ protected EventInfo getEventInfo( @Nonnull Map scriptVariables, .lock( "in-te:" + trackedEntityInstance.getId() ); Enrollment enrollment; + if ( sync || refreshed ) { enrollment = enrollmentService.findLatestActiveRefreshed( program.getId(), Objects.requireNonNull( trackedEntityInstance.getId() ), trackedEntityInstance.isLocal() ).orElse( null ); diff --git a/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/fhir/impl/trackedentity/FhirToTrackedEntityTransformer.java b/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/fhir/impl/trackedentity/FhirToTrackedEntityTransformer.java index 1fe26d03..7239de84 100644 --- a/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/fhir/impl/trackedentity/FhirToTrackedEntityTransformer.java +++ b/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/fhir/impl/trackedentity/FhirToTrackedEntityTransformer.java @@ -47,6 +47,7 @@ import org.dhis2.fhir.adapter.fhir.metadata.model.TrackedEntityRule; import org.dhis2.fhir.adapter.fhir.model.FhirVersion; import org.dhis2.fhir.adapter.fhir.repository.DhisFhirResourceId; +import org.dhis2.fhir.adapter.fhir.script.ScriptExecutionContext; import org.dhis2.fhir.adapter.fhir.script.ScriptExecutor; import org.dhis2.fhir.adapter.fhir.transform.FatalTransformerException; import org.dhis2.fhir.adapter.fhir.transform.TransformerException; @@ -78,15 +79,20 @@ public class FhirToTrackedEntityTransformer extends AbstractFhirToDhisTransforme private final TrackedEntityMetadataService trackedEntityMetadataService; + private final ScriptExecutionContext scriptExecutionContext; + private final ValueConverter valueConverter; public FhirToTrackedEntityTransformer( @Nonnull ScriptExecutor scriptExecutor, @Nonnull LockManager lockManager, @Nonnull TrackedEntityMetadataService trackedEntityMetadataService, @Nonnull TrackedEntityService trackedEntityService, - @Nonnull OrganizationUnitService organizationUnitService, @Nonnull FhirDhisAssignmentRepository fhirDhisAssignmentRepository, @Nonnull ValueConverter valueConverter ) + @Nonnull OrganizationUnitService organizationUnitService, @Nonnull FhirDhisAssignmentRepository fhirDhisAssignmentRepository, + @Nonnull ScriptExecutionContext scriptExecutionContext, @Nonnull ValueConverter valueConverter ) { - super( scriptExecutor, organizationUnitService, new StaticObjectProvider<>( trackedEntityService ), fhirDhisAssignmentRepository ); + super( scriptExecutor, organizationUnitService, new StaticObjectProvider<>( trackedEntityMetadataService ), new StaticObjectProvider<>( trackedEntityService ), fhirDhisAssignmentRepository, scriptExecutionContext, valueConverter ); + this.lockManager = lockManager; this.trackedEntityMetadataService = trackedEntityMetadataService; + this.scriptExecutionContext = scriptExecutionContext; this.valueConverter = valueConverter; } @@ -143,7 +149,7 @@ public FhirToDhisTransformOutcome transform( @Nonnull Fhi } final WritableScriptedTrackedEntityInstance scriptedTrackedEntityInstance = new WritableScriptedTrackedEntityInstance( - trackedEntityAttributes, trackedEntityType, trackedEntityInstance, valueConverter ); + trackedEntityAttributes, trackedEntityType, trackedEntityInstance, scriptExecutionContext, valueConverter ); variables.put( ScriptVariable.OUTPUT.getVariableName(), scriptedTrackedEntityInstance ); final Optional organizationUnit; diff --git a/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/fhir/impl/unsupported/AbstractUnsupportedFhirToDhisTransformer.java b/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/fhir/impl/unsupported/AbstractUnsupportedFhirToDhisTransformer.java index d48b7e8c..4699c4a4 100644 --- a/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/fhir/impl/unsupported/AbstractUnsupportedFhirToDhisTransformer.java +++ b/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/fhir/impl/unsupported/AbstractUnsupportedFhirToDhisTransformer.java @@ -28,9 +28,11 @@ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ +import org.dhis2.fhir.adapter.dhis.converter.ValueConverter; import org.dhis2.fhir.adapter.dhis.model.DhisResource; import org.dhis2.fhir.adapter.dhis.model.DhisResourceType; import org.dhis2.fhir.adapter.dhis.orgunit.OrganizationUnitService; +import org.dhis2.fhir.adapter.dhis.tracker.trackedentity.TrackedEntityMetadataService; import org.dhis2.fhir.adapter.dhis.tracker.trackedentity.TrackedEntityService; import org.dhis2.fhir.adapter.fhir.data.repository.FhirDhisAssignmentRepository; import org.dhis2.fhir.adapter.fhir.metadata.model.AbstractRule; @@ -38,6 +40,7 @@ import org.dhis2.fhir.adapter.fhir.metadata.model.RuleInfo; import org.dhis2.fhir.adapter.fhir.model.FhirVersion; import org.dhis2.fhir.adapter.fhir.repository.DhisFhirResourceId; +import org.dhis2.fhir.adapter.fhir.script.ScriptExecutionContext; import org.dhis2.fhir.adapter.fhir.script.ScriptExecutor; import org.dhis2.fhir.adapter.fhir.transform.TransformerException; import org.dhis2.fhir.adapter.fhir.transform.fhir.FhirToDhisDeleteTransformOutcome; @@ -62,10 +65,11 @@ */ public abstract class AbstractUnsupportedFhirToDhisTransformer extends AbstractFhirToDhisTransformer { - protected AbstractUnsupportedFhirToDhisTransformer( @Nonnull ScriptExecutor scriptExecutor, @Nonnull OrganizationUnitService organizationUnitService, @Nonnull ObjectProvider trackedEntityService, - @Nonnull FhirDhisAssignmentRepository fhirDhisAssignmentRepository ) + protected AbstractUnsupportedFhirToDhisTransformer( @Nonnull ScriptExecutor scriptExecutor, @Nonnull OrganizationUnitService organizationUnitService, + @Nonnull ObjectProvider trackedEntityService, @Nonnull ObjectProvider trackedEntityMetadataService, + @Nonnull FhirDhisAssignmentRepository fhirDhisAssignmentRepository, @Nonnull ScriptExecutionContext scriptExecutionContext, @Nonnull ValueConverter valueConverter ) { - super( scriptExecutor, organizationUnitService, trackedEntityService, fhirDhisAssignmentRepository ); + super( scriptExecutor, organizationUnitService, trackedEntityMetadataService, trackedEntityService, fhirDhisAssignmentRepository, scriptExecutionContext, valueConverter ); } @Nonnull diff --git a/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/fhir/impl/unsupported/OrganizationUnitFhirToDhisTransformer.java b/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/fhir/impl/unsupported/OrganizationUnitFhirToDhisTransformer.java index a8601a2b..bb1b47b5 100644 --- a/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/fhir/impl/unsupported/OrganizationUnitFhirToDhisTransformer.java +++ b/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/fhir/impl/unsupported/OrganizationUnitFhirToDhisTransformer.java @@ -28,12 +28,15 @@ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ +import org.dhis2.fhir.adapter.dhis.converter.ValueConverter; import org.dhis2.fhir.adapter.dhis.model.DhisResourceType; import org.dhis2.fhir.adapter.dhis.orgunit.OrganizationUnit; import org.dhis2.fhir.adapter.dhis.orgunit.OrganizationUnitService; +import org.dhis2.fhir.adapter.dhis.tracker.trackedentity.TrackedEntityMetadataService; import org.dhis2.fhir.adapter.dhis.tracker.trackedentity.TrackedEntityService; import org.dhis2.fhir.adapter.fhir.data.repository.FhirDhisAssignmentRepository; import org.dhis2.fhir.adapter.fhir.metadata.model.OrganizationUnitRule; +import org.dhis2.fhir.adapter.fhir.script.ScriptExecutionContext; import org.dhis2.fhir.adapter.fhir.script.ScriptExecutor; import org.springframework.beans.factory.ObjectProvider; import org.springframework.stereotype.Component; @@ -48,10 +51,11 @@ @Component public class OrganizationUnitFhirToDhisTransformer extends AbstractUnsupportedFhirToDhisTransformer { - public OrganizationUnitFhirToDhisTransformer( @Nonnull ScriptExecutor scriptExecutor, @Nonnull OrganizationUnitService organizationUnitService, @Nonnull ObjectProvider trackedEntityService, - @Nonnull FhirDhisAssignmentRepository fhirDhisAssignmentRepository ) + public OrganizationUnitFhirToDhisTransformer( @Nonnull ScriptExecutor scriptExecutor, @Nonnull OrganizationUnitService organizationUnitService, + @Nonnull ObjectProvider trackedEntityMetadataService, @Nonnull ObjectProvider trackedEntityService, + @Nonnull FhirDhisAssignmentRepository fhirDhisAssignmentRepository, @Nonnull ScriptExecutionContext scriptExecutionContext, @Nonnull ValueConverter valueConverter ) { - super( scriptExecutor, organizationUnitService, trackedEntityService, fhirDhisAssignmentRepository ); + super( scriptExecutor, organizationUnitService, trackedEntityService, trackedEntityMetadataService, fhirDhisAssignmentRepository, scriptExecutionContext, valueConverter ); } @Nonnull diff --git a/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/fhir/impl/unsupported/ProgramMetadataFhirToDhisTransformer.java b/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/fhir/impl/unsupported/ProgramMetadataFhirToDhisTransformer.java index ebb38280..a96b3898 100644 --- a/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/fhir/impl/unsupported/ProgramMetadataFhirToDhisTransformer.java +++ b/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/fhir/impl/unsupported/ProgramMetadataFhirToDhisTransformer.java @@ -28,12 +28,15 @@ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ +import org.dhis2.fhir.adapter.dhis.converter.ValueConverter; import org.dhis2.fhir.adapter.dhis.model.DhisResourceType; import org.dhis2.fhir.adapter.dhis.orgunit.OrganizationUnitService; import org.dhis2.fhir.adapter.dhis.tracker.program.Program; +import org.dhis2.fhir.adapter.dhis.tracker.trackedentity.TrackedEntityMetadataService; import org.dhis2.fhir.adapter.dhis.tracker.trackedentity.TrackedEntityService; import org.dhis2.fhir.adapter.fhir.data.repository.FhirDhisAssignmentRepository; import org.dhis2.fhir.adapter.fhir.metadata.model.ProgramMetadataRule; +import org.dhis2.fhir.adapter.fhir.script.ScriptExecutionContext; import org.dhis2.fhir.adapter.fhir.script.ScriptExecutor; import org.springframework.beans.factory.ObjectProvider; import org.springframework.stereotype.Component; @@ -48,10 +51,11 @@ @Component public class ProgramMetadataFhirToDhisTransformer extends AbstractUnsupportedFhirToDhisTransformer { - public ProgramMetadataFhirToDhisTransformer( @Nonnull ScriptExecutor scriptExecutor, @Nonnull OrganizationUnitService organizationUnitService, @Nonnull ObjectProvider trackedEntityService, - @Nonnull FhirDhisAssignmentRepository fhirDhisAssignmentRepository ) + public ProgramMetadataFhirToDhisTransformer( @Nonnull ScriptExecutor scriptExecutor, @Nonnull OrganizationUnitService organizationUnitService, + @Nonnull ObjectProvider trackedEntityMetadataService, @Nonnull ObjectProvider trackedEntityService, + @Nonnull FhirDhisAssignmentRepository fhirDhisAssignmentRepository, @Nonnull ScriptExecutionContext scriptExecutionContext, @Nonnull ValueConverter valueConverter ) { - super( scriptExecutor, organizationUnitService, trackedEntityService, fhirDhisAssignmentRepository ); + super( scriptExecutor, organizationUnitService, trackedEntityService, trackedEntityMetadataService, fhirDhisAssignmentRepository, scriptExecutionContext, valueConverter ); } @Nonnull diff --git a/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/fhir/impl/unsupported/ProgramStageMetadataFhirToDhisTransformer.java b/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/fhir/impl/unsupported/ProgramStageMetadataFhirToDhisTransformer.java index ddc92b17..4dc838ca 100644 --- a/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/fhir/impl/unsupported/ProgramStageMetadataFhirToDhisTransformer.java +++ b/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/fhir/impl/unsupported/ProgramStageMetadataFhirToDhisTransformer.java @@ -28,12 +28,15 @@ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ +import org.dhis2.fhir.adapter.dhis.converter.ValueConverter; import org.dhis2.fhir.adapter.dhis.model.DhisResourceType; import org.dhis2.fhir.adapter.dhis.orgunit.OrganizationUnitService; import org.dhis2.fhir.adapter.dhis.tracker.program.ProgramStage; +import org.dhis2.fhir.adapter.dhis.tracker.trackedentity.TrackedEntityMetadataService; import org.dhis2.fhir.adapter.dhis.tracker.trackedentity.TrackedEntityService; import org.dhis2.fhir.adapter.fhir.data.repository.FhirDhisAssignmentRepository; import org.dhis2.fhir.adapter.fhir.metadata.model.ProgramStageMetadataRule; +import org.dhis2.fhir.adapter.fhir.script.ScriptExecutionContext; import org.dhis2.fhir.adapter.fhir.script.ScriptExecutor; import org.springframework.beans.factory.ObjectProvider; import org.springframework.stereotype.Component; @@ -48,10 +51,11 @@ @Component public class ProgramStageMetadataFhirToDhisTransformer extends AbstractUnsupportedFhirToDhisTransformer { - public ProgramStageMetadataFhirToDhisTransformer( @Nonnull ScriptExecutor scriptExecutor, @Nonnull OrganizationUnitService organizationUnitService, @Nonnull ObjectProvider trackedEntityService, - @Nonnull FhirDhisAssignmentRepository fhirDhisAssignmentRepository ) + public ProgramStageMetadataFhirToDhisTransformer( @Nonnull ScriptExecutor scriptExecutor, @Nonnull OrganizationUnitService organizationUnitService, + @Nonnull ObjectProvider trackedEntityMetadataService, @Nonnull ObjectProvider trackedEntityService, + @Nonnull FhirDhisAssignmentRepository fhirDhisAssignmentRepository, @Nonnull ScriptExecutionContext scriptExecutionContext, @Nonnull ValueConverter valueConverter ) { - super( scriptExecutor, organizationUnitService, trackedEntityService, fhirDhisAssignmentRepository ); + super( scriptExecutor, organizationUnitService, trackedEntityService, trackedEntityMetadataService, fhirDhisAssignmentRepository, scriptExecutionContext, valueConverter ); } @Nonnull diff --git a/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/scripted/AbstractWritableScriptedDhisResource.java b/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/scripted/AbstractWritableScriptedDhisResource.java new file mode 100644 index 00000000..ea4f2fc9 --- /dev/null +++ b/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/scripted/AbstractWritableScriptedDhisResource.java @@ -0,0 +1,160 @@ +package org.dhis2.fhir.adapter.fhir.transform.scripted; + +/* + * Copyright (c) 2004-2019, University of Oslo + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * Neither the name of the HISP project nor the names of its contributors may + * be used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +import org.dhis2.fhir.adapter.dhis.model.DhisResource; +import org.dhis2.fhir.adapter.dhis.model.DhisResourceId; +import org.dhis2.fhir.adapter.dhis.model.DhisResourceType; +import org.dhis2.fhir.adapter.fhir.script.ScriptExecutionContext; +import org.dhis2.fhir.adapter.fhir.transform.TransformerException; +import org.dhis2.fhir.adapter.scriptable.ScriptMethod; +import org.dhis2.fhir.adapter.scriptable.Scriptable; + +import javax.annotation.Nonnull; +import javax.annotation.Nullable; +import java.time.ZonedDateTime; + +/** + * Abstract implementation of writable scripted resource. + * + * @author volsch + */ +@Scriptable +public abstract class AbstractWritableScriptedDhisResource +{ + private final DhisResourceType resourceType; + + private final String resourceId; + + protected DhisResource resource; + + protected final ScriptExecutionContext scriptExecutionContext; + + protected AbstractWritableScriptedDhisResource( @Nonnull DhisResourceType resourceType, @Nonnull String resourceId, @Nonnull ScriptExecutionContext scriptExecutionContext ) + { + this.resourceType = resourceType; + this.resourceId = resourceId; + this.scriptExecutionContext = scriptExecutionContext; + } + + protected AbstractWritableScriptedDhisResource( @Nonnull DhisResource resource, @Nonnull ScriptExecutionContext scriptExecutionContext ) + { + this.resourceType = resource.getResourceType(); + this.resourceId = resource.getId(); + this.resource = resource; + this.scriptExecutionContext = scriptExecutionContext; + } + + @Nonnull + protected DhisResource getInternalResource() + { + load(); + + return resource; + } + + @Nullable + @ScriptMethod( description = "Returns the ID of the DHIS2 resource. Return null if the instance is new." ) + public String getId() + { + return resourceId; + } + + @Nonnull + public DhisResourceType getResourceType() + { + return resourceType; + } + + @Nullable + public DhisResourceId getResourceId() + { + return resourceId == null ? null : new DhisResourceId( resourceType, resourceId ); + } + + @ScriptMethod( description = "Returns if the DHIS2 resource is new and has not yet been saved on DHIS2." ) + public boolean isNewResource() + { + return getInternalResource().isNewResource(); + } + + public boolean isLocal() + { + return getInternalResource().isLocal(); + } + + public boolean isDeleted() + { + return getInternalResource().isDeleted(); + } + + @Nullable + public ZonedDateTime getLastUpdated() + { + return getInternalResource().getLastUpdated(); + } + + @Nullable + public String getOrganizationUnitId() + { + return getInternalResource().getOrgUnitId(); + } + + @Nullable + public ScriptedTrackedEntityInstance getTrackedEntityInstance() + { + return null; + } + + public void validate() throws TransformerException + { + // nothing to be done + } + + /** + * @return true if the resource itself has already been loaded. + */ + protected boolean isLoaded() + { + return resource != null; + } + + /** + * Method may be overridden to load associated DHIS resource. After invocation of the method + * the resource must have been loaded. If the resource cannot be loaded, an exception must + * be thrown. + */ + protected void load() + { + if ( !isLoaded() ) + { + throw new IllegalStateException( "Resource has not yet been loaded and load method has not been overridden." ); + } + } +} diff --git a/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/scripted/AccessibleWritableScriptedLazyDhisResource.java b/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/scripted/AccessibleWritableScriptedLazyDhisResource.java new file mode 100644 index 00000000..5dd1f0d1 --- /dev/null +++ b/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/scripted/AccessibleWritableScriptedLazyDhisResource.java @@ -0,0 +1,49 @@ +package org.dhis2.fhir.adapter.fhir.transform.scripted; + +/* + * Copyright (c) 2004-2019, University of Oslo + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * Neither the name of the HISP project nor the names of its contributors may + * be used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +import org.dhis2.fhir.adapter.dhis.model.DhisResource; +import org.dhis2.fhir.adapter.fhir.script.ScriptExecutionForbidden; + +import javax.annotation.Nullable; + +/** + * Scripted DHIS2 resource that allows access to the included resource instance. The + * included DHIS2 resource may not have been initialized and retrieving the resource + * may return null. The access is only granted outside script execution. + * Within a script an exception will be thrown. + * + * @author volsch + */ +public interface AccessibleWritableScriptedLazyDhisResource extends ScriptedDhisResource +{ + @Nullable + @ScriptExecutionForbidden + DhisResource getDhisResource(); +} diff --git a/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/scripted/LazyImmutableTrackedEntityType.java b/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/scripted/LazyImmutableTrackedEntityType.java new file mode 100644 index 00000000..68e40e63 --- /dev/null +++ b/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/scripted/LazyImmutableTrackedEntityType.java @@ -0,0 +1,187 @@ +package org.dhis2.fhir.adapter.fhir.transform.scripted; + +/* + * Copyright (c) 2004-2019, University of Oslo + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * Neither the name of the HISP project nor the names of its contributors may + * be used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +import org.dhis2.fhir.adapter.dhis.model.DhisResourceId; +import org.dhis2.fhir.adapter.dhis.model.DhisResourceType; +import org.dhis2.fhir.adapter.dhis.model.Reference; +import org.dhis2.fhir.adapter.dhis.tracker.trackedentity.TrackedEntityType; +import org.dhis2.fhir.adapter.dhis.tracker.trackedentity.TrackedEntityTypeAttribute; + +import javax.annotation.Nonnull; +import javax.annotation.Nullable; +import java.time.ZonedDateTime; +import java.util.List; +import java.util.Optional; +import java.util.Set; +import java.util.function.Supplier; + +/** + * Immutable tracked entity type that is initialized lazily. The class + * implements serializable but + * + * @author volsch + */ +public class LazyImmutableTrackedEntityType implements TrackedEntityType +{ + private static final long serialVersionUID = -2860690024674037484L; + + private final Supplier supplier; + + private TrackedEntityType trackedEntityType; + + public LazyImmutableTrackedEntityType( @Nonnull Supplier supplier ) + { + this.supplier = supplier; + } + + @Override + public List getAttributes() + { + return getDelegate().getAttributes(); + } + + @Override + @Nonnull + public Optional getOptionalTypeAttribute( @Nonnull Reference reference ) + { + return getDelegate().getOptionalTypeAttribute( reference ); + } + + @Override + @Nullable + public TrackedEntityTypeAttribute getTypeAttribute( @Nonnull Reference reference ) + { + return getDelegate().getTypeAttribute( reference ); + } + + @Override + public String getCode() + { + return getDelegate().getCode(); + } + + @Override + public String getName() + { + return getDelegate().getName(); + } + + @Override + @Nonnull + public Set getAllReferences() + { + return getDelegate().getAllReferences(); + } + + @Override + public boolean isReference( @Nonnull Reference reference ) + { + return getDelegate().isReference( reference ); + } + + @Override + public String getOrgUnitId() + { + return getDelegate().getOrgUnitId(); + } + + @Override + public DhisResourceId getResourceId() + { + return getDelegate().getResourceId(); + } + + @Override + public boolean isDeleted() + { + return getDelegate().isDeleted(); + } + + @Override + public ZonedDateTime getLastUpdated() + { + return getDelegate().getLastUpdated(); + } + + @Override + @Nonnull + public DhisResourceType getResourceType() + { + return getDelegate().getResourceType(); + } + + @Override + public boolean isLocal() + { + return getDelegate().isLocal(); + } + + @Override + public boolean isNewResource() + { + return getDelegate().isNewResource(); + } + + @Override + public void resetNewResource() + { + getDelegate().resetNewResource(); + } + + @Override + public String getId() + { + return getDelegate().getId(); + } + + @Override + public void setId( String id ) + { + getDelegate().setId( id ); + } + + @Override + @Nullable + public String getItemId( @Nullable Reference reference ) + { + return getDelegate().getItemId( reference ); + } + + @Nonnull + protected TrackedEntityType getDelegate() + { + if ( trackedEntityType == null ) + { + trackedEntityType = supplier.get(); + } + + return trackedEntityType; + } +} diff --git a/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/scripted/WritableScriptedDhisResource.java b/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/scripted/WritableScriptedDhisResource.java index 8b7b71d9..158c4ea0 100644 --- a/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/scripted/WritableScriptedDhisResource.java +++ b/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/scripted/WritableScriptedDhisResource.java @@ -29,18 +29,12 @@ */ import org.dhis2.fhir.adapter.dhis.model.DhisResource; -import org.dhis2.fhir.adapter.dhis.model.DhisResourceId; -import org.dhis2.fhir.adapter.dhis.model.DhisResourceType; import org.dhis2.fhir.adapter.fhir.script.ScriptExecutionContext; import org.dhis2.fhir.adapter.fhir.script.ScriptExecutionForbidden; import org.dhis2.fhir.adapter.fhir.transform.FatalTransformerException; -import org.dhis2.fhir.adapter.fhir.transform.TransformerException; import org.dhis2.fhir.adapter.scriptable.Scriptable; import javax.annotation.Nonnull; -import javax.annotation.Nullable; -import java.io.Serializable; -import java.time.ZonedDateTime; /** * Implementation of writable scripted resource. The included metadata object can be accessed @@ -49,18 +43,11 @@ * @author volsch */ @Scriptable -public class WritableScriptedDhisResource implements AccessibleScriptedDhisResource, Serializable +public class WritableScriptedDhisResource extends AbstractWritableScriptedDhisResource implements AccessibleScriptedDhisResource { - private static final long serialVersionUID = 8245822986881397171L; - - protected final DhisResource resource; - - protected final ScriptExecutionContext scriptExecutionContext; - public WritableScriptedDhisResource( @Nonnull DhisResource resource, @Nonnull ScriptExecutionContext scriptExecutionContext ) { - this.resource = resource; - this.scriptExecutionContext = scriptExecutionContext; + super( resource, scriptExecutionContext ); } @ScriptExecutionForbidden @@ -72,72 +59,6 @@ public DhisResource getDhisResource() throw new FatalTransformerException( "Resource instance cannot be accessed within script execution." ); } - return resource; - } - - @Nullable - @Override - public String getId() - { - return resource.getId(); - } - - @Nonnull - @Override - public DhisResourceType getResourceType() - { - return resource.getResourceType(); - } - - @Nullable - @Override - public DhisResourceId getResourceId() - { - return resource.getResourceId(); - } - - @Override - public boolean isNewResource() - { - return resource.isNewResource(); - } - - @Override - public boolean isLocal() - { - return resource.isLocal(); - } - - @Override - public boolean isDeleted() - { - return resource.isDeleted(); - } - - @Nullable - @Override - public ZonedDateTime getLastUpdated() - { - return resource.getLastUpdated(); - } - - @Nullable - @Override - public String getOrganizationUnitId() - { - return resource.getOrgUnitId(); - } - - @Nullable - @Override - public ScriptedTrackedEntityInstance getTrackedEntityInstance() - { - return null; - } - - @Override - public void validate() throws TransformerException - { - // nothing to be done + return getInternalResource(); } } diff --git a/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/scripted/WritableScriptedLazyDhisResource.java b/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/scripted/WritableScriptedLazyDhisResource.java new file mode 100644 index 00000000..cec54d71 --- /dev/null +++ b/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/scripted/WritableScriptedLazyDhisResource.java @@ -0,0 +1,72 @@ +package org.dhis2.fhir.adapter.fhir.transform.scripted; + +/* + * Copyright (c) 2004-2019, University of Oslo + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * Neither the name of the HISP project nor the names of its contributors may + * be used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +import org.dhis2.fhir.adapter.dhis.model.DhisResource; +import org.dhis2.fhir.adapter.dhis.model.DhisResourceType; +import org.dhis2.fhir.adapter.fhir.script.ScriptExecutionContext; +import org.dhis2.fhir.adapter.fhir.script.ScriptExecutionForbidden; +import org.dhis2.fhir.adapter.fhir.transform.FatalTransformerException; +import org.dhis2.fhir.adapter.scriptable.Scriptable; + +import javax.annotation.Nonnull; +import javax.annotation.Nullable; + +/** + * Implementation of writable scripted resource. The included resource may not have been initialized. + * The included resource object can be accessed outside a script execution. Within a script + * execution {@link #getDhisResource()} will fail. + * + * @author volsch + */ +@Scriptable +public class WritableScriptedLazyDhisResource extends AbstractWritableScriptedDhisResource implements AccessibleWritableScriptedLazyDhisResource +{ + public WritableScriptedLazyDhisResource( @Nonnull DhisResource resource, @Nonnull ScriptExecutionContext scriptExecutionContext ) + { + super( resource, scriptExecutionContext ); + } + + public WritableScriptedLazyDhisResource( @Nonnull DhisResourceType resourceType, @Nonnull String resourceId, @Nonnull ScriptExecutionContext scriptExecutionContext ) + { + super( resourceType, resourceId, scriptExecutionContext ); + } + + @ScriptExecutionForbidden + @Nullable + public DhisResource getDhisResource() + { + if ( scriptExecutionContext.hasScriptExecution() ) + { + throw new FatalTransformerException( "Resource instance cannot be accessed within script execution." ); + } + + return resource; + } +} diff --git a/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/scripted/WritableScriptedTrackedEntityInstance.java b/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/scripted/WritableScriptedTrackedEntityInstance.java index dc5fc3de..23400a41 100644 --- a/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/scripted/WritableScriptedTrackedEntityInstance.java +++ b/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/scripted/WritableScriptedTrackedEntityInstance.java @@ -30,16 +30,20 @@ import org.dhis2.fhir.adapter.converter.ConversionException; import org.dhis2.fhir.adapter.dhis.converter.ValueConverter; -import org.dhis2.fhir.adapter.dhis.model.DhisResourceId; import org.dhis2.fhir.adapter.dhis.model.DhisResourceType; import org.dhis2.fhir.adapter.dhis.model.Option; import org.dhis2.fhir.adapter.dhis.model.Reference; +import org.dhis2.fhir.adapter.dhis.model.ReferenceType; import org.dhis2.fhir.adapter.dhis.tracker.trackedentity.TrackedEntityAttribute; import org.dhis2.fhir.adapter.dhis.tracker.trackedentity.TrackedEntityAttributeValue; import org.dhis2.fhir.adapter.dhis.tracker.trackedentity.TrackedEntityAttributes; import org.dhis2.fhir.adapter.dhis.tracker.trackedentity.TrackedEntityInstance; +import org.dhis2.fhir.adapter.dhis.tracker.trackedentity.TrackedEntityMetadataService; +import org.dhis2.fhir.adapter.dhis.tracker.trackedentity.TrackedEntityService; import org.dhis2.fhir.adapter.dhis.tracker.trackedentity.TrackedEntityType; import org.dhis2.fhir.adapter.dhis.tracker.trackedentity.TrackedEntityTypeAttribute; +import org.dhis2.fhir.adapter.fhir.script.ScriptExecutionContext; +import org.dhis2.fhir.adapter.fhir.transform.TransformerDataException; import org.dhis2.fhir.adapter.fhir.transform.TransformerException; import org.dhis2.fhir.adapter.fhir.transform.TransformerMappingException; import org.dhis2.fhir.adapter.fhir.transform.fhir.impl.util.ScriptedDateTimeUtils; @@ -65,65 +69,51 @@ @Scriptable @ScriptType( value = "TrackedEntityInstance", var = "trackedEntityInstance", transformDataType = "DHIS_TRACKED_ENTITY_INSTANCE", description = "Tracked entity instance. If tracked entity instance is not new and will be modified, it will be persisted." ) -public class WritableScriptedTrackedEntityInstance implements ScriptedTrackedEntityInstance +public class WritableScriptedTrackedEntityInstance extends WritableScriptedLazyDhisResource implements ScriptedTrackedEntityInstance { + private final TrackedEntityService service; + + private final TrackedEntityMetadataService metadataService; + private final TrackedEntityAttributes trackedEntityAttributes; - private final TrackedEntityType trackedEntityType; + private final TrackedEntityType lazyTrackedEntityType; - private final TrackedEntityInstance trackedEntityInstance; + private TrackedEntityType trackedEntityType; private final ValueConverter valueConverter; - public WritableScriptedTrackedEntityInstance( - @Nonnull TrackedEntityAttributes trackedEntityAttributes, @Nonnull TrackedEntityType trackedEntityType, - @Nonnull TrackedEntityInstance trackedEntityInstance, @Nonnull ValueConverter valueConverter ) + public WritableScriptedTrackedEntityInstance( @Nonnull TrackedEntityAttributes trackedEntityAttributes, @Nonnull TrackedEntityType trackedEntityType, + @Nonnull TrackedEntityInstance trackedEntityInstance, @Nonnull ScriptExecutionContext scriptExecutionContext, @Nonnull ValueConverter valueConverter ) { + super( trackedEntityInstance, scriptExecutionContext ); + + this.service = null; + this.metadataService = null; this.trackedEntityAttributes = trackedEntityAttributes; + this.lazyTrackedEntityType = trackedEntityType; this.trackedEntityType = trackedEntityType; - this.trackedEntityInstance = trackedEntityInstance; this.valueConverter = valueConverter; } - @Override - @ScriptMethod( description = "Returns if the tracked entity instance is new ans has not yet been saved on DHIS2." ) - public boolean isNewResource() + public WritableScriptedTrackedEntityInstance( @Nonnull TrackedEntityMetadataService metadataService, @Nonnull TrackedEntityService service, + @Nonnull TrackedEntityAttributes trackedEntityAttributes, @Nullable TrackedEntityType trackedEntityType, @Nonnull String trackedEntityInstanceId, + @Nonnull ScriptExecutionContext scriptExecutionContext, @Nonnull ValueConverter valueConverter ) { - return trackedEntityInstance.isNewResource(); - } + super( DhisResourceType.TRACKED_ENTITY, trackedEntityInstanceId, scriptExecutionContext ); - @Override - public boolean isLocal() - { - return trackedEntityInstance.isLocal(); - } - - @Override - public boolean isDeleted() - { - return false; - } - - @Nullable - @Override - @ScriptMethod( description = "Returns the ID of the tracked entity instance on DHIS2. Return null if the instance is new." ) - public String getId() - { - return trackedEntityInstance.getId(); + this.metadataService = metadataService; + this.service = service; + this.trackedEntityAttributes = trackedEntityAttributes; + this.lazyTrackedEntityType = trackedEntityType == null ? new LazyImmutableTrackedEntityType( this::getLoadedType ) : trackedEntityType; + this.valueConverter = valueConverter; } @Nonnull @Override - public DhisResourceType getResourceType() - { - return trackedEntityInstance.getResourceType(); - } - - @Nullable - @Override - public DhisResourceId getResourceId() + protected TrackedEntityInstance getInternalResource() { - return trackedEntityInstance.getResourceId(); + return (TrackedEntityInstance) super.getInternalResource(); } @Nullable @@ -131,7 +121,7 @@ public DhisResourceId getResourceId() @ScriptMethod( description = "Returns the date and time when the resource has been updated the last time or null if this is a new resource." ) public ZonedDateTime getLastUpdated() { - return trackedEntityInstance.getLastUpdated(); + return getInternalResource().getLastUpdated(); } @Nonnull @@ -139,7 +129,13 @@ public ZonedDateTime getLastUpdated() @ScriptMethod( description = "Returns the ID of the tracked entity type to which this tracked entity instance belongs to on DHIS2." ) public String getTypeId() { - return trackedEntityType.getId(); + return getType().getId(); + } + + @Override + public boolean isLocal() + { + return isLoaded() ? super.isLocal() : service.isLocal( Objects.requireNonNull( getId() ) ); } @Nullable @@ -160,6 +156,14 @@ public TrackedEntityAttributes getTrackedEntityAttributes() @Override public TrackedEntityType getType() { + return lazyTrackedEntityType; + } + + @Nonnull + protected TrackedEntityType getLoadedType() + { + load(); + return trackedEntityType; } @@ -167,7 +171,7 @@ public TrackedEntityType getType() @ScriptMethod( description = "Returns the national identifier of the tracked entity instance." ) public String getIdentifier() { - return trackedEntityInstance.getIdentifier(); + return getInternalResource().getIdentifier(); } @Override @@ -175,7 +179,7 @@ public String getIdentifier() @ScriptMethod( description = "Returns the organization unit ID of the organization unit to which this tracked entity instance belongs to on DHIS2." ) public String getOrganizationUnitId() { - return trackedEntityInstance.getOrgUnitId(); + return getInternalResource().getOrgUnitId(); } @ScriptMethod( description = "Sets the organization unit ID of the organization unit to which this tracked entity instance belongs to on DHIS2 (must not be null)." ) @@ -185,11 +189,14 @@ public boolean setOrganizationUnitId( @Nullable String id ) throws TransformerEx { throw new TransformerMappingException( "Organization unit ID of tracked entity instance must not be null." ); } - if ( !Objects.equals( trackedEntityInstance.getId(), id ) ) + + if ( !Objects.equals( getInternalResource().getId(), id ) ) { - trackedEntityInstance.setModified( true ); + getInternalResource().setModified( true ); } - trackedEntityInstance.setOrgUnitId( id ); + + getInternalResource().setOrgUnitId( id ); + return true; } @@ -197,7 +204,7 @@ public boolean setOrganizationUnitId( @Nullable String id ) throws TransformerEx @ScriptMethod( description = "Returns the coordinates (normally longitude and latitude) of the tracked entity instance." ) public String getCoordinates() { - return trackedEntityInstance.getCoordinates(); + return getInternalResource().getCoordinates(); } @ScriptMethod( description = "Sets the coordinates of the tracked entity instance. This might be a string representation of the coordinates or a location object.", @@ -214,11 +221,14 @@ public boolean setCoordinates( @Nullable Object coordinates ) { throw new TransformerMappingException( "Value of tracked entity coordinates could not be converted: " + e.getMessage(), e ); } - if ( !Objects.equals( trackedEntityInstance.getCoordinates(), convertedValue ) ) + + if ( !Objects.equals( getInternalResource().getCoordinates(), convertedValue ) ) { - trackedEntityInstance.setModified( true ); + getInternalResource().setModified( true ); } - trackedEntityInstance.setCoordinates( convertedValue ); + + getInternalResource().setCoordinates( convertedValue ); + return true; } @@ -246,6 +256,7 @@ public boolean setValue( @Nonnull Reference attributeReference, @Nullable Object final TrackedEntityAttribute attribute = trackedEntityAttributes.getOptional( attributeReference ).orElseThrow( () -> new TransformerMappingException( "Tracked entity type attribute \"" + attributeReference + "\" does not exist." ) ); final TrackedEntityTypeAttribute typeAttribute = trackedEntityType.getOptionalTypeAttribute( attributeReference ).orElse( null ); + return setValue( attribute, typeAttribute, value, ScriptedDateTimeUtils.toZonedDateTime( lastUpdated, valueConverter ) ); } @@ -274,12 +285,13 @@ public boolean setOptionalValue( @Nullable Reference attributeReference, @Nullab { return setValue( attributeReference, value, lastUpdated ); } + return true; } public void initValue( @Nonnull Reference attributeReference ) { - trackedEntityType.getOptionalTypeAttribute( attributeReference ).ifPresent( ta -> trackedEntityInstance.getAttribute( ta.getAttributeId() ) ); + trackedEntityType.getOptionalTypeAttribute( attributeReference ).ifPresent( ta -> getInternalResource().getAttribute( ta.getAttributeId() ) ); } @Nullable @@ -288,6 +300,7 @@ public void initValue( @Nonnull Reference attributeReference ) public Object getValue( @Nonnull Reference attributeReference ) { final TrackedEntityAttribute attribute = getTypeAttribute( attributeReference ); + return getValue( attribute ); } @@ -301,10 +314,11 @@ public String getStringValue( @Nonnull Reference attributeReference ) protected boolean setValue( @Nonnull TrackedEntityAttribute attribute, @Nullable TrackedEntityTypeAttribute typeAttribute, @Nullable Object value, @Nullable ZonedDateTime lastUpdated ) throws TransformerException { - if ( (value == null) && (typeAttribute != null) && typeAttribute.isMandatory() ) + if ( value == null && typeAttribute != null && typeAttribute.isMandatory() ) { throw new TransformerMappingException( "Value of tracked entity type attribute \"" + typeAttribute.getName() + "\" is mandatory and cannot be null." ); } + if ( attribute.isGenerated() ) { throw new TransformerMappingException( "Value of tracked entity type attribute \"" + attribute.getName() + "\" is generated and cannot be set." ); @@ -320,11 +334,11 @@ protected boolean setValue( @Nonnull TrackedEntityAttribute attribute, @Nullable throw new TransformerMappingException( "Value of tracked entity type attribute \"" + attribute.getName() + "\" could not be converted: " + e.getMessage(), e ); } - if ( (convertedValue != null) && attribute.isOptionSetValue() ) + if ( convertedValue != null && attribute.isOptionSetValue() ) { String checked1 = convertedValue.toString(); - boolean found = attribute.getOptionSet().getOptions().stream() - .anyMatch( o -> Objects.equals( checked1, o.getCode() ) ); + boolean found = attribute.getOptionSet().getOptions().stream().anyMatch( o -> Objects.equals( checked1, o.getCode() ) ); + if ( found ) { convertedValue = checked1; @@ -334,8 +348,8 @@ protected boolean setValue( @Nonnull TrackedEntityAttribute attribute, @Nullable // try locale independent upper case match final String checked2 = checked1.toUpperCase( Locale.ROOT ); // try locale independent upper case match - found = attribute.getOptionSet().getOptions().stream() - .anyMatch( o -> checked2.equals( o.getCode() ) ); + found = attribute.getOptionSet().getOptions().stream().anyMatch( o -> checked2.equals( o.getCode() ) ); + if ( found ) { convertedValue = checked2; @@ -344,6 +358,7 @@ protected boolean setValue( @Nonnull TrackedEntityAttribute attribute, @Nullable { final Optional option = attribute.getOptionSet().getOptions().stream().filter( o -> o.getCode() != null ) .filter( o -> checked2.equals( o.getCode().toUpperCase( Locale.ROOT ) ) ).findFirst(); + if ( option.isPresent() ) { convertedValue = option.get().getCode(); @@ -351,6 +366,7 @@ protected boolean setValue( @Nonnull TrackedEntityAttribute attribute, @Nullable } } } + if ( !found ) { throw new TransformerMappingException( "Code \"" + convertedValue + "\" is not a valid option of \"" + @@ -358,24 +374,28 @@ protected boolean setValue( @Nonnull TrackedEntityAttribute attribute, @Nullable } } - final TrackedEntityAttributeValue attributeValue = trackedEntityInstance.getAttribute( attribute.getId() ); - if ( (lastUpdated != null) && (attributeValue.getLastUpdated() != null) && attributeValue.getLastUpdated().isAfter( lastUpdated ) ) + final TrackedEntityAttributeValue attributeValue = getInternalResource().getAttribute( attribute.getId() ); + + if ( lastUpdated != null && attributeValue.getLastUpdated() != null && attributeValue.getLastUpdated().isAfter( lastUpdated ) ) { return false; } if ( !Objects.equals( attributeValue.getValue(), convertedValue ) ) { - trackedEntityInstance.setModified( true ); + getInternalResource().setModified( true ); } + attributeValue.setValue( convertedValue ); + return true; } protected Object getValue( @Nonnull TrackedEntityAttribute attribute ) throws TransformerException { - final TrackedEntityAttributeValue attributeValue = trackedEntityInstance.getAttribute( attribute.getId() ); + final TrackedEntityAttributeValue attributeValue = getInternalResource().getAttribute( attribute.getId() ); final Object convertedValue; + try { convertedValue = valueConverter.convert( attributeValue.getValue(), attribute.getValueType(), attribute.getValueType().getJavaClass() ); @@ -384,6 +404,7 @@ protected Object getValue( @Nonnull TrackedEntityAttribute attribute ) throws Tr { throw new TransformerMappingException( "Value of tracked entity attribute \"" + attribute.getName() + "\" could not be converted: " + e.getMessage(), e ); } + return convertedValue; } @@ -391,17 +412,20 @@ protected Object getValue( @Nonnull TrackedEntityAttribute attribute ) throws Tr @ScriptMethod( description = "Validates the content of the tracked entity instance and throws an exception if the content is invalid." ) public void validate() throws TransformerException { - if ( trackedEntityInstance.getOrgUnitId() == null ) + if ( isLoaded() ) { - throw new TransformerMappingException( "Organization unit ID of tracked entity instance has not been specified." ); - } - - trackedEntityType.getAttributes().stream().filter( TrackedEntityTypeAttribute::isMandatory ).forEach( ta -> { - if ( !trackedEntityInstance.containsAttributeWithValue( ta.getAttributeId() ) ) + if ( getInternalResource().getOrgUnitId() == null ) { - throw new TransformerMappingException( "Value of tracked entity type attribute \"" + ta.getName() + "\" is mandatory and must be set." ); + throw new TransformerMappingException( "Organization unit ID of tracked entity instance has not been specified." ); } - } ); + + trackedEntityType.getAttributes().stream().filter( TrackedEntityTypeAttribute::isMandatory ).forEach( ta -> { + if ( !getInternalResource().containsAttributeWithValue( ta.getAttributeId() ) ) + { + throw new TransformerMappingException( "Value of tracked entity type attribute \"" + ta.getName() + "\" is mandatory and must be set." ); + } + } ); + } } @Nonnull @@ -410,4 +434,20 @@ private TrackedEntityAttribute getTypeAttribute( @Nonnull Reference attributeRef return trackedEntityAttributes.getOptional( attributeReference ).orElseThrow( () -> new TransformerMappingException( "Tracked entity type attribute does not exist: " + attributeReference ) ); } + + @Override + protected final void load() + { + if ( !isLoaded() || trackedEntityType == null ) + { + final String id = getId(); + final TrackedEntityInstance trackedEntityInstance; + + resource = trackedEntityInstance = service.findOneById( Objects.requireNonNull( id ) ) + .orElseThrow( () -> new TransformerDataException( "Tracked entity instance " + id + " could not be found." ) ); + trackedEntityType = metadataService.findTypeByReference( new Reference( trackedEntityInstance.getTypeId(), ReferenceType.ID ) ) + .orElseThrow( () -> new TransformerDataException( "Tracked entity type " + trackedEntityInstance.getTypeId() + + " of tracked entity instance " + trackedEntityInstance.getId() + " could not be found." ) ); + } + } } diff --git a/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/util/TransformerUtils.java b/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/util/TransformerUtils.java index 94a8f3f0..3b8d60a9 100644 --- a/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/util/TransformerUtils.java +++ b/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/util/TransformerUtils.java @@ -91,6 +91,12 @@ public static T getScriptVariable( @Nonnull Map scriptVariab return value; } + @Nullable + public static T getOptionalScriptVariable( @Nonnull Map scriptVariables, @Nonnull ScriptVariable scriptVariable, @Nonnull Class type ) throws FatalTransformerException + { + return type.cast( scriptVariables.get( scriptVariable.getVariableName() ) ); + } + @Nonnull public static T getScriptVariable( @Nonnull ScriptExecutionContext scriptExecutionContext, @Nonnull String name, @Nonnull Class c ) { diff --git a/fhir/src/test/java/org/dhis2/fhir/adapter/fhir/transform/fhir/impl/program/FhirToEnrollmentTransformerTest.java b/fhir/src/test/java/org/dhis2/fhir/adapter/fhir/transform/fhir/impl/program/FhirToEnrollmentTransformerTest.java index 2e65b89d..def5dbff 100644 --- a/fhir/src/test/java/org/dhis2/fhir/adapter/fhir/transform/fhir/impl/program/FhirToEnrollmentTransformerTest.java +++ b/fhir/src/test/java/org/dhis2/fhir/adapter/fhir/transform/fhir/impl/program/FhirToEnrollmentTransformerTest.java @@ -216,6 +216,7 @@ public void setUp() trackedEntityInstance = new TrackedEntityInstance(); trackedEntityInstance.setId( "jsy278shj12" ); trackedEntityInstance.setIdentifier( "NAT01020304" ); + trackedEntityInstance.setTypeId( "js73jhsyus91" ); organizationUnit = new OrganizationUnit(); organizationUnit.setId( "823njmdhsj2" ); diff --git a/fhir/src/test/java/org/dhis2/fhir/adapter/fhir/transform/scripted/AbstractWritableScriptedDhisResourceTest.java b/fhir/src/test/java/org/dhis2/fhir/adapter/fhir/transform/scripted/AbstractWritableScriptedDhisResourceTest.java new file mode 100644 index 00000000..8958ce29 --- /dev/null +++ b/fhir/src/test/java/org/dhis2/fhir/adapter/fhir/transform/scripted/AbstractWritableScriptedDhisResourceTest.java @@ -0,0 +1,242 @@ +package org.dhis2.fhir.adapter.fhir.transform.scripted; + +/* + * Copyright (c) 2004-2019, University of Oslo + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * Neither the name of the HISP project nor the names of its contributors may + * be used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +import org.dhis2.fhir.adapter.dhis.model.DhisResource; +import org.dhis2.fhir.adapter.dhis.model.DhisResourceId; +import org.dhis2.fhir.adapter.dhis.model.DhisResourceType; +import org.dhis2.fhir.adapter.dhis.tracker.trackedentity.TrackedEntityInstance; +import org.dhis2.fhir.adapter.fhir.script.ScriptExecutionContext; +import org.junit.Assert; +import org.junit.Rule; +import org.junit.Test; +import org.mockito.Mock; +import org.mockito.Mockito; +import org.mockito.junit.MockitoJUnit; +import org.mockito.junit.MockitoRule; + +import java.time.ZonedDateTime; + +/** + * Unit tests for {@link AbstractWritableScriptedDhisResource}. + * + * @author volsch + */ +public class AbstractWritableScriptedDhisResourceTest +{ + @Mock + private ScriptExecutionContext scriptExecutionContext; + + @Mock + private DhisResource dhisResource; + + @Rule + public MockitoRule mockitoRule = MockitoJUnit.rule(); + + @Test + public void getTrackedEntityInstanceUnloaded() + { + final TrackedEntityInstance trackedEntityInstance = new TrackedEntityInstance(); + final AbstractWritableScriptedDhisResource scriptedResource = new AbstractWritableScriptedDhisResource( + DhisResourceType.TRACKED_ENTITY, "4711", scriptExecutionContext ) + { + }; + Assert.assertNull( scriptedResource.getTrackedEntityInstance() ); + } + + @Test + public void getInternalResourceLoad() + { + final TrackedEntityInstance trackedEntityInstance = new TrackedEntityInstance(); + + final AbstractWritableScriptedDhisResource scriptedResource = new AbstractWritableScriptedDhisResource( + DhisResourceType.TRACKED_ENTITY, "4711", scriptExecutionContext ) + { + @Override + protected void load() + { + resource = trackedEntityInstance; + } + }; + + Assert.assertSame( trackedEntityInstance, scriptedResource.getInternalResource() ); + } + + @Test + public void getInternalResourceLoaded() + { + final TrackedEntityInstance trackedEntityInstance = new TrackedEntityInstance(); + final AbstractWritableScriptedDhisResource scriptedResource = new AbstractWritableScriptedDhisResource( trackedEntityInstance, scriptExecutionContext ) + { + }; + Assert.assertSame( trackedEntityInstance, scriptedResource.getInternalResource() ); + } + + @Test + public void getIdUnloaded() + { + final AbstractWritableScriptedDhisResource scriptedResource = new AbstractWritableScriptedDhisResource( + DhisResourceType.TRACKED_ENTITY, "4711", scriptExecutionContext ) + { + }; + Assert.assertSame( "4711", scriptedResource.getId() ); + } + + @Test + public void getResourceIdUnloaded() + { + final AbstractWritableScriptedDhisResource scriptedResource = new AbstractWritableScriptedDhisResource( + DhisResourceType.TRACKED_ENTITY, "4711", scriptExecutionContext ) + { + }; + Assert.assertEquals( new DhisResourceId( DhisResourceType.TRACKED_ENTITY, "4711" ), scriptedResource.getResourceId() ); + } + + @Test + public void getResourceTypeUnloaded() + { + final AbstractWritableScriptedDhisResource scriptedResource = new AbstractWritableScriptedDhisResource( + DhisResourceType.TRACKED_ENTITY, "4711", scriptExecutionContext ) + { + }; + Assert.assertSame( DhisResourceType.TRACKED_ENTITY, scriptedResource.getResourceType() ); + } + + @Test + public void isLoadedUnloaded() + { + final AbstractWritableScriptedDhisResource scriptedResource = new AbstractWritableScriptedDhisResource( + DhisResourceType.TRACKED_ENTITY, "4711", scriptExecutionContext ) + { + }; + Assert.assertFalse( scriptedResource.isLoaded() ); + } + + @Test + public void isLoaded() + { + final TrackedEntityInstance trackedEntityInstance = new TrackedEntityInstance(); + final AbstractWritableScriptedDhisResource scriptedResource = new AbstractWritableScriptedDhisResource( trackedEntityInstance, scriptExecutionContext ) + { + }; + Assert.assertTrue( scriptedResource.isLoaded() ); + } + + @Test + public void isNewResourceUnloaded() + { + Mockito.doReturn( true ).when( dhisResource ).isNewResource(); + + final AbstractWritableScriptedDhisResource scriptedResource = new AbstractWritableScriptedDhisResource( DhisResourceType.TRACKED_ENTITY, "4711", scriptExecutionContext ) + { + @Override + protected void load() + { + resource = dhisResource; + } + }; + Assert.assertTrue( scriptedResource.isNewResource() ); + + Mockito.verify( dhisResource ).isNewResource(); + } + + @Test + public void isDeletedUnloaded() + { + Mockito.doReturn( true ).when( dhisResource ).isNewResource(); + + final AbstractWritableScriptedDhisResource scriptedResource = new AbstractWritableScriptedDhisResource( dhisResource, scriptExecutionContext ) + { + @Override + protected void load() + { + resource = dhisResource; + } + }; + Assert.assertTrue( scriptedResource.isNewResource() ); + + Mockito.verify( dhisResource ).isNewResource(); + } + + @Test + public void isLocalUnloaded() + { + Mockito.doReturn( true ).when( dhisResource ).isLocal(); + + final AbstractWritableScriptedDhisResource scriptedResource = new AbstractWritableScriptedDhisResource( dhisResource, scriptExecutionContext ) + { + @Override + protected void load() + { + resource = dhisResource; + } + }; + Assert.assertTrue( scriptedResource.isLocal() ); + + Mockito.verify( dhisResource ).isLocal(); + } + + @Test + public void getLastUpdatedUnloaded() + { + final ZonedDateTime lastUpdated = ZonedDateTime.now(); + + Mockito.doReturn( lastUpdated ).when( dhisResource ).getLastUpdated(); + + final AbstractWritableScriptedDhisResource scriptedResource = new AbstractWritableScriptedDhisResource( dhisResource, scriptExecutionContext ) + { + @Override + protected void load() + { + resource = dhisResource; + } + }; + Assert.assertEquals( lastUpdated, dhisResource.getLastUpdated() ); + + Mockito.verify( dhisResource ).getLastUpdated(); + } + + @Test + public void getOrgUnitIdUnloaded() + { + Mockito.doReturn( "4899" ).when( dhisResource ).getOrgUnitId(); + + final AbstractWritableScriptedDhisResource scriptedResource = new AbstractWritableScriptedDhisResource( dhisResource, scriptExecutionContext ) + { + @Override + protected void load() + { + resource = dhisResource; + } + }; + Assert.assertEquals( "4899", dhisResource.getOrgUnitId() ); + + Mockito.verify( dhisResource ).getOrgUnitId(); + } +} \ No newline at end of file diff --git a/fhir/src/test/java/org/dhis2/fhir/adapter/fhir/transform/scripted/WritableScriptedDhisResourceTest.java b/fhir/src/test/java/org/dhis2/fhir/adapter/fhir/transform/scripted/WritableScriptedDhisResourceTest.java new file mode 100644 index 00000000..f6b91f0d --- /dev/null +++ b/fhir/src/test/java/org/dhis2/fhir/adapter/fhir/transform/scripted/WritableScriptedDhisResourceTest.java @@ -0,0 +1,72 @@ +package org.dhis2.fhir.adapter.fhir.transform.scripted; + +/* + * Copyright (c) 2004-2019, University of Oslo + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * Neither the name of the HISP project nor the names of its contributors may + * be used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +import org.dhis2.fhir.adapter.dhis.tracker.trackedentity.TrackedEntityInstance; +import org.dhis2.fhir.adapter.fhir.script.ScriptExecutionContext; +import org.dhis2.fhir.adapter.fhir.transform.FatalTransformerException; +import org.junit.Assert; +import org.junit.Rule; +import org.junit.Test; +import org.mockito.Mock; +import org.mockito.Mockito; +import org.mockito.junit.MockitoJUnit; +import org.mockito.junit.MockitoRule; + +/** + * Unit tests for {@link WritableScriptedDhisResource}. + * + * @author volsch + */ +public class WritableScriptedDhisResourceTest +{ + @Mock + private ScriptExecutionContext scriptExecutionContext; + + @Rule + public MockitoRule mockitoRule = MockitoJUnit.rule(); + + @Test + public void getDhisResource() + { + final TrackedEntityInstance resource = new TrackedEntityInstance(); + final WritableScriptedDhisResource scriptedResource = new WritableScriptedDhisResource( resource, scriptExecutionContext ); + Assert.assertSame( resource, scriptedResource.getDhisResource() ); + } + + @Test( expected = FatalTransformerException.class ) + public void getDhisResourceScriptExecution() + { + Mockito.doReturn( true ).when( scriptExecutionContext ).hasScriptExecution(); + + final TrackedEntityInstance resource = new TrackedEntityInstance(); + final WritableScriptedDhisResource scriptedResource = new WritableScriptedDhisResource( resource, scriptExecutionContext ); + Assert.assertSame( resource, scriptedResource.getDhisResource() ); + } +} diff --git a/fhir/src/test/java/org/dhis2/fhir/adapter/fhir/transform/scripted/WritableScriptedLazyDhisResourceTest.java b/fhir/src/test/java/org/dhis2/fhir/adapter/fhir/transform/scripted/WritableScriptedLazyDhisResourceTest.java new file mode 100644 index 00000000..8c74dabf --- /dev/null +++ b/fhir/src/test/java/org/dhis2/fhir/adapter/fhir/transform/scripted/WritableScriptedLazyDhisResourceTest.java @@ -0,0 +1,89 @@ +package org.dhis2.fhir.adapter.fhir.transform.scripted; + +/* + * Copyright (c) 2004-2019, University of Oslo + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * Neither the name of the HISP project nor the names of its contributors may + * be used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +import org.dhis2.fhir.adapter.dhis.model.DhisResourceType; +import org.dhis2.fhir.adapter.dhis.tracker.trackedentity.TrackedEntityInstance; +import org.dhis2.fhir.adapter.fhir.script.ScriptExecutionContext; +import org.dhis2.fhir.adapter.fhir.transform.FatalTransformerException; +import org.junit.Assert; +import org.junit.Rule; +import org.junit.Test; +import org.mockito.Mock; +import org.mockito.Mockito; +import org.mockito.junit.MockitoJUnit; +import org.mockito.junit.MockitoRule; + +/** + * Unit tests for {@link WritableScriptedLazyDhisResource}. + * + * @author volsch + */ +public class WritableScriptedLazyDhisResourceTest +{ + @Mock + private ScriptExecutionContext scriptExecutionContext; + + @Rule + public MockitoRule mockitoRule = MockitoJUnit.rule(); + + @Test + public void getDhisResourceNotSet() + { + final WritableScriptedLazyDhisResource resource = new WritableScriptedLazyDhisResource( DhisResourceType.TRACKED_ENTITY, "6738", scriptExecutionContext ); + Assert.assertNull( resource.getDhisResource() ); + } + + @Test( expected = FatalTransformerException.class ) + public void getDhisResourceNotSetScriptExecution() + { + Mockito.doReturn( true ).when( scriptExecutionContext ).hasScriptExecution(); + + final WritableScriptedLazyDhisResource resource = new WritableScriptedLazyDhisResource( DhisResourceType.TRACKED_ENTITY, "6738", scriptExecutionContext ); + resource.getDhisResource(); + } + + @Test + public void getDhisResource() + { + final TrackedEntityInstance resource = new TrackedEntityInstance(); + final WritableScriptedLazyDhisResource scriptedResource = new WritableScriptedLazyDhisResource( resource, scriptExecutionContext ); + Assert.assertSame( resource, scriptedResource.getDhisResource() ); + } + + @Test( expected = FatalTransformerException.class ) + public void getDhisResourceScriptExecution() + { + Mockito.doReturn( true ).when( scriptExecutionContext ).hasScriptExecution(); + + final TrackedEntityInstance resource = new TrackedEntityInstance(); + final WritableScriptedLazyDhisResource scriptedResource = new WritableScriptedLazyDhisResource( resource, scriptExecutionContext ); + Assert.assertSame( resource, scriptedResource.getDhisResource() ); + } +} diff --git a/fhir/src/test/java/org/dhis2/fhir/adapter/fhir/transform/util/TransformerUtilsTest.java b/fhir/src/test/java/org/dhis2/fhir/adapter/fhir/transform/util/TransformerUtilsTest.java new file mode 100644 index 00000000..6113722d --- /dev/null +++ b/fhir/src/test/java/org/dhis2/fhir/adapter/fhir/transform/util/TransformerUtilsTest.java @@ -0,0 +1,61 @@ +package org.dhis2.fhir.adapter.fhir.transform.util; + +/* + * Copyright (c) 2004-2019, University of Oslo + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * Neither the name of the HISP project nor the names of its contributors may + * be used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +import org.dhis2.fhir.adapter.fhir.metadata.model.ScriptVariable; +import org.junit.Assert; +import org.junit.Test; + +import java.util.HashMap; +import java.util.Map; + +/** + * Unit tests {@link TransformerUtils}. + * + * @author volsch + */ +public class TransformerUtilsTest +{ + @Test + public void getOptionalScriptVariable() + { + final Map variables = new HashMap<>(); + variables.put( ScriptVariable.ORGANIZATION_UNIT_ID.getVariableName(), "test1" ); + + Assert.assertEquals( "test1", TransformerUtils.getOptionalScriptVariable( variables, ScriptVariable.ORGANIZATION_UNIT_ID, String.class ) ); + } + + @Test + public void getOptionalScriptVariableNotFound() + { + final Map variables = new HashMap<>(); + + Assert.assertNull( TransformerUtils.getOptionalScriptVariable( variables, ScriptVariable.ORGANIZATION_UNIT_ID, String.class ) ); + } +} \ No newline at end of file From 3ff40b1ef6a95252fd587ac26c6a1ccf55a76061 Mon Sep 17 00:00:00 2001 From: Volker Schmidt Date: Thu, 25 Jul 2019 12:45:04 +0200 Subject: [PATCH 04/21] Adds tests for transformation of DHIS2 Program to FHIR Plan Definition. --- .../AbstractProgramStageFhirRestAppTest.java | 5 +- .../r4/R4ProgramMetadataFhirRestAppTest.java | 335 ++++++++++++++++++ .../adapter/dhis/model/DhisResourceType.java | 25 +- ...adataToFhirPlanDefinitionTransformer.java} | 6 +- .../fhir/metadata/model/AbstractRule.java | 5 +- .../repository/CustomRuleRepository.java | 2 +- .../impl/CustomRuleRepositoryImpl.java | 7 +- ...ractDhisMetadataToFhirRequestResolver.java | 2 +- ...adataToFhirPlanDefinitionTransformer.java} | 4 +- 9 files changed, 366 insertions(+), 25 deletions(-) create mode 100644 app/src/test/java/org/dhis2/fhir/adapter/fhir/server/r4/R4ProgramMetadataFhirRestAppTest.java rename fhir-r4/src/main/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/metadata/r4/{R4ProgramMetadataToFhirCarePlanTransformer.java => R4ProgramMetadataToFhirPlanDefinitionTransformer.java} (92%) rename fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/metadata/program/{AbstractProgramMetadataToFhirCarePlanTransformer.java => AbstractProgramMetadataToFhirPlanDefinitionTransformer.java} (92%) diff --git a/app/src/test/java/org/dhis2/fhir/adapter/fhir/server/AbstractProgramStageFhirRestAppTest.java b/app/src/test/java/org/dhis2/fhir/adapter/fhir/server/AbstractProgramStageFhirRestAppTest.java index fba6ac0b..8962a7e8 100644 --- a/app/src/test/java/org/dhis2/fhir/adapter/fhir/server/AbstractProgramStageFhirRestAppTest.java +++ b/app/src/test/java/org/dhis2/fhir/adapter/fhir/server/AbstractProgramStageFhirRestAppTest.java @@ -74,9 +74,8 @@ protected void expectProgramStageMetadataRequests() throws Exception systemDhis2Server.expect( ExpectedCount.between( 0, 1 ), method( HttpMethod.GET ) ).andExpect( header( "Authorization", testConfiguration.getDhis2SystemAuthorization() ) ) .andExpect( requestTo( dhis2BaseUrl + "/api/" + dhis2ApiVersion + "/programs.json?paging=false&fields=id,name,code,description,selectIncidentDatesInFuture,selectEnrollmentDatesInFuture,displayIncidentDate,registration," + "withoutRegistration,captureCoordinates,trackedEntityType%5Bid%5D,programTrackedEntityAttributes%5Bid,name,valueType,mandatory,allowFutureDate,trackedEntityAttribute%5Bid,name,code,valueType,generated%5D%5D,programStages%5Bid,name," + - "description," + - "repeatable,captureCoordinates,generatedByEnrollmentDate,minDaysFromStart,programStageDataElements%5Bid,compulsory,allowProvidedElsewhere,dataElement%5Bid,name,code,formName,valueType,optionSetValue,optionSet%5Bid,name,options%5Bcode," + - "name%5D%5D%5D%5D%5D&filter=name:eq:Child%20Programme" ) ) + "description,repeatable,captureCoordinates,generatedByEnrollmentDate,minDaysFromStart,programStageDataElements%5Bid,compulsory,allowProvidedElsewhere,dataElement%5Bid,name,code,formName,valueType,optionSetValue,optionSet%5Bid,name," + + "options%5Bcode,name%5D%5D%5D%5D%5D&filter=name:eq:Child%20Programme" ) ) .andRespond( withSuccess( IOUtils.resourceToString( "/org/dhis2/fhir/adapter/dhis/test/default-program.json", StandardCharsets.UTF_8 ), MediaType.APPLICATION_JSON ) ); } } diff --git a/app/src/test/java/org/dhis2/fhir/adapter/fhir/server/r4/R4ProgramMetadataFhirRestAppTest.java b/app/src/test/java/org/dhis2/fhir/adapter/fhir/server/r4/R4ProgramMetadataFhirRestAppTest.java new file mode 100644 index 00000000..e7d1c616 --- /dev/null +++ b/app/src/test/java/org/dhis2/fhir/adapter/fhir/server/r4/R4ProgramMetadataFhirRestAppTest.java @@ -0,0 +1,335 @@ +package org.dhis2.fhir.adapter.fhir.server.r4; + +/* + * Copyright (c) 2004-2019, University of Oslo + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * Neither the name of the HISP project nor the names of its contributors may + * be used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +import ca.uhn.fhir.rest.client.api.IGenericClient; +import ca.uhn.fhir.rest.client.interceptor.BasicAuthInterceptor; +import ca.uhn.fhir.rest.server.exceptions.AuthenticationException; +import ca.uhn.fhir.rest.server.exceptions.ResourceNotFoundException; +import org.apache.commons.io.IOUtils; +import org.dhis2.fhir.adapter.AbstractAppTest; +import org.dhis2.fhir.adapter.fhir.model.FhirVersion; +import org.hl7.fhir.r4.model.Bundle; +import org.hl7.fhir.r4.model.PlanDefinition; +import org.junit.Assert; +import org.junit.Ignore; +import org.junit.Test; +import org.springframework.http.HttpMethod; +import org.springframework.http.HttpStatus; +import org.springframework.http.MediaType; +import org.springframework.test.web.client.ExpectedCount; + +import javax.annotation.Nonnull; +import java.nio.charset.StandardCharsets; + +import static org.springframework.test.web.client.match.MockRestRequestMatchers.*; +import static org.springframework.test.web.client.response.MockRestResponseCreators.withStatus; +import static org.springframework.test.web.client.response.MockRestResponseCreators.withSuccess; + +/** + * R4 tests for rest interfaces that access + * DHIS2 program metadata. + * + * @author volsch + */ +@Ignore +public class R4ProgramMetadataFhirRestAppTest extends AbstractAppTest +{ + @Nonnull + @Override + protected FhirVersion getFhirVersion() + { + return FhirVersion.R4; + } + + @Test( expected = AuthenticationException.class ) + public void getPlanDefinitionWithoutAuthorization() + { + final IGenericClient client = createGenericClient(); + client.read().resource( PlanDefinition.class ).withId( "ldXIdLNUNEn" ).execute(); + } + + @Test( expected = AuthenticationException.class ) + public void getPlanDefinitionWithInvalidAuthorization() + { + userDhis2Server.expect( ExpectedCount.once(), method( HttpMethod.GET ) ).andExpect( header( "Authorization", "Basic Zmhpcl9jbGllbnQ6aW52YWxpZF8x" ) ) + .andExpect( requestTo( dhis2BaseUrl + "/api/" + dhis2ApiVersion + "/programs/EPDyQuoRnXk.json?" + + "fields=id,name,code,description,selectIncidentDatesInFuture,selectEnrollmentDatesInFuture,displayIncidentDate,registration,withoutRegistration,captureCoordinates,trackedEntityType%5Bid%5D,programTrackedEntityAttributes%5Bid,name,valueType," + + "mandatory,allowFutureDate,trackedEntityAttribute%5Bid,name,code,valueType,generated%5D%5D,programStages%5Bid,name,description,repeatable,captureCoordinates,generatedByEnrollmentDate,minDaysFromStart,programStageDataElements%5Bid,compulsory," + + "allowProvidedElsewhere,dataElement%5Bid,name,code,formName,valueType,optionSetValue,optionSet%5Bid,name,options%5Bcode,name%5D%5D%5D%5D%5D" ) ) + .andRespond( withStatus( HttpStatus.UNAUTHORIZED ) ); + + final IGenericClient client = createGenericClient(); + client.registerInterceptor( new BasicAuthInterceptor( "fhir_client", "invalid_1" ) ); + client.read().resource( PlanDefinition.class ).withId( "EPDyQuoRnXk" ).execute(); + } + + @Test + public void getPlanDefinitionRepeated() throws Exception + { + getPlanDefinition(); + getPlanDefinition(); + } + + private void getPlanDefinition() throws Exception + { + systemDhis2Server.reset(); + userDhis2Server.reset(); + + systemDhis2Server.expect( ExpectedCount.between( 0, 1 ), method( HttpMethod.GET ) ).andExpect( header( "Authorization", testConfiguration.getDhis2SystemAuthorization() ) ) + .andExpect( requestTo( dhis2BaseUrl + "/api/" + dhis2ApiVersion + "/trackedEntityTypes/MCPQUTHX1Ze.json?" + + "fields=id,name,trackedEntityTypeAttributes%5Bid,name,valueType,mandatory,trackedEntityAttribute%5Bid,name,code,valueType,generated,optionSetValue,optionSet%5Bid,name,options%5Bcode,name%5D%5D%5D%5D" ) ) + .andRespond( withSuccess( IOUtils.resourceToString( "/org/dhis2/fhir/adapter/dhis/test/single-tracked-entity-type.json", StandardCharsets.UTF_8 ), MediaType.APPLICATION_JSON ) ); + userDhis2Server.expect( ExpectedCount.once(), method( HttpMethod.GET ) ).andExpect( header( "Authorization", "Basic Zmhpcl9jbGllbnQ6Zmhpcl9jbGllbnRfMQ==" ) ) + .andExpect( requestTo( dhis2BaseUrl + "/api/" + dhis2ApiVersion + "/programs/EPDyQuoRnXk.json?fields=id,name,code,description,selectIncidentDatesInFuture,selectEnrollmentDatesInFuture,displayIncidentDate,registration," + + "withoutRegistration,captureCoordinates,trackedEntityType%5Bid%5D,programTrackedEntityAttributes%5Bid,name,valueType,mandatory,allowFutureDate,trackedEntityAttribute%5Bid,name,code,valueType,generated%5D%5D,programStages%5Bid,name," + + "description,repeatable,captureCoordinates,generatedByEnrollmentDate,minDaysFromStart,programStageDataElements%5Bid,compulsory,allowProvidedElsewhere,dataElement%5Bid,name,code,formName,valueType,optionSetValue,optionSet%5Bid,name," + + "options%5Bcode,name%5D%5D%5D%5D%5D" ) ) + .andRespond( withSuccess( IOUtils.resourceToString( "/org/dhis2/fhir/adapter/dhis/test/single-program.json", StandardCharsets.UTF_8 ), MediaType.APPLICATION_JSON ) ); + userDhis2Server.expect( ExpectedCount.between( 0, 1 ), method( HttpMethod.GET ) ).andExpect( header( "Authorization", "Basic Zmhpcl9jbGllbnQ6Zmhpcl9jbGllbnRfMQ==" ) ) + .andExpect( requestTo( dhis2BaseUrl + "/api/" + dhis2ApiVersion + "/programs.json?paging=false&fields=id,name,code,description,selectIncidentDatesInFuture,selectEnrollmentDatesInFuture,displayIncidentDate,registration," + + "withoutRegistration,captureCoordinates,trackedEntityType%5Bid%5D,programTrackedEntityAttributes%5Bid,name,valueType,mandatory,allowFutureDate,trackedEntityAttribute%5Bid,name,code,valueType,generated%5D%5D,programStages%5Bid,name," + + "description,repeatable,captureCoordinates,generatedByEnrollmentDate,minDaysFromStart,programStageDataElements%5Bid,compulsory,allowProvidedElsewhere,dataElement%5Bid,name,code,formName,valueType,optionSetValue,optionSet%5Bid,name," + + "options%5Bcode,name%5D%5D%5D%5D%5D" ) ) + .andRespond( withSuccess( IOUtils.resourceToString( "/org/dhis2/fhir/adapter/dhis/test/all-programs.json", StandardCharsets.UTF_8 ), MediaType.APPLICATION_JSON ) ); + + final IGenericClient client = createGenericClient(); + client.registerInterceptor( new BasicAuthInterceptor( "fhir_client", "fhir_client_1" ) ); + PlanDefinition planDefinition = client.read().resource( PlanDefinition.class ).withId( "EPDyQuoRnXk" ).execute(); + Assert.assertEquals( "Child Programme", planDefinition.getName() ); + + systemDhis2Server.verify(); + userDhis2Server.verify(); + } + + @Test( expected = ResourceNotFoundException.class ) + public void getPlanDefinitionNotExistsRepeated() + { + try + { + getPlanDefinitionNotExists(); + Assert.fail( "Exception expected also an first invocation." ); + } + catch ( ResourceNotFoundException e ) + { + getPlanDefinitionNotExists(); + } + } + + private void getPlanDefinitionNotExists() + { + systemDhis2Server.reset(); + userDhis2Server.reset(); + + userDhis2Server.expect( ExpectedCount.once(), method( HttpMethod.GET ) ).andExpect( header( "Authorization", "Basic Zmhpcl9jbGllbnQ6Zmhpcl9jbGllbnRfMQ==" ) ) + .andExpect( requestTo( dhis2BaseUrl + "/api/" + dhis2ApiVersion + "/programs/0dXIdLNUNEn.json?fields=id,name,code,description,selectIncidentDatesInFuture,selectEnrollmentDatesInFuture,displayIncidentDate,registration," + + "withoutRegistration,captureCoordinates,trackedEntityType%5Bid%5D,programTrackedEntityAttributes%5Bid,name,valueType,mandatory,allowFutureDate,trackedEntityAttribute%5Bid,name,code,valueType,generated%5D%5D,programStages%5Bid,name," + + "description,repeatable,captureCoordinates,generatedByEnrollmentDate,minDaysFromStart,programStageDataElements%5Bid,compulsory,allowProvidedElsewhere,dataElement%5Bid,name,code,formName,valueType,optionSetValue,optionSet%5Bid,name," + + "options%5Bcode,name%5D%5D%5D%5D%5D" ) ) + .andRespond( withStatus( HttpStatus.NOT_FOUND ).contentType( MediaType.APPLICATION_JSON ).body( "{}" ) ); + + try + { + final IGenericClient client = createGenericClient(); + client.registerInterceptor( new BasicAuthInterceptor( "fhir_client", "fhir_client_1" ) ); + client.read().resource( PlanDefinition.class ).withId( "0dXIdLNUNEn" ).execute(); + } + catch ( ResourceNotFoundException e ) + { + systemDhis2Server.verify(); + userDhis2Server.verify(); + throw e; + } + } + + @Test( expected = ResourceNotFoundException.class ) + public void getPlanDefinitionRuleNotFoundRepeated() + { + try + { + getPlanDefinitionNotExists(); + Assert.fail( "Exception expected also an first invocation." ); + } + catch ( ResourceNotFoundException e ) + { + getPlanDefinitionNotExists(); + } + } + + private void getPlanDefinitionRuleNotFound() + { + systemDhis2Server.reset(); + userDhis2Server.reset(); + + try + { + final IGenericClient client = createGenericClient(); + client.registerInterceptor( new BasicAuthInterceptor( "fhir_client", "fhir_client_1" ) ); + client.read().resource( PlanDefinition.class ).withId( "ldXIdLNUNEn" ).execute(); + } + catch ( ResourceNotFoundException e ) + { + systemDhis2Server.verify(); + userDhis2Server.verify(); + throw e; + } + } + + @Test( expected = AuthenticationException.class ) + public void getPlanDefinitionByIdentifierWithoutAuthorization() + { + final IGenericClient client = createGenericClient(); + client.search().forResource( PlanDefinition.class ).where( PlanDefinition.IDENTIFIER.exactly().systemAndIdentifier( "http://www.dhis2.org/dhis2fhiradapter/systems/plan-definition-identifier", "XX_1234" ) ).returnBundle( Bundle.class ).execute(); + } + + @Test( expected = AuthenticationException.class ) + public void getPlanDefinitionByIdentifierInvalidAuthorization() + { + userDhis2Server.expect( ExpectedCount.once(), method( HttpMethod.GET ) ).andExpect( header( "Authorization", "Basic Zmhpcl9jbGllbnQ6aW52YWxpZF8x" ) ) + .andExpect( requestTo( dhis2BaseUrl + "/api/" + dhis2ApiVersion + "/organisationUnits.json?paging=false&fields=lastUpdated,id,code,name,shortName,displayName,level,openingDate,closedDate,coordinates,leaf,parent%5Bid%5D&filter=code:eq:OU_1234" ) ) + .andRespond( withStatus( HttpStatus.UNAUTHORIZED ) ); + + final IGenericClient client = createGenericClient(); + client.registerInterceptor( new BasicAuthInterceptor( "fhir_client", "invalid_1" ) ); + client.search().forResource( PlanDefinition.class ).where( PlanDefinition.IDENTIFIER.exactly().systemAndIdentifier( "http://www.dhis2.org/dhis2fhiradapter/systems/planDefinition-identifier", "OU_1234" ) ).returnBundle( Bundle.class ).execute(); + } + + @Test + public void getPlanDefinitionByIdentifierRepeated() throws Exception + { + getPlanDefinitionByIdentifier(); + getPlanDefinitionByIdentifier(); + } + + private void getPlanDefinitionByIdentifier() throws Exception + { + systemDhis2Server.reset(); + userDhis2Server.reset(); + + userDhis2Server.expect( ExpectedCount.between( 1, 2 ), method( HttpMethod.GET ) ).andExpect( header( "Authorization", "Basic Zmhpcl9jbGllbnQ6Zmhpcl9jbGllbnRfMQ==" ) ) + .andExpect( requestTo( dhis2BaseUrl + "/api/" + dhis2ApiVersion + "/organisationUnits.json?paging=false&fields=lastUpdated,id,code,name,shortName,displayName,level,openingDate,closedDate,coordinates,leaf,parent%5Bid%5D&filter=code:eq:OU_1234" ) ) + .andRespond( withSuccess( IOUtils.resourceToString( "/org/dhis2/fhir/adapter/dhis/test/default-org-unit-OU_1234.json", StandardCharsets.UTF_8 ), MediaType.APPLICATION_JSON ) ); + + final IGenericClient client = createGenericClient(); + client.registerInterceptor( new BasicAuthInterceptor( "fhir_client", "fhir_client_1" ) ); + Bundle bundle = + client.search().forResource( PlanDefinition.class ).where( PlanDefinition.IDENTIFIER.exactly().systemAndIdentifier( "http://www.dhis2.org/dhis2fhiradapter/systems/planDefinition-identifier", "OU_1234" ) ).returnBundle( Bundle.class ).execute(); + Assert.assertEquals( 1, bundle.getEntry().size() ); + PlanDefinition planDefinition = (PlanDefinition) bundle.getEntry().get( 0 ).getResource(); + Assert.assertEquals( "Test Hospital", planDefinition.getName() ); + Assert.assertEquals( 1, planDefinition.getIdentifier().size() ); + Assert.assertEquals( "http://www.dhis2.org/dhis2fhiradapter/systems/planDefinition-identifier", planDefinition.getIdentifier().get( 0 ).getSystem() ); + Assert.assertEquals( "OU_1234", planDefinition.getIdentifier().get( 0 ).getValue() ); + + systemDhis2Server.verify(); + userDhis2Server.verify(); + } + + @Test( expected = AuthenticationException.class ) + public void searchPlanDefinitionWithoutAuthorization() + { + final IGenericClient client = createGenericClient(); + client.search().forResource( PlanDefinition.class ).where( PlanDefinition.NAME.matches().value( "Test" ) ).returnBundle( Bundle.class ).execute(); + } + + @Test( expected = AuthenticationException.class ) + public void searchPlanDefinitionInvalidAuthorization() + { + userDhis2Server.expect( ExpectedCount.once(), method( HttpMethod.GET ) ).andExpect( header( "Authorization", "Basic Zmhpcl9jbGllbnQ6aW52YWxpZF8x" ) ) + .andExpect( requestTo( dhis2BaseUrl + "/api/" + dhis2ApiVersion + "/programs.json?filter=name:$ilike:Test&paging=true&page=1&pageSize=10&order=id&fields=id,name,code,description,selectIncidentDatesInFuture,selectEnrollmentDatesInFuture," + + "displayIncidentDate,registration,withoutRegistration,captureCoordinates,trackedEntityType%5Bid%5D,programTrackedEntityAttributes%5Bid,name,valueType,mandatory,allowFutureDate,trackedEntityAttribute%5Bid,name,code,valueType,generated%5D%5D," + + "programStages%5Bid,name,description,repeatable,captureCoordinates,generatedByEnrollmentDate,minDaysFromStart,programStageDataElements%5Bid,compulsory,allowProvidedElsewhere,dataElement%5Bid,name,code,formName,valueType,optionSetValue," + + "optionSet%5Bid,name,options%5Bcode,name%5D%5D%5D%5D%5D" ) ) + .andRespond( withStatus( HttpStatus.UNAUTHORIZED ) ); + + final IGenericClient client = createGenericClient(); + client.registerInterceptor( new BasicAuthInterceptor( "fhir_client", "invalid_1" ) ); + client.search().forResource( PlanDefinition.class ).where( PlanDefinition.NAME.matches().value( "Test" ) ).returnBundle( Bundle.class ).execute(); + } + + @Test + public void searchPlanDefinition() throws Exception + { + userDhis2Server.expect( ExpectedCount.once(), method( HttpMethod.GET ) ).andExpect( header( "Authorization", "Basic Zmhpcl9jbGllbnQ6Zmhpcl9jbGllbnRfMQ==" ) ) + .andExpect( requestTo( dhis2BaseUrl + "/api/" + dhis2ApiVersion + "/organisationUnits.json?filter=name:$ilike:Test&paging=true&page=1&pageSize=10&order=id" + + "&fields=lastUpdated,id,code,name,shortName,displayName,level,openingDate,closedDate,coordinates,leaf,parent%5Bid%5D" ) ) + .andRespond( withSuccess( IOUtils.resourceToString( "/org/dhis2/fhir/adapter/dhis/test/multi-org-unit.json", StandardCharsets.UTF_8 ), MediaType.APPLICATION_JSON ) ); + userDhis2Server.expect( ExpectedCount.between( 0, 1 ), method( HttpMethod.GET ) ).andExpect( header( "Authorization", "Basic Zmhpcl9jbGllbnQ6Zmhpcl9jbGllbnRfMQ==" ) ) + .andExpect( requestTo( dhis2BaseUrl + "/api/" + dhis2ApiVersion + "/organisationUnits.json?paging=false&fields=lastUpdated,id,code,name,shortName,displayName,level,openingDate,closedDate,coordinates,leaf,parent%5Bid%5D&filter=code:eq:OU_1234" ) ) + .andRespond( withSuccess( IOUtils.resourceToString( "/org/dhis2/fhir/adapter/dhis/test/default-org-unit-OU_1234.json", StandardCharsets.UTF_8 ), MediaType.APPLICATION_JSON ) ); + userDhis2Server.expect( ExpectedCount.between( 0, 1 ), method( HttpMethod.GET ) ).andExpect( header( "Authorization", "Basic Zmhpcl9jbGllbnQ6Zmhpcl9jbGllbnRfMQ==" ) ) + .andExpect( requestTo( dhis2BaseUrl + "/api/" + dhis2ApiVersion + "/organisationUnits.json?paging=false&fields=lastUpdated,id,code,name,shortName,displayName,level,openingDate,closedDate,coordinates,leaf,parent%5Bid%5D&filter=code:eq:OU_4567" ) ) + .andRespond( withSuccess( IOUtils.resourceToString( "/org/dhis2/fhir/adapter/dhis/test/default-org-unit-OU_4567.json", StandardCharsets.UTF_8 ), MediaType.APPLICATION_JSON ) ); + + final IGenericClient client = createGenericClient(); + client.registerInterceptor( new BasicAuthInterceptor( "fhir_client", "fhir_client_1" ) ); + Bundle bundle = client.search().forResource( PlanDefinition.class ).where( PlanDefinition.NAME.matches().value( "Test" ) ).returnBundle( Bundle.class ).execute(); + Assert.assertEquals( 2, bundle.getEntry().size() ); + PlanDefinition planDefinition = (PlanDefinition) bundle.getEntry().get( 0 ).getResource(); + Assert.assertEquals( "Test Hospital", planDefinition.getName() ); + Assert.assertEquals( 1, planDefinition.getIdentifier().size() ); + Assert.assertEquals( "http://www.dhis2.org/dhis2fhiradapter/systems/planDefinition-identifier", planDefinition.getIdentifier().get( 0 ).getSystem() ); + Assert.assertEquals( "OU_1234", planDefinition.getIdentifier().get( 0 ).getValue() ); + planDefinition = (PlanDefinition) bundle.getEntry().get( 1 ).getResource(); + Assert.assertEquals( "Test Hospital 2", planDefinition.getName() ); + Assert.assertEquals( 1, planDefinition.getIdentifier().size() ); + Assert.assertEquals( "http://www.dhis2.org/dhis2fhiradapter/systems/planDefinition-identifier", planDefinition.getIdentifier().get( 0 ).getSystem() ); + Assert.assertEquals( "OU_4567", planDefinition.getIdentifier().get( 0 ).getValue() ); + } + + @Test + public void searchParentPlanDefinition() throws Exception + { + userDhis2Server.expect( ExpectedCount.once(), method( HttpMethod.GET ) ).andExpect( header( "Authorization", "Basic Zmhpcl9jbGllbnQ6Zmhpcl9jbGllbnRfMQ==" ) ) + .andExpect( requestTo( dhis2BaseUrl + "/api/" + dhis2ApiVersion + "/organisationUnits.json?filter=parent.id:eq:bdXIaLNUNEp&paging=true&page=1&pageSize=10&order=id" + + "&fields=lastUpdated,id,code,name,shortName,displayName,level,openingDate,closedDate,coordinates,leaf,parent%5Bid%5D" ) ) + .andRespond( withSuccess( IOUtils.resourceToString( "/org/dhis2/fhir/adapter/dhis/test/multi-org-unit.json", StandardCharsets.UTF_8 ), MediaType.APPLICATION_JSON ) ); + userDhis2Server.expect( ExpectedCount.between( 0, 1 ), method( HttpMethod.GET ) ).andExpect( header( "Authorization", "Basic Zmhpcl9jbGllbnQ6Zmhpcl9jbGllbnRfMQ==" ) ) + .andExpect( requestTo( dhis2BaseUrl + "/api/" + dhis2ApiVersion + "/organisationUnits.json?paging=false&fields=lastUpdated,id,code,name,shortName,displayName,level,openingDate,closedDate,coordinates,leaf,parent%5Bid%5D&filter=code:eq:OU_1234" ) ) + .andRespond( withSuccess( IOUtils.resourceToString( "/org/dhis2/fhir/adapter/dhis/test/default-org-unit-OU_1234.json", StandardCharsets.UTF_8 ), MediaType.APPLICATION_JSON ) ); + userDhis2Server.expect( ExpectedCount.between( 0, 1 ), method( HttpMethod.GET ) ).andExpect( header( "Authorization", "Basic Zmhpcl9jbGllbnQ6Zmhpcl9jbGllbnRfMQ==" ) ) + .andExpect( requestTo( dhis2BaseUrl + "/api/" + dhis2ApiVersion + "/organisationUnits.json?paging=false&fields=lastUpdated,id,code,name,shortName,displayName,level,openingDate,closedDate,coordinates,leaf,parent%5Bid%5D&filter=code:eq:OU_4567" ) ) + .andRespond( withSuccess( IOUtils.resourceToString( "/org/dhis2/fhir/adapter/dhis/test/default-org-unit-OU_4567.json", StandardCharsets.UTF_8 ), MediaType.APPLICATION_JSON ) ); + + final IGenericClient client = createGenericClient(); + client.registerInterceptor( new BasicAuthInterceptor( "fhir_client", "fhir_client_1" ) ); + Bundle bundle = client.search().forResource( PlanDefinition.class ).where( PlanDefinition.NAME.matchesExactly().value( "ChildProgram" ) ).returnBundle( Bundle.class ).execute(); + Assert.assertEquals( 2, bundle.getEntry().size() ); + PlanDefinition planDefinition = (PlanDefinition) bundle.getEntry().get( 0 ).getResource(); + Assert.assertEquals( "Test Hospital", planDefinition.getName() ); + Assert.assertEquals( 1, planDefinition.getIdentifier().size() ); + Assert.assertEquals( "http://www.dhis2.org/dhis2fhiradapter/systems/planDefinition-identifier", planDefinition.getIdentifier().get( 0 ).getSystem() ); + Assert.assertEquals( "OU_1234", planDefinition.getIdentifier().get( 0 ).getValue() ); + planDefinition = (PlanDefinition) bundle.getEntry().get( 1 ).getResource(); + Assert.assertEquals( "Test Hospital 2", planDefinition.getName() ); + Assert.assertEquals( 1, planDefinition.getIdentifier().size() ); + Assert.assertEquals( "http://www.dhis2.org/dhis2fhiradapter/systems/planDefinition-identifier", planDefinition.getIdentifier().get( 0 ).getSystem() ); + Assert.assertEquals( "OU_4567", planDefinition.getIdentifier().get( 0 ).getValue() ); + } +} diff --git a/dhis/src/main/java/org/dhis2/fhir/adapter/dhis/model/DhisResourceType.java b/dhis/src/main/java/org/dhis2/fhir/adapter/dhis/model/DhisResourceType.java index 654008ad..80d8238f 100644 --- a/dhis/src/main/java/org/dhis2/fhir/adapter/dhis/model/DhisResourceType.java +++ b/dhis/src/main/java/org/dhis2/fhir/adapter/dhis/model/DhisResourceType.java @@ -44,37 +44,37 @@ public enum DhisResourceType /** * Resource is a tracked entity instance. */ - TRACKED_ENTITY( "trackedEntityInstances", "te" ), + TRACKED_ENTITY( "trackedEntityInstances", "te", "TrackedEntityRule" ), /** * Resource is a tracked entity type. */ - TRACKED_ENTITY_TYPE( "trackedEntityTypes", "tt" ), + TRACKED_ENTITY_TYPE( "trackedEntityTypes", "tt", "TrackedEntityTypeRule" ), /** * The program metadata. */ - PROGRAM_METADATA( "programs", "pm" ), + PROGRAM_METADATA( "programs", "pm", "ProgramMetadataRule" ), /** * The program stage metadata. */ - PROGRAM_STAGE_METADATA( "programStages", "sm" ), + PROGRAM_STAGE_METADATA( "programStages", "sm", "ProgramStageMetadataRule" ), /** * Resource is a program instance (aka enrollment). */ - ENROLLMENT( "enrollments", "en" ), + ENROLLMENT( "enrollments", "en", "EnrollmentRule" ), /** * Resource is a program stage instance (aka event of a program instance). */ - PROGRAM_STAGE_EVENT( "events", "ps" ), + PROGRAM_STAGE_EVENT( "events", "ps", "ProgramStageRule" ), /** * Resource is a organisation unit. */ - ORGANIZATION_UNIT( "organisationUnits", "ou" ); + ORGANIZATION_UNIT( "organisationUnits", "ou", "OrganizationUnitRule" ); private static final Map byTypeName = Arrays.stream( values() ).collect( Collectors.toMap( DhisResourceType::getTypeName, v -> v ) ); @@ -96,10 +96,13 @@ public static DhisResourceType getByAbbreviation( @Nullable String abbreviation private final String abbreviation; - DhisResourceType( @Nonnull String typeName, @Nonnull String abbreviation ) + private final String ruleType; + + DhisResourceType( @Nonnull String typeName, @Nonnull String abbreviation, @Nonnull String ruleType ) { this.typeName = typeName; this.abbreviation = abbreviation; + this.ruleType = ruleType; } @Nonnull @@ -113,4 +116,10 @@ public String getAbbreviation() { return abbreviation; } + + @Nonnull + public String getRuleType() + { + return ruleType; + } } diff --git a/fhir-r4/src/main/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/metadata/r4/R4ProgramMetadataToFhirCarePlanTransformer.java b/fhir-r4/src/main/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/metadata/r4/R4ProgramMetadataToFhirPlanDefinitionTransformer.java similarity index 92% rename from fhir-r4/src/main/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/metadata/r4/R4ProgramMetadataToFhirCarePlanTransformer.java rename to fhir-r4/src/main/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/metadata/r4/R4ProgramMetadataToFhirPlanDefinitionTransformer.java index 4fbd6e20..6002c267 100644 --- a/fhir-r4/src/main/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/metadata/r4/R4ProgramMetadataToFhirCarePlanTransformer.java +++ b/fhir-r4/src/main/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/metadata/r4/R4ProgramMetadataToFhirPlanDefinitionTransformer.java @@ -42,7 +42,7 @@ import org.dhis2.fhir.adapter.fhir.repository.FhirResourceRepository; import org.dhis2.fhir.adapter.fhir.script.ScriptExecutor; import org.dhis2.fhir.adapter.fhir.transform.dhis.DhisToFhirTransformerContext; -import org.dhis2.fhir.adapter.fhir.transform.dhis.impl.metadata.program.AbstractProgramMetadataToFhirCarePlanTransformer; +import org.dhis2.fhir.adapter.fhir.transform.dhis.impl.metadata.program.AbstractProgramMetadataToFhirPlanDefinitionTransformer; import org.dhis2.fhir.adapter.fhir.transform.scripted.AccessibleScriptedDhisMetadata; import org.dhis2.fhir.adapter.lock.LockManager; import org.hl7.fhir.instance.model.api.IBaseResource; @@ -62,9 +62,9 @@ * @author volsch */ @Component -public class R4ProgramMetadataToFhirCarePlanTransformer extends AbstractProgramMetadataToFhirCarePlanTransformer +public class R4ProgramMetadataToFhirPlanDefinitionTransformer extends AbstractProgramMetadataToFhirPlanDefinitionTransformer { - public R4ProgramMetadataToFhirCarePlanTransformer( @Nonnull ScriptExecutor scriptExecutor, @Nonnull LockManager lockManager, @Nonnull SystemRepository systemRepository, @Nonnull FhirResourceRepository fhirResourceRepository, + public R4ProgramMetadataToFhirPlanDefinitionTransformer( @Nonnull ScriptExecutor scriptExecutor, @Nonnull LockManager lockManager, @Nonnull SystemRepository systemRepository, @Nonnull FhirResourceRepository fhirResourceRepository, @Nonnull FhirDhisAssignmentRepository fhirDhisAssignmentRepository, @Nonnull OrganizationUnitService organizationUnitService, @Nonnull TrackedEntityMetadataService trackedEntityMetadataService, @Nonnull TrackedEntityRuleRepository trackedEntityRuleRepository ) { diff --git a/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/metadata/model/AbstractRule.java b/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/metadata/model/AbstractRule.java index b5bf83f7..14e64cc8 100644 --- a/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/metadata/model/AbstractRule.java +++ b/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/metadata/model/AbstractRule.java @@ -98,8 +98,7 @@ "SELECT r FROM AbstractRule r JOIN r.applicableCodeSet acs WHERE r.fhirResourceType=:fhirResourceType AND r.enabled=true " + "AND r.expEnabled=true AND acs.code IN (:codeSetCodes)" ), @NamedQuery( name = AbstractRule.FIND_IMP_RULE_BY_ID_NAMED_QUERY, query = - "SELECT r FROM AbstractRule r WHERE r.fhirResourceType=:fhirResourceType AND TYPE(r)=:dhisResourceType AND r.id=:ruleId AND r.enabled=true AND r.impEnabled=true" ), - @NamedQuery( name = AbstractRule.FIND_ALL_EXP_NAMED_QUERY, query = "SELECT our FROM OrganizationUnitRule our WHERE our.enabled=true AND our.expEnabled=true AND (our.fhirCreateEnabled=true OR our.fhirUpdateEnabled=true)" ) + "SELECT r FROM AbstractRule r WHERE r.fhirResourceType=:fhirResourceType AND TYPE(r)=:dhisResourceType AND r.id=:ruleId AND r.enabled=true AND r.impEnabled=true" ) } ) @JsonTypeInfo( use = JsonTypeInfo.Id.NAME, property = "dhisResourceType", include = JsonTypeInfo.As.EXISTING_PROPERTY ) @JsonSubTypes( { @@ -129,8 +128,6 @@ public abstract class AbstractRule extends VersionedBaseMetadata implements Seri public static final String FIND_IMP_RULE_BY_ID_NAMED_QUERY = "AbstractRule.findImpById"; - public static final String FIND_ALL_EXP_NAMED_QUERY = "AbstractRule.findAllExp"; - public static final int MAX_NAME_LENGTH = 230; @NotBlank diff --git a/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/metadata/repository/CustomRuleRepository.java b/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/metadata/repository/CustomRuleRepository.java index fcd443a4..f1b83c57 100644 --- a/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/metadata/repository/CustomRuleRepository.java +++ b/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/metadata/repository/CustomRuleRepository.java @@ -67,6 +67,6 @@ public interface CustomRuleRepository @RestResource( exported = false ) @Nonnull - Collection> findAllExp(); + Collection> findAllExp( @Nonnull DhisResourceType dhisResourceType ); } diff --git a/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/metadata/repository/impl/CustomRuleRepositoryImpl.java b/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/metadata/repository/impl/CustomRuleRepositoryImpl.java index 5305b436..5f070628 100644 --- a/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/metadata/repository/impl/CustomRuleRepositoryImpl.java +++ b/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/metadata/repository/impl/CustomRuleRepositoryImpl.java @@ -111,11 +111,12 @@ public List> findAllExpByInputData( @Nonnull Fh @RestResource( exported = false ) @Nonnull - @Cacheable( key = "{#root.methodName}", cacheManager = "metadataCacheManager", cacheNames = "rule" ) + @Cacheable( key = "{#root.methodName, #a0}", cacheManager = "metadataCacheManager", cacheNames = "rule" ) @Transactional( readOnly = true ) - public Collection> findAllExp() + public Collection> findAllExp( @Nonnull DhisResourceType dhisResourceType ) { - final List rules = entityManager.createNamedQuery( AbstractRule.FIND_ALL_EXP_NAMED_QUERY, AbstractRule.class ).getResultList(); + final List rules = entityManager.createQuery( "SELECT r FROM " + dhisResourceType.getRuleType() + + " r WHERE r.enabled=true AND r.expEnabled=true AND (r.fhirCreateEnabled=true OR r.fhirUpdateEnabled=true)", AbstractRule.class ).getResultList(); return rules.stream().map( r -> { Hibernate.initialize( r.getDhisDataReferences() ); diff --git a/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/metadata/AbstractDhisMetadataToFhirRequestResolver.java b/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/metadata/AbstractDhisMetadataToFhirRequestResolver.java index 97782994..574e8695 100644 --- a/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/metadata/AbstractDhisMetadataToFhirRequestResolver.java +++ b/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/metadata/AbstractDhisMetadataToFhirRequestResolver.java @@ -61,7 +61,7 @@ protected AbstractDhisMetadataToFhirRequestResolver( @Nonnull FhirClientReposito @Override public List> resolveRules( @Nonnull ScriptedDhisResource dhisResource ) { - return ruleRepository.findAllExp().stream().sorted().collect( Collectors.toList() ); + return ruleRepository.findAllExp( dhisResource.getResourceType() ).stream().sorted().collect( Collectors.toList() ); } @Nonnull diff --git a/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/metadata/program/AbstractProgramMetadataToFhirCarePlanTransformer.java b/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/metadata/program/AbstractProgramMetadataToFhirPlanDefinitionTransformer.java similarity index 92% rename from fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/metadata/program/AbstractProgramMetadataToFhirCarePlanTransformer.java rename to fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/metadata/program/AbstractProgramMetadataToFhirPlanDefinitionTransformer.java index 8707f71d..bb7aaefe 100644 --- a/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/metadata/program/AbstractProgramMetadataToFhirCarePlanTransformer.java +++ b/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/metadata/program/AbstractProgramMetadataToFhirPlanDefinitionTransformer.java @@ -59,7 +59,7 @@ * @param the concrete type of the FHIR resource into which the DHIS2 resource should be transformed. * @author volsch */ -public abstract class AbstractProgramMetadataToFhirCarePlanTransformer extends AbstractReadOnlyDhisMetadataToTypedFhirTransformer +public abstract class AbstractProgramMetadataToFhirPlanDefinitionTransformer extends AbstractReadOnlyDhisMetadataToTypedFhirTransformer { private final Logger logger = LoggerFactory.getLogger( getClass() ); @@ -67,7 +67,7 @@ public abstract class AbstractProgramMetadataToFhirCarePlanTransformer Date: Mon, 29 Jul 2019 16:30:30 +0200 Subject: [PATCH 05/21] Adds tests for program to plan definition transformation. --- .../main/resources/default-application.yml | 1 + .../r4/R4ProgramMetadataFhirRestAppTest.java | 91 +++----- .../adapter/dhis/test/default-program.json | 1 + ...ProgramMetadataToFhirDataProviderTest.java | 93 ++++++++ ...taToFhirPlanDefinitionTransformerTest.java | 171 +++++++++++++++ .../impl/AbstractDhisToFhirDataProvider.java | 4 +- .../AbstractDhisToFhirRequestResolver.java | 4 +- .../impl/AbstractDhisToFhirTransformer.java | 33 +++ .../dhis/impl/DhisToFhirRuleComparator.java | 6 +- .../DhisToFhirTransformerContextImpl.java | 3 + .../DhisToFhirTransformerRequestImpl.java | 7 +- .../DhisToFhirTransformerServiceImpl.java | 17 ++ .../dhis/impl/TransformerRequestKey.java | 16 +- .../dhis/search/SearchFilterCollector.java | 6 +- ...0.41_10_0__Plan_Definition_Sample_Data.sql | 38 ++++ ...taToFhirPlanDefinitionTransformerTest.java | 206 ++++++++++++++++++ 16 files changed, 624 insertions(+), 73 deletions(-) create mode 100644 fhir-r4/src/test/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/metadata/r4/R4ProgramMetadataToFhirDataProviderTest.java create mode 100644 fhir-r4/src/test/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/metadata/r4/R4ProgramMetadataToFhirPlanDefinitionTransformerTest.java create mode 100644 fhir/src/main/resources/db/migration/sample/V1.1.0.41_10_0__Plan_Definition_Sample_Data.sql create mode 100644 fhir/src/test/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/metadata/program/AbstractProgramMetadataToFhirPlanDefinitionTransformerTest.java diff --git a/app/src/main/resources/default-application.yml b/app/src/main/resources/default-application.yml index e4fc83f5..1feb5c15 100644 --- a/app/src/main/resources/default-application.yml +++ b/app/src/main/resources/default-application.yml @@ -356,6 +356,7 @@ dhis2.fhir-adapter: # DHIS 2 resource types that will be synchronized. resource-types: - ORGANIZATION_UNIT + - PROGRAM_METADATA - TRACKED_ENTITY - PROGRAM_STAGE_EVENT # The queue that is used to store distributed requests to synchronize the data from diff --git a/app/src/test/java/org/dhis2/fhir/adapter/fhir/server/r4/R4ProgramMetadataFhirRestAppTest.java b/app/src/test/java/org/dhis2/fhir/adapter/fhir/server/r4/R4ProgramMetadataFhirRestAppTest.java index e7d1c616..1402d045 100644 --- a/app/src/test/java/org/dhis2/fhir/adapter/fhir/server/r4/R4ProgramMetadataFhirRestAppTest.java +++ b/app/src/test/java/org/dhis2/fhir/adapter/fhir/server/r4/R4ProgramMetadataFhirRestAppTest.java @@ -38,7 +38,6 @@ import org.hl7.fhir.r4.model.Bundle; import org.hl7.fhir.r4.model.PlanDefinition; import org.junit.Assert; -import org.junit.Ignore; import org.junit.Test; import org.springframework.http.HttpMethod; import org.springframework.http.HttpStatus; @@ -58,7 +57,6 @@ * * @author volsch */ -@Ignore public class R4ProgramMetadataFhirRestAppTest extends AbstractAppTest { @Nonnull @@ -205,19 +203,22 @@ private void getPlanDefinitionRuleNotFound() public void getPlanDefinitionByIdentifierWithoutAuthorization() { final IGenericClient client = createGenericClient(); - client.search().forResource( PlanDefinition.class ).where( PlanDefinition.IDENTIFIER.exactly().systemAndIdentifier( "http://www.dhis2.org/dhis2fhiradapter/systems/plan-definition-identifier", "XX_1234" ) ).returnBundle( Bundle.class ).execute(); + client.search().forResource( PlanDefinition.class ).where( PlanDefinition.IDENTIFIER.exactly().systemAndIdentifier( "http://www.dhis2.org/dhis2-fhir-adapter/systems/plan-definition-identifier", "XX_1234" ) ).returnBundle( Bundle.class ).execute(); } @Test( expected = AuthenticationException.class ) public void getPlanDefinitionByIdentifierInvalidAuthorization() { userDhis2Server.expect( ExpectedCount.once(), method( HttpMethod.GET ) ).andExpect( header( "Authorization", "Basic Zmhpcl9jbGllbnQ6aW52YWxpZF8x" ) ) - .andExpect( requestTo( dhis2BaseUrl + "/api/" + dhis2ApiVersion + "/organisationUnits.json?paging=false&fields=lastUpdated,id,code,name,shortName,displayName,level,openingDate,closedDate,coordinates,leaf,parent%5Bid%5D&filter=code:eq:OU_1234" ) ) + .andExpect( requestTo( dhis2BaseUrl + "/api/" + dhis2ApiVersion + "/programs.json?paging=false&fields=id,name,code,description,selectIncidentDatesInFuture,selectEnrollmentDatesInFuture,displayIncidentDate,registration,withoutRegistration," + + "captureCoordinates,trackedEntityType%5Bid%5D,programTrackedEntityAttributes%5Bid,name,valueType,mandatory,allowFutureDate,trackedEntityAttribute%5Bid,name,code,valueType,generated%5D%5D,programStages%5Bid,name,description,repeatable," + + "captureCoordinates,generatedByEnrollmentDate,minDaysFromStart,programStageDataElements%5Bid,compulsory,allowProvidedElsewhere,dataElement%5Bid,name,code,formName,valueType,optionSetValue,optionSet%5Bid,name,options%5Bcode," + + "name%5D%5D%5D%5D%5D&filter=code:eq:OU_1234" ) ) .andRespond( withStatus( HttpStatus.UNAUTHORIZED ) ); final IGenericClient client = createGenericClient(); client.registerInterceptor( new BasicAuthInterceptor( "fhir_client", "invalid_1" ) ); - client.search().forResource( PlanDefinition.class ).where( PlanDefinition.IDENTIFIER.exactly().systemAndIdentifier( "http://www.dhis2.org/dhis2fhiradapter/systems/planDefinition-identifier", "OU_1234" ) ).returnBundle( Bundle.class ).execute(); + client.search().forResource( PlanDefinition.class ).where( PlanDefinition.IDENTIFIER.exactly().systemAndIdentifier( "http://www.dhis2.org/dhis2-fhir-adapter/systems/plan-definition-identifier", "OU_1234" ) ).returnBundle( Bundle.class ).execute(); } @Test @@ -232,20 +233,24 @@ private void getPlanDefinitionByIdentifier() throws Exception systemDhis2Server.reset(); userDhis2Server.reset(); + systemDhis2Server.expect( ExpectedCount.between( 0, 1 ), method( HttpMethod.GET ) ).andExpect( header( "Authorization", testConfiguration.getDhis2SystemAuthorization() ) ) + .andExpect( requestTo( dhis2BaseUrl + "/api/" + dhis2ApiVersion + "/trackedEntityTypes/MCPQUTHX1Ze.json?" + + "fields=id,name,trackedEntityTypeAttributes%5Bid,name,valueType,mandatory,trackedEntityAttribute%5Bid,name,code,valueType,generated,optionSetValue,optionSet%5Bid,name,options%5Bcode,name%5D%5D%5D%5D" ) ) + .andRespond( withSuccess( IOUtils.resourceToString( "/org/dhis2/fhir/adapter/dhis/test/single-tracked-entity-type.json", StandardCharsets.UTF_8 ), MediaType.APPLICATION_JSON ) ); userDhis2Server.expect( ExpectedCount.between( 1, 2 ), method( HttpMethod.GET ) ).andExpect( header( "Authorization", "Basic Zmhpcl9jbGllbnQ6Zmhpcl9jbGllbnRfMQ==" ) ) - .andExpect( requestTo( dhis2BaseUrl + "/api/" + dhis2ApiVersion + "/organisationUnits.json?paging=false&fields=lastUpdated,id,code,name,shortName,displayName,level,openingDate,closedDate,coordinates,leaf,parent%5Bid%5D&filter=code:eq:OU_1234" ) ) - .andRespond( withSuccess( IOUtils.resourceToString( "/org/dhis2/fhir/adapter/dhis/test/default-org-unit-OU_1234.json", StandardCharsets.UTF_8 ), MediaType.APPLICATION_JSON ) ); + .andExpect( requestTo( dhis2BaseUrl + "/api/" + dhis2ApiVersion + "/programs.json?paging=false&fields=id,name,code,description,selectIncidentDatesInFuture,selectEnrollmentDatesInFuture,displayIncidentDate,registration,withoutRegistration," + + "captureCoordinates,trackedEntityType%5Bid%5D,programTrackedEntityAttributes%5Bid,name,valueType,mandatory,allowFutureDate,trackedEntityAttribute%5Bid,name,code,valueType,generated%5D%5D,programStages%5Bid,name,description,repeatable," + + "captureCoordinates,generatedByEnrollmentDate,minDaysFromStart,programStageDataElements%5Bid,compulsory,allowProvidedElsewhere,dataElement%5Bid,name,code,formName,valueType,optionSetValue,optionSet%5Bid,name,options%5Bcode," + + "name%5D%5D%5D%5D%5D&filter=code:eq:PD_1234" ) ) + .andRespond( withSuccess( IOUtils.resourceToString( "/org/dhis2/fhir/adapter/dhis/test/default-program.json", StandardCharsets.UTF_8 ), MediaType.APPLICATION_JSON ) ); final IGenericClient client = createGenericClient(); client.registerInterceptor( new BasicAuthInterceptor( "fhir_client", "fhir_client_1" ) ); Bundle bundle = - client.search().forResource( PlanDefinition.class ).where( PlanDefinition.IDENTIFIER.exactly().systemAndIdentifier( "http://www.dhis2.org/dhis2fhiradapter/systems/planDefinition-identifier", "OU_1234" ) ).returnBundle( Bundle.class ).execute(); + client.search().forResource( PlanDefinition.class ).where( PlanDefinition.IDENTIFIER.exactly().systemAndIdentifier( "http://www.dhis2.org/dhis2-fhir-adapter/systems/plan-definition-identifier", "PD_1234" ) ).returnBundle( Bundle.class ).execute(); Assert.assertEquals( 1, bundle.getEntry().size() ); PlanDefinition planDefinition = (PlanDefinition) bundle.getEntry().get( 0 ).getResource(); - Assert.assertEquals( "Test Hospital", planDefinition.getName() ); - Assert.assertEquals( 1, planDefinition.getIdentifier().size() ); - Assert.assertEquals( "http://www.dhis2.org/dhis2fhiradapter/systems/planDefinition-identifier", planDefinition.getIdentifier().get( 0 ).getSystem() ); - Assert.assertEquals( "OU_1234", planDefinition.getIdentifier().get( 0 ).getValue() ); + Assert.assertEquals( "Child Programme", planDefinition.getName() ); systemDhis2Server.verify(); userDhis2Server.verify(); @@ -276,60 +281,22 @@ public void searchPlanDefinitionInvalidAuthorization() @Test public void searchPlanDefinition() throws Exception { + systemDhis2Server.expect( ExpectedCount.between( 0, 1 ), method( HttpMethod.GET ) ).andExpect( header( "Authorization", testConfiguration.getDhis2SystemAuthorization() ) ) + .andExpect( requestTo( dhis2BaseUrl + "/api/" + dhis2ApiVersion + "/trackedEntityTypes/MCPQUTHX1Ze.json?" + + "fields=id,name,trackedEntityTypeAttributes%5Bid,name,valueType,mandatory,trackedEntityAttribute%5Bid,name,code,valueType,generated,optionSetValue,optionSet%5Bid,name,options%5Bcode,name%5D%5D%5D%5D" ) ) + .andRespond( withSuccess( IOUtils.resourceToString( "/org/dhis2/fhir/adapter/dhis/test/single-tracked-entity-type.json", StandardCharsets.UTF_8 ), MediaType.APPLICATION_JSON ) ); userDhis2Server.expect( ExpectedCount.once(), method( HttpMethod.GET ) ).andExpect( header( "Authorization", "Basic Zmhpcl9jbGllbnQ6Zmhpcl9jbGllbnRfMQ==" ) ) - .andExpect( requestTo( dhis2BaseUrl + "/api/" + dhis2ApiVersion + "/organisationUnits.json?filter=name:$ilike:Test&paging=true&page=1&pageSize=10&order=id" + - "&fields=lastUpdated,id,code,name,shortName,displayName,level,openingDate,closedDate,coordinates,leaf,parent%5Bid%5D" ) ) - .andRespond( withSuccess( IOUtils.resourceToString( "/org/dhis2/fhir/adapter/dhis/test/multi-org-unit.json", StandardCharsets.UTF_8 ), MediaType.APPLICATION_JSON ) ); - userDhis2Server.expect( ExpectedCount.between( 0, 1 ), method( HttpMethod.GET ) ).andExpect( header( "Authorization", "Basic Zmhpcl9jbGllbnQ6Zmhpcl9jbGllbnRfMQ==" ) ) - .andExpect( requestTo( dhis2BaseUrl + "/api/" + dhis2ApiVersion + "/organisationUnits.json?paging=false&fields=lastUpdated,id,code,name,shortName,displayName,level,openingDate,closedDate,coordinates,leaf,parent%5Bid%5D&filter=code:eq:OU_1234" ) ) - .andRespond( withSuccess( IOUtils.resourceToString( "/org/dhis2/fhir/adapter/dhis/test/default-org-unit-OU_1234.json", StandardCharsets.UTF_8 ), MediaType.APPLICATION_JSON ) ); - userDhis2Server.expect( ExpectedCount.between( 0, 1 ), method( HttpMethod.GET ) ).andExpect( header( "Authorization", "Basic Zmhpcl9jbGllbnQ6Zmhpcl9jbGllbnRfMQ==" ) ) - .andExpect( requestTo( dhis2BaseUrl + "/api/" + dhis2ApiVersion + "/organisationUnits.json?paging=false&fields=lastUpdated,id,code,name,shortName,displayName,level,openingDate,closedDate,coordinates,leaf,parent%5Bid%5D&filter=code:eq:OU_4567" ) ) - .andRespond( withSuccess( IOUtils.resourceToString( "/org/dhis2/fhir/adapter/dhis/test/default-org-unit-OU_4567.json", StandardCharsets.UTF_8 ), MediaType.APPLICATION_JSON ) ); - - final IGenericClient client = createGenericClient(); - client.registerInterceptor( new BasicAuthInterceptor( "fhir_client", "fhir_client_1" ) ); - Bundle bundle = client.search().forResource( PlanDefinition.class ).where( PlanDefinition.NAME.matches().value( "Test" ) ).returnBundle( Bundle.class ).execute(); - Assert.assertEquals( 2, bundle.getEntry().size() ); - PlanDefinition planDefinition = (PlanDefinition) bundle.getEntry().get( 0 ).getResource(); - Assert.assertEquals( "Test Hospital", planDefinition.getName() ); - Assert.assertEquals( 1, planDefinition.getIdentifier().size() ); - Assert.assertEquals( "http://www.dhis2.org/dhis2fhiradapter/systems/planDefinition-identifier", planDefinition.getIdentifier().get( 0 ).getSystem() ); - Assert.assertEquals( "OU_1234", planDefinition.getIdentifier().get( 0 ).getValue() ); - planDefinition = (PlanDefinition) bundle.getEntry().get( 1 ).getResource(); - Assert.assertEquals( "Test Hospital 2", planDefinition.getName() ); - Assert.assertEquals( 1, planDefinition.getIdentifier().size() ); - Assert.assertEquals( "http://www.dhis2.org/dhis2fhiradapter/systems/planDefinition-identifier", planDefinition.getIdentifier().get( 0 ).getSystem() ); - Assert.assertEquals( "OU_4567", planDefinition.getIdentifier().get( 0 ).getValue() ); - } - - @Test - public void searchParentPlanDefinition() throws Exception - { - userDhis2Server.expect( ExpectedCount.once(), method( HttpMethod.GET ) ).andExpect( header( "Authorization", "Basic Zmhpcl9jbGllbnQ6Zmhpcl9jbGllbnRfMQ==" ) ) - .andExpect( requestTo( dhis2BaseUrl + "/api/" + dhis2ApiVersion + "/organisationUnits.json?filter=parent.id:eq:bdXIaLNUNEp&paging=true&page=1&pageSize=10&order=id" + - "&fields=lastUpdated,id,code,name,shortName,displayName,level,openingDate,closedDate,coordinates,leaf,parent%5Bid%5D" ) ) - .andRespond( withSuccess( IOUtils.resourceToString( "/org/dhis2/fhir/adapter/dhis/test/multi-org-unit.json", StandardCharsets.UTF_8 ), MediaType.APPLICATION_JSON ) ); - userDhis2Server.expect( ExpectedCount.between( 0, 1 ), method( HttpMethod.GET ) ).andExpect( header( "Authorization", "Basic Zmhpcl9jbGllbnQ6Zmhpcl9jbGllbnRfMQ==" ) ) - .andExpect( requestTo( dhis2BaseUrl + "/api/" + dhis2ApiVersion + "/organisationUnits.json?paging=false&fields=lastUpdated,id,code,name,shortName,displayName,level,openingDate,closedDate,coordinates,leaf,parent%5Bid%5D&filter=code:eq:OU_1234" ) ) - .andRespond( withSuccess( IOUtils.resourceToString( "/org/dhis2/fhir/adapter/dhis/test/default-org-unit-OU_1234.json", StandardCharsets.UTF_8 ), MediaType.APPLICATION_JSON ) ); - userDhis2Server.expect( ExpectedCount.between( 0, 1 ), method( HttpMethod.GET ) ).andExpect( header( "Authorization", "Basic Zmhpcl9jbGllbnQ6Zmhpcl9jbGllbnRfMQ==" ) ) - .andExpect( requestTo( dhis2BaseUrl + "/api/" + dhis2ApiVersion + "/organisationUnits.json?paging=false&fields=lastUpdated,id,code,name,shortName,displayName,level,openingDate,closedDate,coordinates,leaf,parent%5Bid%5D&filter=code:eq:OU_4567" ) ) - .andRespond( withSuccess( IOUtils.resourceToString( "/org/dhis2/fhir/adapter/dhis/test/default-org-unit-OU_4567.json", StandardCharsets.UTF_8 ), MediaType.APPLICATION_JSON ) ); + .andExpect( requestTo( dhis2BaseUrl + "/api/" + dhis2ApiVersion + "/programs.json?filter=name:$ilike:Child%20Programme&paging=true&page=1&pageSize=10&order=id&fields=id,name,code,description,selectIncidentDatesInFuture," + + "selectEnrollmentDatesInFuture,displayIncidentDate,registration,withoutRegistration,captureCoordinates,trackedEntityType%5Bid%5D,programTrackedEntityAttributes%5Bid,name,valueType,mandatory,allowFutureDate,trackedEntityAttribute%5Bid,name," + + "code,valueType,generated%5D%5D,programStages%5Bid,name,description,repeatable,captureCoordinates,generatedByEnrollmentDate,minDaysFromStart,programStageDataElements%5Bid,compulsory,allowProvidedElsewhere,dataElement%5Bid,name,code,formName," + + "valueType,optionSetValue,optionSet%5Bid,name,options%5Bcode,name%5D%5D%5D%5D%5D" ) ) + .andRespond( withSuccess( IOUtils.resourceToString( "/org/dhis2/fhir/adapter/dhis/test/default-program.json", StandardCharsets.UTF_8 ), MediaType.APPLICATION_JSON ) ); final IGenericClient client = createGenericClient(); client.registerInterceptor( new BasicAuthInterceptor( "fhir_client", "fhir_client_1" ) ); - Bundle bundle = client.search().forResource( PlanDefinition.class ).where( PlanDefinition.NAME.matchesExactly().value( "ChildProgram" ) ).returnBundle( Bundle.class ).execute(); - Assert.assertEquals( 2, bundle.getEntry().size() ); + Bundle bundle = client.search().forResource( PlanDefinition.class ).where( PlanDefinition.NAME.matches().value( "Child Programme" ) ).returnBundle( Bundle.class ).execute(); + Assert.assertEquals( 1, bundle.getEntry().size() ); PlanDefinition planDefinition = (PlanDefinition) bundle.getEntry().get( 0 ).getResource(); - Assert.assertEquals( "Test Hospital", planDefinition.getName() ); - Assert.assertEquals( 1, planDefinition.getIdentifier().size() ); - Assert.assertEquals( "http://www.dhis2.org/dhis2fhiradapter/systems/planDefinition-identifier", planDefinition.getIdentifier().get( 0 ).getSystem() ); - Assert.assertEquals( "OU_1234", planDefinition.getIdentifier().get( 0 ).getValue() ); - planDefinition = (PlanDefinition) bundle.getEntry().get( 1 ).getResource(); - Assert.assertEquals( "Test Hospital 2", planDefinition.getName() ); - Assert.assertEquals( 1, planDefinition.getIdentifier().size() ); - Assert.assertEquals( "http://www.dhis2.org/dhis2fhiradapter/systems/planDefinition-identifier", planDefinition.getIdentifier().get( 0 ).getSystem() ); - Assert.assertEquals( "OU_4567", planDefinition.getIdentifier().get( 0 ).getValue() ); + Assert.assertEquals( "Child Programme", planDefinition.getName() ); } } diff --git a/app/src/test/resources/org/dhis2/fhir/adapter/dhis/test/default-program.json b/app/src/test/resources/org/dhis2/fhir/adapter/dhis/test/default-program.json index b1fe9749..1d348852 100644 --- a/app/src/test/resources/org/dhis2/fhir/adapter/dhis/test/default-program.json +++ b/app/src/test/resources/org/dhis2/fhir/adapter/dhis/test/default-program.json @@ -1,4 +1,5 @@ { + "pager": { "page": 1, "pageCount": 1, "total": 0, "pageSize": 1 }, "programs": [ { "name": "Child Programme", diff --git a/fhir-r4/src/test/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/metadata/r4/R4ProgramMetadataToFhirDataProviderTest.java b/fhir-r4/src/test/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/metadata/r4/R4ProgramMetadataToFhirDataProviderTest.java new file mode 100644 index 00000000..4798edd1 --- /dev/null +++ b/fhir-r4/src/test/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/metadata/r4/R4ProgramMetadataToFhirDataProviderTest.java @@ -0,0 +1,93 @@ +package org.dhis2.fhir.adapter.fhir.transform.dhis.impl.metadata.r4; + +/* + * Copyright (c) 2004-2019, University of Oslo + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * Neither the name of the HISP project nor the names of its contributors may + * be used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +import org.dhis2.fhir.adapter.dhis.tracker.program.ProgramMetadataService; +import org.dhis2.fhir.adapter.fhir.metadata.model.ProgramMetadataRule; +import org.dhis2.fhir.adapter.fhir.metadata.model.RuleInfo; +import org.dhis2.fhir.adapter.fhir.model.FhirVersion; +import org.dhis2.fhir.adapter.fhir.script.ScriptExecutor; +import org.dhis2.fhir.adapter.fhir.transform.dhis.search.SearchFilter; +import org.dhis2.fhir.adapter.fhir.transform.dhis.search.SearchFilterCollector; +import org.junit.Assert; +import org.junit.Rule; +import org.junit.Test; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.junit.MockitoJUnit; +import org.mockito.junit.MockitoRule; +import org.springframework.web.util.DefaultUriBuilderFactory; +import org.springframework.web.util.UriBuilder; + +import java.net.URI; +import java.net.URISyntaxException; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +/** + * Unit tests for {@link R4ProgramMetadataToFhirDataProvider}. + * + * @author volsch + */ +public class R4ProgramMetadataToFhirDataProviderTest +{ + @Mock + private ScriptExecutor scriptExecutor; + + @Mock + private ProgramMetadataService programMetadataService; + + @InjectMocks + private R4ProgramMetadataToFhirDataProvider provider; + + @Rule + public MockitoRule rule = MockitoJUnit.rule(); + + @Test + public void getFhirVersions() + { + Assert.assertEquals( FhirVersion.R4_ONLY, provider.getFhirVersions() ); + } + + @Test + public void searchFilterName() throws URISyntaxException + { + final SearchFilterCollector searchFilterCollector = new SearchFilterCollector( Collections.singletonMap( "name", Collections.singletonList( "Child Programme" ) ) ); + final SearchFilter searchFilter = new SearchFilter( searchFilterCollector, false ); + + provider.initSearchFilter( FhirVersion.R4, new RuleInfo<>( new ProgramMetadataRule(), Collections.emptyList() ), searchFilter ); + + final List variables = new ArrayList<>(); + UriBuilder uriBuilder = new DefaultUriBuilderFactory().builder(); + uriBuilder = searchFilterCollector.add( uriBuilder, variables ); + + Assert.assertEquals( new URI( "?filter=%5Bname:$ilike:Child%20Programme%5D" ), uriBuilder.build( variables ) ); + } +} diff --git a/fhir-r4/src/test/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/metadata/r4/R4ProgramMetadataToFhirPlanDefinitionTransformerTest.java b/fhir-r4/src/test/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/metadata/r4/R4ProgramMetadataToFhirPlanDefinitionTransformerTest.java new file mode 100644 index 00000000..3d14b07b --- /dev/null +++ b/fhir-r4/src/test/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/metadata/r4/R4ProgramMetadataToFhirPlanDefinitionTransformerTest.java @@ -0,0 +1,171 @@ +package org.dhis2.fhir.adapter.fhir.transform.dhis.impl.metadata.r4; + +/* + * Copyright (c) 2004-2019, University of Oslo + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * Neither the name of the HISP project nor the names of its contributors may + * be used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +import org.dhis2.fhir.adapter.dhis.model.Reference; +import org.dhis2.fhir.adapter.dhis.model.ReferenceType; +import org.dhis2.fhir.adapter.dhis.orgunit.OrganizationUnitService; +import org.dhis2.fhir.adapter.dhis.tracker.program.WritableProgram; +import org.dhis2.fhir.adapter.dhis.tracker.program.WritableProgramStage; +import org.dhis2.fhir.adapter.dhis.tracker.trackedentity.TrackedEntityMetadataService; +import org.dhis2.fhir.adapter.dhis.tracker.trackedentity.WritableTrackedEntityType; +import org.dhis2.fhir.adapter.fhir.data.repository.FhirDhisAssignmentRepository; +import org.dhis2.fhir.adapter.fhir.metadata.model.FhirClient; +import org.dhis2.fhir.adapter.fhir.metadata.model.ProgramMetadataRule; +import org.dhis2.fhir.adapter.fhir.metadata.model.RuleInfo; +import org.dhis2.fhir.adapter.fhir.metadata.repository.SystemRepository; +import org.dhis2.fhir.adapter.fhir.metadata.repository.TrackedEntityRuleRepository; +import org.dhis2.fhir.adapter.fhir.model.FhirVersion; +import org.dhis2.fhir.adapter.fhir.repository.FhirResourceRepository; +import org.dhis2.fhir.adapter.fhir.script.ScriptExecutionContext; +import org.dhis2.fhir.adapter.fhir.script.ScriptExecutor; +import org.dhis2.fhir.adapter.fhir.transform.dhis.DhisToFhirTransformerContext; +import org.dhis2.fhir.adapter.fhir.transform.scripted.WritableScriptedDhisMetadata; +import org.dhis2.fhir.adapter.lock.LockManager; +import org.hl7.fhir.r4.model.PlanDefinition; +import org.junit.Assert; +import org.junit.Rule; +import org.junit.Test; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.Mockito; +import org.mockito.junit.MockitoJUnit; +import org.mockito.junit.MockitoRule; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Optional; + +/** + * Unit tests for {@link R4ProgramMetadataToFhirPlanDefinitionTransformer}. + * + * @author volsch + */ +public class R4ProgramMetadataToFhirPlanDefinitionTransformerTest +{ + @Mock + private ScriptExecutor scriptExecutor; + + @Mock + private LockManager lockManager; + + @Mock + private SystemRepository systemRepository; + + @Mock + private FhirResourceRepository fhirResourceRepository; + + @Mock + private FhirDhisAssignmentRepository fhirDhisAssignmentRepository; + + @Mock + private OrganizationUnitService organizationUnitService; + + @Mock + private TrackedEntityMetadataService trackedEntityMetadataService; + + @Mock + private TrackedEntityRuleRepository trackedEntityRuleRepository; + + @Mock + private DhisToFhirTransformerContext context; + + @Mock + private FhirClient fhirClient; + + @Mock + private ScriptExecutionContext scriptExecutionContext; + + @InjectMocks + private R4ProgramMetadataToFhirPlanDefinitionTransformer transformer; + + @Rule + public MockitoRule rule = MockitoJUnit.rule(); + + @Test + public void getFhirVersions() + { + Assert.assertEquals( FhirVersion.R4_ONLY, transformer.getFhirVersions() ); + } + + @Test + public void transformInternal() + { + Mockito.doReturn( Optional.of( new WritableTrackedEntityType( "a1234567890", "Test", Collections.emptyList() ) ) ) + .when( trackedEntityMetadataService ).findTypeByReference( Mockito.eq( new Reference( "a1234567890", ReferenceType.ID ) ) ); + Mockito.doReturn( Collections.singletonList( new RuleInfo<>( new ProgramMetadataRule(), Collections.emptyList() ) ) ) + .when( trackedEntityRuleRepository ).findByTypeRefs( Mockito.eq( new HashSet<>( + Arrays.asList( new Reference( "a1234567890", ReferenceType.ID ), new Reference( "Test", ReferenceType.NAME ) ) ) ) ); + + final WritableProgram program = new WritableProgram(); + program.setName( "Test Program" ); + program.setDescription( "Test Description" ); + program.setWithoutRegistration( false ); + program.setTrackedEntityTypeId( "a1234567890" ); + program.setStages( new ArrayList<>() ); + + WritableProgramStage programStage = new WritableProgramStage(); + programStage.setId( "b1234567890" ); + programStage.setName( "Test Stage 1" ); + programStage.setDescription( "Test Description 1" ); + programStage.setRepeatable( false ); + program.getStages().add( programStage ); + + programStage = new WritableProgramStage(); + programStage.setId( "b1234567891" ); + programStage.setName( "Test Stage 2" ); + programStage.setDescription( "Test Description 2" ); + programStage.setRepeatable( true ); + program.getStages().add( programStage ); + + final PlanDefinition fhirPlanDefinition = new PlanDefinition(); + + transformer.transformInternal( fhirClient, context, new RuleInfo<>( new ProgramMetadataRule(), Collections.emptyList() ), new HashMap<>(), + new WritableScriptedDhisMetadata( program, scriptExecutionContext ), fhirPlanDefinition ); + + Assert.assertEquals( "Test Program", fhirPlanDefinition.getName() ); + Assert.assertEquals( "Test Description", fhirPlanDefinition.getDescription() ); + Assert.assertEquals( 2, fhirPlanDefinition.getAction().size() ); + + Assert.assertEquals( "b1234567890", fhirPlanDefinition.getAction().get( 0 ).getId() ); + Assert.assertEquals( "Test Stage 1", fhirPlanDefinition.getAction().get( 0 ).getTitle() ); + Assert.assertEquals( "Test Description 1", fhirPlanDefinition.getAction().get( 0 ).getDescription() ); + Assert.assertEquals( PlanDefinition.ActionCardinalityBehavior.SINGLE, fhirPlanDefinition.getAction().get( 0 ).getCardinalityBehavior() ); + Assert.assertEquals( "Questionnaire/b1234567890", fhirPlanDefinition.getAction().get( 0 ).getDefinition().primitiveValue() ); + + Assert.assertEquals( "b1234567891", fhirPlanDefinition.getAction().get( 1 ).getId() ); + Assert.assertEquals( "Test Stage 2", fhirPlanDefinition.getAction().get( 1 ).getTitle() ); + Assert.assertEquals( "Test Description 2", fhirPlanDefinition.getAction().get( 1 ).getDescription() ); + Assert.assertEquals( PlanDefinition.ActionCardinalityBehavior.MULTIPLE, fhirPlanDefinition.getAction().get( 1 ).getCardinalityBehavior() ); + Assert.assertEquals( "Questionnaire/b1234567891", fhirPlanDefinition.getAction().get( 1 ).getDefinition().primitiveValue() ); + } +} diff --git a/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/AbstractDhisToFhirDataProvider.java b/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/AbstractDhisToFhirDataProvider.java index fb0448d5..ca186c88 100644 --- a/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/AbstractDhisToFhirDataProvider.java +++ b/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/AbstractDhisToFhirDataProvider.java @@ -110,8 +110,8 @@ public DhisResource findByDhisFhirIdentifierCasted( @Nonnull FhirClient fhirClie public PreparedDhisToFhirSearch prepareSearchCasted( @Nonnull FhirVersion fhirVersion, @Nonnull List> ruleInfos, @Nullable Map> filter, @Nullable DateRangeParam lastUpdatedDateRange, int count ) throws DhisToFhirDataProviderException { final Class ruleClass = getRuleClass(); - final List> castedRuleInfos = - ruleInfos.stream().map( r -> new RuleInfo<>( getRuleClass().cast( r.getRule() ), r.getDhisDataReferences() ) ).collect( Collectors.toList() ); + final List> castedRuleInfos = ruleInfos.stream().map( r -> new RuleInfo<>( getRuleClass().cast( r.getRule() ), r.getDhisDataReferences() ) ).collect( Collectors.toList() ); + return prepareSearch( fhirVersion, castedRuleInfos, filter, lastUpdatedDateRange, count ); } } diff --git a/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/AbstractDhisToFhirRequestResolver.java b/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/AbstractDhisToFhirRequestResolver.java index 7a9d2b0f..74ef3c83 100644 --- a/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/AbstractDhisToFhirRequestResolver.java +++ b/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/AbstractDhisToFhirRequestResolver.java @@ -58,11 +58,13 @@ public AbstractDhisToFhirRequestResolver( @Nonnull FhirClientRepository fhirClie @Nonnull public Optional resolveFhirClient( @Nonnull ScriptedDhisResource scriptedDhisResource ) { - Optional fhirClient = fhirClientRepository.findExpEnabledOnly(); + final Optional fhirClient = fhirClientRepository.findExpEnabledOnly(); + if ( fhirClient.isPresent() && fhirClient.get().isEnabled() && fhirClient.get().isExpEnabled() ) { return fhirClient; } + return Optional.empty(); } } diff --git a/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/AbstractDhisToFhirTransformer.java b/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/AbstractDhisToFhirTransformer.java index 222cdbeb..ed19b435 100644 --- a/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/AbstractDhisToFhirTransformer.java +++ b/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/AbstractDhisToFhirTransformer.java @@ -144,47 +144,59 @@ protected Optional getExistingResource( @Nonnull FhirCl if ( context.isUseAdapterIdentifier() ) { final IBaseResource resource = getResourceByAdapterIdentifier( fhirClient, context, ruleInfo, scriptVariables ).orElse( null ); + if ( resource != null ) { if ( !ruleInfo.getRule().isEffectiveFhirUpdateEnable() ) { logger.info( "Existing FHIR resource could be found by adapter identifier, but rule {} does not allow updating FHIR resources.", ruleInfo ); + return Optional.empty(); } + return Optional.of( resource ); } } IBaseResource resource = getResourceBySystemIdentifier( fhirClient, context, ruleInfo, scriptVariables ).orElse( null ); + if ( resource != null ) { if ( !ruleInfo.getRule().isEffectiveFhirUpdateEnable() ) { logger.info( "Existing FHIR resource could be found by system identifier, but rule {} does not allow updating FHIR resources.", ruleInfo ); + return Optional.empty(); } + return Optional.of( resource ); } resource = getResourceByAssignment( fhirClient, context, ruleInfo, scriptVariables ).orElse( null ); + if ( resource != null ) { if ( !ruleInfo.getRule().isEffectiveFhirUpdateEnable() ) { logger.info( "Existing FHIR resource could be found by assigned identifier, but rule {} does not allow updating FHIR resources.", ruleInfo ); + return Optional.empty(); } + return Optional.of( resource ); } resource = getActiveResource( fhirClient, context, ruleInfo, scriptVariables ).orElse( null ); + if ( resource != null ) { if ( !ruleInfo.getRule().isEffectiveFhirUpdateEnable() ) { logger.info( "Existing active FHIR resource could be found, but rule {} does not allow updating FHIR resources.", ruleInfo ); + return Optional.empty(); } + return Optional.of( resource ); } @@ -217,6 +229,7 @@ protected Optional getResource( @Nonnull FhirClient fhi } IBaseResource resource = getExistingResource( fhirClient, context, ruleInfo, scriptVariables ).orElse( null ); + if ( resource != null ) { return Optional.of( resource ); @@ -225,6 +238,7 @@ protected Optional getResource( @Nonnull FhirClient fhi if ( ruleInfo.getRule().isEffectiveFhirCreateEnable() ) { resource = createResource( fhirClient, context, ruleInfo, scriptVariables, sync ); + if ( (resource != null) && !sync ) { lockResource( fhirClient, context, ruleInfo, scriptVariables ); @@ -255,6 +269,7 @@ protected Optional getResourceByAdapterIdentifier( { final R scriptedDhisResource = getDhisResourceClass().cast( TransformerUtils.getScriptVariable( scriptVariables, ScriptVariable.INPUT, ScriptedDhisResource.class ) ); + return getResourceByAdapterIdentifierInternal( fhirClient, context, ruleInfo, scriptedDhisResource, scriptVariables ); } @@ -269,6 +284,7 @@ private Optional clone( context, r ) ); } @@ -279,6 +295,7 @@ protected Optional getResourceBySystemIdentifier( @Nonn { final R scriptedDhisResource = getDhisResourceClass().cast( TransformerUtils.getScriptVariable( scriptVariables, ScriptVariable.INPUT, ScriptedDhisResource.class ) ); + return getResourceBySystemIdentifierInternal( fhirClient, context, ruleInfo, scriptedDhisResource, scriptVariables, new DefaultIdentifierValueProvider() ); } @@ -291,10 +308,12 @@ protected Optional getResourceByAssignment( @Nonnull Fh TransformerUtils.getScriptVariable( scriptVariables, ScriptVariable.INPUT, ScriptedDhisResource.class ) ); final String fhirResourceId = fhirDhisAssignmentRepository.findFirstFhirResourceId( ruleInfo.getRule(), fhirClient, Objects.requireNonNull( scriptedDhisResource.getResourceId() ) ); + if ( fhirResourceId == null ) { return Optional.empty(); } + return getFhirResourceRepository().findRefreshed( fhirClient.getId(), fhirClient.getFhirVersion(), fhirClient.getFhirEndpoint(), ruleInfo.getRule().getFhirResourceType().getResourceTypeName(), fhirResourceId ) .map( r -> clone( context, r ) ); } @@ -305,17 +324,20 @@ private Optional scriptVariables, @Nonnull IdentifierValueProvider identifierValueProvider ) throws TransformerException { final ResourceSystem resourceSystem = context.getResourceSystem( ruleInfo.getRule().getFhirResourceType() ); + if ( resourceSystem == null ) { return Optional.empty(); } final String identifierValue = identifierValueProvider.getIdentifierValue( context, ruleInfo, null, scriptedDhisResource, scriptVariables ); + if ( identifierValue == null ) { logger.info( "FHIR resource type {} defines resource system, but resource does not include an identifier.", ruleInfo.getRule().getFhirResourceType() ); return Optional.empty(); } + if ( StringUtils.isNotBlank( resourceSystem.getCodePrefix() ) && (!identifierValue.startsWith( resourceSystem.getCodePrefix() ) || identifierValue.equals( resourceSystem.getCodePrefix() )) ) { logger.info( "Resource identifier \"{}\" does not start with required prefix \"{}\" for resource type {}.", @@ -324,6 +346,7 @@ private Optional clone( context, r ) ); } @@ -359,6 +382,7 @@ protected boolean updateIdentifiers( @Nonnull DhisToFhirTransformerContext conte if ( resourceSystem != null ) { final String identifierValue = getIdentifierValue( context, ruleInfo, null, scriptedDhisResource, scriptVariables ); + if ( identifierValue == null ) { logger.debug( "FHIR resource type {} defines resource system, but tracked entity does not include an identifier.", ruleInfo.getRule().getFhirResourceType() ); @@ -369,6 +393,7 @@ protected boolean updateIdentifiers( @Nonnull DhisToFhirTransformerContext conte { logger.info( "Tracked entity identifier \"{}\" does not start with required prefix \"{}\" for resource type {}.", identifierValue, resourceSystem.getCodePrefix(), ruleInfo.getRule().getFhirResourceType() ); + return false; } @@ -420,9 +445,11 @@ protected Optional getTrackedEntityFhirResource( @Nonnu @Nonnull ScriptedTrackedEntityInstance scriptedTrackedEntityInstance, @Nonnull Map scriptVariables ) throws TransformerException { IBaseResource resource; + if ( context.getDhisRequest().isDhisFhirId() ) { final IBaseReference reference = context.getDhisFhirResourceReference( scriptedTrackedEntityInstance, trackedEntityResourceType ); + if ( reference == null ) { return Optional.empty(); @@ -436,6 +463,7 @@ protected Optional getTrackedEntityFhirResource( @Nonnu else { resource = getResourceBySystemIdentifierInternal( fhirClient, context, ruleInfo, scriptedTrackedEntityInstance, scriptVariables, new TrackedEntityIdentifierValueProvider() ).orElse( null ); + if ( resource != null ) { return Optional.of( resource ); @@ -445,6 +473,7 @@ protected Optional getTrackedEntityFhirResource( @Nonnu resource = getResourceByAdapterIdentifierInternal( fhirClient, context, ruleInfo, scriptedTrackedEntityInstance, scriptVariables ).orElse( null ); } } + return Optional.ofNullable( resource ); } @@ -471,10 +500,12 @@ protected T cloneToModified( @Nonnull DhisToFhirTransf } final T modifiedResource = clone( context, resource ); + if ( !updateIdentifiers( context, ruleInfo, modifiedResource, scriptVariables, false ) ) { return null; } + return modifiedResource; } @@ -496,6 +527,7 @@ protected boolean evaluateNotModified( @Nonnull DhisToFhirTransformerContext con final AbstractFhirResourceDhisToFhirTransformerUtils fhirResourceUtils = TransformerUtils.getScriptVariable( scriptVariables, ScriptVariable.FHIR_RESOURCE_UTILS, AbstractFhirResourceDhisToFhirTransformerUtils.class ); + return fhirResourceUtils.equalsDeep( base1, base2 ); } @@ -563,6 +595,7 @@ public String getIdentifierValue( @Nonnull DhisToFhirTransformerContext context, { throw new FatalTransformerException( "Identifier value of a tracked entity instance cannot be looked up with an alternative lookup script." ); } + return AbstractDhisToFhirTransformer.this.getTrackedEntityIdentifierValue( context, ruleInfo, scriptedDhisResource, scriptVariables ); } } diff --git a/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/DhisToFhirRuleComparator.java b/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/DhisToFhirRuleComparator.java index 9168e4fd..2fb3fc35 100644 --- a/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/DhisToFhirRuleComparator.java +++ b/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/DhisToFhirRuleComparator.java @@ -1,7 +1,7 @@ package org.dhis2.fhir.adapter.fhir.transform.dhis.impl; /* - * Copyright (c) 2004-2018, University of Oslo + * Copyright (c) 2004-2019, University of Oslo * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -51,11 +51,13 @@ protected DhisToFhirRuleComparator() @Override public int compare( RuleInfo o1, RuleInfo o2 ) { - int value = o1.getRule().getFhirResourceType().getOrder() - o2.getRule().getFhirResourceType().getOrder(); + final int value = o1.getRule().getFhirResourceType().getOrder() - o2.getRule().getFhirResourceType().getOrder(); + if ( value != 0 ) { return value; } + return o1.getRule().compareTo( o2.getRule() ); } } diff --git a/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/DhisToFhirTransformerContextImpl.java b/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/DhisToFhirTransformerContextImpl.java index cc3b73d6..ec19a786 100644 --- a/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/DhisToFhirTransformerContextImpl.java +++ b/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/DhisToFhirTransformerContextImpl.java @@ -208,10 +208,12 @@ public Object getAttribute( @Nonnull String name ) public IBaseReference getDhisFhirResourceReference( @Nullable ScriptedDhisResource dhisResource, Object... fhirResourceTypes ) { final List references = getDhisFhirResourceReferencesLimited( dhisResource, 1, fhirResourceTypes ); + if ( references.isEmpty() ) { return null; } + return references.get( 0 ); } @@ -229,6 +231,7 @@ protected List getDhisFhirResourceReferencesLimited( @Nullable S { return Collections.emptyList(); } + return fhirReferenceResolver.resolveFhirReferences( fhirClient, dhisResource, Stream.of( fhirResourceTypes ).map( frt -> { try { diff --git a/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/DhisToFhirTransformerRequestImpl.java b/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/DhisToFhirTransformerRequestImpl.java index 1248979f..1ca9d5ce 100644 --- a/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/DhisToFhirTransformerRequestImpl.java +++ b/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/DhisToFhirTransformerRequestImpl.java @@ -93,6 +93,7 @@ public ScriptedDhisResource getInput() { throw new FatalTransformerException( "Transformer input has not yet been set." ); } + return input; } @@ -128,6 +129,7 @@ public UUID getRuleId() { throw new FatalTransformerException( "Rule ID is not available." ); } + return rules.get( ruleIndex ).getRule().getId(); } @@ -159,11 +161,14 @@ public RuleInfo nextRule() { return null; } + final RuleInfo ruleInfo = rules.get( ruleIndex++ ); - if ( (input != null) && (ruleInfo.getRule().getDhisResourceType() != input.getResourceType()) ) + + if ( input != null && ruleInfo.getRule().getDhisResourceType() != input.getResourceType() ) { input = null; } + return ruleInfo; } } diff --git a/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/DhisToFhirTransformerServiceImpl.java b/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/DhisToFhirTransformerServiceImpl.java index 64e391ac..5b90e841 100644 --- a/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/DhisToFhirTransformerServiceImpl.java +++ b/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/DhisToFhirTransformerServiceImpl.java @@ -246,6 +246,7 @@ public List resolveFhirReferences( @Nonnull FhirClient fhirClien do { final DhisToFhirTransformOutcome outcome = transform( transformerRequest ); + if ( outcome == null ) { transformerRequest = null; @@ -301,6 +302,7 @@ public DhisToFhirTransformerRequest createTransformerRequest( @Nonnull FhirClien final DhisToFhirRequestResolver requestResolver = getRequestResolver( dhisRequest.getResourceType() ); final ScriptedDhisResource scriptedResource = requestResolver.convert( Objects.requireNonNull( DhisBeanTransformerUtils.clone( resource ) ), dhisRequest ); + return createTransformerRequest( false, fhirClient, dhisRequest, scriptedResource, Collections.singletonList( ruleInfo ) ); } @@ -309,6 +311,7 @@ public DhisToFhirTransformerRequest createTransformerRequest( @Nonnull FhirClien public DhisToFhirTransformerRequest createTransformerRequest( @Nonnull FhirClient fhirClient, @Nonnull DhisRequest dhisRequest, @Nonnull DhisResource resource, @Nonnull List> rules ) { final DhisToFhirRequestResolver requestResolver = getRequestResolver( dhisRequest.getResourceType() ); + return createTransformerRequest( dhisRequest, rr -> rr.convert( resource, dhisRequest ), requestResolver, ( rr, scriptedResource ) -> rr.resolveRules( scriptedResource, rules ), ri -> true, ( si, rr ) -> fhirClient ); } @@ -326,6 +329,7 @@ protected DhisToFhirTransformerRequest createTransformerRequest( @Nonnull DhisRe @Nonnull Predicate> ruleInfoPredicate, @Nonnull BiFunction fhirClientFunction ) { final DhisToFhirRequestResolver requestResolver = getRequestResolver( dhisRequest.getResourceType() ); + return createTransformerRequest( dhisRequest, scriptedDhisResourceFunction, requestResolver, DhisToFhirRequestResolver::resolveRules, ruleInfoPredicate, fhirClientFunction ); } @@ -337,6 +341,7 @@ protected DhisToFhirTransformerRequest createTransformerRequest( @Nonnull DhisRe final ScriptedDhisResource scriptedResource = scriptedDhisResourceFunction.apply( requestResolver ); final List> ruleInfos = ruleResolverFunction.apply( requestResolver, scriptedResource ) .stream().filter( ruleInfoPredicate ).collect( Collectors.toList() ); + if ( ruleInfos.isEmpty() ) { logger.info( "Could not find any rule to process DHIS resource." ); @@ -344,6 +349,7 @@ protected DhisToFhirTransformerRequest createTransformerRequest( @Nonnull DhisRe } final FhirClient fhirClient = fhirClientFunction.apply( scriptedResource, requestResolver ); + if ( fhirClient == null ) { logger.info( "Could not determine FHIR client to process DHIS resource." ); @@ -363,15 +369,19 @@ public DhisToFhirTransformerRequest createTransformerRequest( @Nonnull FhirClien final List> ruleInfos = requestResolver.resolveRules( scriptedDhisResource ); final RuleInfo matchingRuleInfo = ruleInfos.stream().filter( ri -> ri.getRule().getId().equals( ruleId ) ).findFirst().orElse( null ); + if ( (matchingRuleInfo == null) || !fhirResourceType.equals( matchingRuleInfo.getRule().getFhirResourceType() ) ) { logger.info( "Could not find any matching rule to process DHIS resource." ); + return null; } + final List> groupingRuleInfos = ruleInfos.stream() .filter( ri -> ri.getRule().isGrouping() ).collect( Collectors.toList() ); final List> resultingRuleInfos; + if ( groupingRuleInfos.isEmpty() ) { resultingRuleInfos = Collections.singletonList( matchingRuleInfo ); @@ -385,6 +395,7 @@ else if ( matchingRuleInfo.getRule().isGrouping() ) resultingRuleInfos = ruleInfos.stream() .filter( ri -> ri.getRule().isGrouping() || ri.getRule().getId().equals( ruleId ) ).collect( Collectors.toList() ); } + return createTransformerRequest( matchingRuleInfo.getRule().isGrouping(), fhirClient, dhisRequest, scriptedDhisResource, resultingRuleInfos ); } @@ -400,6 +411,7 @@ protected DhisToFhirTransformerRequest createTransformerRequest( boolean groupin .collect( Collectors.toMap( ResourceSystem::getFhirResourceType, rs -> rs ) ); final Map transformerUtils = this.transformerUtils.get( fhirClient.getFhirVersion() ); + if ( transformerUtils == null ) { throw new TransformerMappingException( "No transformer utils can be found for FHIR version " + fhirClient.getFhirVersion() ); @@ -420,6 +432,7 @@ public DhisToFhirTransformerRequest updateTransformerRequest( @Nonnull DhisReque final DhisToFhirTransformerRequestImpl transformerRequestImpl = (DhisToFhirTransformerRequestImpl) transformerRequest; transformerRequestImpl.setInput( scriptedInput ); + return transformerRequest; } @@ -428,6 +441,7 @@ private List> filterAvailableResourceRules( @No { final Map availableResourcesByType = availableResources.stream() .collect( Collectors.toMap( AvailableFhirClientResource::getResourceType, a -> a ) ); + // not all resources may be available on FHIR client return ruleInfos.stream().filter( r -> { final AvailableFhirClientResource availableResource = availableResourcesByType.get( r.getRule().getFhirResourceType() ); @@ -443,10 +457,12 @@ private List> filterAvailableResourceRules( @No private DhisToFhirRequestResolver getRequestResolver( @Nonnull DhisResourceType resourceType ) { final DhisToFhirRequestResolver requestResolver = requestResolvers.get( resourceType ); + if ( requestResolver == null ) { throw new TransformerMappingException( "No request resolver can be found for DHIS resource type " + resourceType ); } + return requestResolver; } @@ -458,6 +474,7 @@ public DhisToFhirTransformOutcome transform( @Nonnull D final boolean firstRule = transformerRequestImpl.isFirstRule(); RuleInfo ruleInfo; + while ( (ruleInfo = transformerRequestImpl.nextRule()) != null ) { Cache transformerRequestCache = null; diff --git a/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/TransformerRequestKey.java b/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/TransformerRequestKey.java index 59f08884..e7fc60e2 100644 --- a/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/TransformerRequestKey.java +++ b/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/TransformerRequestKey.java @@ -57,11 +57,19 @@ public TransformerRequestKey( @Nonnull UUID ruleId, @Nonnull DhisResourceId dhis @Override public boolean equals( Object o ) { - if ( this == o ) return true; - if ( o == null || getClass() != o.getClass() ) return false; + if ( this == o ) + { + return true; + } + + if ( o == null || getClass() != o.getClass() ) + { + return false; + } + TransformerRequestKey that = (TransformerRequestKey) o; - return ruleId.equals( that.ruleId ) && - dhisResourceId.equals( that.dhisResourceId ); + + return ruleId.equals( that.ruleId ) && dhisResourceId.equals( that.dhisResourceId ); } @Override diff --git a/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/dhis/search/SearchFilterCollector.java b/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/dhis/search/SearchFilterCollector.java index a818504b..9a57a55a 100644 --- a/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/dhis/search/SearchFilterCollector.java +++ b/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/dhis/search/SearchFilterCollector.java @@ -79,6 +79,7 @@ public SearchFilterCollector( @Nullable ItemContainerType itemContainerType, @Nu public SearchFilterCollector( @Nullable Function dhisPropertyRefResolver, @Nullable Map> filter ) { this.dhisPropertyRefResolver = dhisPropertyRefResolver; + if ( filter != null ) { filter.forEach( ( key, value ) -> { @@ -142,10 +143,11 @@ public void addQueryParam( @Nonnull String propertyName, @Nullable String value public void addFilterExpression( @Nonnull String propertyName, @Nonnull String operator, @Nullable String value ) { - if ( ( value != null ) && ( value.indexOf( ':' ) >= 0 ) ) + if ( value != null && value.indexOf( ':' ) >= 0 ) { throw new DhisToFhirDataProviderException( "Colon characters in search parameter values are not supported." ); } + filterExpressions.add( propertyName + ':' + operator + ':' + StringUtils.defaultString( value ) ); } @@ -162,11 +164,13 @@ public U add( @Nonnull U uriBuilder, @Nonnull List uriBuilder.queryParam( key, values.toArray() ) ); filterExpressions.forEach( fe -> { uriBuilder.queryParam( "filter", "{sfc" + variables.size() + "}" ); variables.add( fe ); } ); + return uriBuilder; } } diff --git a/fhir/src/main/resources/db/migration/sample/V1.1.0.41_10_0__Plan_Definition_Sample_Data.sql b/fhir/src/main/resources/db/migration/sample/V1.1.0.41_10_0__Plan_Definition_Sample_Data.sql new file mode 100644 index 00000000..e21ec8fd --- /dev/null +++ b/fhir/src/main/resources/db/migration/sample/V1.1.0.41_10_0__Plan_Definition_Sample_Data.sql @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2004-2018, University of Oslo + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * Neither the name of the HISP project nor the names of its contributors may + * be used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO PROGRAM_STAGE_EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +-- @formatter:off + +UPDATE fhir_client SET fhir_version='R4', remote_base_url='http://localhost:8082/hapi-fhir-jpaserver/fhir' +WHERE id='73cd99c5-0ca8-42ad-a53b-1891fccce08f'; + +-- virtual subscription for FHIR organizations +INSERT INTO fhir_client_resource (id, version, fhir_client_id, fhir_resource_type, exp_only, fhir_criteria_parameters, description) +VALUES ('2520acc5-86b4-4716-ae0f-ea0531eb885a', 0, '73cd99c5-0ca8-42ad-a53b-1891fccce08f', 'PLAN_DEFINITION', TRUE, NULL, 'Virtual subscription for all Plan Definitions.'); +INSERT INTO fhir_client_resource_update(id) +VALUES ('2520acc5-86b4-4716-ae0f-ea0531eb885a'); diff --git a/fhir/src/test/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/metadata/program/AbstractProgramMetadataToFhirPlanDefinitionTransformerTest.java b/fhir/src/test/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/metadata/program/AbstractProgramMetadataToFhirPlanDefinitionTransformerTest.java new file mode 100644 index 00000000..8ee2c709 --- /dev/null +++ b/fhir/src/test/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/metadata/program/AbstractProgramMetadataToFhirPlanDefinitionTransformerTest.java @@ -0,0 +1,206 @@ +package org.dhis2.fhir.adapter.fhir.transform.dhis.impl.metadata.program; + +/* + * Copyright (c) 2004-2019, University of Oslo + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * Neither the name of the HISP project nor the names of its contributors may + * be used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +import org.dhis2.fhir.adapter.dhis.model.DhisResourceType; +import org.dhis2.fhir.adapter.dhis.model.Reference; +import org.dhis2.fhir.adapter.dhis.model.ReferenceType; +import org.dhis2.fhir.adapter.dhis.orgunit.OrganizationUnitService; +import org.dhis2.fhir.adapter.dhis.tracker.program.WritableProgram; +import org.dhis2.fhir.adapter.dhis.tracker.trackedentity.TrackedEntityMetadataService; +import org.dhis2.fhir.adapter.dhis.tracker.trackedentity.WritableTrackedEntityType; +import org.dhis2.fhir.adapter.fhir.data.repository.FhirDhisAssignmentRepository; +import org.dhis2.fhir.adapter.fhir.metadata.model.FhirResourceType; +import org.dhis2.fhir.adapter.fhir.metadata.model.ProgramMetadataRule; +import org.dhis2.fhir.adapter.fhir.metadata.model.RuleInfo; +import org.dhis2.fhir.adapter.fhir.metadata.repository.SystemRepository; +import org.dhis2.fhir.adapter.fhir.metadata.repository.TrackedEntityRuleRepository; +import org.dhis2.fhir.adapter.fhir.repository.FhirResourceRepository; +import org.dhis2.fhir.adapter.fhir.script.ScriptExecutor; +import org.dhis2.fhir.adapter.fhir.transform.scripted.AccessibleScriptedDhisMetadata; +import org.dhis2.fhir.adapter.lock.LockManager; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.mockito.Mock; +import org.mockito.Mockito; +import org.mockito.junit.MockitoJUnit; +import org.mockito.junit.MockitoRule; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.HashSet; +import java.util.Optional; + +import static org.mockito.Mockito.CALLS_REAL_METHODS; +import static org.mockito.Mockito.withSettings; + +/** + * Unit tests for {@link AbstractProgramMetadataToFhirPlanDefinitionTransformer}. + * + * @author volsch + */ +public class AbstractProgramMetadataToFhirPlanDefinitionTransformerTest +{ + @Mock + private ScriptExecutor scriptExecutor; + + @Mock + private LockManager lockManager; + + @Mock + private SystemRepository systemRepository; + + @Mock + private FhirResourceRepository fhirResourceRepository; + + @Mock + private FhirDhisAssignmentRepository fhirDhisAssignmentRepository; + + @Mock + private OrganizationUnitService organizationUnitService; + + @Mock + private TrackedEntityMetadataService trackedEntityMetadataService; + + @Mock + private TrackedEntityRuleRepository trackedEntityRuleRepository; + + private AbstractProgramMetadataToFhirPlanDefinitionTransformer transformer; + + @Rule + public MockitoRule rule = MockitoJUnit.rule(); + + @Before + public void setUp() + { + transformer = Mockito.mock( AbstractProgramMetadataToFhirPlanDefinitionTransformer.class, + withSettings().useConstructor( scriptExecutor, lockManager, systemRepository, fhirResourceRepository, fhirDhisAssignmentRepository, organizationUnitService, + trackedEntityMetadataService, trackedEntityRuleRepository ).defaultAnswer( CALLS_REAL_METHODS ) ); + } + + @Test + public void getDhisResourceType() + { + Assert.assertEquals( DhisResourceType.PROGRAM_METADATA, transformer.getDhisResourceType() ); + } + + @Test + public void getFhirResourceType() + { + Assert.assertEquals( FhirResourceType.PLAN_DEFINITION, transformer.getFhirResourceType() ); + } + + @Test + public void getDhisResourceClass() + { + Assert.assertEquals( AccessibleScriptedDhisMetadata.class, transformer.getDhisResourceClass() ); + } + + @Test + public void isApplicableProgram() + { + Mockito.doReturn( Optional.of( new WritableTrackedEntityType( "a1234567890", "Test", Collections.emptyList() ) ) ) + .when( trackedEntityMetadataService ).findTypeByReference( Mockito.eq( new Reference( "a1234567890", ReferenceType.ID ) ) ); + Mockito.doReturn( Collections.singletonList( new RuleInfo<>( new ProgramMetadataRule(), Collections.emptyList() ) ) ) + .when( trackedEntityRuleRepository ).findByTypeRefs( Mockito.eq( new HashSet<>( + Arrays.asList( new Reference( "a1234567890", ReferenceType.ID ), new Reference( "Test", ReferenceType.NAME ) ) ) ) ); + + final WritableProgram program = new WritableProgram(); + program.setName( "Test Program" ); + program.setDescription( "Test Description" ); + program.setWithoutRegistration( false ); + program.setTrackedEntityTypeId( "a1234567890" ); + program.setStages( new ArrayList<>() ); + + Assert.assertTrue( transformer.isApplicableProgram( program ) ); + } + + @Test + public void isApplicableProgramWithoutRegistration() + { + final WritableProgram program = new WritableProgram(); + program.setName( "Test Program" ); + program.setDescription( "Test Description" ); + program.setWithoutRegistration( true ); + program.setTrackedEntityTypeId( "a1234567890" ); + program.setStages( new ArrayList<>() ); + + Assert.assertFalse( transformer.isApplicableProgram( program ) ); + } + + @Test + public void isApplicableProgramWithoutTrackedEntityType() + { + final WritableProgram program = new WritableProgram(); + program.setName( "Test Program" ); + program.setDescription( "Test Description" ); + program.setWithoutRegistration( false ); + program.setStages( new ArrayList<>() ); + + Assert.assertFalse( transformer.isApplicableProgram( program ) ); + } + + @Test + public void isApplicableProgramWithoutTrackedEntityMetadata() + { + Mockito.doReturn( Optional.empty() ) + .when( trackedEntityMetadataService ).findTypeByReference( Mockito.eq( new Reference( "a1234567890", ReferenceType.ID ) ) ); + + final WritableProgram program = new WritableProgram(); + program.setName( "Test Program" ); + program.setDescription( "Test Description" ); + program.setWithoutRegistration( false ); + program.setTrackedEntityTypeId( "a1234567890" ); + program.setStages( new ArrayList<>() ); + + Assert.assertFalse( transformer.isApplicableProgram( program ) ); + } + + @Test + public void isApplicableProgramWithoutRules() + { + Mockito.doReturn( Optional.of( new WritableTrackedEntityType( "a1234567890", "Test", Collections.emptyList() ) ) ) + .when( trackedEntityMetadataService ).findTypeByReference( Mockito.eq( new Reference( "a1234567890", ReferenceType.ID ) ) ); + Mockito.doReturn( Collections.emptyList() ) + .when( trackedEntityRuleRepository ).findByTypeRefs( Mockito.eq( new HashSet<>( + Arrays.asList( new Reference( "a1234567890", ReferenceType.ID ), new Reference( "Test", ReferenceType.NAME ) ) ) ) ); + + final WritableProgram program = new WritableProgram(); + program.setName( "Test Program" ); + program.setDescription( "Test Description" ); + program.setWithoutRegistration( false ); + program.setTrackedEntityTypeId( "a1234567890" ); + program.setStages( new ArrayList<>() ); + + Assert.assertFalse( transformer.isApplicableProgram( program ) ); + } +} \ No newline at end of file From f29bf00647d7584c7ae50617ae0d0c270cd8cd26 Mon Sep 17 00:00:00 2001 From: Volker Schmidt Date: Mon, 29 Jul 2019 19:36:34 +0200 Subject: [PATCH 06/21] Enables export of program metadata. --- .../impl/DhisDataProcessorItemRetrieverImpl.java | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/dhis/src/main/java/org/dhis2/fhir/adapter/dhis/sync/impl/DhisDataProcessorItemRetrieverImpl.java b/dhis/src/main/java/org/dhis2/fhir/adapter/dhis/sync/impl/DhisDataProcessorItemRetrieverImpl.java index cdf131af..6a655afa 100644 --- a/dhis/src/main/java/org/dhis2/fhir/adapter/dhis/sync/impl/DhisDataProcessorItemRetrieverImpl.java +++ b/dhis/src/main/java/org/dhis2/fhir/adapter/dhis/sync/impl/DhisDataProcessorItemRetrieverImpl.java @@ -1,7 +1,7 @@ package org.dhis2.fhir.adapter.dhis.sync.impl; /* - * Copyright (c) 2004-2018, University of Oslo + * Copyright (c) 2004-2019, University of Oslo * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -40,6 +40,7 @@ import org.dhis2.fhir.adapter.dhis.orgunit.OrganizationUnitService; import org.dhis2.fhir.adapter.dhis.sync.SyncExcludedDhisUsernameRetriever; import org.dhis2.fhir.adapter.dhis.tracker.program.EventService; +import org.dhis2.fhir.adapter.dhis.tracker.program.ProgramMetadataService; import org.dhis2.fhir.adapter.dhis.tracker.trackedentity.TrackedEntityService; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.stereotype.Component; @@ -67,6 +68,8 @@ public class DhisDataProcessorItemRetrieverImpl implements DataProcessorItemRetr private final OrganizationUnitService organizationUnitService; + private final ProgramMetadataService programMetadataService; + private final TrackedEntityService trackedEntityService; private final EventService eventService; @@ -78,6 +81,7 @@ public DhisDataProcessorItemRetrieverImpl( @Nonnull @Qualifier( "systemDhis2Authorization" ) Authorization systemDhis2Authorization, @Nonnull SyncExcludedDhisUsernameRetriever excludedDhisUsernameRetriever, @Nonnull OrganizationUnitService organizationUnitService, + @Nonnull ProgramMetadataService programMetadataService, @Nonnull TrackedEntityService trackedEntityService, @Nonnull EventService eventService, @Nonnull DhisSyncProcessorConfig processorConfig, @@ -89,6 +93,7 @@ public DhisDataProcessorItemRetrieverImpl( this.trackedEntityService = trackedEntityService; this.eventService = eventService; this.organizationUnitService = organizationUnitService; + this.programMetadataService = programMetadataService; this.processorConfig = processorConfig; } @@ -110,6 +115,12 @@ public Instant poll( @Nonnull DhisSyncGroup group, @Nonnull Instant lastUpdated, excludedDhisUsernames, consumer ); result = ObjectUtils.min( result, currentResult ); } + if ( resourceTypes.contains( DhisResourceType.PROGRAM_METADATA ) ) + { + final Instant currentResult = programMetadataService.poll( group, lastUpdated, toleranceMillis, maxSearchCount, + excludedDhisUsernames, consumer ); + result = ObjectUtils.min( result, currentResult ); + } if ( resourceTypes.contains( DhisResourceType.TRACKED_ENTITY ) ) { final Instant currentResult = trackedEntityService.poll( group, lastUpdated, toleranceMillis, maxSearchCount, From c6cc85f960adb33843bccddff1bfc9f08fd2707d Mon Sep 17 00:00:00 2001 From: Volker Schmidt Date: Mon, 29 Jul 2019 22:45:12 +0200 Subject: [PATCH 07/21] Provides polling support for plan definitions. --- .../AbstractProgramStageFhirRestAppTest.java | 5 +- .../r4/R4ProgramMetadataFhirRestAppTest.java | 17 ++--- ...stractEnrollmentTransformationAppTest.java | 7 +- ...ractProgramStageTransformationAppTest.java | 5 +- .../adapter/dhis/model/DhisResourceType.java | 28 ++++---- .../impl/OrganizationUnitServiceImpl.java | 2 +- .../dhis/service/DhisMetadataService.java | 12 +--- .../dhis/service/DhisPolledService.java | 52 +++++++++++++++ .../adapter/dhis/service/DhisService.java | 46 +++++++++++++ .../impl/AbstractDhisMetadataServiceImpl.java | 2 +- .../DhisDataProcessorItemRetrieverImpl.java | 66 ++++++------------- .../tracker/program/EnrollmentService.java | 4 +- .../dhis/tracker/program/EventService.java | 13 +--- .../dhis/tracker/program/WritableProgram.java | 4 +- .../program/impl/EnrollmentServiceImpl.java | 8 +++ .../program/impl/EventServiceImpl.java | 8 +++ .../impl/ProgramMetadataServiceImpl.java | 4 +- .../trackedentity/TrackedEntityService.java | 13 +--- .../impl/TrackedEntityServiceImpl.java | 7 ++ .../impl/ProgramMetadataServiceImplTest.java | 15 +++-- .../impl/AbstractDhisToFhirTransformer.java | 7 +- 21 files changed, 204 insertions(+), 121 deletions(-) create mode 100644 dhis/src/main/java/org/dhis2/fhir/adapter/dhis/service/DhisPolledService.java create mode 100644 dhis/src/main/java/org/dhis2/fhir/adapter/dhis/service/DhisService.java diff --git a/app/src/test/java/org/dhis2/fhir/adapter/fhir/server/AbstractProgramStageFhirRestAppTest.java b/app/src/test/java/org/dhis2/fhir/adapter/fhir/server/AbstractProgramStageFhirRestAppTest.java index 8962a7e8..b7ed5cd6 100644 --- a/app/src/test/java/org/dhis2/fhir/adapter/fhir/server/AbstractProgramStageFhirRestAppTest.java +++ b/app/src/test/java/org/dhis2/fhir/adapter/fhir/server/AbstractProgramStageFhirRestAppTest.java @@ -67,12 +67,13 @@ protected void expectProgramStageMetadataRequests() throws Exception .andRespond( withSuccess( IOUtils.resourceToString( "/org/dhis2/fhir/adapter/dhis/test/single-org-unit-OU_1234.json", StandardCharsets.UTF_8 ), MediaType.APPLICATION_JSON ) ); systemDhis2Server.expect( ExpectedCount.between( 0, 1 ), method( HttpMethod.GET ) ).andExpect( header( "Authorization", testConfiguration.getDhis2SystemAuthorization() ) ) .andExpect( requestTo( dhis2BaseUrl + "/api/" + dhis2ApiVersion + "/programs/EPDyQuoRnXk.json?" + - "fields=id,name,code,description,selectIncidentDatesInFuture,selectEnrollmentDatesInFuture,displayIncidentDate,registration,withoutRegistration,captureCoordinates,trackedEntityType%5Bid%5D,programTrackedEntityAttributes%5Bid,name,valueType," + + "fields=id,name,code,description,lastUpdated,selectIncidentDatesInFuture,selectEnrollmentDatesInFuture,displayIncidentDate,registration,withoutRegistration,captureCoordinates,trackedEntityType%5Bid%5D,programTrackedEntityAttributes%5Bid," + + "name,valueType," + "mandatory,allowFutureDate,trackedEntityAttribute%5Bid,name,code,valueType,generated%5D%5D,programStages%5Bid,name,description,repeatable,captureCoordinates,generatedByEnrollmentDate,minDaysFromStart,programStageDataElements%5Bid,compulsory," + "allowProvidedElsewhere,dataElement%5Bid,name,code,formName,valueType,optionSetValue,optionSet%5Bid,name,options%5Bcode,name%5D%5D%5D%5D%5D" ) ) .andRespond( withSuccess( IOUtils.resourceToString( "/org/dhis2/fhir/adapter/dhis/test/single-program.json", StandardCharsets.UTF_8 ), MediaType.APPLICATION_JSON ) ); systemDhis2Server.expect( ExpectedCount.between( 0, 1 ), method( HttpMethod.GET ) ).andExpect( header( "Authorization", testConfiguration.getDhis2SystemAuthorization() ) ) - .andExpect( requestTo( dhis2BaseUrl + "/api/" + dhis2ApiVersion + "/programs.json?paging=false&fields=id,name,code,description,selectIncidentDatesInFuture,selectEnrollmentDatesInFuture,displayIncidentDate,registration," + + .andExpect( requestTo( dhis2BaseUrl + "/api/" + dhis2ApiVersion + "/programs.json?paging=false&fields=id,name,code,description,lastUpdated,selectIncidentDatesInFuture,selectEnrollmentDatesInFuture,displayIncidentDate,registration," + "withoutRegistration,captureCoordinates,trackedEntityType%5Bid%5D,programTrackedEntityAttributes%5Bid,name,valueType,mandatory,allowFutureDate,trackedEntityAttribute%5Bid,name,code,valueType,generated%5D%5D,programStages%5Bid,name," + "description,repeatable,captureCoordinates,generatedByEnrollmentDate,minDaysFromStart,programStageDataElements%5Bid,compulsory,allowProvidedElsewhere,dataElement%5Bid,name,code,formName,valueType,optionSetValue,optionSet%5Bid,name," + "options%5Bcode,name%5D%5D%5D%5D%5D&filter=name:eq:Child%20Programme" ) ) diff --git a/app/src/test/java/org/dhis2/fhir/adapter/fhir/server/r4/R4ProgramMetadataFhirRestAppTest.java b/app/src/test/java/org/dhis2/fhir/adapter/fhir/server/r4/R4ProgramMetadataFhirRestAppTest.java index 1402d045..695d91d2 100644 --- a/app/src/test/java/org/dhis2/fhir/adapter/fhir/server/r4/R4ProgramMetadataFhirRestAppTest.java +++ b/app/src/test/java/org/dhis2/fhir/adapter/fhir/server/r4/R4ProgramMetadataFhirRestAppTest.java @@ -78,7 +78,8 @@ public void getPlanDefinitionWithInvalidAuthorization() { userDhis2Server.expect( ExpectedCount.once(), method( HttpMethod.GET ) ).andExpect( header( "Authorization", "Basic Zmhpcl9jbGllbnQ6aW52YWxpZF8x" ) ) .andExpect( requestTo( dhis2BaseUrl + "/api/" + dhis2ApiVersion + "/programs/EPDyQuoRnXk.json?" + - "fields=id,name,code,description,selectIncidentDatesInFuture,selectEnrollmentDatesInFuture,displayIncidentDate,registration,withoutRegistration,captureCoordinates,trackedEntityType%5Bid%5D,programTrackedEntityAttributes%5Bid,name,valueType," + + "fields=id,name,code,description,lastUpdated,selectIncidentDatesInFuture,selectEnrollmentDatesInFuture,displayIncidentDate,registration,withoutRegistration,captureCoordinates,trackedEntityType%5Bid%5D,programTrackedEntityAttributes%5Bid," + + "name,valueType," + "mandatory,allowFutureDate,trackedEntityAttribute%5Bid,name,code,valueType,generated%5D%5D,programStages%5Bid,name,description,repeatable,captureCoordinates,generatedByEnrollmentDate,minDaysFromStart,programStageDataElements%5Bid,compulsory," + "allowProvidedElsewhere,dataElement%5Bid,name,code,formName,valueType,optionSetValue,optionSet%5Bid,name,options%5Bcode,name%5D%5D%5D%5D%5D" ) ) .andRespond( withStatus( HttpStatus.UNAUTHORIZED ) ); @@ -105,13 +106,13 @@ private void getPlanDefinition() throws Exception "fields=id,name,trackedEntityTypeAttributes%5Bid,name,valueType,mandatory,trackedEntityAttribute%5Bid,name,code,valueType,generated,optionSetValue,optionSet%5Bid,name,options%5Bcode,name%5D%5D%5D%5D" ) ) .andRespond( withSuccess( IOUtils.resourceToString( "/org/dhis2/fhir/adapter/dhis/test/single-tracked-entity-type.json", StandardCharsets.UTF_8 ), MediaType.APPLICATION_JSON ) ); userDhis2Server.expect( ExpectedCount.once(), method( HttpMethod.GET ) ).andExpect( header( "Authorization", "Basic Zmhpcl9jbGllbnQ6Zmhpcl9jbGllbnRfMQ==" ) ) - .andExpect( requestTo( dhis2BaseUrl + "/api/" + dhis2ApiVersion + "/programs/EPDyQuoRnXk.json?fields=id,name,code,description,selectIncidentDatesInFuture,selectEnrollmentDatesInFuture,displayIncidentDate,registration," + + .andExpect( requestTo( dhis2BaseUrl + "/api/" + dhis2ApiVersion + "/programs/EPDyQuoRnXk.json?fields=id,name,code,description,lastUpdated,selectIncidentDatesInFuture,selectEnrollmentDatesInFuture,displayIncidentDate,registration," + "withoutRegistration,captureCoordinates,trackedEntityType%5Bid%5D,programTrackedEntityAttributes%5Bid,name,valueType,mandatory,allowFutureDate,trackedEntityAttribute%5Bid,name,code,valueType,generated%5D%5D,programStages%5Bid,name," + "description,repeatable,captureCoordinates,generatedByEnrollmentDate,minDaysFromStart,programStageDataElements%5Bid,compulsory,allowProvidedElsewhere,dataElement%5Bid,name,code,formName,valueType,optionSetValue,optionSet%5Bid,name," + "options%5Bcode,name%5D%5D%5D%5D%5D" ) ) .andRespond( withSuccess( IOUtils.resourceToString( "/org/dhis2/fhir/adapter/dhis/test/single-program.json", StandardCharsets.UTF_8 ), MediaType.APPLICATION_JSON ) ); userDhis2Server.expect( ExpectedCount.between( 0, 1 ), method( HttpMethod.GET ) ).andExpect( header( "Authorization", "Basic Zmhpcl9jbGllbnQ6Zmhpcl9jbGllbnRfMQ==" ) ) - .andExpect( requestTo( dhis2BaseUrl + "/api/" + dhis2ApiVersion + "/programs.json?paging=false&fields=id,name,code,description,selectIncidentDatesInFuture,selectEnrollmentDatesInFuture,displayIncidentDate,registration," + + .andExpect( requestTo( dhis2BaseUrl + "/api/" + dhis2ApiVersion + "/programs.json?paging=false&fields=id,name,code,description,lastUpdated,selectIncidentDatesInFuture,selectEnrollmentDatesInFuture,displayIncidentDate,registration," + "withoutRegistration,captureCoordinates,trackedEntityType%5Bid%5D,programTrackedEntityAttributes%5Bid,name,valueType,mandatory,allowFutureDate,trackedEntityAttribute%5Bid,name,code,valueType,generated%5D%5D,programStages%5Bid,name," + "description,repeatable,captureCoordinates,generatedByEnrollmentDate,minDaysFromStart,programStageDataElements%5Bid,compulsory,allowProvidedElsewhere,dataElement%5Bid,name,code,formName,valueType,optionSetValue,optionSet%5Bid,name," + "options%5Bcode,name%5D%5D%5D%5D%5D" ) ) @@ -146,7 +147,7 @@ private void getPlanDefinitionNotExists() userDhis2Server.reset(); userDhis2Server.expect( ExpectedCount.once(), method( HttpMethod.GET ) ).andExpect( header( "Authorization", "Basic Zmhpcl9jbGllbnQ6Zmhpcl9jbGllbnRfMQ==" ) ) - .andExpect( requestTo( dhis2BaseUrl + "/api/" + dhis2ApiVersion + "/programs/0dXIdLNUNEn.json?fields=id,name,code,description,selectIncidentDatesInFuture,selectEnrollmentDatesInFuture,displayIncidentDate,registration," + + .andExpect( requestTo( dhis2BaseUrl + "/api/" + dhis2ApiVersion + "/programs/0dXIdLNUNEn.json?fields=id,name,code,description,lastUpdated,selectIncidentDatesInFuture,selectEnrollmentDatesInFuture,displayIncidentDate,registration," + "withoutRegistration,captureCoordinates,trackedEntityType%5Bid%5D,programTrackedEntityAttributes%5Bid,name,valueType,mandatory,allowFutureDate,trackedEntityAttribute%5Bid,name,code,valueType,generated%5D%5D,programStages%5Bid,name," + "description,repeatable,captureCoordinates,generatedByEnrollmentDate,minDaysFromStart,programStageDataElements%5Bid,compulsory,allowProvidedElsewhere,dataElement%5Bid,name,code,formName,valueType,optionSetValue,optionSet%5Bid,name," + "options%5Bcode,name%5D%5D%5D%5D%5D" ) ) @@ -210,7 +211,7 @@ public void getPlanDefinitionByIdentifierWithoutAuthorization() public void getPlanDefinitionByIdentifierInvalidAuthorization() { userDhis2Server.expect( ExpectedCount.once(), method( HttpMethod.GET ) ).andExpect( header( "Authorization", "Basic Zmhpcl9jbGllbnQ6aW52YWxpZF8x" ) ) - .andExpect( requestTo( dhis2BaseUrl + "/api/" + dhis2ApiVersion + "/programs.json?paging=false&fields=id,name,code,description,selectIncidentDatesInFuture,selectEnrollmentDatesInFuture,displayIncidentDate,registration,withoutRegistration," + + .andExpect( requestTo( dhis2BaseUrl + "/api/" + dhis2ApiVersion + "/programs.json?paging=false&fields=id,name,code,description,lastUpdated,selectIncidentDatesInFuture,selectEnrollmentDatesInFuture,displayIncidentDate,registration,withoutRegistration," + "captureCoordinates,trackedEntityType%5Bid%5D,programTrackedEntityAttributes%5Bid,name,valueType,mandatory,allowFutureDate,trackedEntityAttribute%5Bid,name,code,valueType,generated%5D%5D,programStages%5Bid,name,description,repeatable," + "captureCoordinates,generatedByEnrollmentDate,minDaysFromStart,programStageDataElements%5Bid,compulsory,allowProvidedElsewhere,dataElement%5Bid,name,code,formName,valueType,optionSetValue,optionSet%5Bid,name,options%5Bcode," + "name%5D%5D%5D%5D%5D&filter=code:eq:OU_1234" ) ) @@ -238,7 +239,7 @@ private void getPlanDefinitionByIdentifier() throws Exception "fields=id,name,trackedEntityTypeAttributes%5Bid,name,valueType,mandatory,trackedEntityAttribute%5Bid,name,code,valueType,generated,optionSetValue,optionSet%5Bid,name,options%5Bcode,name%5D%5D%5D%5D" ) ) .andRespond( withSuccess( IOUtils.resourceToString( "/org/dhis2/fhir/adapter/dhis/test/single-tracked-entity-type.json", StandardCharsets.UTF_8 ), MediaType.APPLICATION_JSON ) ); userDhis2Server.expect( ExpectedCount.between( 1, 2 ), method( HttpMethod.GET ) ).andExpect( header( "Authorization", "Basic Zmhpcl9jbGllbnQ6Zmhpcl9jbGllbnRfMQ==" ) ) - .andExpect( requestTo( dhis2BaseUrl + "/api/" + dhis2ApiVersion + "/programs.json?paging=false&fields=id,name,code,description,selectIncidentDatesInFuture,selectEnrollmentDatesInFuture,displayIncidentDate,registration,withoutRegistration," + + .andExpect( requestTo( dhis2BaseUrl + "/api/" + dhis2ApiVersion + "/programs.json?paging=false&fields=id,name,code,description,lastUpdated,selectIncidentDatesInFuture,selectEnrollmentDatesInFuture,displayIncidentDate,registration,withoutRegistration," + "captureCoordinates,trackedEntityType%5Bid%5D,programTrackedEntityAttributes%5Bid,name,valueType,mandatory,allowFutureDate,trackedEntityAttribute%5Bid,name,code,valueType,generated%5D%5D,programStages%5Bid,name,description,repeatable," + "captureCoordinates,generatedByEnrollmentDate,minDaysFromStart,programStageDataElements%5Bid,compulsory,allowProvidedElsewhere,dataElement%5Bid,name,code,formName,valueType,optionSetValue,optionSet%5Bid,name,options%5Bcode," + "name%5D%5D%5D%5D%5D&filter=code:eq:PD_1234" ) ) @@ -267,7 +268,7 @@ public void searchPlanDefinitionWithoutAuthorization() public void searchPlanDefinitionInvalidAuthorization() { userDhis2Server.expect( ExpectedCount.once(), method( HttpMethod.GET ) ).andExpect( header( "Authorization", "Basic Zmhpcl9jbGllbnQ6aW52YWxpZF8x" ) ) - .andExpect( requestTo( dhis2BaseUrl + "/api/" + dhis2ApiVersion + "/programs.json?filter=name:$ilike:Test&paging=true&page=1&pageSize=10&order=id&fields=id,name,code,description,selectIncidentDatesInFuture,selectEnrollmentDatesInFuture," + + .andExpect( requestTo( dhis2BaseUrl + "/api/" + dhis2ApiVersion + "/programs.json?filter=name:$ilike:Test&paging=true&page=1&pageSize=10&order=id&fields=id,name,code,description,lastUpdated,selectIncidentDatesInFuture,selectEnrollmentDatesInFuture," + "displayIncidentDate,registration,withoutRegistration,captureCoordinates,trackedEntityType%5Bid%5D,programTrackedEntityAttributes%5Bid,name,valueType,mandatory,allowFutureDate,trackedEntityAttribute%5Bid,name,code,valueType,generated%5D%5D," + "programStages%5Bid,name,description,repeatable,captureCoordinates,generatedByEnrollmentDate,minDaysFromStart,programStageDataElements%5Bid,compulsory,allowProvidedElsewhere,dataElement%5Bid,name,code,formName,valueType,optionSetValue," + "optionSet%5Bid,name,options%5Bcode,name%5D%5D%5D%5D%5D" ) ) @@ -286,7 +287,7 @@ public void searchPlanDefinition() throws Exception "fields=id,name,trackedEntityTypeAttributes%5Bid,name,valueType,mandatory,trackedEntityAttribute%5Bid,name,code,valueType,generated,optionSetValue,optionSet%5Bid,name,options%5Bcode,name%5D%5D%5D%5D" ) ) .andRespond( withSuccess( IOUtils.resourceToString( "/org/dhis2/fhir/adapter/dhis/test/single-tracked-entity-type.json", StandardCharsets.UTF_8 ), MediaType.APPLICATION_JSON ) ); userDhis2Server.expect( ExpectedCount.once(), method( HttpMethod.GET ) ).andExpect( header( "Authorization", "Basic Zmhpcl9jbGllbnQ6Zmhpcl9jbGllbnRfMQ==" ) ) - .andExpect( requestTo( dhis2BaseUrl + "/api/" + dhis2ApiVersion + "/programs.json?filter=name:$ilike:Child%20Programme&paging=true&page=1&pageSize=10&order=id&fields=id,name,code,description,selectIncidentDatesInFuture," + + .andExpect( requestTo( dhis2BaseUrl + "/api/" + dhis2ApiVersion + "/programs.json?filter=name:$ilike:Child%20Programme&paging=true&page=1&pageSize=10&order=id&fields=id,name,code,description,lastUpdated,selectIncidentDatesInFuture," + "selectEnrollmentDatesInFuture,displayIncidentDate,registration,withoutRegistration,captureCoordinates,trackedEntityType%5Bid%5D,programTrackedEntityAttributes%5Bid,name,valueType,mandatory,allowFutureDate,trackedEntityAttribute%5Bid,name," + "code,valueType,generated%5D%5D,programStages%5Bid,name,description,repeatable,captureCoordinates,generatedByEnrollmentDate,minDaysFromStart,programStageDataElements%5Bid,compulsory,allowProvidedElsewhere,dataElement%5Bid,name,code,formName," + "valueType,optionSetValue,optionSet%5Bid,name,options%5Bcode,name%5D%5D%5D%5D%5D" ) ) diff --git a/app/src/test/java/org/dhis2/fhir/adapter/fhir/transform/fhir/impl/enrollment/AbstractEnrollmentTransformationAppTest.java b/app/src/test/java/org/dhis2/fhir/adapter/fhir/transform/fhir/impl/enrollment/AbstractEnrollmentTransformationAppTest.java index f96cac5b..7b0009fc 100644 --- a/app/src/test/java/org/dhis2/fhir/adapter/fhir/transform/fhir/impl/enrollment/AbstractEnrollmentTransformationAppTest.java +++ b/app/src/test/java/org/dhis2/fhir/adapter/fhir/transform/fhir/impl/enrollment/AbstractEnrollmentTransformationAppTest.java @@ -49,13 +49,14 @@ public abstract class AbstractEnrollmentTransformationAppTest extends AbstractAp protected void expectMetadataRequests() throws Exception { systemDhis2Server.expect( ExpectedCount.between( 0, 1 ), method( HttpMethod.GET ) ).andExpect( header( "Authorization", testConfiguration.getDhis2SystemAuthorization() ) ) - .andExpect( requestTo( dhis2BaseUrl + "/api/" + dhis2ApiVersion + "/programs/EPDyQuoRnXk.json?fields=id,name,code,description,selectIncidentDatesInFuture,selectEnrollmentDatesInFuture,displayIncidentDate,registration," + + .andExpect( requestTo( dhis2BaseUrl + "/api/" + dhis2ApiVersion + "/programs/EPDyQuoRnXk.json?fields=id,name,code,description,lastUpdated,selectIncidentDatesInFuture,selectEnrollmentDatesInFuture,displayIncidentDate,registration," + "withoutRegistration,captureCoordinates,trackedEntityType%5Bid%5D,programTrackedEntityAttributes%5Bid,name,valueType,mandatory,allowFutureDate,trackedEntityAttribute%5Bid,name,code,valueType,generated%5D%5D,programStages%5Bid,name," + "description,repeatable,captureCoordinates,generatedByEnrollmentDate,minDaysFromStart,programStageDataElements%5Bid,compulsory,allowProvidedElsewhere,dataElement%5Bid,name,code,formName,valueType,optionSetValue,optionSet%5Bid,name," + "options%5Bcode,name%5D%5D%5D%5D%5D" ) ) .andRespond( withSuccess( IOUtils.resourceToString( "/org/dhis2/fhir/adapter/dhis/test/single-program.json", StandardCharsets.UTF_8 ), MediaType.APPLICATION_JSON ) ); systemDhis2Server.expect( ExpectedCount.between( 0, 1 ), method( HttpMethod.GET ) ).andExpect( header( "Authorization", testConfiguration.getDhis2SystemAuthorization() ) ) - .andExpect( requestTo( dhis2BaseUrl + "/api/" + dhis2ApiVersion + "/programs.json?paging=false&fields=id,name,code,description,selectIncidentDatesInFuture,selectEnrollmentDatesInFuture,displayIncidentDate,registration,withoutRegistration," + + .andExpect( requestTo( dhis2BaseUrl + "/api/" + dhis2ApiVersion + "/programs.json?paging=false&fields=id,name,code,description,lastUpdated,selectIncidentDatesInFuture,selectEnrollmentDatesInFuture,displayIncidentDate,registration," + + "withoutRegistration," + "captureCoordinates," + "trackedEntityType%5Bid%5D,programTrackedEntityAttributes%5Bid,name,valueType,mandatory,allowFutureDate,trackedEntityAttribute%5Bid,name,code,valueType,generated%5D%5D,programStages%5Bid,name,description,repeatable,captureCoordinates," + "generatedByEnrollmentDate,minDaysFromStart,programStageDataElements%5Bid,compulsory,allowProvidedElsewhere,dataElement%5Bid,name,code,description,formName,valueType,optionSetValue,optionSet%5Bid,name,options%5Bcode," + @@ -74,7 +75,7 @@ protected void expectMetadataRequests() throws Exception .andRespond( withSuccess( IOUtils.resourceToString( "/org/dhis2/fhir/adapter/dhis/test/default-tracked-entity-type.json", StandardCharsets.UTF_8 ), MediaType.APPLICATION_JSON ) ); systemDhis2Server.expect( ExpectedCount.between( 0, 1 ), method( HttpMethod.GET ) ).andExpect( header( "Authorization", testConfiguration.getDhis2SystemAuthorization() ) ) .andExpect( requestTo( dhis2BaseUrl + "/api/" + dhis2ApiVersion + "/programs.json?paging=false&" + - "fields=id,name,code,description,selectIncidentDatesInFuture,selectEnrollmentDatesInFuture,displayIncidentDate,registration,withoutRegistration,captureCoordinates,trackedEntityType%5Bid%5D,programTrackedEntityAttributes%5Bid,name,valueType," + + "fields=id,name,code,description,lastUpdated,selectIncidentDatesInFuture,selectEnrollmentDatesInFuture,displayIncidentDate,registration,withoutRegistration,captureCoordinates,trackedEntityType%5Bid%5D,programTrackedEntityAttributes%5Bid,name,valueType," + "mandatory,allowFutureDate,trackedEntityAttribute%5Bid,name,code,valueType,generated%5D%5D,programStages%5Bid,name,description,repeatable,captureCoordinates,generatedByEnrollmentDate,minDaysFromStart,programStageDataElements%5Bid,compulsory," + "allowProvidedElsewhere,dataElement%5Bid,name,code,description,formName,valueType,optionSetValue,optionSet%5Bid,name,options%5Bcode,name%5D%5D%5D%5D%5D&filter=name:eq:Child%20Programme" ) ) .andRespond( withSuccess( IOUtils.resourceToString( "/org/dhis2/fhir/adapter/dhis/test/single-program.json", StandardCharsets.UTF_8 ), MediaType.APPLICATION_JSON ) ); diff --git a/app/src/test/java/org/dhis2/fhir/adapter/fhir/transform/fhir/impl/programstage/AbstractProgramStageTransformationAppTest.java b/app/src/test/java/org/dhis2/fhir/adapter/fhir/transform/fhir/impl/programstage/AbstractProgramStageTransformationAppTest.java index b104bc05..742bfca8 100644 --- a/app/src/test/java/org/dhis2/fhir/adapter/fhir/transform/fhir/impl/programstage/AbstractProgramStageTransformationAppTest.java +++ b/app/src/test/java/org/dhis2/fhir/adapter/fhir/transform/fhir/impl/programstage/AbstractProgramStageTransformationAppTest.java @@ -50,7 +50,8 @@ public abstract class AbstractProgramStageTransformationAppTest extends Abstract protected void expectMetadataRequests() throws Exception { systemDhis2Server.expect( ExpectedCount.between( 0, 1 ), method( HttpMethod.GET ) ).andExpect( header( "Authorization", testConfiguration.getDhis2SystemAuthorization() ) ) - .andExpect( requestTo( dhis2BaseUrl + "/api/" + dhis2ApiVersion + "/programs.json?paging=false&fields=id,name,code,description,selectIncidentDatesInFuture,selectEnrollmentDatesInFuture,displayIncidentDate,registration,withoutRegistration," + + .andExpect( requestTo( dhis2BaseUrl + "/api/" + dhis2ApiVersion + "/programs.json?paging=false&fields=id,name,code,description,lastUpdated,selectIncidentDatesInFuture,selectEnrollmentDatesInFuture,displayIncidentDate,registration," + + "withoutRegistration," + "captureCoordinates,trackedEntityType%5Bid%5D,programTrackedEntityAttributes%5Bid,name,valueType,mandatory,allowFutureDate,trackedEntityAttribute%5Bid,name,code,valueType,generated%5D%5D,programStages%5Bid,name,description,repeatable," + "captureCoordinates," + "generatedByEnrollmentDate,minDaysFromStart,programStageDataElements%5Bid,compulsory,allowProvidedElsewhere,dataElement%5Bid,name,code,formName,valueType,optionSetValue,optionSet%5Bid,name,options%5Bcode," + @@ -69,7 +70,7 @@ protected void expectMetadataRequests() throws Exception .andRespond( withSuccess( IOUtils.resourceToString( "/org/dhis2/fhir/adapter/dhis/test/default-tracked-entity-type.json", StandardCharsets.UTF_8 ), MediaType.APPLICATION_JSON ) ); systemDhis2Server.expect( ExpectedCount.between( 0, 1 ), method( HttpMethod.GET ) ).andExpect( header( "Authorization", testConfiguration.getDhis2SystemAuthorization() ) ) .andExpect( requestTo( dhis2BaseUrl + "/api/" + dhis2ApiVersion + "/programs.json?paging=false&" + - "fields=id,name,code,description,selectIncidentDatesInFuture,selectEnrollmentDatesInFuture,displayIncidentDate,registration,withoutRegistration,captureCoordinates,trackedEntityType%5Bid%5D,programTrackedEntityAttributes%5Bid,name,valueType," + + "fields=id,name,code,description,lastUpdated,selectIncidentDatesInFuture,selectEnrollmentDatesInFuture,displayIncidentDate,registration,withoutRegistration,captureCoordinates,trackedEntityType%5Bid%5D,programTrackedEntityAttributes%5Bid,name,valueType," + "mandatory,allowFutureDate,trackedEntityAttribute%5Bid,name,code,valueType,generated%5D%5D,programStages%5Bid,name,description,repeatable,captureCoordinates,generatedByEnrollmentDate,minDaysFromStart,programStageDataElements%5Bid,compulsory," + "allowProvidedElsewhere,dataElement%5Bid,name,code,description,formName,valueType,optionSetValue,optionSet%5Bid,name,options%5Bcode,name%5D%5D%5D%5D%5D&filter=name:eq:Child%20Programme" ) ) .andRespond( withSuccess( IOUtils.resourceToString( "/org/dhis2/fhir/adapter/dhis/test/single-program.json", StandardCharsets.UTF_8 ), MediaType.APPLICATION_JSON ) ); diff --git a/dhis/src/main/java/org/dhis2/fhir/adapter/dhis/model/DhisResourceType.java b/dhis/src/main/java/org/dhis2/fhir/adapter/dhis/model/DhisResourceType.java index 80d8238f..30e669e9 100644 --- a/dhis/src/main/java/org/dhis2/fhir/adapter/dhis/model/DhisResourceType.java +++ b/dhis/src/main/java/org/dhis2/fhir/adapter/dhis/model/DhisResourceType.java @@ -36,20 +36,16 @@ /** * Contains the different types of DHIS2 Resources that are can be created. + * The enum values should be listed in the order of the dependencies. * * @author volsch */ public enum DhisResourceType { /** - * Resource is a tracked entity instance. - */ - TRACKED_ENTITY( "trackedEntityInstances", "te", "TrackedEntityRule" ), - - /** - * Resource is a tracked entity type. + * Resource is a organisation unit. */ - TRACKED_ENTITY_TYPE( "trackedEntityTypes", "tt", "TrackedEntityTypeRule" ), + ORGANIZATION_UNIT( "organisationUnits", "ou", "OrganizationUnitRule" ), /** * The program metadata. @@ -61,20 +57,26 @@ public enum DhisResourceType */ PROGRAM_STAGE_METADATA( "programStages", "sm", "ProgramStageMetadataRule" ), + /** - * Resource is a program instance (aka enrollment). + * Resource is a tracked entity type. */ - ENROLLMENT( "enrollments", "en", "EnrollmentRule" ), + TRACKED_ENTITY_TYPE( "trackedEntityTypes", "tt", "TrackedEntityTypeRule" ), /** - * Resource is a program stage instance (aka event of a program instance). + * Resource is a tracked entity instance. */ - PROGRAM_STAGE_EVENT( "events", "ps", "ProgramStageRule" ), + TRACKED_ENTITY( "trackedEntityInstances", "te", "TrackedEntityRule" ), /** - * Resource is a organisation unit. + * Resource is a program instance (aka enrollment). + */ + ENROLLMENT( "enrollments", "en", "EnrollmentRule" ), + + /** + * Resource is a program stage instance (aka event of a program instance). */ - ORGANIZATION_UNIT( "organisationUnits", "ou", "OrganizationUnitRule" ); + PROGRAM_STAGE_EVENT( "events", "ps", "ProgramStageRule" ); private static final Map byTypeName = Arrays.stream( values() ).collect( Collectors.toMap( DhisResourceType::getTypeName, v -> v ) ); diff --git a/dhis/src/main/java/org/dhis2/fhir/adapter/dhis/orgunit/impl/OrganizationUnitServiceImpl.java b/dhis/src/main/java/org/dhis2/fhir/adapter/dhis/orgunit/impl/OrganizationUnitServiceImpl.java index c0839e5d..da9839d4 100644 --- a/dhis/src/main/java/org/dhis2/fhir/adapter/dhis/orgunit/impl/OrganizationUnitServiceImpl.java +++ b/dhis/src/main/java/org/dhis2/fhir/adapter/dhis/orgunit/impl/OrganizationUnitServiceImpl.java @@ -58,7 +58,7 @@ public OrganizationUnitServiceImpl( @Nonnull @Qualifier( "systemDhis2RestTemplat @Nonnull @Override - protected DhisResourceType getDhisResourceType() + public DhisResourceType getDhisResourceType() { return DhisResourceType.ORGANIZATION_UNIT; } diff --git a/dhis/src/main/java/org/dhis2/fhir/adapter/dhis/service/DhisMetadataService.java b/dhis/src/main/java/org/dhis2/fhir/adapter/dhis/service/DhisMetadataService.java index e36d5ce3..2e2e08ae 100644 --- a/dhis/src/main/java/org/dhis2/fhir/adapter/dhis/service/DhisMetadataService.java +++ b/dhis/src/main/java/org/dhis2/fhir/adapter/dhis/service/DhisMetadataService.java @@ -28,8 +28,6 @@ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -import org.dhis2.fhir.adapter.data.model.ProcessedItemInfo; -import org.dhis2.fhir.adapter.dhis.metadata.model.DhisSyncGroup; import org.dhis2.fhir.adapter.dhis.model.DhisMetadata; import org.dhis2.fhir.adapter.dhis.model.DhisResource; import org.dhis2.fhir.adapter.dhis.model.DhisResourceResult; @@ -37,11 +35,7 @@ import org.dhis2.fhir.adapter.dhis.model.UriFilterApplier; import javax.annotation.Nonnull; -import java.time.Instant; -import java.util.Collection; import java.util.Optional; -import java.util.Set; -import java.util.function.Consumer; /** * Service that provides access to DHIS2 metadata. @@ -49,7 +43,7 @@ * @param the concrete type of the metadata. * @author volsch */ -public interface DhisMetadataService +public interface DhisMetadataService extends DhisPolledService, DhisService { @Nonnull Optional findMetadataByReference( @Nonnull Reference reference ); @@ -62,8 +56,4 @@ public interface DhisMetadataService @Nonnull DhisResourceResult find( @Nonnull UriFilterApplier uriFilterApplier, int from, int max ); - - @Nonnull - Instant poll( @Nonnull DhisSyncGroup group, @Nonnull Instant lastUpdated, int toleranceMillis, - int maxSearchCount, @Nonnull Set excludedStoredBy, @Nonnull Consumer> consumer ); } diff --git a/dhis/src/main/java/org/dhis2/fhir/adapter/dhis/service/DhisPolledService.java b/dhis/src/main/java/org/dhis2/fhir/adapter/dhis/service/DhisPolledService.java new file mode 100644 index 00000000..87a7481f --- /dev/null +++ b/dhis/src/main/java/org/dhis2/fhir/adapter/dhis/service/DhisPolledService.java @@ -0,0 +1,52 @@ +package org.dhis2.fhir.adapter.dhis.service; + +/* + * Copyright (c) 2004-2019, University of Oslo + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * Neither the name of the HISP project nor the names of its contributors may + * be used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +import org.dhis2.fhir.adapter.data.model.ProcessedItemInfo; +import org.dhis2.fhir.adapter.dhis.metadata.model.DhisSyncGroup; +import org.dhis2.fhir.adapter.dhis.model.DhisResource; + +import javax.annotation.Nonnull; +import java.time.Instant; +import java.util.Collection; +import java.util.Set; +import java.util.function.Consumer; + +/** + * Service that provides polled access to DHIS2 data. + * + * @param the concrete type of the data. + * @author volsch + */ +public interface DhisPolledService extends DhisService +{ + @Nonnull + Instant poll( @Nonnull DhisSyncGroup group, @Nonnull Instant lastUpdated, int toleranceMillis, + int maxSearchCount, @Nonnull Set excludedStoredBy, @Nonnull Consumer> consumer ); +} diff --git a/dhis/src/main/java/org/dhis2/fhir/adapter/dhis/service/DhisService.java b/dhis/src/main/java/org/dhis2/fhir/adapter/dhis/service/DhisService.java new file mode 100644 index 00000000..9eaf55ba --- /dev/null +++ b/dhis/src/main/java/org/dhis2/fhir/adapter/dhis/service/DhisService.java @@ -0,0 +1,46 @@ +package org.dhis2.fhir.adapter.dhis.service; + +/* + * Copyright (c) 2004-2019, University of Oslo + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * Neither the name of the HISP project nor the names of its contributors may + * be used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +import org.dhis2.fhir.adapter.dhis.model.DhisResource; +import org.dhis2.fhir.adapter.dhis.model.DhisResourceType; + +import javax.annotation.Nonnull; + +/** + * Service that provides access to DHIS2 data. + * + * @param the concrete type of the DHIS2 resource. + * @author volsch + */ +public interface DhisService +{ + @Nonnull + DhisResourceType getDhisResourceType(); +} diff --git a/dhis/src/main/java/org/dhis2/fhir/adapter/dhis/service/impl/AbstractDhisMetadataServiceImpl.java b/dhis/src/main/java/org/dhis2/fhir/adapter/dhis/service/impl/AbstractDhisMetadataServiceImpl.java index 767102db..fc466c7b 100644 --- a/dhis/src/main/java/org/dhis2/fhir/adapter/dhis/service/impl/AbstractDhisMetadataServiceImpl.java +++ b/dhis/src/main/java/org/dhis2/fhir/adapter/dhis/service/impl/AbstractDhisMetadataServiceImpl.java @@ -91,7 +91,7 @@ protected AbstractDhisMetadataServiceImpl( @Nonnull @Qualifier( "systemDhis2Rest } @Nonnull - protected abstract DhisResourceType getDhisResourceType(); + public abstract DhisResourceType getDhisResourceType(); @Nonnull protected abstract Class getItemClass(); diff --git a/dhis/src/main/java/org/dhis2/fhir/adapter/dhis/sync/impl/DhisDataProcessorItemRetrieverImpl.java b/dhis/src/main/java/org/dhis2/fhir/adapter/dhis/sync/impl/DhisDataProcessorItemRetrieverImpl.java index 6a655afa..9f59da2c 100644 --- a/dhis/src/main/java/org/dhis2/fhir/adapter/dhis/sync/impl/DhisDataProcessorItemRetrieverImpl.java +++ b/dhis/src/main/java/org/dhis2/fhir/adapter/dhis/sync/impl/DhisDataProcessorItemRetrieverImpl.java @@ -36,20 +36,25 @@ import org.dhis2.fhir.adapter.data.processor.DataProcessorItemRetriever; import org.dhis2.fhir.adapter.dhis.config.DhisConfig; import org.dhis2.fhir.adapter.dhis.metadata.model.DhisSyncGroup; +import org.dhis2.fhir.adapter.dhis.model.DhisResource; import org.dhis2.fhir.adapter.dhis.model.DhisResourceType; -import org.dhis2.fhir.adapter.dhis.orgunit.OrganizationUnitService; +import org.dhis2.fhir.adapter.dhis.service.DhisPolledService; +import org.dhis2.fhir.adapter.dhis.service.DhisService; import org.dhis2.fhir.adapter.dhis.sync.SyncExcludedDhisUsernameRetriever; -import org.dhis2.fhir.adapter.dhis.tracker.program.EventService; -import org.dhis2.fhir.adapter.dhis.tracker.program.ProgramMetadataService; -import org.dhis2.fhir.adapter.dhis.tracker.trackedentity.TrackedEntityService; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.stereotype.Component; import javax.annotation.Nonnull; import java.time.Instant; import java.util.Collection; +import java.util.List; +import java.util.Map; +import java.util.Objects; import java.util.Set; +import java.util.concurrent.atomic.AtomicReference; import java.util.function.Consumer; +import java.util.stream.Collectors; +import java.util.stream.Stream; /** * The item retriever that polls DHIS2 resources. All relevant resources are @@ -66,13 +71,7 @@ public class DhisDataProcessorItemRetrieverImpl implements DataProcessorItemRetr private final Authorization systemDhis2Authorization; - private final OrganizationUnitService organizationUnitService; - - private final ProgramMetadataService programMetadataService; - - private final TrackedEntityService trackedEntityService; - - private final EventService eventService; + private final Map> polledServices; private final DhisSyncProcessorConfig processorConfig; @@ -80,20 +79,14 @@ public DhisDataProcessorItemRetrieverImpl( @Nonnull AuthorizationContext authorizationContext, @Nonnull @Qualifier( "systemDhis2Authorization" ) Authorization systemDhis2Authorization, @Nonnull SyncExcludedDhisUsernameRetriever excludedDhisUsernameRetriever, - @Nonnull OrganizationUnitService organizationUnitService, - @Nonnull ProgramMetadataService programMetadataService, - @Nonnull TrackedEntityService trackedEntityService, - @Nonnull EventService eventService, + @Nonnull List> polledServices, @Nonnull DhisSyncProcessorConfig processorConfig, @Nonnull DhisConfig config ) { this.authorizationContext = authorizationContext; this.systemDhis2Authorization = systemDhis2Authorization; this.excludedDhisUsernameRetriever = excludedDhisUsernameRetriever; - this.trackedEntityService = trackedEntityService; - this.eventService = eventService; - this.organizationUnitService = organizationUnitService; - this.programMetadataService = programMetadataService; + this.polledServices = polledServices.stream().collect( Collectors.toMap( DhisService::getDhisResourceType, ps -> ps ) ); this.processorConfig = processorConfig; } @@ -103,38 +96,21 @@ public Instant poll( @Nonnull DhisSyncGroup group, @Nonnull Instant lastUpdated, { final int toleranceMillis = processorConfig.getToleranceMillis(); final Set resourceTypes = processorConfig.getResourceTypes(); + authorizationContext.setAuthorization( systemDhis2Authorization ); try { final Set excludedDhisUsernames = excludedDhisUsernameRetriever.findAllDhisUsernames(); - Instant result = Instant.now(); + final AtomicReference result = new AtomicReference<>( Instant.now() ); - if ( resourceTypes.contains( DhisResourceType.ORGANIZATION_UNIT ) ) - { - final Instant currentResult = organizationUnitService.poll( group, lastUpdated, toleranceMillis, maxSearchCount, - excludedDhisUsernames, consumer ); - result = ObjectUtils.min( result, currentResult ); - } - if ( resourceTypes.contains( DhisResourceType.PROGRAM_METADATA ) ) - { - final Instant currentResult = programMetadataService.poll( group, lastUpdated, toleranceMillis, maxSearchCount, - excludedDhisUsernames, consumer ); - result = ObjectUtils.min( result, currentResult ); - } - if ( resourceTypes.contains( DhisResourceType.TRACKED_ENTITY ) ) - { - final Instant currentResult = trackedEntityService.poll( group, lastUpdated, toleranceMillis, maxSearchCount, - excludedDhisUsernames, consumer ); - result = ObjectUtils.min( result, currentResult ); - } - if ( resourceTypes.contains( DhisResourceType.PROGRAM_STAGE_EVENT ) ) - { - final Instant currentResult = eventService.poll( group, lastUpdated, toleranceMillis, maxSearchCount, - excludedDhisUsernames, consumer ); - result = ObjectUtils.min( result, currentResult ); - } + Stream.of( DhisResourceType.values() ).filter( resourceTypes::contains ).map( polledServices::get ).filter( Objects::nonNull ) + .forEach( polledService -> { + final Instant currentResult = polledService.poll( group, lastUpdated, toleranceMillis, maxSearchCount, + excludedDhisUsernames, consumer ); + result.set( ObjectUtils.min( result.get(), currentResult ) ); + } ); - return result; + return result.get(); } finally { diff --git a/dhis/src/main/java/org/dhis2/fhir/adapter/dhis/tracker/program/EnrollmentService.java b/dhis/src/main/java/org/dhis2/fhir/adapter/dhis/tracker/program/EnrollmentService.java index 4326ea09..fa3db68c 100644 --- a/dhis/src/main/java/org/dhis2/fhir/adapter/dhis/tracker/program/EnrollmentService.java +++ b/dhis/src/main/java/org/dhis2/fhir/adapter/dhis/tracker/program/EnrollmentService.java @@ -28,6 +28,8 @@ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ +import org.dhis2.fhir.adapter.dhis.service.DhisService; + import javax.annotation.Nonnull; import java.util.Optional; @@ -38,7 +40,7 @@ * @author volsch * @author Charles Chigoriwa (ITINORDIC) */ -public interface EnrollmentService +public interface EnrollmentService extends DhisService { @Nonnull Optional findLatestActiveRefreshed( @Nonnull String programId, @Nonnull String trackedEntityInstanceId, boolean localOnly ); diff --git a/dhis/src/main/java/org/dhis2/fhir/adapter/dhis/tracker/program/EventService.java b/dhis/src/main/java/org/dhis2/fhir/adapter/dhis/tracker/program/EventService.java index 99582bae..80215e74 100644 --- a/dhis/src/main/java/org/dhis2/fhir/adapter/dhis/tracker/program/EventService.java +++ b/dhis/src/main/java/org/dhis2/fhir/adapter/dhis/tracker/program/EventService.java @@ -28,17 +28,14 @@ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -import org.dhis2.fhir.adapter.data.model.ProcessedItemInfo; -import org.dhis2.fhir.adapter.dhis.metadata.model.DhisSyncGroup; import org.dhis2.fhir.adapter.dhis.model.DhisResourceResult; import org.dhis2.fhir.adapter.dhis.model.UriFilterApplier; +import org.dhis2.fhir.adapter.dhis.service.DhisPolledService; +import org.dhis2.fhir.adapter.dhis.service.DhisService; import javax.annotation.Nonnull; -import java.time.Instant; import java.util.Collection; import java.util.Optional; -import java.util.Set; -import java.util.function.Consumer; /** * Service to create, update and read DHIS2 Program Stage Instances (aka events) @@ -46,7 +43,7 @@ * * @author volsch */ -public interface EventService +public interface EventService extends DhisService, DhisPolledService { @Nonnull Collection findRefreshed( @Nonnull String programId, @Nonnull String programStageId, @@ -69,8 +66,4 @@ Collection find( @Nonnull String programId, @Nonnull String programStageI @Nonnull DhisResourceResult find( @Nonnull String programId, @Nonnull String programStageId, @Nonnull UriFilterApplier uriFilterApplier, int from, int max ); - - @Nonnull - Instant poll( @Nonnull DhisSyncGroup group, @Nonnull Instant lastUpdated, int toleranceMillis, - int maxSearchCount, @Nonnull Set excludedStoredBy, @Nonnull Consumer> consumer ); } diff --git a/dhis/src/main/java/org/dhis2/fhir/adapter/dhis/tracker/program/WritableProgram.java b/dhis/src/main/java/org/dhis2/fhir/adapter/dhis/tracker/program/WritableProgram.java index b6327539..457168ae 100644 --- a/dhis/src/main/java/org/dhis2/fhir/adapter/dhis/tracker/program/WritableProgram.java +++ b/dhis/src/main/java/org/dhis2/fhir/adapter/dhis/tracker/program/WritableProgram.java @@ -50,6 +50,8 @@ public class WritableProgram extends AbstractDhisMetadata implements Program, Se { private static final long serialVersionUID = -4906529875383953995L; + private ZonedDateTime lastUpdated; + @JsonInclude( JsonInclude.Include.NON_NULL ) private String id; @@ -294,7 +296,7 @@ public boolean isDeleted() @Override public ZonedDateTime getLastUpdated() { - return null; + return lastUpdated; } @Override diff --git a/dhis/src/main/java/org/dhis2/fhir/adapter/dhis/tracker/program/impl/EnrollmentServiceImpl.java b/dhis/src/main/java/org/dhis2/fhir/adapter/dhis/tracker/program/impl/EnrollmentServiceImpl.java index 1e33b3a5..b5067391 100644 --- a/dhis/src/main/java/org/dhis2/fhir/adapter/dhis/tracker/program/impl/EnrollmentServiceImpl.java +++ b/dhis/src/main/java/org/dhis2/fhir/adapter/dhis/tracker/program/impl/EnrollmentServiceImpl.java @@ -39,6 +39,7 @@ import org.dhis2.fhir.adapter.dhis.local.LocalDhisRepositoryPersistStatus; import org.dhis2.fhir.adapter.dhis.local.LocalDhisResourceRepositoryTemplate; import org.dhis2.fhir.adapter.dhis.model.DhisResourceComparator; +import org.dhis2.fhir.adapter.dhis.model.DhisResourceType; import org.dhis2.fhir.adapter.dhis.model.ImportStatus; import org.dhis2.fhir.adapter.dhis.model.ImportSummaries; import org.dhis2.fhir.adapter.dhis.model.ImportSummary; @@ -117,6 +118,13 @@ public EnrollmentServiceImpl( @Nonnull @Qualifier( "userDhis2RestTemplate" ) Res this.resourceRepositoryTemplate = new LocalDhisResourceRepositoryTemplate<>( Enrollment.class, requestCacheService, this ); } + @Nonnull + @Override + public DhisResourceType getDhisResourceType() + { + return DhisResourceType.ENROLLMENT; + } + @HystrixCommand( ignoreExceptions = UnauthorizedException.class ) @Nonnull @Override diff --git a/dhis/src/main/java/org/dhis2/fhir/adapter/dhis/tracker/program/impl/EventServiceImpl.java b/dhis/src/main/java/org/dhis2/fhir/adapter/dhis/tracker/program/impl/EventServiceImpl.java index fc9b8fc7..5799e5b4 100644 --- a/dhis/src/main/java/org/dhis2/fhir/adapter/dhis/tracker/program/impl/EventServiceImpl.java +++ b/dhis/src/main/java/org/dhis2/fhir/adapter/dhis/tracker/program/impl/EventServiceImpl.java @@ -45,6 +45,7 @@ import org.dhis2.fhir.adapter.dhis.model.DataValue; import org.dhis2.fhir.adapter.dhis.model.DhisResourceComparator; import org.dhis2.fhir.adapter.dhis.model.DhisResourceResult; +import org.dhis2.fhir.adapter.dhis.model.DhisResourceType; import org.dhis2.fhir.adapter.dhis.model.ImportStatus; import org.dhis2.fhir.adapter.dhis.model.ImportSummary; import org.dhis2.fhir.adapter.dhis.model.ImportSummaryWebMessage; @@ -136,6 +137,13 @@ public EventServiceImpl( @Nonnull @Qualifier( "userDhis2RestTemplate" ) RestTemp this.resourceRepositoryTemplate = new LocalDhisResourceRepositoryTemplate<>( Event.class, requestCacheService, this ); } + @Nonnull + @Override + public DhisResourceType getDhisResourceType() + { + return DhisResourceType.PROGRAM_STAGE_EVENT; + } + @HystrixCommand( ignoreExceptions = { DhisConflictException.class, UnauthorizedException.class } ) @Override public boolean delete( @Nonnull String eventId ) diff --git a/dhis/src/main/java/org/dhis2/fhir/adapter/dhis/tracker/program/impl/ProgramMetadataServiceImpl.java b/dhis/src/main/java/org/dhis2/fhir/adapter/dhis/tracker/program/impl/ProgramMetadataServiceImpl.java index 070f1d71..a17179c8 100644 --- a/dhis/src/main/java/org/dhis2/fhir/adapter/dhis/tracker/program/impl/ProgramMetadataServiceImpl.java +++ b/dhis/src/main/java/org/dhis2/fhir/adapter/dhis/tracker/program/impl/ProgramMetadataServiceImpl.java @@ -49,7 +49,7 @@ public class ProgramMetadataServiceImpl extends AbstractDhisMetadataServiceImpl implements ProgramMetadataService { protected static final String FIELDS = - "id,name,code,description,selectIncidentDatesInFuture,selectEnrollmentDatesInFuture,displayIncidentDate," + + "id,name,code,description,lastUpdated,selectIncidentDatesInFuture,selectEnrollmentDatesInFuture,displayIncidentDate," + "registration,withoutRegistration,captureCoordinates,trackedEntityType[id]," + "programTrackedEntityAttributes[id,name,valueType,mandatory,allowFutureDate," + "trackedEntityAttribute[id,name,code,valueType,generated]]," + @@ -64,7 +64,7 @@ public ProgramMetadataServiceImpl( @Nonnull @Qualifier( "systemDhis2RestTemplate @Nonnull @Override - protected DhisResourceType getDhisResourceType() + public DhisResourceType getDhisResourceType() { return DhisResourceType.PROGRAM_METADATA; } diff --git a/dhis/src/main/java/org/dhis2/fhir/adapter/dhis/tracker/trackedentity/TrackedEntityService.java b/dhis/src/main/java/org/dhis2/fhir/adapter/dhis/tracker/trackedentity/TrackedEntityService.java index 34688f99..c8348eac 100644 --- a/dhis/src/main/java/org/dhis2/fhir/adapter/dhis/tracker/trackedentity/TrackedEntityService.java +++ b/dhis/src/main/java/org/dhis2/fhir/adapter/dhis/tracker/trackedentity/TrackedEntityService.java @@ -28,25 +28,22 @@ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -import org.dhis2.fhir.adapter.data.model.ProcessedItemInfo; -import org.dhis2.fhir.adapter.dhis.metadata.model.DhisSyncGroup; import org.dhis2.fhir.adapter.dhis.model.DhisResourceResult; import org.dhis2.fhir.adapter.dhis.model.UriFilterApplier; +import org.dhis2.fhir.adapter.dhis.service.DhisPolledService; +import org.dhis2.fhir.adapter.dhis.service.DhisService; import javax.annotation.Nonnull; -import java.time.Instant; import java.util.Collection; import java.util.Map; import java.util.Optional; -import java.util.Set; -import java.util.function.Consumer; /** * Service that allows to create, read and update tracked entity instances. * * @author volsch */ -public interface TrackedEntityService +public interface TrackedEntityService extends DhisService, DhisPolledService { void updateGeneratedValues( @Nonnull TrackedEntityInstance trackedEntityInstance, @Nonnull TrackedEntityType type, @Nonnull Map requiredValues ); @@ -74,8 +71,4 @@ Collection findByAttrValue( @Nonnull String typeId, @Nonnull DhisResourceResult find( @Nonnull String trackedEntityTypeId, @Nonnull UriFilterApplier uriFilterApplier, int from, int max ); - - @Nonnull - Instant poll( @Nonnull DhisSyncGroup group, @Nonnull Instant lastUpdated, int toleranceMillis, - int maxSearchCount, @Nonnull Set excludedStoredBy, @Nonnull Consumer> consumer ); } diff --git a/dhis/src/main/java/org/dhis2/fhir/adapter/dhis/tracker/trackedentity/impl/TrackedEntityServiceImpl.java b/dhis/src/main/java/org/dhis2/fhir/adapter/dhis/tracker/trackedentity/impl/TrackedEntityServiceImpl.java index beda50e6..7c8102f0 100644 --- a/dhis/src/main/java/org/dhis2/fhir/adapter/dhis/tracker/trackedentity/impl/TrackedEntityServiceImpl.java +++ b/dhis/src/main/java/org/dhis2/fhir/adapter/dhis/tracker/trackedentity/impl/TrackedEntityServiceImpl.java @@ -154,6 +154,13 @@ public TrackedEntityServiceImpl( @Nonnull @Qualifier( "userDhis2RestTemplate" ) this.resourceRepositoryTemplate = new LocalDhisResourceRepositoryTemplate<>( TrackedEntityInstance.class, requestCacheService, this ); } + @Nonnull + @Override + public DhisResourceType getDhisResourceType() + { + return DhisResourceType.TRACKED_ENTITY; + } + @HystrixCommand( ignoreExceptions = { DhisConflictException.class, UnauthorizedException.class } ) @Override public void updateGeneratedValues( @Nonnull TrackedEntityInstance trackedEntityInstance, @Nonnull TrackedEntityType type, @Nonnull Map requiredValues ) diff --git a/dhis/src/test/java/org/dhis2/fhir/adapter/dhis/tracker/program/impl/ProgramMetadataServiceImplTest.java b/dhis/src/test/java/org/dhis2/fhir/adapter/dhis/tracker/program/impl/ProgramMetadataServiceImplTest.java index b1aabe2f..27653d09 100644 --- a/dhis/src/test/java/org/dhis2/fhir/adapter/dhis/tracker/program/impl/ProgramMetadataServiceImplTest.java +++ b/dhis/src/test/java/org/dhis2/fhir/adapter/dhis/tracker/program/impl/ProgramMetadataServiceImplTest.java @@ -81,7 +81,8 @@ public void setUp() @Test public void findMetadataByReferenceId() throws IOException { - mockServer.expect( requestTo( "http://localhost:8080/api/programs/93783.json?fields=id,name,code,description,selectIncidentDatesInFuture,selectEnrollmentDatesInFuture,displayIncidentDate,registration,withoutRegistration,captureCoordinates," + + mockServer.expect( requestTo( "http://localhost:8080/api/programs/93783.json?fields=id,name,code,description,lastUpdated,selectIncidentDatesInFuture,selectEnrollmentDatesInFuture,displayIncidentDate,registration,withoutRegistration," + + "captureCoordinates," + "trackedEntityType%5Bid%5D,programTrackedEntityAttributes%5Bid,name,valueType,mandatory,allowFutureDate,trackedEntityAttribute%5Bid,name,code,valueType,generated%5D%5D,programStages%5Bid,name,description,repeatable,captureCoordinates," + "generatedByEnrollmentDate,minDaysFromStart,programStageDataElements%5Bid,compulsory,allowProvidedElsewhere,dataElement%5Bid,name,code,formName,valueType,optionSetValue,optionSet%5Bid,name,options%5Bcode,name%5D%5D%5D%5D%5D" ) ) .andExpect( method( HttpMethod.GET ) ).andRespond( withSuccess( IOUtils.resourceToByteArray( "/org/dhis2/fhir/adapter/dhis/tracker/program/impl/program.json" ), MediaType.APPLICATION_JSON ) ); @@ -94,7 +95,7 @@ public void findMetadataByReferenceId() throws IOException @Test public void findMetadataByReferenceIdNotFound() { - mockServer.expect( requestTo( "http://localhost:8080/api/programs/93783.json?fields=id,name,code,description,selectIncidentDatesInFuture,selectEnrollmentDatesInFuture,displayIncidentDate,registration,withoutRegistration,captureCoordinates," + + mockServer.expect( requestTo( "http://localhost:8080/api/programs/93783.json?fields=id,name,code,description,lastUpdated,selectIncidentDatesInFuture,selectEnrollmentDatesInFuture,displayIncidentDate,registration,withoutRegistration,captureCoordinates," + "trackedEntityType%5Bid%5D,programTrackedEntityAttributes%5Bid,name,valueType,mandatory,allowFutureDate,trackedEntityAttribute%5Bid,name,code,valueType,generated%5D%5D,programStages%5Bid,name,description,repeatable,captureCoordinates," + "generatedByEnrollmentDate,minDaysFromStart,programStageDataElements%5Bid,compulsory,allowProvidedElsewhere,dataElement%5Bid,name,code,formName,valueType,optionSetValue,optionSet%5Bid,name,options%5Bcode,name%5D%5D%5D%5D%5D" ) ).andExpect( method( HttpMethod.GET ) ) .andRespond( MockRestResponseCreators.withStatus( HttpStatus.NOT_FOUND ).body( "{}" ).contentType( MediaType.APPLICATION_JSON ) ); @@ -106,7 +107,7 @@ public void findMetadataByReferenceIdNotFound() @Test( expected = HttpServerErrorException.class ) public void findMetadataByReferenceIdServerError() { - mockServer.expect( requestTo( "http://localhost:8080/api/programs/93783.json?fields=id,name,code,description,selectIncidentDatesInFuture,selectEnrollmentDatesInFuture,displayIncidentDate,registration,withoutRegistration,captureCoordinates," + + mockServer.expect( requestTo( "http://localhost:8080/api/programs/93783.json?fields=id,name,code,description,lastUpdated,selectIncidentDatesInFuture,selectEnrollmentDatesInFuture,displayIncidentDate,registration,withoutRegistration,captureCoordinates," + "trackedEntityType%5Bid%5D,programTrackedEntityAttributes%5Bid,name,valueType,mandatory,allowFutureDate,trackedEntityAttribute%5Bid,name,code,valueType,generated%5D%5D,programStages%5Bid,name,description,repeatable,captureCoordinates," + "generatedByEnrollmentDate,minDaysFromStart,programStageDataElements%5Bid,compulsory,allowProvidedElsewhere,dataElement%5Bid,name,code,formName,valueType,optionSetValue,optionSet%5Bid,name,options%5Bcode,name%5D%5D%5D%5D%5D" ) ).andExpect( method( HttpMethod.GET ) ) .andRespond( MockRestResponseCreators.withServerError() ); @@ -116,7 +117,7 @@ public void findMetadataByReferenceIdServerError() @Test public void findMetadataByReferenceCode() throws IOException { - mockServer.expect( requestTo( "http://localhost:8080/api/programs.json?paging=false&fields=id,name,code,description,selectIncidentDatesInFuture,selectEnrollmentDatesInFuture,displayIncidentDate,registration,withoutRegistration,captureCoordinates," + + mockServer.expect( requestTo( "http://localhost:8080/api/programs.json?paging=false&fields=id,name,code,description,lastUpdated,selectIncidentDatesInFuture,selectEnrollmentDatesInFuture,displayIncidentDate,registration,withoutRegistration,captureCoordinates," + "trackedEntityType%5Bid%5D,programTrackedEntityAttributes%5Bid,name,valueType,mandatory,allowFutureDate,trackedEntityAttribute%5Bid,name,code,valueType,generated%5D%5D,programStages%5Bid,name,description,repeatable,captureCoordinates," + "generatedByEnrollmentDate,minDaysFromStart,programStageDataElements%5Bid,compulsory,allowProvidedElsewhere,dataElement%5Bid,name,code,formName,valueType,optionSetValue,optionSet%5Bid,name,options%5Bcode," + "name%5D%5D%5D%5D%5D&filter=code:eq:OU_3783" ) ).andExpect( method( HttpMethod.GET ) ) @@ -130,7 +131,7 @@ public void findMetadataByReferenceCode() throws IOException @Test public void findMetadataByReferenceCodeNotFound() throws IOException { - mockServer.expect( requestTo( "http://localhost:8080/api/programs.json?paging=false&fields=id,name,code,description,selectIncidentDatesInFuture,selectEnrollmentDatesInFuture,displayIncidentDate,registration,withoutRegistration,captureCoordinates," + + mockServer.expect( requestTo( "http://localhost:8080/api/programs.json?paging=false&fields=id,name,code,description,lastUpdated,selectIncidentDatesInFuture,selectEnrollmentDatesInFuture,displayIncidentDate,registration,withoutRegistration,captureCoordinates," + "trackedEntityType%5Bid%5D,programTrackedEntityAttributes%5Bid,name,valueType,mandatory,allowFutureDate,trackedEntityAttribute%5Bid,name,code,valueType,generated%5D%5D,programStages%5Bid,name,description,repeatable,captureCoordinates," + "generatedByEnrollmentDate,minDaysFromStart,programStageDataElements%5Bid,compulsory,allowProvidedElsewhere,dataElement%5Bid,name,code,formName,valueType,optionSetValue,optionSet%5Bid,name,options%5Bcode," + "name%5D%5D%5D%5D%5D&filter=code:eq:OU_3783" ) ).andExpect( method( HttpMethod.GET ) ) @@ -143,7 +144,7 @@ public void findMetadataByReferenceCodeNotFound() throws IOException @Test public void findMetadataByReferenceName() throws IOException { - mockServer.expect( requestTo( "http://localhost:8080/api/programs.json?paging=false&fields=id,name,code,description,selectIncidentDatesInFuture,selectEnrollmentDatesInFuture,displayIncidentDate,registration,withoutRegistration,captureCoordinates," + + mockServer.expect( requestTo( "http://localhost:8080/api/programs.json?paging=false&fields=id,name,code,description,lastUpdated,selectIncidentDatesInFuture,selectEnrollmentDatesInFuture,displayIncidentDate,registration,withoutRegistration,captureCoordinates," + "trackedEntityType%5Bid%5D,programTrackedEntityAttributes%5Bid,name,valueType,mandatory,allowFutureDate,trackedEntityAttribute%5Bid,name,code,valueType,generated%5D%5D,programStages%5Bid,name,description,repeatable,captureCoordinates," + "generatedByEnrollmentDate,minDaysFromStart,programStageDataElements%5Bid,compulsory,allowProvidedElsewhere,dataElement%5Bid,name,code,formName,valueType,optionSetValue,optionSet%5Bid,name,options%5Bcode," + "name%5D%5D%5D%5D%5D&filter=name:eq:Freetown" ) ).andExpect( method( HttpMethod.GET ) ) @@ -157,7 +158,7 @@ public void findMetadataByReferenceName() throws IOException @Test public void findMetadataByReferenceNameNotFound() throws IOException { - mockServer.expect( requestTo( "http://localhost:8080/api/programs.json?paging=false&fields=id,name,code,description,selectIncidentDatesInFuture,selectEnrollmentDatesInFuture,displayIncidentDate,registration,withoutRegistration,captureCoordinates," + + mockServer.expect( requestTo( "http://localhost:8080/api/programs.json?paging=false&fields=id,name,code,description,lastUpdated,selectIncidentDatesInFuture,selectEnrollmentDatesInFuture,displayIncidentDate,registration,withoutRegistration,captureCoordinates," + "trackedEntityType%5Bid%5D,programTrackedEntityAttributes%5Bid,name,valueType,mandatory,allowFutureDate,trackedEntityAttribute%5Bid,name,code,valueType,generated%5D%5D,programStages%5Bid,name,description,repeatable,captureCoordinates," + "generatedByEnrollmentDate,minDaysFromStart,programStageDataElements%5Bid,compulsory,allowProvidedElsewhere,dataElement%5Bid,name,code,formName,valueType,optionSetValue,optionSet%5Bid,name,options%5Bcode," + "name%5D%5D%5D%5D%5D&filter=name:eq:Freetown" ) ).andExpect( method( HttpMethod.GET ) ) diff --git a/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/AbstractDhisToFhirTransformer.java b/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/AbstractDhisToFhirTransformer.java index ed19b435..91e0517c 100644 --- a/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/AbstractDhisToFhirTransformer.java +++ b/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/AbstractDhisToFhirTransformer.java @@ -250,7 +250,7 @@ protected Optional getResource( @Nonnull FhirClient fhi } @Nonnull - protected String createDhisFhirResourceId( @Nonnull RuleInfo ruleInfo, @Nonnull ScriptedDhisResource dhisResource ) + protected String createDhisFhirResourceId( @Nonnull RuleInfo ruleInfo, @Nonnull ScriptedDhisResource dhisResource ) { if ( ruleInfo.getRule().isSimpleFhirId() ) { @@ -532,10 +532,9 @@ protected boolean evaluateNotModified( @Nonnull DhisToFhirTransformerContext con } @Nonnull - protected String createAdapterIdentifierValue( @Nonnull RuleInfo rule, @Nonnull ScriptedDhisResource dhisResource ) + protected String createAdapterIdentifierValue( @Nonnull RuleInfo ruleInfo, @Nonnull ScriptedDhisResource dhisResource ) { - return DhisFhirResourceId.toString( rule.getRule().getDhisResourceType(), - Objects.requireNonNull( dhisResource.getId() ), rule.getRule().getId() ); + return createDhisFhirResourceId( ruleInfo, dhisResource ); } @Nullable From fbd04106d9807d48d982f725a0a8136f1c9766f6 Mon Sep 17 00:00:00 2001 From: Volker Schmidt Date: Tue, 30 Jul 2019 14:26:00 +0200 Subject: [PATCH 08/21] Adds support for DHIS2 program stage to FHIR Questionnaire. --- .../main/resources/default-application.yml | 1 + .../AbstractProgramStageFhirRestAppTest.java | 6 +- .../r4/R4ProgramMetadataFhirRestAppTest.java | 35 +-- ...R4ProgramStageMetadataFhirRestAppTest.java | 221 ++++++++++++++++++ ...stractEnrollmentTransformationAppTest.java | 22 +- ...ractProgramStageTransformationAppTest.java | 10 +- .../adapter/dhis/test/all-program-stages.json | 185 +++++++++++++++ .../dhis/test/default-program-stage.json | 101 ++++++++ .../dhis/test/single-program-stage.json | 99 ++++++++ .../sync/impl/DhisResourceRepositoryImpl.java | 10 +- .../program/ImmutableProgramStage.java | 7 + .../dhis/tracker/program/ProgramStage.java | 2 + .../program/ProgramStageMetadataService.java | 40 ++++ .../dhis/tracker/program/WritableProgram.java | 4 +- .../tracker/program/WritableProgramStage.java | 19 +- .../program/impl/DhisProgramStages.java | 62 +++++ .../impl/ProgramMetadataServiceImpl.java | 4 +- .../impl/ProgramStageMetadataServiceImpl.java | 86 +++++++ .../impl/ProgramMetadataServiceImplTest.java | 38 +-- .../ProgramStageMetadataServiceImplTest.java | 156 +++++++++++++ .../program/impl/emptyProgramStages.json | 3 + .../tracker/program/impl/programStage.json | 11 + .../tracker/program/impl/programStages.json | 24 ++ ...gramPlanDefinition.StructureDefinition.xml | 6 +- ...ogramQuestionnaire.StructureDefinition.xml | 16 +- .../R4ProgramMetadataToFhirDataProvider.java | 3 +- ...tadataToFhirPlanDefinitionTransformer.java | 3 +- ...rogramStageMetadataToFhirDataProvider.java | 70 ++++++ ...etadataToFhirQuestionnaireTransformer.java | 158 +++++++++++++ ...ProgramMetadataToFhirDataProviderTest.java | 2 +- ...taToFhirPlanDefinitionTransformerTest.java | 2 +- ...amStageMetadataToFhirDataProviderTest.java | 93 ++++++++ ...ataToFhirQuestionnaireTransformerTest.java | 170 ++++++++++++++ ...rogramStageMetadataToFhirDataProvider.java | 66 ++++++ ...etadataToFhirQuestionnaireTransformer.java | 93 ++++++++ ...ramStageMetadataToFhirRequestResolver.java | 76 ++++++ ...0.41_10_0__Plan_Definition_Sample_Data.sql | 2 + 37 files changed, 1843 insertions(+), 63 deletions(-) create mode 100644 app/src/test/java/org/dhis2/fhir/adapter/fhir/server/r4/R4ProgramStageMetadataFhirRestAppTest.java create mode 100644 app/src/test/resources/org/dhis2/fhir/adapter/dhis/test/all-program-stages.json create mode 100644 app/src/test/resources/org/dhis2/fhir/adapter/dhis/test/default-program-stage.json create mode 100644 app/src/test/resources/org/dhis2/fhir/adapter/dhis/test/single-program-stage.json create mode 100644 dhis/src/main/java/org/dhis2/fhir/adapter/dhis/tracker/program/ProgramStageMetadataService.java create mode 100644 dhis/src/main/java/org/dhis2/fhir/adapter/dhis/tracker/program/impl/DhisProgramStages.java create mode 100644 dhis/src/main/java/org/dhis2/fhir/adapter/dhis/tracker/program/impl/ProgramStageMetadataServiceImpl.java create mode 100644 dhis/src/test/java/org/dhis2/fhir/adapter/dhis/tracker/program/impl/ProgramStageMetadataServiceImplTest.java create mode 100644 dhis/src/test/resources/org/dhis2/fhir/adapter/dhis/tracker/program/impl/emptyProgramStages.json create mode 100644 dhis/src/test/resources/org/dhis2/fhir/adapter/dhis/tracker/program/impl/programStage.json create mode 100644 dhis/src/test/resources/org/dhis2/fhir/adapter/dhis/tracker/program/impl/programStages.json create mode 100644 fhir-r4/src/main/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/metadata/r4/R4ProgramStageMetadataToFhirDataProvider.java create mode 100644 fhir-r4/src/main/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/metadata/r4/R4ProgramStageMetadataToFhirQuestionnaireTransformer.java create mode 100644 fhir-r4/src/test/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/metadata/r4/R4ProgramStageMetadataToFhirDataProviderTest.java create mode 100644 fhir-r4/src/test/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/metadata/r4/R4ProgramStageMetadataToFhirQuestionnaireTransformerTest.java create mode 100644 fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/metadata/program/AbstractProgramStageMetadataToFhirDataProvider.java create mode 100644 fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/metadata/program/AbstractProgramStageMetadataToFhirQuestionnaireTransformer.java create mode 100644 fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/metadata/program/ProgramStageMetadataToFhirRequestResolver.java diff --git a/app/src/main/resources/default-application.yml b/app/src/main/resources/default-application.yml index 1feb5c15..2fb496db 100644 --- a/app/src/main/resources/default-application.yml +++ b/app/src/main/resources/default-application.yml @@ -357,6 +357,7 @@ dhis2.fhir-adapter: resource-types: - ORGANIZATION_UNIT - PROGRAM_METADATA + - PROGRAM_STAGE_METADATA - TRACKED_ENTITY - PROGRAM_STAGE_EVENT # The queue that is used to store distributed requests to synchronize the data from diff --git a/app/src/test/java/org/dhis2/fhir/adapter/fhir/server/AbstractProgramStageFhirRestAppTest.java b/app/src/test/java/org/dhis2/fhir/adapter/fhir/server/AbstractProgramStageFhirRestAppTest.java index b7ed5cd6..a5683b12 100644 --- a/app/src/test/java/org/dhis2/fhir/adapter/fhir/server/AbstractProgramStageFhirRestAppTest.java +++ b/app/src/test/java/org/dhis2/fhir/adapter/fhir/server/AbstractProgramStageFhirRestAppTest.java @@ -69,12 +69,14 @@ protected void expectProgramStageMetadataRequests() throws Exception .andExpect( requestTo( dhis2BaseUrl + "/api/" + dhis2ApiVersion + "/programs/EPDyQuoRnXk.json?" + "fields=id,name,code,description,lastUpdated,selectIncidentDatesInFuture,selectEnrollmentDatesInFuture,displayIncidentDate,registration,withoutRegistration,captureCoordinates,trackedEntityType%5Bid%5D,programTrackedEntityAttributes%5Bid," + "name,valueType," + - "mandatory,allowFutureDate,trackedEntityAttribute%5Bid,name,code,valueType,generated%5D%5D,programStages%5Bid,name,description,repeatable,captureCoordinates,generatedByEnrollmentDate,minDaysFromStart,programStageDataElements%5Bid,compulsory," + + "mandatory,allowFutureDate,trackedEntityAttribute%5Bid,name,code,valueType,generated%5D%5D,programStages%5Bid,program%5Bid%5D,lastUpdated,name,description,repeatable,captureCoordinates,generatedByEnrollmentDate,minDaysFromStart," + + "programStageDataElements%5Bid,compulsory," + "allowProvidedElsewhere,dataElement%5Bid,name,code,formName,valueType,optionSetValue,optionSet%5Bid,name,options%5Bcode,name%5D%5D%5D%5D%5D" ) ) .andRespond( withSuccess( IOUtils.resourceToString( "/org/dhis2/fhir/adapter/dhis/test/single-program.json", StandardCharsets.UTF_8 ), MediaType.APPLICATION_JSON ) ); systemDhis2Server.expect( ExpectedCount.between( 0, 1 ), method( HttpMethod.GET ) ).andExpect( header( "Authorization", testConfiguration.getDhis2SystemAuthorization() ) ) .andExpect( requestTo( dhis2BaseUrl + "/api/" + dhis2ApiVersion + "/programs.json?paging=false&fields=id,name,code,description,lastUpdated,selectIncidentDatesInFuture,selectEnrollmentDatesInFuture,displayIncidentDate,registration," + - "withoutRegistration,captureCoordinates,trackedEntityType%5Bid%5D,programTrackedEntityAttributes%5Bid,name,valueType,mandatory,allowFutureDate,trackedEntityAttribute%5Bid,name,code,valueType,generated%5D%5D,programStages%5Bid,name," + + "withoutRegistration,captureCoordinates,trackedEntityType%5Bid%5D,programTrackedEntityAttributes%5Bid,name,valueType,mandatory,allowFutureDate,trackedEntityAttribute%5Bid,name,code,valueType,generated%5D%5D,programStages%5Bid," + + "program%5Bid%5D,lastUpdated,name," + "description,repeatable,captureCoordinates,generatedByEnrollmentDate,minDaysFromStart,programStageDataElements%5Bid,compulsory,allowProvidedElsewhere,dataElement%5Bid,name,code,formName,valueType,optionSetValue,optionSet%5Bid,name," + "options%5Bcode,name%5D%5D%5D%5D%5D&filter=name:eq:Child%20Programme" ) ) .andRespond( withSuccess( IOUtils.resourceToString( "/org/dhis2/fhir/adapter/dhis/test/default-program.json", StandardCharsets.UTF_8 ), MediaType.APPLICATION_JSON ) ); diff --git a/app/src/test/java/org/dhis2/fhir/adapter/fhir/server/r4/R4ProgramMetadataFhirRestAppTest.java b/app/src/test/java/org/dhis2/fhir/adapter/fhir/server/r4/R4ProgramMetadataFhirRestAppTest.java index 695d91d2..e86e33b7 100644 --- a/app/src/test/java/org/dhis2/fhir/adapter/fhir/server/r4/R4ProgramMetadataFhirRestAppTest.java +++ b/app/src/test/java/org/dhis2/fhir/adapter/fhir/server/r4/R4ProgramMetadataFhirRestAppTest.java @@ -80,7 +80,8 @@ public void getPlanDefinitionWithInvalidAuthorization() .andExpect( requestTo( dhis2BaseUrl + "/api/" + dhis2ApiVersion + "/programs/EPDyQuoRnXk.json?" + "fields=id,name,code,description,lastUpdated,selectIncidentDatesInFuture,selectEnrollmentDatesInFuture,displayIncidentDate,registration,withoutRegistration,captureCoordinates,trackedEntityType%5Bid%5D,programTrackedEntityAttributes%5Bid," + "name,valueType," + - "mandatory,allowFutureDate,trackedEntityAttribute%5Bid,name,code,valueType,generated%5D%5D,programStages%5Bid,name,description,repeatable,captureCoordinates,generatedByEnrollmentDate,minDaysFromStart,programStageDataElements%5Bid,compulsory," + + "mandatory,allowFutureDate,trackedEntityAttribute%5Bid,name,code,valueType,generated%5D%5D,programStages%5Bid,program%5Bid%5D,lastUpdated,name,description,repeatable,captureCoordinates,generatedByEnrollmentDate,minDaysFromStart," + + "programStageDataElements%5Bid,compulsory," + "allowProvidedElsewhere,dataElement%5Bid,name,code,formName,valueType,optionSetValue,optionSet%5Bid,name,options%5Bcode,name%5D%5D%5D%5D%5D" ) ) .andRespond( withStatus( HttpStatus.UNAUTHORIZED ) ); @@ -107,13 +108,14 @@ private void getPlanDefinition() throws Exception .andRespond( withSuccess( IOUtils.resourceToString( "/org/dhis2/fhir/adapter/dhis/test/single-tracked-entity-type.json", StandardCharsets.UTF_8 ), MediaType.APPLICATION_JSON ) ); userDhis2Server.expect( ExpectedCount.once(), method( HttpMethod.GET ) ).andExpect( header( "Authorization", "Basic Zmhpcl9jbGllbnQ6Zmhpcl9jbGllbnRfMQ==" ) ) .andExpect( requestTo( dhis2BaseUrl + "/api/" + dhis2ApiVersion + "/programs/EPDyQuoRnXk.json?fields=id,name,code,description,lastUpdated,selectIncidentDatesInFuture,selectEnrollmentDatesInFuture,displayIncidentDate,registration," + - "withoutRegistration,captureCoordinates,trackedEntityType%5Bid%5D,programTrackedEntityAttributes%5Bid,name,valueType,mandatory,allowFutureDate,trackedEntityAttribute%5Bid,name,code,valueType,generated%5D%5D,programStages%5Bid,name," + + "withoutRegistration,captureCoordinates,trackedEntityType%5Bid%5D,programTrackedEntityAttributes%5Bid,name,valueType,mandatory,allowFutureDate,trackedEntityAttribute%5Bid,name,code,valueType,generated%5D%5D,programStages%5Bid," + + "program%5Bid%5D,lastUpdated,name," + "description,repeatable,captureCoordinates,generatedByEnrollmentDate,minDaysFromStart,programStageDataElements%5Bid,compulsory,allowProvidedElsewhere,dataElement%5Bid,name,code,formName,valueType,optionSetValue,optionSet%5Bid,name," + "options%5Bcode,name%5D%5D%5D%5D%5D" ) ) .andRespond( withSuccess( IOUtils.resourceToString( "/org/dhis2/fhir/adapter/dhis/test/single-program.json", StandardCharsets.UTF_8 ), MediaType.APPLICATION_JSON ) ); userDhis2Server.expect( ExpectedCount.between( 0, 1 ), method( HttpMethod.GET ) ).andExpect( header( "Authorization", "Basic Zmhpcl9jbGllbnQ6Zmhpcl9jbGllbnRfMQ==" ) ) .andExpect( requestTo( dhis2BaseUrl + "/api/" + dhis2ApiVersion + "/programs.json?paging=false&fields=id,name,code,description,lastUpdated,selectIncidentDatesInFuture,selectEnrollmentDatesInFuture,displayIncidentDate,registration," + - "withoutRegistration,captureCoordinates,trackedEntityType%5Bid%5D,programTrackedEntityAttributes%5Bid,name,valueType,mandatory,allowFutureDate,trackedEntityAttribute%5Bid,name,code,valueType,generated%5D%5D,programStages%5Bid,name," + + "withoutRegistration,captureCoordinates,trackedEntityType%5Bid%5D,programTrackedEntityAttributes%5Bid,name,valueType,mandatory,allowFutureDate,trackedEntityAttribute%5Bid,name,code,valueType,generated%5D%5D,programStages%5Bid,program%5Bid%5D,lastUpdated,name," + "description,repeatable,captureCoordinates,generatedByEnrollmentDate,minDaysFromStart,programStageDataElements%5Bid,compulsory,allowProvidedElsewhere,dataElement%5Bid,name,code,formName,valueType,optionSetValue,optionSet%5Bid,name," + "options%5Bcode,name%5D%5D%5D%5D%5D" ) ) .andRespond( withSuccess( IOUtils.resourceToString( "/org/dhis2/fhir/adapter/dhis/test/all-programs.json", StandardCharsets.UTF_8 ), MediaType.APPLICATION_JSON ) ); @@ -121,7 +123,7 @@ private void getPlanDefinition() throws Exception final IGenericClient client = createGenericClient(); client.registerInterceptor( new BasicAuthInterceptor( "fhir_client", "fhir_client_1" ) ); PlanDefinition planDefinition = client.read().resource( PlanDefinition.class ).withId( "EPDyQuoRnXk" ).execute(); - Assert.assertEquals( "Child Programme", planDefinition.getName() ); + Assert.assertEquals( "Child Programme", planDefinition.getTitle() ); systemDhis2Server.verify(); userDhis2Server.verify(); @@ -148,7 +150,7 @@ private void getPlanDefinitionNotExists() userDhis2Server.expect( ExpectedCount.once(), method( HttpMethod.GET ) ).andExpect( header( "Authorization", "Basic Zmhpcl9jbGllbnQ6Zmhpcl9jbGllbnRfMQ==" ) ) .andExpect( requestTo( dhis2BaseUrl + "/api/" + dhis2ApiVersion + "/programs/0dXIdLNUNEn.json?fields=id,name,code,description,lastUpdated,selectIncidentDatesInFuture,selectEnrollmentDatesInFuture,displayIncidentDate,registration," + - "withoutRegistration,captureCoordinates,trackedEntityType%5Bid%5D,programTrackedEntityAttributes%5Bid,name,valueType,mandatory,allowFutureDate,trackedEntityAttribute%5Bid,name,code,valueType,generated%5D%5D,programStages%5Bid,name," + + "withoutRegistration,captureCoordinates,trackedEntityType%5Bid%5D,programTrackedEntityAttributes%5Bid,name,valueType,mandatory,allowFutureDate,trackedEntityAttribute%5Bid,name,code,valueType,generated%5D%5D,programStages%5Bid,program%5Bid%5D,lastUpdated,name," + "description,repeatable,captureCoordinates,generatedByEnrollmentDate,minDaysFromStart,programStageDataElements%5Bid,compulsory,allowProvidedElsewhere,dataElement%5Bid,name,code,formName,valueType,optionSetValue,optionSet%5Bid,name," + "options%5Bcode,name%5D%5D%5D%5D%5D" ) ) .andRespond( withStatus( HttpStatus.NOT_FOUND ).contentType( MediaType.APPLICATION_JSON ).body( "{}" ) ); @@ -211,8 +213,9 @@ public void getPlanDefinitionByIdentifierWithoutAuthorization() public void getPlanDefinitionByIdentifierInvalidAuthorization() { userDhis2Server.expect( ExpectedCount.once(), method( HttpMethod.GET ) ).andExpect( header( "Authorization", "Basic Zmhpcl9jbGllbnQ6aW52YWxpZF8x" ) ) - .andExpect( requestTo( dhis2BaseUrl + "/api/" + dhis2ApiVersion + "/programs.json?paging=false&fields=id,name,code,description,lastUpdated,selectIncidentDatesInFuture,selectEnrollmentDatesInFuture,displayIncidentDate,registration,withoutRegistration," + - "captureCoordinates,trackedEntityType%5Bid%5D,programTrackedEntityAttributes%5Bid,name,valueType,mandatory,allowFutureDate,trackedEntityAttribute%5Bid,name,code,valueType,generated%5D%5D,programStages%5Bid,name,description,repeatable," + + .andExpect( requestTo( dhis2BaseUrl + "/api/" + dhis2ApiVersion + + "/programs.json?paging=false&fields=id,name,code,description,lastUpdated,selectIncidentDatesInFuture,selectEnrollmentDatesInFuture,displayIncidentDate,registration,withoutRegistration," + + "captureCoordinates,trackedEntityType%5Bid%5D,programTrackedEntityAttributes%5Bid,name,valueType,mandatory,allowFutureDate,trackedEntityAttribute%5Bid,name,code,valueType,generated%5D%5D,programStages%5Bid,program%5Bid%5D,lastUpdated,name,description,repeatable," + "captureCoordinates,generatedByEnrollmentDate,minDaysFromStart,programStageDataElements%5Bid,compulsory,allowProvidedElsewhere,dataElement%5Bid,name,code,formName,valueType,optionSetValue,optionSet%5Bid,name,options%5Bcode," + "name%5D%5D%5D%5D%5D&filter=code:eq:OU_1234" ) ) .andRespond( withStatus( HttpStatus.UNAUTHORIZED ) ); @@ -239,8 +242,9 @@ private void getPlanDefinitionByIdentifier() throws Exception "fields=id,name,trackedEntityTypeAttributes%5Bid,name,valueType,mandatory,trackedEntityAttribute%5Bid,name,code,valueType,generated,optionSetValue,optionSet%5Bid,name,options%5Bcode,name%5D%5D%5D%5D" ) ) .andRespond( withSuccess( IOUtils.resourceToString( "/org/dhis2/fhir/adapter/dhis/test/single-tracked-entity-type.json", StandardCharsets.UTF_8 ), MediaType.APPLICATION_JSON ) ); userDhis2Server.expect( ExpectedCount.between( 1, 2 ), method( HttpMethod.GET ) ).andExpect( header( "Authorization", "Basic Zmhpcl9jbGllbnQ6Zmhpcl9jbGllbnRfMQ==" ) ) - .andExpect( requestTo( dhis2BaseUrl + "/api/" + dhis2ApiVersion + "/programs.json?paging=false&fields=id,name,code,description,lastUpdated,selectIncidentDatesInFuture,selectEnrollmentDatesInFuture,displayIncidentDate,registration,withoutRegistration," + - "captureCoordinates,trackedEntityType%5Bid%5D,programTrackedEntityAttributes%5Bid,name,valueType,mandatory,allowFutureDate,trackedEntityAttribute%5Bid,name,code,valueType,generated%5D%5D,programStages%5Bid,name,description,repeatable," + + .andExpect( requestTo( dhis2BaseUrl + "/api/" + dhis2ApiVersion + "/programs.json?paging=false&fields=id,name,code,description,lastUpdated,selectIncidentDatesInFuture,selectEnrollmentDatesInFuture,displayIncidentDate,registration," + + "withoutRegistration," + + "captureCoordinates,trackedEntityType%5Bid%5D,programTrackedEntityAttributes%5Bid,name,valueType,mandatory,allowFutureDate,trackedEntityAttribute%5Bid,name,code,valueType,generated%5D%5D,programStages%5Bid,program%5Bid%5D,lastUpdated,name,description,repeatable," + "captureCoordinates,generatedByEnrollmentDate,minDaysFromStart,programStageDataElements%5Bid,compulsory,allowProvidedElsewhere,dataElement%5Bid,name,code,formName,valueType,optionSetValue,optionSet%5Bid,name,options%5Bcode," + "name%5D%5D%5D%5D%5D&filter=code:eq:PD_1234" ) ) .andRespond( withSuccess( IOUtils.resourceToString( "/org/dhis2/fhir/adapter/dhis/test/default-program.json", StandardCharsets.UTF_8 ), MediaType.APPLICATION_JSON ) ); @@ -251,7 +255,7 @@ private void getPlanDefinitionByIdentifier() throws Exception client.search().forResource( PlanDefinition.class ).where( PlanDefinition.IDENTIFIER.exactly().systemAndIdentifier( "http://www.dhis2.org/dhis2-fhir-adapter/systems/plan-definition-identifier", "PD_1234" ) ).returnBundle( Bundle.class ).execute(); Assert.assertEquals( 1, bundle.getEntry().size() ); PlanDefinition planDefinition = (PlanDefinition) bundle.getEntry().get( 0 ).getResource(); - Assert.assertEquals( "Child Programme", planDefinition.getName() ); + Assert.assertEquals( "Child Programme", planDefinition.getTitle() ); systemDhis2Server.verify(); userDhis2Server.verify(); @@ -270,13 +274,14 @@ public void searchPlanDefinitionInvalidAuthorization() userDhis2Server.expect( ExpectedCount.once(), method( HttpMethod.GET ) ).andExpect( header( "Authorization", "Basic Zmhpcl9jbGllbnQ6aW52YWxpZF8x" ) ) .andExpect( requestTo( dhis2BaseUrl + "/api/" + dhis2ApiVersion + "/programs.json?filter=name:$ilike:Test&paging=true&page=1&pageSize=10&order=id&fields=id,name,code,description,lastUpdated,selectIncidentDatesInFuture,selectEnrollmentDatesInFuture," + "displayIncidentDate,registration,withoutRegistration,captureCoordinates,trackedEntityType%5Bid%5D,programTrackedEntityAttributes%5Bid,name,valueType,mandatory,allowFutureDate,trackedEntityAttribute%5Bid,name,code,valueType,generated%5D%5D," + - "programStages%5Bid,name,description,repeatable,captureCoordinates,generatedByEnrollmentDate,minDaysFromStart,programStageDataElements%5Bid,compulsory,allowProvidedElsewhere,dataElement%5Bid,name,code,formName,valueType,optionSetValue," + + "programStages%5Bid,program%5Bid%5D,lastUpdated,name,description,repeatable,captureCoordinates,generatedByEnrollmentDate,minDaysFromStart,programStageDataElements%5Bid,compulsory,allowProvidedElsewhere,dataElement%5Bid,name,code,formName," + + "valueType,optionSetValue," + "optionSet%5Bid,name,options%5Bcode,name%5D%5D%5D%5D%5D" ) ) .andRespond( withStatus( HttpStatus.UNAUTHORIZED ) ); final IGenericClient client = createGenericClient(); client.registerInterceptor( new BasicAuthInterceptor( "fhir_client", "invalid_1" ) ); - client.search().forResource( PlanDefinition.class ).where( PlanDefinition.NAME.matches().value( "Test" ) ).returnBundle( Bundle.class ).execute(); + client.search().forResource( PlanDefinition.class ).where( PlanDefinition.TITLE.matches().value( "Test" ) ).returnBundle( Bundle.class ).execute(); } @Test @@ -289,15 +294,15 @@ public void searchPlanDefinition() throws Exception userDhis2Server.expect( ExpectedCount.once(), method( HttpMethod.GET ) ).andExpect( header( "Authorization", "Basic Zmhpcl9jbGllbnQ6Zmhpcl9jbGllbnRfMQ==" ) ) .andExpect( requestTo( dhis2BaseUrl + "/api/" + dhis2ApiVersion + "/programs.json?filter=name:$ilike:Child%20Programme&paging=true&page=1&pageSize=10&order=id&fields=id,name,code,description,lastUpdated,selectIncidentDatesInFuture," + "selectEnrollmentDatesInFuture,displayIncidentDate,registration,withoutRegistration,captureCoordinates,trackedEntityType%5Bid%5D,programTrackedEntityAttributes%5Bid,name,valueType,mandatory,allowFutureDate,trackedEntityAttribute%5Bid,name," + - "code,valueType,generated%5D%5D,programStages%5Bid,name,description,repeatable,captureCoordinates,generatedByEnrollmentDate,minDaysFromStart,programStageDataElements%5Bid,compulsory,allowProvidedElsewhere,dataElement%5Bid,name,code,formName," + + "code,valueType,generated%5D%5D,programStages%5Bid,program%5Bid%5D,lastUpdated,name,description,repeatable,captureCoordinates,generatedByEnrollmentDate,minDaysFromStart,programStageDataElements%5Bid,compulsory,allowProvidedElsewhere,dataElement%5Bid,name,code,formName," + "valueType,optionSetValue,optionSet%5Bid,name,options%5Bcode,name%5D%5D%5D%5D%5D" ) ) .andRespond( withSuccess( IOUtils.resourceToString( "/org/dhis2/fhir/adapter/dhis/test/default-program.json", StandardCharsets.UTF_8 ), MediaType.APPLICATION_JSON ) ); final IGenericClient client = createGenericClient(); client.registerInterceptor( new BasicAuthInterceptor( "fhir_client", "fhir_client_1" ) ); - Bundle bundle = client.search().forResource( PlanDefinition.class ).where( PlanDefinition.NAME.matches().value( "Child Programme" ) ).returnBundle( Bundle.class ).execute(); + Bundle bundle = client.search().forResource( PlanDefinition.class ).where( PlanDefinition.TITLE.matches().value( "Child Programme" ) ).returnBundle( Bundle.class ).execute(); Assert.assertEquals( 1, bundle.getEntry().size() ); PlanDefinition planDefinition = (PlanDefinition) bundle.getEntry().get( 0 ).getResource(); - Assert.assertEquals( "Child Programme", planDefinition.getName() ); + Assert.assertEquals( "Child Programme", planDefinition.getTitle() ); } } diff --git a/app/src/test/java/org/dhis2/fhir/adapter/fhir/server/r4/R4ProgramStageMetadataFhirRestAppTest.java b/app/src/test/java/org/dhis2/fhir/adapter/fhir/server/r4/R4ProgramStageMetadataFhirRestAppTest.java new file mode 100644 index 00000000..b86a8b22 --- /dev/null +++ b/app/src/test/java/org/dhis2/fhir/adapter/fhir/server/r4/R4ProgramStageMetadataFhirRestAppTest.java @@ -0,0 +1,221 @@ +package org.dhis2.fhir.adapter.fhir.server.r4; + +/* + * Copyright (c) 2004-2019, University of Oslo + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * Neither the name of the HISP project nor the names of its contributors may + * be used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +import ca.uhn.fhir.rest.client.api.IGenericClient; +import ca.uhn.fhir.rest.client.interceptor.BasicAuthInterceptor; +import ca.uhn.fhir.rest.server.exceptions.AuthenticationException; +import ca.uhn.fhir.rest.server.exceptions.ResourceNotFoundException; +import org.apache.commons.io.IOUtils; +import org.dhis2.fhir.adapter.AbstractAppTest; +import org.dhis2.fhir.adapter.fhir.model.FhirVersion; +import org.hl7.fhir.r4.model.Bundle; +import org.hl7.fhir.r4.model.Questionnaire; +import org.junit.Assert; +import org.junit.Test; +import org.springframework.http.HttpMethod; +import org.springframework.http.HttpStatus; +import org.springframework.http.MediaType; +import org.springframework.test.web.client.ExpectedCount; + +import javax.annotation.Nonnull; +import java.nio.charset.StandardCharsets; + +import static org.springframework.test.web.client.match.MockRestRequestMatchers.*; +import static org.springframework.test.web.client.response.MockRestResponseCreators.withStatus; +import static org.springframework.test.web.client.response.MockRestResponseCreators.withSuccess; + +/** + * R4 tests for rest interfaces that access + * DHIS2 program stage metadata. + * + * @author volsch + */ +public class R4ProgramStageMetadataFhirRestAppTest extends AbstractAppTest +{ + @Nonnull + @Override + protected FhirVersion getFhirVersion() + { + return FhirVersion.R4; + } + + @Test( expected = AuthenticationException.class ) + public void getQuestionnaireWithoutAuthorization() + { + final IGenericClient client = createGenericClient(); + client.read().resource( Questionnaire.class ).withId( "MsWxkiY6tMS" ).execute(); + } + + @Test( expected = AuthenticationException.class ) + public void getQuestionnaireWithInvalidAuthorization() + { + userDhis2Server.expect( ExpectedCount.once(), method( HttpMethod.GET ) ).andExpect( header( "Authorization", "Basic Zmhpcl9jbGllbnQ6aW52YWxpZF8x" ) ) + .andExpect( requestTo( dhis2BaseUrl + "/api/" + dhis2ApiVersion + "/programStages/MsWxkiY6tMS.json?fields=id,program%5Bid%5D,lastUpdated,name,description,repeatable,captureCoordinates,generatedByEnrollmentDate,minDaysFromStart," + + "programStageDataElements%5Bid,compulsory,allowProvidedElsewhere,dataElement%5Bid,name,code,formName,valueType,optionSetValue,optionSet%5Bid,name,options%5Bcode,name%5D%5D%5D%5D" ) ) + .andRespond( withStatus( HttpStatus.UNAUTHORIZED ) ); + + final IGenericClient client = createGenericClient(); + client.registerInterceptor( new BasicAuthInterceptor( "fhir_client", "invalid_1" ) ); + client.read().resource( Questionnaire.class ).withId( "MsWxkiY6tMS" ).execute(); + } + + @Test + public void getQuestionnaireRepeated() throws Exception + { + getQuestionnaire(); + getQuestionnaire(); + } + + private void getQuestionnaire() throws Exception + { + systemDhis2Server.reset(); + userDhis2Server.reset(); + + userDhis2Server.expect( ExpectedCount.once(), method( HttpMethod.GET ) ).andExpect( header( "Authorization", "Basic Zmhpcl9jbGllbnQ6Zmhpcl9jbGllbnRfMQ==" ) ) + .andExpect( requestTo( dhis2BaseUrl + "/api/" + dhis2ApiVersion + "/programStages/MsWxkiY6tMS.json?fields=id,program%5Bid%5D,lastUpdated,name,description,repeatable,captureCoordinates,generatedByEnrollmentDate,minDaysFromStart," + + "programStageDataElements%5Bid,compulsory,allowProvidedElsewhere,dataElement%5Bid,name,code,formName,valueType,optionSetValue,optionSet%5Bid,name,options%5Bcode,name%5D%5D%5D%5D" ) ) + .andRespond( withSuccess( IOUtils.resourceToString( "/org/dhis2/fhir/adapter/dhis/test/single-program-stage.json", StandardCharsets.UTF_8 ), MediaType.APPLICATION_JSON ) ); + + final IGenericClient client = createGenericClient(); + client.registerInterceptor( new BasicAuthInterceptor( "fhir_client", "fhir_client_1" ) ); + Questionnaire planDefinition = client.read().resource( Questionnaire.class ).withId( "MsWxkiY6tMS" ).execute(); + Assert.assertEquals( "Birth", planDefinition.getTitle() ); + + systemDhis2Server.verify(); + userDhis2Server.verify(); + } + + @Test( expected = ResourceNotFoundException.class ) + public void getQuestionnaireNotExistsRepeated() + { + try + { + getQuestionnaireNotExists(); + Assert.fail( "Exception expected also an first invocation." ); + } + catch ( ResourceNotFoundException e ) + { + getQuestionnaireNotExists(); + } + } + + private void getQuestionnaireNotExists() + { + systemDhis2Server.reset(); + userDhis2Server.reset(); + + userDhis2Server.expect( ExpectedCount.once(), method( HttpMethod.GET ) ).andExpect( header( "Authorization", "Basic Zmhpcl9jbGllbnQ6Zmhpcl9jbGllbnRfMQ==" ) ) + .andExpect( requestTo( dhis2BaseUrl + "/api/" + dhis2ApiVersion + "/programStages/0dXIdLNUNEn.json?fields=id,program%5Bid%5D,lastUpdated,name,description,repeatable,captureCoordinates,generatedByEnrollmentDate,minDaysFromStart," + + "programStageDataElements%5Bid,compulsory,allowProvidedElsewhere,dataElement%5Bid,name,code,formName,valueType,optionSetValue,optionSet%5Bid,name,options%5Bcode,name%5D%5D%5D%5D" ) ) + .andRespond( withStatus( HttpStatus.NOT_FOUND ).contentType( MediaType.APPLICATION_JSON ).body( "{}" ) ); + + try + { + final IGenericClient client = createGenericClient(); + client.registerInterceptor( new BasicAuthInterceptor( "fhir_client", "fhir_client_1" ) ); + client.read().resource( Questionnaire.class ).withId( "0dXIdLNUNEn" ).execute(); + } + catch ( ResourceNotFoundException e ) + { + systemDhis2Server.verify(); + userDhis2Server.verify(); + throw e; + } + } + + @Test( expected = ResourceNotFoundException.class ) + public void getQuestionnaireRuleNotFoundRepeated() + { + try + { + getQuestionnaireNotExists(); + Assert.fail( "Exception expected also an first invocation." ); + } + catch ( ResourceNotFoundException e ) + { + getQuestionnaireNotExists(); + } + } + + private void getQuestionnaireRuleNotFound() + { + systemDhis2Server.reset(); + userDhis2Server.reset(); + + try + { + final IGenericClient client = createGenericClient(); + client.registerInterceptor( new BasicAuthInterceptor( "fhir_client", "fhir_client_1" ) ); + client.read().resource( Questionnaire.class ).withId( "ldXIdLNUNEn" ).execute(); + } + catch ( ResourceNotFoundException e ) + { + systemDhis2Server.verify(); + userDhis2Server.verify(); + throw e; + } + } + + @Test( expected = AuthenticationException.class ) + public void searchQuestionnaireWithoutAuthorization() + { + final IGenericClient client = createGenericClient(); + client.search().forResource( Questionnaire.class ).where( Questionnaire.TITLE.matches().value( "Test" ) ).returnBundle( Bundle.class ).execute(); + } + + @Test( expected = AuthenticationException.class ) + public void searchQuestionnaireInvalidAuthorization() + { + userDhis2Server.expect( ExpectedCount.once(), method( HttpMethod.GET ) ).andExpect( header( "Authorization", "Basic Zmhpcl9jbGllbnQ6aW52YWxpZF8x" ) ) + .andExpect( requestTo( dhis2BaseUrl + "/api/" + dhis2ApiVersion + "/programStages.json?filter=name:$ilike:Test&paging=true&page=1&pageSize=10&order=id&fields=id,program%5Bid%5D,lastUpdated,name,description,repeatable,captureCoordinates," + + "generatedByEnrollmentDate,minDaysFromStart,programStageDataElements%5Bid,compulsory,allowProvidedElsewhere,dataElement%5Bid,name,code,formName,valueType,optionSetValue,optionSet%5Bid,name,options%5Bcode,name%5D%5D%5D%5D" ) ) + .andRespond( withStatus( HttpStatus.UNAUTHORIZED ) ); + + final IGenericClient client = createGenericClient(); + client.registerInterceptor( new BasicAuthInterceptor( "fhir_client", "invalid_1" ) ); + client.search().forResource( Questionnaire.class ).where( Questionnaire.TITLE.matches().value( "Test" ) ).returnBundle( Bundle.class ).execute(); + } + + @Test + public void searchQuestionnaire() throws Exception + { + userDhis2Server.expect( ExpectedCount.once(), method( HttpMethod.GET ) ).andExpect( header( "Authorization", "Basic Zmhpcl9jbGllbnQ6Zmhpcl9jbGllbnRfMQ==" ) ) + .andExpect( requestTo( dhis2BaseUrl + "/api/" + dhis2ApiVersion + "/programStages.json?filter=name:$ilike:Birth&paging=true&page=1&pageSize=10&order=id&fields=id,program%5Bid%5D,lastUpdated,name,description,repeatable,captureCoordinates," + + "generatedByEnrollmentDate,minDaysFromStart,programStageDataElements%5Bid,compulsory,allowProvidedElsewhere,dataElement%5Bid,name,code,formName,valueType,optionSetValue,optionSet%5Bid,name,options%5Bcode,name%5D%5D%5D%5D" ) ) + .andRespond( withSuccess( IOUtils.resourceToString( "/org/dhis2/fhir/adapter/dhis/test/default-program-stage.json", StandardCharsets.UTF_8 ), MediaType.APPLICATION_JSON ) ); + + final IGenericClient client = createGenericClient(); + client.registerInterceptor( new BasicAuthInterceptor( "fhir_client", "fhir_client_1" ) ); + Bundle bundle = client.search().forResource( Questionnaire.class ).where( Questionnaire.TITLE.matches().value( "Birth" ) ).returnBundle( Bundle.class ).execute(); + Assert.assertEquals( 1, bundle.getEntry().size() ); + Questionnaire planDefinition = (Questionnaire) bundle.getEntry().get( 0 ).getResource(); + Assert.assertEquals( "Birth", planDefinition.getTitle() ); + } +} diff --git a/app/src/test/java/org/dhis2/fhir/adapter/fhir/transform/fhir/impl/enrollment/AbstractEnrollmentTransformationAppTest.java b/app/src/test/java/org/dhis2/fhir/adapter/fhir/transform/fhir/impl/enrollment/AbstractEnrollmentTransformationAppTest.java index 7b0009fc..aec89b39 100644 --- a/app/src/test/java/org/dhis2/fhir/adapter/fhir/transform/fhir/impl/enrollment/AbstractEnrollmentTransformationAppTest.java +++ b/app/src/test/java/org/dhis2/fhir/adapter/fhir/transform/fhir/impl/enrollment/AbstractEnrollmentTransformationAppTest.java @@ -50,17 +50,16 @@ protected void expectMetadataRequests() throws Exception { systemDhis2Server.expect( ExpectedCount.between( 0, 1 ), method( HttpMethod.GET ) ).andExpect( header( "Authorization", testConfiguration.getDhis2SystemAuthorization() ) ) .andExpect( requestTo( dhis2BaseUrl + "/api/" + dhis2ApiVersion + "/programs/EPDyQuoRnXk.json?fields=id,name,code,description,lastUpdated,selectIncidentDatesInFuture,selectEnrollmentDatesInFuture,displayIncidentDate,registration," + - "withoutRegistration,captureCoordinates,trackedEntityType%5Bid%5D,programTrackedEntityAttributes%5Bid,name,valueType,mandatory,allowFutureDate,trackedEntityAttribute%5Bid,name,code,valueType,generated%5D%5D,programStages%5Bid,name," + - "description,repeatable,captureCoordinates,generatedByEnrollmentDate,minDaysFromStart,programStageDataElements%5Bid,compulsory,allowProvidedElsewhere,dataElement%5Bid,name,code,formName,valueType,optionSetValue,optionSet%5Bid,name," + - "options%5Bcode,name%5D%5D%5D%5D%5D" ) ) + "withoutRegistration,captureCoordinates,trackedEntityType%5Bid%5D,programTrackedEntityAttributes%5Bid,name,valueType,mandatory,allowFutureDate,trackedEntityAttribute%5Bid,name,code,valueType,generated%5D%5D,programStages%5Bid," + + "program%5Bid%5D,lastUpdated,name,description,repeatable,captureCoordinates,generatedByEnrollmentDate,minDaysFromStart,programStageDataElements%5Bid,compulsory,allowProvidedElsewhere,dataElement%5Bid,name,code,formName,valueType," + + "optionSetValue,optionSet%5Bid,name,options%5Bcode,name%5D%5D%5D%5D%5D" ) ) .andRespond( withSuccess( IOUtils.resourceToString( "/org/dhis2/fhir/adapter/dhis/test/single-program.json", StandardCharsets.UTF_8 ), MediaType.APPLICATION_JSON ) ); systemDhis2Server.expect( ExpectedCount.between( 0, 1 ), method( HttpMethod.GET ) ).andExpect( header( "Authorization", testConfiguration.getDhis2SystemAuthorization() ) ) .andExpect( requestTo( dhis2BaseUrl + "/api/" + dhis2ApiVersion + "/programs.json?paging=false&fields=id,name,code,description,lastUpdated,selectIncidentDatesInFuture,selectEnrollmentDatesInFuture,displayIncidentDate,registration," + - "withoutRegistration," + - "captureCoordinates," + - "trackedEntityType%5Bid%5D,programTrackedEntityAttributes%5Bid,name,valueType,mandatory,allowFutureDate,trackedEntityAttribute%5Bid,name,code,valueType,generated%5D%5D,programStages%5Bid,name,description,repeatable,captureCoordinates," + - "generatedByEnrollmentDate,minDaysFromStart,programStageDataElements%5Bid,compulsory,allowProvidedElsewhere,dataElement%5Bid,name,code,description,formName,valueType,optionSetValue,optionSet%5Bid,name,options%5Bcode," + - "name%5D%5D%5D%5D%5D&filter=name:eq:Child%20Programme" ) ) + "withoutRegistration,captureCoordinates,trackedEntityType%5Bid%5D,programTrackedEntityAttributes%5Bid,name,valueType,mandatory,allowFutureDate,trackedEntityAttribute%5Bid,name,code,valueType,generated%5D%5D,programStages%5Bid," + + "program%5Bid%5D," + + "lastUpdated,name,description,repeatable,captureCoordinates,generatedByEnrollmentDate,minDaysFromStart,programStageDataElements%5Bid,compulsory,allowProvidedElsewhere,dataElement%5Bid,name,code,description,formName,valueType,optionSetValue," + + "optionSet%5Bid,name,options%5Bcode,name%5D%5D%5D%5D%5D&filter=name:eq:Child%20Programme" ) ) .andRespond( withSuccess( IOUtils.resourceToString( "/org/dhis2/fhir/adapter/dhis/test/all-programs.json", StandardCharsets.UTF_8 ), MediaType.APPLICATION_JSON ) ); systemDhis2Server.expect( ExpectedCount.between( 0, 1 ), method( HttpMethod.GET ) ).andExpect( header( "Authorization", testConfiguration.getDhis2SystemAuthorization() ) ) .andExpect( requestTo( dhis2BaseUrl + "/api/" + dhis2ApiVersion + "/trackedEntityTypes/MCPQUTHX1Ze.json?fields=id,name,trackedEntityTypeAttributes%5Bid,name,valueType,mandatory,trackedEntityAttribute%5Bid,name,code,valueType,generated," + @@ -75,9 +74,10 @@ protected void expectMetadataRequests() throws Exception .andRespond( withSuccess( IOUtils.resourceToString( "/org/dhis2/fhir/adapter/dhis/test/default-tracked-entity-type.json", StandardCharsets.UTF_8 ), MediaType.APPLICATION_JSON ) ); systemDhis2Server.expect( ExpectedCount.between( 0, 1 ), method( HttpMethod.GET ) ).andExpect( header( "Authorization", testConfiguration.getDhis2SystemAuthorization() ) ) .andExpect( requestTo( dhis2BaseUrl + "/api/" + dhis2ApiVersion + "/programs.json?paging=false&" + - "fields=id,name,code,description,lastUpdated,selectIncidentDatesInFuture,selectEnrollmentDatesInFuture,displayIncidentDate,registration,withoutRegistration,captureCoordinates,trackedEntityType%5Bid%5D,programTrackedEntityAttributes%5Bid,name,valueType," + - "mandatory,allowFutureDate,trackedEntityAttribute%5Bid,name,code,valueType,generated%5D%5D,programStages%5Bid,name,description,repeatable,captureCoordinates,generatedByEnrollmentDate,minDaysFromStart,programStageDataElements%5Bid,compulsory," + - "allowProvidedElsewhere,dataElement%5Bid,name,code,description,formName,valueType,optionSetValue,optionSet%5Bid,name,options%5Bcode,name%5D%5D%5D%5D%5D&filter=name:eq:Child%20Programme" ) ) + "fields=id,name,code,description,lastUpdated,selectIncidentDatesInFuture,selectEnrollmentDatesInFuture,displayIncidentDate,registration,withoutRegistration,captureCoordinates,trackedEntityType%5Bid%5D,programTrackedEntityAttributes%5Bid," + + "name,valueType,mandatory,allowFutureDate,trackedEntityAttribute%5Bid,name,code,valueType,generated%5D%5D,programStages%5Bid,program%5Bid%5D,lastUpdated,name,description,repeatable,captureCoordinates,generatedByEnrollmentDate," + + "minDaysFromStart,programStageDataElements%5Bid,compulsory,allowProvidedElsewhere,dataElement%5Bid,name,code,description,formName,valueType,optionSetValue,optionSet%5Bid,name,options%5Bcode,name%5D%5D%5D%5D%5D" + + "&filter=name:eq:Child%20Programme" ) ) .andRespond( withSuccess( IOUtils.resourceToString( "/org/dhis2/fhir/adapter/dhis/test/single-program.json", StandardCharsets.UTF_8 ), MediaType.APPLICATION_JSON ) ); } } diff --git a/app/src/test/java/org/dhis2/fhir/adapter/fhir/transform/fhir/impl/programstage/AbstractProgramStageTransformationAppTest.java b/app/src/test/java/org/dhis2/fhir/adapter/fhir/transform/fhir/impl/programstage/AbstractProgramStageTransformationAppTest.java index 742bfca8..1cfadd70 100644 --- a/app/src/test/java/org/dhis2/fhir/adapter/fhir/transform/fhir/impl/programstage/AbstractProgramStageTransformationAppTest.java +++ b/app/src/test/java/org/dhis2/fhir/adapter/fhir/transform/fhir/impl/programstage/AbstractProgramStageTransformationAppTest.java @@ -52,7 +52,9 @@ protected void expectMetadataRequests() throws Exception systemDhis2Server.expect( ExpectedCount.between( 0, 1 ), method( HttpMethod.GET ) ).andExpect( header( "Authorization", testConfiguration.getDhis2SystemAuthorization() ) ) .andExpect( requestTo( dhis2BaseUrl + "/api/" + dhis2ApiVersion + "/programs.json?paging=false&fields=id,name,code,description,lastUpdated,selectIncidentDatesInFuture,selectEnrollmentDatesInFuture,displayIncidentDate,registration," + "withoutRegistration," + - "captureCoordinates,trackedEntityType%5Bid%5D,programTrackedEntityAttributes%5Bid,name,valueType,mandatory,allowFutureDate,trackedEntityAttribute%5Bid,name,code,valueType,generated%5D%5D,programStages%5Bid,name,description,repeatable," + + "captureCoordinates,trackedEntityType%5Bid%5D,programTrackedEntityAttributes%5Bid,name,valueType,mandatory,allowFutureDate,trackedEntityAttribute%5Bid,name,code,valueType,generated%5D%5D,programStages%5Bid,program%5Bid%5D,lastUpdated,name," + + "description," + + "repeatable," + "captureCoordinates," + "generatedByEnrollmentDate,minDaysFromStart,programStageDataElements%5Bid,compulsory,allowProvidedElsewhere,dataElement%5Bid,name,code,formName,valueType,optionSetValue,optionSet%5Bid,name,options%5Bcode," + "name%5D%5D%5D%5D%5D&filter=name:eq:Child%20Programme" ) ) @@ -70,8 +72,10 @@ protected void expectMetadataRequests() throws Exception .andRespond( withSuccess( IOUtils.resourceToString( "/org/dhis2/fhir/adapter/dhis/test/default-tracked-entity-type.json", StandardCharsets.UTF_8 ), MediaType.APPLICATION_JSON ) ); systemDhis2Server.expect( ExpectedCount.between( 0, 1 ), method( HttpMethod.GET ) ).andExpect( header( "Authorization", testConfiguration.getDhis2SystemAuthorization() ) ) .andExpect( requestTo( dhis2BaseUrl + "/api/" + dhis2ApiVersion + "/programs.json?paging=false&" + - "fields=id,name,code,description,lastUpdated,selectIncidentDatesInFuture,selectEnrollmentDatesInFuture,displayIncidentDate,registration,withoutRegistration,captureCoordinates,trackedEntityType%5Bid%5D,programTrackedEntityAttributes%5Bid,name,valueType," + - "mandatory,allowFutureDate,trackedEntityAttribute%5Bid,name,code,valueType,generated%5D%5D,programStages%5Bid,name,description,repeatable,captureCoordinates,generatedByEnrollmentDate,minDaysFromStart,programStageDataElements%5Bid,compulsory," + + "fields=id,name,code,description,lastUpdated,selectIncidentDatesInFuture,selectEnrollmentDatesInFuture,displayIncidentDate,registration,withoutRegistration,captureCoordinates,trackedEntityType%5Bid%5D,programTrackedEntityAttributes%5Bid," + + "name,valueType," + + "mandatory,allowFutureDate,trackedEntityAttribute%5Bid,name,code,valueType,generated%5D%5D,programStages%5Bid,program%5Bid%5D,lastUpdated,name,description,repeatable,captureCoordinates,generatedByEnrollmentDate,minDaysFromStart," + + "programStageDataElements%5Bid,compulsory," + "allowProvidedElsewhere,dataElement%5Bid,name,code,description,formName,valueType,optionSetValue,optionSet%5Bid,name,options%5Bcode,name%5D%5D%5D%5D%5D&filter=name:eq:Child%20Programme" ) ) .andRespond( withSuccess( IOUtils.resourceToString( "/org/dhis2/fhir/adapter/dhis/test/single-program.json", StandardCharsets.UTF_8 ), MediaType.APPLICATION_JSON ) ); } diff --git a/app/src/test/resources/org/dhis2/fhir/adapter/dhis/test/all-program-stages.json b/app/src/test/resources/org/dhis2/fhir/adapter/dhis/test/all-program-stages.json new file mode 100644 index 00000000..99a77892 --- /dev/null +++ b/app/src/test/resources/org/dhis2/fhir/adapter/dhis/test/all-program-stages.json @@ -0,0 +1,185 @@ +{ + "programStages": [ + { + "name": "Birth", + "id": "MsWxkiY6tMS", + "program": { + "id": "EPDyQuoRnXk" + }, + "generatedByEnrollmentDate": false, + "repeatable": false, + "minDaysFromStart": 0, + "programStageDataElements": [ + { + "id": "uRAfo19lLtY", + "compulsory": false, + "allowProvidedElsewhere": false, + "dataElement": { + "code": "DE_391382", + "name": "MCH Apgar comment", + "id": "wquzcS4X6He", + "formName": "Apgar comment", + "valueType": "TEXT", + "optionSetValue": false + } + }, + { + "id": "Ije8HSPh71d", + "compulsory": false, + "allowProvidedElsewhere": false, + "dataElement": { + "code": "DE_2006098", + "name": "MCH Apgar Score", + "id": "uzKeaNjgQz9", + "formName": "Apgar Score", + "valueType": "NUMBER", + "optionSetValue": false + } + }, + { + "id": "GLrVTw75zSK", + "compulsory": false, + "allowProvidedElsewhere": false, + "dataElement": { + "code": "DE_2005736", + "name": "MCH Weight (g)", + "id": "BnplxU2jGvX", + "formName": "Weight (g)", + "valueType": "NUMBER", + "optionSetValue": false + } + }, + { + "id": "aQd5KKFge1U", + "compulsory": false, + "allowProvidedElsewhere": false, + "dataElement": { + "code": "DE_2006101", + "name": "MCH BCG dose", + "id": "Lon4lYDF0SH", + "formName": "BCG dose", + "valueType": "BOOLEAN", + "optionSetValue": false + } + }, + { + "id": "lm3Cxju7CL0", + "compulsory": false, + "allowProvidedElsewhere": false, + "dataElement": { + "code": "DE_2006104", + "name": "MCH OPV dose", + "id": "ft7iD5ZzPxJ", + "formName": "OPV dose", + "valueType": "TEXT", + "optionSetValue": true, + "optionSet": { + "name": "MNCH Polio doses (0-3)", + "id": "kzgQRhOCadd", + "options": [ + { + "code": "0", + "name": "Dose 0" + }, + { + "code": "1", + "name": "Dose 1" + }, + { + "code": "2", + "name": "Dose 2" + }, + { + "code": "3", + "name": "Dose 3" + } + ] + } + } + } + ] + }, + { + "name": "Baby Postnatal", + "id": "qowTSevVSkd", + "program": { + "id": "EPDyQuoRnXk" + }, + "generatedByEnrollmentDate": false, + "repeatable": false, + "minDaysFromStart": 6, + "programStageDataElements": [ + { + "id": "s7bKmA7gfXq", + "compulsory": false, + "allowProvidedElsewhere": false, + "dataElement": { + "code": "DE_2006099", + "name": "MCH Infant Weight (g)", + "id": "UnN0Jdrfr4o", + "formName": "Infant Weight (g)", + "valueType": "NUMBER", + "optionSetValue": false + } + }, + { + "id": "g1eWTx5nsNx", + "compulsory": false, + "allowProvidedElsewhere": false, + "dataElement": { + "code": "DE_2006125", + "name": "MCH Measles dose", + "id": "KBLBjsTfNSS", + "formName": "Measles dose", + "valueType": "BOOLEAN", + "optionSetValue": false + } + }, + { + "id": "TKnlmmxUEss", + "compulsory": false, + "allowProvidedElsewhere": false, + "dataElement": { + "code": "DE_2006126", + "name": "MCH Yellow fever dose", + "id": "TNg7ABaHeP7", + "formName": "Yellow fever dose", + "valueType": "BOOLEAN", + "optionSetValue": false + } + }, + { + "id": "mx5k0I1SLDt", + "compulsory": false, + "allowProvidedElsewhere": false, + "dataElement": { + "code": "DE_2006105", + "name": "MCH DPT dose", + "id": "vNGGmCPNfky", + "formName": "DPT dose", + "valueType": "TEXT", + "optionSetValue": true, + "optionSet": { + "name": "MNCH DPT doses (1-3)", + "id": "udkr3ihaeD3", + "options": [ + { + "code": "1", + "name": "Dose 1" + }, + { + "code": "2", + "name": "Dose 2" + }, + { + "code": "3", + "name": "Dose 3" + } + ] + } + } + } + ] + } + ] +} diff --git a/app/src/test/resources/org/dhis2/fhir/adapter/dhis/test/default-program-stage.json b/app/src/test/resources/org/dhis2/fhir/adapter/dhis/test/default-program-stage.json new file mode 100644 index 00000000..15a57544 --- /dev/null +++ b/app/src/test/resources/org/dhis2/fhir/adapter/dhis/test/default-program-stage.json @@ -0,0 +1,101 @@ +{ + "pager": { "page": 1, "pageCount": 1, "total": 0, "pageSize": 1 }, + "programStages": [ + { + "name": "Birth", + "id": "MsWxkiY6tMS", + "generatedByEnrollmentDate": false, + "repeatable": false, + "minDaysFromStart": 0, + "programStageDataElements": [ + { + "id": "uRAfo19lLtY", + "compulsory": false, + "allowProvidedElsewhere": false, + "dataElement": { + "code": "DE_391382", + "name": "MCH Apgar comment", + "id": "wquzcS4X6He", + "formName": "Apgar comment", + "valueType": "TEXT", + "optionSetValue": false + } + }, + { + "id": "Ije8HSPh71d", + "compulsory": false, + "allowProvidedElsewhere": false, + "dataElement": { + "code": "DE_2006098", + "name": "MCH Apgar Score", + "id": "uzKeaNjgQz9", + "formName": "Apgar Score", + "valueType": "NUMBER", + "optionSetValue": false + } + }, + { + "id": "GLrVTw75zSK", + "compulsory": false, + "allowProvidedElsewhere": false, + "dataElement": { + "code": "DE_2005736", + "name": "MCH Weight (g)", + "id": "BnplxU2jGvX", + "formName": "Weight (g)", + "valueType": "NUMBER", + "optionSetValue": false + } + }, + { + "id": "aQd5KKFge1U", + "compulsory": false, + "allowProvidedElsewhere": false, + "dataElement": { + "code": "DE_2006101", + "name": "MCH BCG dose", + "id": "Lon4lYDF0SH", + "formName": "BCG dose", + "valueType": "BOOLEAN", + "optionSetValue": false + } + }, + { + "id": "lm3Cxju7CL0", + "compulsory": false, + "allowProvidedElsewhere": false, + "dataElement": { + "code": "DE_2006104", + "name": "MCH OPV dose", + "id": "ft7iD5ZzPxJ", + "formName": "OPV dose", + "valueType": "TEXT", + "optionSetValue": true, + "optionSet": { + "name": "MNCH Polio doses (0-3)", + "id": "kzgQRhOCadd", + "options": [ + { + "code": "0", + "name": "Dose 0" + }, + { + "code": "1", + "name": "Dose 1" + }, + { + "code": "2", + "name": "Dose 2" + }, + { + "code": "3", + "name": "Dose 3" + } + ] + } + } + } + ] + } + ] +} diff --git a/app/src/test/resources/org/dhis2/fhir/adapter/dhis/test/single-program-stage.json b/app/src/test/resources/org/dhis2/fhir/adapter/dhis/test/single-program-stage.json new file mode 100644 index 00000000..1bad997c --- /dev/null +++ b/app/src/test/resources/org/dhis2/fhir/adapter/dhis/test/single-program-stage.json @@ -0,0 +1,99 @@ +{ + "name": "Birth", + "id": "MsWxkiY6tMS", + "program": { + "id": "EPDyQuoRnXk" + }, + "generatedByEnrollmentDate": false, + "repeatable": false, + "minDaysFromStart": 0, + "programStageDataElements": [ + { + "id": "uRAfo19lLtY", + "compulsory": false, + "allowProvidedElsewhere": false, + "dataElement": { + "code": "DE_391382", + "name": "MCH Apgar comment", + "id": "wquzcS4X6He", + "formName": "Apgar comment", + "valueType": "TEXT", + "optionSetValue": false + } + }, + { + "id": "Ije8HSPh71d", + "compulsory": false, + "allowProvidedElsewhere": false, + "dataElement": { + "code": "DE_2006098", + "name": "MCH Apgar Score", + "id": "uzKeaNjgQz9", + "formName": "Apgar Score", + "valueType": "NUMBER", + "optionSetValue": false + } + }, + { + "id": "GLrVTw75zSK", + "compulsory": false, + "allowProvidedElsewhere": false, + "dataElement": { + "code": "DE_2005736", + "name": "MCH Weight (g)", + "id": "BnplxU2jGvX", + "formName": "Weight (g)", + "valueType": "NUMBER", + "optionSetValue": false + } + }, + { + "id": "aQd5KKFge1U", + "compulsory": false, + "allowProvidedElsewhere": false, + "dataElement": { + "code": "DE_2006101", + "name": "MCH BCG dose", + "id": "Lon4lYDF0SH", + "formName": "BCG dose", + "valueType": "BOOLEAN", + "optionSetValue": false + } + }, + { + "id": "lm3Cxju7CL0", + "compulsory": false, + "allowProvidedElsewhere": false, + "dataElement": { + "code": "DE_2006104", + "name": "MCH OPV dose", + "id": "ft7iD5ZzPxJ", + "formName": "OPV dose", + "valueType": "TEXT", + "optionSetValue": true, + "optionSet": { + "name": "MNCH Polio doses (0-3)", + "id": "kzgQRhOCadd", + "options": [ + { + "code": "0", + "name": "Dose 0" + }, + { + "code": "1", + "name": "Dose 1" + }, + { + "code": "2", + "name": "Dose 2" + }, + { + "code": "3", + "name": "Dose 3" + } + ] + } + } + } + ] +} diff --git a/dhis/src/main/java/org/dhis2/fhir/adapter/dhis/sync/impl/DhisResourceRepositoryImpl.java b/dhis/src/main/java/org/dhis2/fhir/adapter/dhis/sync/impl/DhisResourceRepositoryImpl.java index fef873ad..5d44677c 100644 --- a/dhis/src/main/java/org/dhis2/fhir/adapter/dhis/sync/impl/DhisResourceRepositoryImpl.java +++ b/dhis/src/main/java/org/dhis2/fhir/adapter/dhis/sync/impl/DhisResourceRepositoryImpl.java @@ -39,6 +39,7 @@ import org.dhis2.fhir.adapter.dhis.tracker.program.Event; import org.dhis2.fhir.adapter.dhis.tracker.program.EventService; import org.dhis2.fhir.adapter.dhis.tracker.program.ProgramMetadataService; +import org.dhis2.fhir.adapter.dhis.tracker.program.ProgramStageMetadataService; import org.dhis2.fhir.adapter.dhis.tracker.trackedentity.TrackedEntityInstance; import org.dhis2.fhir.adapter.dhis.tracker.trackedentity.TrackedEntityService; import org.slf4j.Logger; @@ -65,6 +66,8 @@ public class DhisResourceRepositoryImpl implements DhisResourceRepository private final ProgramMetadataService programMetadataService; + private final ProgramStageMetadataService programStageMetadataService; + private final TrackedEntityService trackedEntityService; private final EnrollmentService enrollmentService; @@ -72,10 +75,12 @@ public class DhisResourceRepositoryImpl implements DhisResourceRepository private final EventService eventService; public DhisResourceRepositoryImpl( @Nonnull OrganizationUnitService organizationUnitService, ProgramMetadataService programMetadataService, - @Nonnull TrackedEntityService trackedEntityService, @Nonnull EnrollmentService enrollmentService, @Nonnull EventService eventService ) + @Nonnull ProgramStageMetadataService programStageMetadataService, @Nonnull TrackedEntityService trackedEntityService, + @Nonnull EnrollmentService enrollmentService, @Nonnull EventService eventService ) { this.organizationUnitService = organizationUnitService; this.programMetadataService = programMetadataService; + this.programStageMetadataService = programStageMetadataService; this.trackedEntityService = trackedEntityService; this.enrollmentService = enrollmentService; this.eventService = eventService; @@ -91,6 +96,8 @@ public Optional findRefreshed( @Nonnull DhisResourceId d return organizationUnitService.findOneByReference( new Reference( dhisResourceId.getId(), ReferenceType.ID ) ); case PROGRAM_METADATA: return programMetadataService.findOneByReference( new Reference( dhisResourceId.getId(), ReferenceType.ID ) ); + case PROGRAM_STAGE_METADATA: + return programStageMetadataService.findOneByReference( new Reference( dhisResourceId.getId(), ReferenceType.ID ) ); case TRACKED_ENTITY: return trackedEntityService.findOneByIdRefreshed( dhisResourceId.getId() ); case PROGRAM_STAGE_EVENT: @@ -112,6 +119,7 @@ public Optional findRefreshedDeleted( @Nonnull DhisResou return eventService.findOneDeletedById( dhisResourceId.getId() ); case ORGANIZATION_UNIT: case PROGRAM_METADATA: + case PROGRAM_STAGE_METADATA: case TRACKED_ENTITY: case ENROLLMENT: throw new UnsupportedOperationException( "Retrieving deleted " + dhisResourceId.getType() + " DHIS resource items is not supported." ); diff --git a/dhis/src/main/java/org/dhis2/fhir/adapter/dhis/tracker/program/ImmutableProgramStage.java b/dhis/src/main/java/org/dhis2/fhir/adapter/dhis/tracker/program/ImmutableProgramStage.java index 405d180e..bb461091 100644 --- a/dhis/src/main/java/org/dhis2/fhir/adapter/dhis/tracker/program/ImmutableProgramStage.java +++ b/dhis/src/main/java/org/dhis2/fhir/adapter/dhis/tracker/program/ImmutableProgramStage.java @@ -66,6 +66,13 @@ public Set getAllReferences() return delegate.getAllReferences(); } + @JsonIgnore + @Override + public String getProgramId() + { + return delegate.getProgramId(); + } + @JsonIgnore @Override public String getId() diff --git a/dhis/src/main/java/org/dhis2/fhir/adapter/dhis/tracker/program/ProgramStage.java b/dhis/src/main/java/org/dhis2/fhir/adapter/dhis/tracker/program/ProgramStage.java index 435f596b..0535b110 100644 --- a/dhis/src/main/java/org/dhis2/fhir/adapter/dhis/tracker/program/ProgramStage.java +++ b/dhis/src/main/java/org/dhis2/fhir/adapter/dhis/tracker/program/ProgramStage.java @@ -48,6 +48,8 @@ @Scriptable public interface ProgramStage extends DhisResource, DhisMetadata { + String getProgramId(); + boolean isRepeatable(); boolean isCaptureCoordinates(); diff --git a/dhis/src/main/java/org/dhis2/fhir/adapter/dhis/tracker/program/ProgramStageMetadataService.java b/dhis/src/main/java/org/dhis2/fhir/adapter/dhis/tracker/program/ProgramStageMetadataService.java new file mode 100644 index 00000000..ac5093e7 --- /dev/null +++ b/dhis/src/main/java/org/dhis2/fhir/adapter/dhis/tracker/program/ProgramStageMetadataService.java @@ -0,0 +1,40 @@ +package org.dhis2.fhir.adapter.dhis.tracker.program; + +/* + * Copyright (c) 2004-2019, University of Oslo + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * Neither the name of the HISP project nor the names of its contributors may + * be used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +import org.dhis2.fhir.adapter.dhis.service.DhisMetadataService; + +/** + * Metadata service for DHIS2 program stages. + * + * @author volsch + */ +public interface ProgramStageMetadataService extends DhisMetadataService +{ +} diff --git a/dhis/src/main/java/org/dhis2/fhir/adapter/dhis/tracker/program/WritableProgram.java b/dhis/src/main/java/org/dhis2/fhir/adapter/dhis/tracker/program/WritableProgram.java index 457168ae..ceecf229 100644 --- a/dhis/src/main/java/org/dhis2/fhir/adapter/dhis/tracker/program/WritableProgram.java +++ b/dhis/src/main/java/org/dhis2/fhir/adapter/dhis/tracker/program/WritableProgram.java @@ -139,12 +139,12 @@ public void setDescription( String description ) @Override public String getTrackedEntityTypeId() { - return (trackedEntityType == null) ? null : trackedEntityType.getId(); + return trackedEntityType == null ? null : trackedEntityType.getId(); } public void setTrackedEntityTypeId( String trackedEntityTypeId ) { - this.trackedEntityType = (trackedEntityTypeId == null) ? null : new Id( trackedEntityTypeId ); + this.trackedEntityType = trackedEntityTypeId == null ? null : new Id( trackedEntityTypeId ); } @Override diff --git a/dhis/src/main/java/org/dhis2/fhir/adapter/dhis/tracker/program/WritableProgramStage.java b/dhis/src/main/java/org/dhis2/fhir/adapter/dhis/tracker/program/WritableProgramStage.java index 588b057c..d0474ce4 100644 --- a/dhis/src/main/java/org/dhis2/fhir/adapter/dhis/tracker/program/WritableProgramStage.java +++ b/dhis/src/main/java/org/dhis2/fhir/adapter/dhis/tracker/program/WritableProgramStage.java @@ -34,6 +34,7 @@ import org.dhis2.fhir.adapter.dhis.model.DhisResourceId; import org.dhis2.fhir.adapter.dhis.model.DhisResourceType; import org.dhis2.fhir.adapter.dhis.model.Reference; +import org.dhis2.fhir.adapter.model.Id; import javax.annotation.Nonnull; import javax.annotation.Nullable; @@ -48,6 +49,11 @@ public class WritableProgramStage extends AbstractDhisMetadata implements Progra { private static final long serialVersionUID = -7544648580734783374L; + private ZonedDateTime lastUpdated; + + @JsonProperty + private Id program; + private String id; private String name; @@ -74,6 +80,17 @@ public class WritableProgramStage extends AbstractDhisMetadata implements Progra @JsonIgnore private transient volatile Map dataElementsByCode; + @JsonIgnore + public String getProgramId() + { + return program == null ? null : program.getId(); + } + + public void setProgramId( String programId ) + { + program = programId == null ? null : new Id( programId ); + } + @Override public String getId() { @@ -262,7 +279,7 @@ public boolean isDeleted() @Override public ZonedDateTime getLastUpdated() { - return null; + return lastUpdated; } @Override diff --git a/dhis/src/main/java/org/dhis2/fhir/adapter/dhis/tracker/program/impl/DhisProgramStages.java b/dhis/src/main/java/org/dhis2/fhir/adapter/dhis/tracker/program/impl/DhisProgramStages.java new file mode 100644 index 00000000..8757310e --- /dev/null +++ b/dhis/src/main/java/org/dhis2/fhir/adapter/dhis/tracker/program/impl/DhisProgramStages.java @@ -0,0 +1,62 @@ +package org.dhis2.fhir.adapter.dhis.tracker.program.impl; + +/* + * Copyright (c) 2004-2019, University of Oslo + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * Neither the name of the HISP project nor the names of its contributors may + * be used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +import com.fasterxml.jackson.annotation.JsonProperty; +import org.dhis2.fhir.adapter.dhis.service.impl.DhisMetadataItems; +import org.dhis2.fhir.adapter.dhis.tracker.program.WritableProgramStage; + +import java.io.Serializable; +import java.util.List; + +/** + * Container object used for DHIS2 program stages. + * + * @author volsch + */ +public class DhisProgramStages extends DhisMetadataItems implements Serializable +{ + private static final long serialVersionUID = 1084527285362478422L; + + @JsonProperty( "programStages" ) + public List getProgramStages() + { + return getItems(); + } + + public void setProgramStages( List programStages ) + { + setItems( programStages ); + } + + public List toModel() + { + return getItems(); + } +} diff --git a/dhis/src/main/java/org/dhis2/fhir/adapter/dhis/tracker/program/impl/ProgramMetadataServiceImpl.java b/dhis/src/main/java/org/dhis2/fhir/adapter/dhis/tracker/program/impl/ProgramMetadataServiceImpl.java index a17179c8..eaa0e56d 100644 --- a/dhis/src/main/java/org/dhis2/fhir/adapter/dhis/tracker/program/impl/ProgramMetadataServiceImpl.java +++ b/dhis/src/main/java/org/dhis2/fhir/adapter/dhis/tracker/program/impl/ProgramMetadataServiceImpl.java @@ -53,9 +53,7 @@ public class ProgramMetadataServiceImpl extends AbstractDhisMetadataServiceImpl< "registration,withoutRegistration,captureCoordinates,trackedEntityType[id]," + "programTrackedEntityAttributes[id,name,valueType,mandatory,allowFutureDate," + "trackedEntityAttribute[id,name,code,valueType,generated]]," + - "programStages[id,name,description,repeatable,captureCoordinates,generatedByEnrollmentDate,minDaysFromStart," + - "programStageDataElements[id,compulsory,allowProvidedElsewhere," + - "dataElement[id,name,code,formName,valueType,optionSetValue,optionSet[id,name,options[code,name]]]]]"; + "programStages[" + ProgramStageMetadataServiceImpl.FIELDS + "]"; public ProgramMetadataServiceImpl( @Nonnull @Qualifier( "systemDhis2RestTemplate" ) RestTemplate systemRestTemplate, @Nonnull @Qualifier( "userDhis2RestTemplate" ) RestTemplate userRestTemplate ) { diff --git a/dhis/src/main/java/org/dhis2/fhir/adapter/dhis/tracker/program/impl/ProgramStageMetadataServiceImpl.java b/dhis/src/main/java/org/dhis2/fhir/adapter/dhis/tracker/program/impl/ProgramStageMetadataServiceImpl.java new file mode 100644 index 00000000..554f04bd --- /dev/null +++ b/dhis/src/main/java/org/dhis2/fhir/adapter/dhis/tracker/program/impl/ProgramStageMetadataServiceImpl.java @@ -0,0 +1,86 @@ +package org.dhis2.fhir.adapter.dhis.tracker.program.impl; + +/* + * Copyright (c) 2004-2019, University of Oslo + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * Neither the name of the HISP project nor the names of its contributors may + * be used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +import org.dhis2.fhir.adapter.dhis.model.DhisResourceType; +import org.dhis2.fhir.adapter.dhis.service.impl.AbstractDhisMetadataServiceImpl; +import org.dhis2.fhir.adapter.dhis.service.impl.DhisMetadataItems; +import org.dhis2.fhir.adapter.dhis.tracker.program.ProgramStage; +import org.dhis2.fhir.adapter.dhis.tracker.program.ProgramStageMetadataService; +import org.dhis2.fhir.adapter.dhis.tracker.program.WritableProgramStage; +import org.springframework.beans.factory.annotation.Qualifier; +import org.springframework.stereotype.Service; +import org.springframework.web.client.RestTemplate; + +import javax.annotation.Nonnull; + +/** + * Implementation of a {@link ProgramStageMetadataService}. + * + * @author volsch + */ +@Service +public class ProgramStageMetadataServiceImpl extends AbstractDhisMetadataServiceImpl implements ProgramStageMetadataService +{ + protected static final String FIELDS = "id,program[id],lastUpdated,name,description,repeatable,captureCoordinates,generatedByEnrollmentDate,minDaysFromStart," + + "programStageDataElements[id,compulsory,allowProvidedElsewhere,dataElement[id,name,code,formName,valueType,optionSetValue,optionSet[id,name,options[code,name]]]]"; + + public ProgramStageMetadataServiceImpl( @Nonnull @Qualifier( "systemDhis2RestTemplate" ) RestTemplate systemRestTemplate, @Nonnull @Qualifier( "userDhis2RestTemplate" ) RestTemplate userRestTemplate ) + { + super( systemRestTemplate, userRestTemplate ); + } + + @Nonnull + @Override + public DhisResourceType getDhisResourceType() + { + return DhisResourceType.PROGRAM_STAGE_METADATA; + } + + @Nonnull + @Override + protected Class getItemClass() + { + return WritableProgramStage.class; + } + + @Nonnull + @Override + protected Class> getItemsClass() + { + return DhisProgramStages.class; + } + + @Nonnull + @Override + protected String getFieldNames() + { + return FIELDS; + } +} diff --git a/dhis/src/test/java/org/dhis2/fhir/adapter/dhis/tracker/program/impl/ProgramMetadataServiceImplTest.java b/dhis/src/test/java/org/dhis2/fhir/adapter/dhis/tracker/program/impl/ProgramMetadataServiceImplTest.java index 27653d09..c12402da 100644 --- a/dhis/src/test/java/org/dhis2/fhir/adapter/dhis/tracker/program/impl/ProgramMetadataServiceImplTest.java +++ b/dhis/src/test/java/org/dhis2/fhir/adapter/dhis/tracker/program/impl/ProgramMetadataServiceImplTest.java @@ -82,9 +82,10 @@ public void setUp() public void findMetadataByReferenceId() throws IOException { mockServer.expect( requestTo( "http://localhost:8080/api/programs/93783.json?fields=id,name,code,description,lastUpdated,selectIncidentDatesInFuture,selectEnrollmentDatesInFuture,displayIncidentDate,registration,withoutRegistration," + - "captureCoordinates," + - "trackedEntityType%5Bid%5D,programTrackedEntityAttributes%5Bid,name,valueType,mandatory,allowFutureDate,trackedEntityAttribute%5Bid,name,code,valueType,generated%5D%5D,programStages%5Bid,name,description,repeatable,captureCoordinates," + - "generatedByEnrollmentDate,minDaysFromStart,programStageDataElements%5Bid,compulsory,allowProvidedElsewhere,dataElement%5Bid,name,code,formName,valueType,optionSetValue,optionSet%5Bid,name,options%5Bcode,name%5D%5D%5D%5D%5D" ) ) + "captureCoordinates,trackedEntityType%5Bid%5D,programTrackedEntityAttributes%5Bid,name,valueType,mandatory,allowFutureDate,trackedEntityAttribute%5Bid,name,code,valueType,generated%5D%5D,programStages%5Bid,program%5Bid%5D,lastUpdated,name," + + "description," + + "repeatable,captureCoordinates,generatedByEnrollmentDate,minDaysFromStart,programStageDataElements%5Bid,compulsory,allowProvidedElsewhere,dataElement%5Bid,name,code,formName,valueType,optionSetValue,optionSet%5Bid,name," + + "options%5Bcode,name%5D%5D%5D%5D%5D" ) ) .andExpect( method( HttpMethod.GET ) ).andRespond( withSuccess( IOUtils.resourceToByteArray( "/org/dhis2/fhir/adapter/dhis/tracker/program/impl/program.json" ), MediaType.APPLICATION_JSON ) ); Optional ou = service.findMetadataByReference( new Reference( "93783", ReferenceType.ID ) ); @@ -95,8 +96,10 @@ public void findMetadataByReferenceId() throws IOException @Test public void findMetadataByReferenceIdNotFound() { - mockServer.expect( requestTo( "http://localhost:8080/api/programs/93783.json?fields=id,name,code,description,lastUpdated,selectIncidentDatesInFuture,selectEnrollmentDatesInFuture,displayIncidentDate,registration,withoutRegistration,captureCoordinates," + - "trackedEntityType%5Bid%5D,programTrackedEntityAttributes%5Bid,name,valueType,mandatory,allowFutureDate,trackedEntityAttribute%5Bid,name,code,valueType,generated%5D%5D,programStages%5Bid,name,description,repeatable,captureCoordinates," + + mockServer.expect( requestTo( + "http://localhost:8080/api/programs/93783.json?fields=id,name,code,description,lastUpdated,selectIncidentDatesInFuture,selectEnrollmentDatesInFuture,displayIncidentDate,registration,withoutRegistration,captureCoordinates," + + "trackedEntityType%5Bid%5D,programTrackedEntityAttributes%5Bid,name,valueType,mandatory,allowFutureDate,trackedEntityAttribute%5Bid,name,code,valueType,generated%5D%5D,programStages%5Bid,program%5Bid%5D,lastUpdated,name,description,repeatable," + + "captureCoordinates," + "generatedByEnrollmentDate,minDaysFromStart,programStageDataElements%5Bid,compulsory,allowProvidedElsewhere,dataElement%5Bid,name,code,formName,valueType,optionSetValue,optionSet%5Bid,name,options%5Bcode,name%5D%5D%5D%5D%5D" ) ).andExpect( method( HttpMethod.GET ) ) .andRespond( MockRestResponseCreators.withStatus( HttpStatus.NOT_FOUND ).body( "{}" ).contentType( MediaType.APPLICATION_JSON ) ); @@ -107,8 +110,9 @@ public void findMetadataByReferenceIdNotFound() @Test( expected = HttpServerErrorException.class ) public void findMetadataByReferenceIdServerError() { - mockServer.expect( requestTo( "http://localhost:8080/api/programs/93783.json?fields=id,name,code,description,lastUpdated,selectIncidentDatesInFuture,selectEnrollmentDatesInFuture,displayIncidentDate,registration,withoutRegistration,captureCoordinates," + - "trackedEntityType%5Bid%5D,programTrackedEntityAttributes%5Bid,name,valueType,mandatory,allowFutureDate,trackedEntityAttribute%5Bid,name,code,valueType,generated%5D%5D,programStages%5Bid,name,description,repeatable,captureCoordinates," + + mockServer.expect( requestTo( + "http://localhost:8080/api/programs/93783.json?fields=id,name,code,description,lastUpdated,selectIncidentDatesInFuture,selectEnrollmentDatesInFuture,displayIncidentDate,registration,withoutRegistration,captureCoordinates," + + "trackedEntityType%5Bid%5D,programTrackedEntityAttributes%5Bid,name,valueType,mandatory,allowFutureDate,trackedEntityAttribute%5Bid,name,code,valueType,generated%5D%5D,programStages%5Bid,program%5Bid%5D,lastUpdated,name,description,repeatable,captureCoordinates," + "generatedByEnrollmentDate,minDaysFromStart,programStageDataElements%5Bid,compulsory,allowProvidedElsewhere,dataElement%5Bid,name,code,formName,valueType,optionSetValue,optionSet%5Bid,name,options%5Bcode,name%5D%5D%5D%5D%5D" ) ).andExpect( method( HttpMethod.GET ) ) .andRespond( MockRestResponseCreators.withServerError() ); service.findMetadataByReference( new Reference( "93783", ReferenceType.ID ) ); @@ -117,8 +121,9 @@ public void findMetadataByReferenceIdServerError() @Test public void findMetadataByReferenceCode() throws IOException { - mockServer.expect( requestTo( "http://localhost:8080/api/programs.json?paging=false&fields=id,name,code,description,lastUpdated,selectIncidentDatesInFuture,selectEnrollmentDatesInFuture,displayIncidentDate,registration,withoutRegistration,captureCoordinates," + - "trackedEntityType%5Bid%5D,programTrackedEntityAttributes%5Bid,name,valueType,mandatory,allowFutureDate,trackedEntityAttribute%5Bid,name,code,valueType,generated%5D%5D,programStages%5Bid,name,description,repeatable,captureCoordinates," + + mockServer.expect( requestTo( "http://localhost:8080/api/programs.json?paging=false&fields=id,name,code,description,lastUpdated,selectIncidentDatesInFuture,selectEnrollmentDatesInFuture,displayIncidentDate,registration,withoutRegistration," + + "captureCoordinates," + + "trackedEntityType%5Bid%5D,programTrackedEntityAttributes%5Bid,name,valueType,mandatory,allowFutureDate,trackedEntityAttribute%5Bid,name,code,valueType,generated%5D%5D,programStages%5Bid,program%5Bid%5D,lastUpdated,name,description,repeatable,captureCoordinates," + "generatedByEnrollmentDate,minDaysFromStart,programStageDataElements%5Bid,compulsory,allowProvidedElsewhere,dataElement%5Bid,name,code,formName,valueType,optionSetValue,optionSet%5Bid,name,options%5Bcode," + "name%5D%5D%5D%5D%5D&filter=code:eq:OU_3783" ) ).andExpect( method( HttpMethod.GET ) ) .andRespond( withSuccess( IOUtils.resourceToByteArray( "/org/dhis2/fhir/adapter/dhis/tracker/program/impl/programs.json" ), MediaType.APPLICATION_JSON ) ); @@ -131,8 +136,9 @@ public void findMetadataByReferenceCode() throws IOException @Test public void findMetadataByReferenceCodeNotFound() throws IOException { - mockServer.expect( requestTo( "http://localhost:8080/api/programs.json?paging=false&fields=id,name,code,description,lastUpdated,selectIncidentDatesInFuture,selectEnrollmentDatesInFuture,displayIncidentDate,registration,withoutRegistration,captureCoordinates," + - "trackedEntityType%5Bid%5D,programTrackedEntityAttributes%5Bid,name,valueType,mandatory,allowFutureDate,trackedEntityAttribute%5Bid,name,code,valueType,generated%5D%5D,programStages%5Bid,name,description,repeatable,captureCoordinates," + + mockServer.expect( requestTo( "http://localhost:8080/api/programs.json?paging=false&fields=id,name,code,description,lastUpdated,selectIncidentDatesInFuture,selectEnrollmentDatesInFuture,displayIncidentDate,registration,withoutRegistration," + + "captureCoordinates," + + "trackedEntityType%5Bid%5D,programTrackedEntityAttributes%5Bid,name,valueType,mandatory,allowFutureDate,trackedEntityAttribute%5Bid,name,code,valueType,generated%5D%5D,programStages%5Bid,program%5Bid%5D,lastUpdated,name,description,repeatable,captureCoordinates," + "generatedByEnrollmentDate,minDaysFromStart,programStageDataElements%5Bid,compulsory,allowProvidedElsewhere,dataElement%5Bid,name,code,formName,valueType,optionSetValue,optionSet%5Bid,name,options%5Bcode," + "name%5D%5D%5D%5D%5D&filter=code:eq:OU_3783" ) ).andExpect( method( HttpMethod.GET ) ) .andRespond( withSuccess( IOUtils.resourceToByteArray( "/org/dhis2/fhir/adapter/dhis/tracker/program/impl/emptyPrograms.json" ), MediaType.APPLICATION_JSON ) ); @@ -144,8 +150,9 @@ public void findMetadataByReferenceCodeNotFound() throws IOException @Test public void findMetadataByReferenceName() throws IOException { - mockServer.expect( requestTo( "http://localhost:8080/api/programs.json?paging=false&fields=id,name,code,description,lastUpdated,selectIncidentDatesInFuture,selectEnrollmentDatesInFuture,displayIncidentDate,registration,withoutRegistration,captureCoordinates," + - "trackedEntityType%5Bid%5D,programTrackedEntityAttributes%5Bid,name,valueType,mandatory,allowFutureDate,trackedEntityAttribute%5Bid,name,code,valueType,generated%5D%5D,programStages%5Bid,name,description,repeatable,captureCoordinates," + + mockServer.expect( requestTo( "http://localhost:8080/api/programs.json?paging=false&fields=id,name,code,description,lastUpdated,selectIncidentDatesInFuture,selectEnrollmentDatesInFuture,displayIncidentDate,registration,withoutRegistration," + + "captureCoordinates," + + "trackedEntityType%5Bid%5D,programTrackedEntityAttributes%5Bid,name,valueType,mandatory,allowFutureDate,trackedEntityAttribute%5Bid,name,code,valueType,generated%5D%5D,programStages%5Bid,program%5Bid%5D,lastUpdated,name,description,repeatable,captureCoordinates," + "generatedByEnrollmentDate,minDaysFromStart,programStageDataElements%5Bid,compulsory,allowProvidedElsewhere,dataElement%5Bid,name,code,formName,valueType,optionSetValue,optionSet%5Bid,name,options%5Bcode," + "name%5D%5D%5D%5D%5D&filter=name:eq:Freetown" ) ).andExpect( method( HttpMethod.GET ) ) .andRespond( withSuccess( IOUtils.resourceToByteArray( "/org/dhis2/fhir/adapter/dhis/tracker/program/impl/programs.json" ), MediaType.APPLICATION_JSON ) ); @@ -158,8 +165,9 @@ public void findMetadataByReferenceName() throws IOException @Test public void findMetadataByReferenceNameNotFound() throws IOException { - mockServer.expect( requestTo( "http://localhost:8080/api/programs.json?paging=false&fields=id,name,code,description,lastUpdated,selectIncidentDatesInFuture,selectEnrollmentDatesInFuture,displayIncidentDate,registration,withoutRegistration,captureCoordinates," + - "trackedEntityType%5Bid%5D,programTrackedEntityAttributes%5Bid,name,valueType,mandatory,allowFutureDate,trackedEntityAttribute%5Bid,name,code,valueType,generated%5D%5D,programStages%5Bid,name,description,repeatable,captureCoordinates," + + mockServer.expect( requestTo( "http://localhost:8080/api/programs.json?paging=false&fields=id,name,code,description,lastUpdated,selectIncidentDatesInFuture,selectEnrollmentDatesInFuture,displayIncidentDate,registration,withoutRegistration," + + "captureCoordinates," + + "trackedEntityType%5Bid%5D,programTrackedEntityAttributes%5Bid,name,valueType,mandatory,allowFutureDate,trackedEntityAttribute%5Bid,name,code,valueType,generated%5D%5D,programStages%5Bid,program%5Bid%5D,lastUpdated,name,description,repeatable,captureCoordinates," + "generatedByEnrollmentDate,minDaysFromStart,programStageDataElements%5Bid,compulsory,allowProvidedElsewhere,dataElement%5Bid,name,code,formName,valueType,optionSetValue,optionSet%5Bid,name,options%5Bcode," + "name%5D%5D%5D%5D%5D&filter=name:eq:Freetown" ) ).andExpect( method( HttpMethod.GET ) ) .andRespond( withSuccess( IOUtils.resourceToByteArray( "/org/dhis2/fhir/adapter/dhis/tracker/program/impl/emptyPrograms.json" ), MediaType.APPLICATION_JSON ) ); diff --git a/dhis/src/test/java/org/dhis2/fhir/adapter/dhis/tracker/program/impl/ProgramStageMetadataServiceImplTest.java b/dhis/src/test/java/org/dhis2/fhir/adapter/dhis/tracker/program/impl/ProgramStageMetadataServiceImplTest.java new file mode 100644 index 00000000..eba3e0b4 --- /dev/null +++ b/dhis/src/test/java/org/dhis2/fhir/adapter/dhis/tracker/program/impl/ProgramStageMetadataServiceImplTest.java @@ -0,0 +1,156 @@ +package org.dhis2.fhir.adapter.dhis.tracker.program.impl; + +/* + * Copyright (c) 2004-2019, University of Oslo + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * Neither the name of the HISP project nor the names of its contributors may + * be used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +import org.apache.commons.io.IOUtils; +import org.dhis2.fhir.adapter.dhis.model.Reference; +import org.dhis2.fhir.adapter.dhis.model.ReferenceType; +import org.dhis2.fhir.adapter.dhis.tracker.program.ProgramStage; +import org.dhis2.fhir.adapter.dhis.tracker.program.ProgramStageMetadataService; +import org.dhis2.fhir.adapter.model.ValueType; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; +import org.springframework.boot.web.client.RestTemplateBuilder; +import org.springframework.http.HttpMethod; +import org.springframework.http.HttpStatus; +import org.springframework.http.MediaType; +import org.springframework.test.web.client.MockRestServiceServer; +import org.springframework.test.web.client.response.MockRestResponseCreators; +import org.springframework.web.client.HttpServerErrorException; +import org.springframework.web.client.RestTemplate; + +import java.io.IOException; +import java.util.Optional; + +import static org.junit.Assert.*; +import static org.springframework.test.web.client.match.MockRestRequestMatchers.method; +import static org.springframework.test.web.client.match.MockRestRequestMatchers.requestTo; +import static org.springframework.test.web.client.response.MockRestResponseCreators.withSuccess; + +/** + * Unit tests for {@link ProgramStageMetadataServiceImpl}. + * + * @author volsch + */ +public class ProgramStageMetadataServiceImplTest +{ + private RestTemplate systemRestTemplate; + + private RestTemplate userRestTemplate; + + private MockRestServiceServer mockServer; + + private ProgramStageMetadataService service; + + @Before + public void setUp() + { + systemRestTemplate = new RestTemplateBuilder().rootUri( "http://localhost:8080/api" ).build(); + userRestTemplate = new RestTemplateBuilder().rootUri( "http://localhost:8080/api" ).build(); + mockServer = MockRestServiceServer.createServer( systemRestTemplate ); + service = new ProgramStageMetadataServiceImpl( systemRestTemplate, userRestTemplate ); + } + + @Test + public void findMetadataByReferenceId() throws IOException + { + mockServer.expect( requestTo( "http://localhost:8080/api/programStages/93783.json?fields=id,program%5Bid%5D,lastUpdated,name,description,repeatable,captureCoordinates,generatedByEnrollmentDate,minDaysFromStart,programStageDataElements%5Bid," + + "compulsory," + + "allowProvidedElsewhere,dataElement%5Bid,name,code,formName,valueType,optionSetValue,optionSet%5Bid,name,options%5Bcode,name%5D%5D%5D%5D" ) ) + .andExpect( method( HttpMethod.GET ) ).andRespond( withSuccess( IOUtils.resourceToByteArray( "/org/dhis2/fhir/adapter/dhis/tracker/program/impl/programStage.json" ), MediaType.APPLICATION_JSON ) ); + + Optional ou = service.findMetadataByReference( new Reference( "93783", ReferenceType.ID ) ); + Assert.assertTrue( ou.isPresent() ); + assertProgramStage( ou.get() ); + } + + @Test + public void findMetadataByReferenceIdNotFound() + { + mockServer.expect( requestTo( "http://localhost:8080/api/programStages/93783.json?fields=id,program%5Bid%5D,lastUpdated,name,description,repeatable,captureCoordinates,generatedByEnrollmentDate,minDaysFromStart,programStageDataElements%5Bid,compulsory," + + "allowProvidedElsewhere,dataElement%5Bid,name,code,formName,valueType,optionSetValue,optionSet%5Bid,name,options%5Bcode,name%5D%5D%5D%5D" ) ).andExpect( method( HttpMethod.GET ) ) + .andRespond( MockRestResponseCreators.withStatus( HttpStatus.NOT_FOUND ).body( "{}" ).contentType( MediaType.APPLICATION_JSON ) ); + + Optional ou = service.findMetadataByReference( new Reference( "93783", ReferenceType.ID ) ); + Assert.assertFalse( ou.isPresent() ); + } + + @Test( expected = HttpServerErrorException.class ) + public void findMetadataByReferenceIdServerError() + { + mockServer.expect( requestTo( "http://localhost:8080/api/programStages/93783.json?fields=id,program%5Bid%5D,lastUpdated,name,description,repeatable,captureCoordinates,generatedByEnrollmentDate,minDaysFromStart,programStageDataElements%5Bid,compulsory," + + "allowProvidedElsewhere,dataElement%5Bid,name,code,formName,valueType,optionSetValue,optionSet%5Bid,name,options%5Bcode,name%5D%5D%5D%5D" ) ).andExpect( method( HttpMethod.GET ) ) + .andRespond( MockRestResponseCreators.withServerError() ); + service.findMetadataByReference( new Reference( "93783", ReferenceType.ID ) ); + } + + @Test + public void findMetadataByReferenceName() throws IOException + { + mockServer.expect( requestTo( "http://localhost:8080/api/programStages.json?paging=false&fields=id,program%5Bid%5D,lastUpdated,name,description,repeatable,captureCoordinates,generatedByEnrollmentDate,minDaysFromStart,programStageDataElements%5Bid,compulsory," + + "allowProvidedElsewhere,dataElement%5Bid,name,code,formName,valueType,optionSetValue,optionSet%5Bid,name,options%5Bcode,name%5D%5D%5D%5D&filter=name:eq:Birth" ) ).andExpect( method( HttpMethod.GET ) ) + .andRespond( withSuccess( IOUtils.resourceToByteArray( "/org/dhis2/fhir/adapter/dhis/tracker/program/impl/programStages.json" ), MediaType.APPLICATION_JSON ) ); + + Optional ou = service.findMetadataByReference( new Reference( "Birth", ReferenceType.NAME ) ); + Assert.assertTrue( ou.isPresent() ); + assertProgramStage( ou.get() ); + } + + @Test + public void findMetadataByReferenceNameNotFound() throws IOException + { + mockServer.expect( requestTo( "http://localhost:8080/api/programStages.json?paging=false&fields=id,program%5Bid%5D,lastUpdated,name,description,repeatable,captureCoordinates,generatedByEnrollmentDate,minDaysFromStart,programStageDataElements%5Bid,compulsory," + + "allowProvidedElsewhere,dataElement%5Bid,name,code,formName,valueType,optionSetValue,optionSet%5Bid,name,options%5Bcode,name%5D%5D%5D%5D&filter=name:eq:xBirth" ) ).andExpect( method( HttpMethod.GET ) ) + .andRespond( withSuccess( IOUtils.resourceToByteArray( "/org/dhis2/fhir/adapter/dhis/tracker/program/impl/emptyPrograms.json" ), MediaType.APPLICATION_JSON ) ); + + Optional ou = service.findMetadataByReference( new Reference( "xBirth", ReferenceType.NAME ) ); + Assert.assertFalse( ou.isPresent() ); + } + + private void assertProgramStage( ProgramStage programStage ) + { + assertNotNull( programStage ); + assertEquals( "A03MvHHogjR", programStage.getId() ); + assertEquals( "IpHINAT79UW", programStage.getProgramId() ); + assertEquals( "Birth", programStage.getName() ); + assertEquals( 2, programStage.getMinDaysFromStart() ); + assertNotNull( programStage.getDataElements() ); + assertEquals( "a3kGcGDCuk6", programStage.getDataElements().get( 0 ).getElement().getId() ); + assertEquals( "MCH Apgar Score", programStage.getDataElements().get( 0 ).getElement().getName() ); + assertEquals( "DE_2006098", programStage.getDataElements().get( 0 ).getElement().getCode() ); + assertEquals( ValueType.NUMBER, programStage.getDataElements().get( 0 ).getElement().getValueType() ); + assertTrue( programStage.getDataElements().get( 0 ).isAllowProvidedElsewhere() ); + assertTrue( programStage.getDataElements().get( 0 ).isCompulsory() ); + assertNull( programStage.getDataElements().get( 0 ).getElement().getOptionSet() ); + assertTrue( programStage.isGeneratedByEnrollmentDate() ); + assertTrue( programStage.isCaptureCoordinates() ); + assertTrue( programStage.isRepeatable() ); + } +} \ No newline at end of file diff --git a/dhis/src/test/resources/org/dhis2/fhir/adapter/dhis/tracker/program/impl/emptyProgramStages.json b/dhis/src/test/resources/org/dhis2/fhir/adapter/dhis/tracker/program/impl/emptyProgramStages.json new file mode 100644 index 00000000..6c232bc6 --- /dev/null +++ b/dhis/src/test/resources/org/dhis2/fhir/adapter/dhis/tracker/program/impl/emptyProgramStages.json @@ -0,0 +1,3 @@ +{ + "programStages": [ ] +} \ No newline at end of file diff --git a/dhis/src/test/resources/org/dhis2/fhir/adapter/dhis/tracker/program/impl/programStage.json b/dhis/src/test/resources/org/dhis2/fhir/adapter/dhis/tracker/program/impl/programStage.json new file mode 100644 index 00000000..93b9829f --- /dev/null +++ b/dhis/src/test/resources/org/dhis2/fhir/adapter/dhis/tracker/program/impl/programStage.json @@ -0,0 +1,11 @@ +{ + "name": "Birth", "id": "A03MvHHogjR", "program": { "id": "IpHINAT79UW" }, "generatedByEnrollmentDate": true, "repeatable": true, "captureCoordinates": true, "minDaysFromStart": 2, "programStageDataElements": [ + { "id": "LBNxoXdMnkv", "compulsory": true, "allowProvidedElsewhere": true, "dataElement": { "code": "DE_2006098", "name": "MCH Apgar Score", "id": "a3kGcGDCuk6", "valueType": "NUMBER", "optionSetValue": false } }, + { + "id": "O4dwFWakvGO", "compulsory": false, "allowProvidedElsewhere": false, "dataElement": { + "code": "DE_2006104", "name": "MCH OPV dose", "id": "ebaJjqltK5N", "valueType": "TEXT", "optionSetValue": true, + "optionSet": { "name": "MNCH Polio doses (0-3)", "id": "kzgQRhOCadd", "options": [ { "code": "0", "name": "Dose 0" }, { "code": "1", "name": "Dose 1" } ] } + } + } +] +} diff --git a/dhis/src/test/resources/org/dhis2/fhir/adapter/dhis/tracker/program/impl/programStages.json b/dhis/src/test/resources/org/dhis2/fhir/adapter/dhis/tracker/program/impl/programStages.json new file mode 100644 index 00000000..8be57dd1 --- /dev/null +++ b/dhis/src/test/resources/org/dhis2/fhir/adapter/dhis/tracker/program/impl/programStages.json @@ -0,0 +1,24 @@ +{ + "programStages": [ + { + "name": "Birth", "id": "A03MvHHogjR", "program": { "id": "IpHINAT79UW" }, "generatedByEnrollmentDate": true, "repeatable": true, "captureCoordinates": true, "minDaysFromStart": 2, "programStageDataElements": [ + { "id": "LBNxoXdMnkv", "compulsory": true, "allowProvidedElsewhere": true, "dataElement": { "code": "DE_2006098", "name": "MCH Apgar Score", "id": "a3kGcGDCuk6", "valueType": "NUMBER", "optionSetValue": false } }, + { + "id": "O4dwFWakvGO", "compulsory": false, "allowProvidedElsewhere": false, "dataElement": { + "code": "DE_2006104", "name": "MCH OPV dose", "id": "ebaJjqltK5N", "valueType": "TEXT", "optionSetValue": true, + "optionSet": { "name": "MNCH Polio doses (0-3)", "id": "kzgQRhOCadd", "options": [ { "code": "0", "name": "Dose 0" }, { "code": "1", "name": "Dose 1" } ] } + } + } + ] + }, { + "name": "Baby Postnatal", "id": "ZzYYXq4fJie", "generatedByEnrollmentDate": false, "repeatable": false, "captureCoordinates": false, "minDaysFromStart": 6, "programStageDataElements": [ + { "id": "ztoQtbuXzsI", "compulsory": false, "allowProvidedElsewhere": true, "dataElement": { "code": "DE_2006099", "name": "MCH Infant Weight (g)", "id": "GQY2lXrypjO", "valueType": "NUMBER", "optionSetValue": false } } + ] + } + ], "programTrackedEntityAttributes": [ + { + "name": "Child Programme First name", "id": "l2T72XzBCLd", "program": { "id": "IpHINAT79UW" }, "valueType": "TEXT", "allowFutureDate": true, "mandatory": true, + "trackedEntityAttribute": { "code": "MMD_PER_NAM", "name": "First name", "id": "w75KJ2mc4zz", "generated": true, "valueType": "TEXT" } + }, { "name": "Child Programme Last name", "id": "XmRd2ZDjWhF", "valueType": "TEXT", "allowFutureDate": false, "mandatory": false, "trackedEntityAttribute": { "name": "Last name", "id": "zDhUuAYrxNC", "generated": false, "valueType": "TEXT" } } +] +} diff --git a/docs/fhir/profiles/TrackerProgramPlanDefinition.StructureDefinition.xml b/docs/fhir/profiles/TrackerProgramPlanDefinition.StructureDefinition.xml index 38b47048..bf33d0bb 100644 --- a/docs/fhir/profiles/TrackerProgramPlanDefinition.StructureDefinition.xml +++ b/docs/fhir/profiles/TrackerProgramPlanDefinition.StructureDefinition.xml @@ -96,13 +96,9 @@ - - - - - + diff --git a/docs/fhir/profiles/TrackerProgramQuestionnaire.StructureDefinition.xml b/docs/fhir/profiles/TrackerProgramQuestionnaire.StructureDefinition.xml index 0702ebad..eeef7fc0 100644 --- a/docs/fhir/profiles/TrackerProgramQuestionnaire.StructureDefinition.xml +++ b/docs/fhir/profiles/TrackerProgramQuestionnaire.StructureDefinition.xml @@ -96,9 +96,13 @@ + + + + - + @@ -186,6 +190,10 @@ + + + + @@ -200,6 +208,12 @@ + + + + + + diff --git a/fhir-r4/src/main/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/metadata/r4/R4ProgramMetadataToFhirDataProvider.java b/fhir-r4/src/main/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/metadata/r4/R4ProgramMetadataToFhirDataProvider.java index 180a4560..da5594af 100644 --- a/fhir-r4/src/main/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/metadata/r4/R4ProgramMetadataToFhirDataProvider.java +++ b/fhir-r4/src/main/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/metadata/r4/R4ProgramMetadataToFhirDataProvider.java @@ -65,6 +65,7 @@ public Set getFhirVersions() @Override protected void initSearchFilter( @Nonnull FhirVersion fhirVersion, @Nonnull RuleInfo ruleInfo, @Nonnull SearchFilter searchFilter ) { - searchFilter.add( PlanDefinition.SP_NAME, SearchParamType.STRING, "name" ); + searchFilter.add( PlanDefinition.SP_NAME, SearchParamType.STRING, "code" ); + searchFilter.add( PlanDefinition.SP_TITLE, SearchParamType.STRING, "name" ); } } diff --git a/fhir-r4/src/main/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/metadata/r4/R4ProgramMetadataToFhirPlanDefinitionTransformer.java b/fhir-r4/src/main/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/metadata/r4/R4ProgramMetadataToFhirPlanDefinitionTransformer.java index 6002c267..44333c79 100644 --- a/fhir-r4/src/main/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/metadata/r4/R4ProgramMetadataToFhirPlanDefinitionTransformer.java +++ b/fhir-r4/src/main/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/metadata/r4/R4ProgramMetadataToFhirPlanDefinitionTransformer.java @@ -92,7 +92,8 @@ protected boolean transformInternal( @Nonnull FhirClient fhirClient, @Nonnull Dh } fhirPlanDefinition.setUrl( dhisProgram.getId() ); - fhirPlanDefinition.setName( dhisProgram.getName() ); + fhirPlanDefinition.setName( dhisProgram.getCode() ); + fhirPlanDefinition.setTitle( dhisProgram.getName() ); fhirPlanDefinition.setStatus( Enumerations.PublicationStatus.ACTIVE ); fhirPlanDefinition.setDescription( dhisProgram.getDescription() ); fhirPlanDefinition.setAction( null ); diff --git a/fhir-r4/src/main/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/metadata/r4/R4ProgramStageMetadataToFhirDataProvider.java b/fhir-r4/src/main/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/metadata/r4/R4ProgramStageMetadataToFhirDataProvider.java new file mode 100644 index 00000000..5801cc6b --- /dev/null +++ b/fhir-r4/src/main/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/metadata/r4/R4ProgramStageMetadataToFhirDataProvider.java @@ -0,0 +1,70 @@ +package org.dhis2.fhir.adapter.fhir.transform.dhis.impl.metadata.r4; + +/* + * Copyright (c) 2004-2019, University of Oslo + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * Neither the name of the HISP project nor the names of its contributors may + * be used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +import org.dhis2.fhir.adapter.dhis.tracker.program.ProgramStageMetadataService; +import org.dhis2.fhir.adapter.fhir.metadata.model.ProgramStageMetadataRule; +import org.dhis2.fhir.adapter.fhir.metadata.model.RuleInfo; +import org.dhis2.fhir.adapter.fhir.model.FhirVersion; +import org.dhis2.fhir.adapter.fhir.script.ScriptExecutor; +import org.dhis2.fhir.adapter.fhir.transform.dhis.impl.metadata.program.AbstractProgramStageMetadataToFhirDataProvider; +import org.dhis2.fhir.adapter.fhir.transform.dhis.search.SearchFilter; +import org.dhis2.fhir.adapter.fhir.transform.dhis.search.SearchParamType; +import org.hl7.fhir.r4.model.Questionnaire; +import org.springframework.stereotype.Component; + +import javax.annotation.Nonnull; +import java.util.Set; + +/** + * R4 specific version of DHIS2 Program Stage Metadata data provider. + * + * @author volsch + */ +@Component +public class R4ProgramStageMetadataToFhirDataProvider extends AbstractProgramStageMetadataToFhirDataProvider +{ + public R4ProgramStageMetadataToFhirDataProvider( @Nonnull ScriptExecutor scriptExecutor, @Nonnull ProgramStageMetadataService programStageMetadataService ) + { + super( scriptExecutor, programStageMetadataService ); + } + + @Nonnull + @Override + public Set getFhirVersions() + { + return FhirVersion.R4_ONLY; + } + + @Override + protected void initSearchFilter( @Nonnull FhirVersion fhirVersion, @Nonnull RuleInfo ruleInfo, @Nonnull SearchFilter searchFilter ) + { + searchFilter.add( Questionnaire.SP_TITLE, SearchParamType.STRING, "name" ); + } +} diff --git a/fhir-r4/src/main/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/metadata/r4/R4ProgramStageMetadataToFhirQuestionnaireTransformer.java b/fhir-r4/src/main/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/metadata/r4/R4ProgramStageMetadataToFhirQuestionnaireTransformer.java new file mode 100644 index 00000000..2575b8ee --- /dev/null +++ b/fhir-r4/src/main/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/metadata/r4/R4ProgramStageMetadataToFhirQuestionnaireTransformer.java @@ -0,0 +1,158 @@ +package org.dhis2.fhir.adapter.fhir.transform.dhis.impl.metadata.r4; + +/* + * Copyright (c) 2004-2019, University of Oslo + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * Neither the name of the HISP project nor the names of its contributors may + * be used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +import org.dhis2.fhir.adapter.dhis.orgunit.OrganizationUnitService; +import org.dhis2.fhir.adapter.dhis.tracker.program.ProgramStage; +import org.dhis2.fhir.adapter.fhir.data.repository.FhirDhisAssignmentRepository; +import org.dhis2.fhir.adapter.fhir.metadata.model.FhirClient; +import org.dhis2.fhir.adapter.fhir.metadata.model.ProgramStageMetadataRule; +import org.dhis2.fhir.adapter.fhir.metadata.model.RuleInfo; +import org.dhis2.fhir.adapter.fhir.metadata.repository.SystemRepository; +import org.dhis2.fhir.adapter.fhir.model.FhirVersion; +import org.dhis2.fhir.adapter.fhir.repository.FhirResourceRepository; +import org.dhis2.fhir.adapter.fhir.script.ScriptExecutor; +import org.dhis2.fhir.adapter.fhir.transform.dhis.DhisToFhirTransformerContext; +import org.dhis2.fhir.adapter.fhir.transform.dhis.impl.metadata.program.AbstractProgramStageMetadataToFhirQuestionnaireTransformer; +import org.dhis2.fhir.adapter.fhir.transform.scripted.AccessibleScriptedDhisMetadata; +import org.dhis2.fhir.adapter.lock.LockManager; +import org.dhis2.fhir.adapter.model.ValueType; +import org.hl7.fhir.instance.model.api.IBaseResource; +import org.hl7.fhir.r4.model.Coding; +import org.hl7.fhir.r4.model.Enumerations; +import org.hl7.fhir.r4.model.Questionnaire; +import org.hl7.fhir.r4.model.Questionnaire.QuestionnaireItemComponent; +import org.hl7.fhir.r4.model.Questionnaire.QuestionnaireItemType; +import org.springframework.stereotype.Component; + +import javax.annotation.Nonnull; +import javax.annotation.Nullable; +import java.util.Map; +import java.util.Set; + +import static org.hl7.fhir.r4.model.Questionnaire.QuestionnaireItemType.*; + +/** + * R4 specific version of DHIS2 Program Stage Metadata to FHIR Questionnaire transformer. + * + * @author volsch + */ +@Component +public class R4ProgramStageMetadataToFhirQuestionnaireTransformer extends AbstractProgramStageMetadataToFhirQuestionnaireTransformer +{ + public R4ProgramStageMetadataToFhirQuestionnaireTransformer( @Nonnull ScriptExecutor scriptExecutor, @Nonnull LockManager lockManager, @Nonnull SystemRepository systemRepository, @Nonnull FhirResourceRepository fhirResourceRepository, + @Nonnull FhirDhisAssignmentRepository fhirDhisAssignmentRepository, @Nonnull OrganizationUnitService organizationUnitService ) + { + super( scriptExecutor, lockManager, systemRepository, fhirResourceRepository, fhirDhisAssignmentRepository, organizationUnitService ); + } + + @Nonnull + @Override + public Set getFhirVersions() + { + return FhirVersion.R4_ONLY; + } + + @Override + protected boolean transformInternal( @Nonnull FhirClient fhirClient, @Nonnull DhisToFhirTransformerContext context, @Nonnull RuleInfo ruleInfo, @Nonnull Map scriptVariables, + @Nonnull AccessibleScriptedDhisMetadata input, @Nonnull IBaseResource output ) + { + final ProgramStage dhisProgramStage = (ProgramStage) input.getDhisResource(); + final Questionnaire fhirQuestionnaire = (Questionnaire) output; + + fhirQuestionnaire.setUrl( dhisProgramStage.getId() ); + fhirQuestionnaire.setTitle( dhisProgramStage.getName() ); + fhirQuestionnaire.setStatus( Enumerations.PublicationStatus.ACTIVE ); + fhirQuestionnaire.setDescription( dhisProgramStage.getDescription() ); + fhirQuestionnaire.setItem( null ); + + dhisProgramStage.getDataElements().forEach( dataElement -> { + final QuestionnaireItemType type = convertValueType( dataElement.getElement().getValueType() ); + + if ( type != null ) + { + final QuestionnaireItemComponent itemComponent = fhirQuestionnaire.addItem(); + + itemComponent.setLinkId( dataElement.getElementId() ); + itemComponent.setText( dataElement.getElement().getName() ); + itemComponent.setRequired( dataElement.isCompulsory() ); + itemComponent.setType( type ); + + if ( dataElement.getElement().isOptionSetValue() ) + { + dataElement.getElement().getOptionSet().getOptions().forEach( option -> itemComponent.addAnswerOption().setValue( + new Coding().setCode( option.getCode() ).setDisplay( option.getName() ) ) ); + } + } + } ); + + return true; + } + + @Nullable + protected QuestionnaireItemType convertValueType( @Nonnull ValueType valueType ) + { + switch ( valueType ) + { + case TEXT: + case EMAIL: + case LETTER: + case ORGANISATION_UNIT: + case PHONE_NUMBER: + case TRACKER_ASSOCIATE: + case URL: + case USERNAME: + return STRING; + case LONG_TEXT: + return TEXT; + case INTEGER: + case INTEGER_POSITIVE: + case INTEGER_NEGATIVE: + case INTEGER_ZERO_OR_POSITIVE: + return INTEGER; + case NUMBER: + case PERCENTAGE: + case UNIT_INTERVAL: + return DECIMAL; + case DATETIME: + case AGE: + return DATETIME; + case DATE: + return DATE; + case TIME: + return TIME; + case BOOLEAN: + case TRUE_ONLY: + return BOOLEAN; + } + + // unhandled data type + return null; + } +} diff --git a/fhir-r4/src/test/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/metadata/r4/R4ProgramMetadataToFhirDataProviderTest.java b/fhir-r4/src/test/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/metadata/r4/R4ProgramMetadataToFhirDataProviderTest.java index 4798edd1..6ed3a090 100644 --- a/fhir-r4/src/test/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/metadata/r4/R4ProgramMetadataToFhirDataProviderTest.java +++ b/fhir-r4/src/test/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/metadata/r4/R4ProgramMetadataToFhirDataProviderTest.java @@ -79,7 +79,7 @@ public void getFhirVersions() @Test public void searchFilterName() throws URISyntaxException { - final SearchFilterCollector searchFilterCollector = new SearchFilterCollector( Collections.singletonMap( "name", Collections.singletonList( "Child Programme" ) ) ); + final SearchFilterCollector searchFilterCollector = new SearchFilterCollector( Collections.singletonMap( "title", Collections.singletonList( "Child Programme" ) ) ); final SearchFilter searchFilter = new SearchFilter( searchFilterCollector, false ); provider.initSearchFilter( FhirVersion.R4, new RuleInfo<>( new ProgramMetadataRule(), Collections.emptyList() ), searchFilter ); diff --git a/fhir-r4/src/test/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/metadata/r4/R4ProgramMetadataToFhirPlanDefinitionTransformerTest.java b/fhir-r4/src/test/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/metadata/r4/R4ProgramMetadataToFhirPlanDefinitionTransformerTest.java index 3d14b07b..adccd703 100644 --- a/fhir-r4/src/test/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/metadata/r4/R4ProgramMetadataToFhirPlanDefinitionTransformerTest.java +++ b/fhir-r4/src/test/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/metadata/r4/R4ProgramMetadataToFhirPlanDefinitionTransformerTest.java @@ -152,7 +152,7 @@ public void transformInternal() transformer.transformInternal( fhirClient, context, new RuleInfo<>( new ProgramMetadataRule(), Collections.emptyList() ), new HashMap<>(), new WritableScriptedDhisMetadata( program, scriptExecutionContext ), fhirPlanDefinition ); - Assert.assertEquals( "Test Program", fhirPlanDefinition.getName() ); + Assert.assertEquals( "Test Program", fhirPlanDefinition.getTitle() ); Assert.assertEquals( "Test Description", fhirPlanDefinition.getDescription() ); Assert.assertEquals( 2, fhirPlanDefinition.getAction().size() ); diff --git a/fhir-r4/src/test/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/metadata/r4/R4ProgramStageMetadataToFhirDataProviderTest.java b/fhir-r4/src/test/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/metadata/r4/R4ProgramStageMetadataToFhirDataProviderTest.java new file mode 100644 index 00000000..8b2c37f7 --- /dev/null +++ b/fhir-r4/src/test/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/metadata/r4/R4ProgramStageMetadataToFhirDataProviderTest.java @@ -0,0 +1,93 @@ +package org.dhis2.fhir.adapter.fhir.transform.dhis.impl.metadata.r4; + +/* + * Copyright (c) 2004-2019, University of Oslo + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * Neither the name of the HISP project nor the names of its contributors may + * be used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +import org.dhis2.fhir.adapter.dhis.tracker.program.ProgramStageMetadataService; +import org.dhis2.fhir.adapter.fhir.metadata.model.ProgramStageMetadataRule; +import org.dhis2.fhir.adapter.fhir.metadata.model.RuleInfo; +import org.dhis2.fhir.adapter.fhir.model.FhirVersion; +import org.dhis2.fhir.adapter.fhir.script.ScriptExecutor; +import org.dhis2.fhir.adapter.fhir.transform.dhis.search.SearchFilter; +import org.dhis2.fhir.adapter.fhir.transform.dhis.search.SearchFilterCollector; +import org.junit.Assert; +import org.junit.Rule; +import org.junit.Test; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.junit.MockitoJUnit; +import org.mockito.junit.MockitoRule; +import org.springframework.web.util.DefaultUriBuilderFactory; +import org.springframework.web.util.UriBuilder; + +import java.net.URI; +import java.net.URISyntaxException; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +/** + * Unit tests for {@link R4ProgramStageMetadataToFhirDataProvider}. + * + * @author volsch + */ +public class R4ProgramStageMetadataToFhirDataProviderTest +{ + @Mock + private ScriptExecutor scriptExecutor; + + @Mock + private ProgramStageMetadataService programStageMetadataService; + + @InjectMocks + private R4ProgramStageMetadataToFhirDataProvider provider; + + @Rule + public MockitoRule rule = MockitoJUnit.rule(); + + @Test + public void getFhirVersions() + { + Assert.assertEquals( FhirVersion.R4_ONLY, provider.getFhirVersions() ); + } + + @Test + public void searchFilterName() throws URISyntaxException + { + final SearchFilterCollector searchFilterCollector = new SearchFilterCollector( Collections.singletonMap( "title", Collections.singletonList( "Birth" ) ) ); + final SearchFilter searchFilter = new SearchFilter( searchFilterCollector, false ); + + provider.initSearchFilter( FhirVersion.R4, new RuleInfo<>( new ProgramStageMetadataRule(), Collections.emptyList() ), searchFilter ); + + final List variables = new ArrayList<>(); + UriBuilder uriBuilder = new DefaultUriBuilderFactory().builder(); + uriBuilder = searchFilterCollector.add( uriBuilder, variables ); + + Assert.assertEquals( new URI( "?filter=%5Bname:$ilike:Birth%5D" ), uriBuilder.build( variables ) ); + } +} diff --git a/fhir-r4/src/test/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/metadata/r4/R4ProgramStageMetadataToFhirQuestionnaireTransformerTest.java b/fhir-r4/src/test/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/metadata/r4/R4ProgramStageMetadataToFhirQuestionnaireTransformerTest.java new file mode 100644 index 00000000..3cf0b25c --- /dev/null +++ b/fhir-r4/src/test/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/metadata/r4/R4ProgramStageMetadataToFhirQuestionnaireTransformerTest.java @@ -0,0 +1,170 @@ +package org.dhis2.fhir.adapter.fhir.transform.dhis.impl.metadata.r4; + +/* + * Copyright (c) 2004-2019, University of Oslo + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * Neither the name of the HISP project nor the names of its contributors may + * be used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +import org.dhis2.fhir.adapter.dhis.model.WritableDataElement; +import org.dhis2.fhir.adapter.dhis.model.WritableOption; +import org.dhis2.fhir.adapter.dhis.model.WritableOptionSet; +import org.dhis2.fhir.adapter.dhis.orgunit.OrganizationUnitService; +import org.dhis2.fhir.adapter.dhis.tracker.program.WritableProgramStage; +import org.dhis2.fhir.adapter.dhis.tracker.program.WritableProgramStageDataElement; +import org.dhis2.fhir.adapter.fhir.data.repository.FhirDhisAssignmentRepository; +import org.dhis2.fhir.adapter.fhir.metadata.model.FhirClient; +import org.dhis2.fhir.adapter.fhir.metadata.model.ProgramStageMetadataRule; +import org.dhis2.fhir.adapter.fhir.metadata.model.RuleInfo; +import org.dhis2.fhir.adapter.fhir.metadata.repository.SystemRepository; +import org.dhis2.fhir.adapter.fhir.model.FhirVersion; +import org.dhis2.fhir.adapter.fhir.repository.FhirResourceRepository; +import org.dhis2.fhir.adapter.fhir.script.ScriptExecutionContext; +import org.dhis2.fhir.adapter.fhir.script.ScriptExecutor; +import org.dhis2.fhir.adapter.fhir.transform.dhis.DhisToFhirTransformerContext; +import org.dhis2.fhir.adapter.fhir.transform.scripted.WritableScriptedDhisMetadata; +import org.dhis2.fhir.adapter.lock.LockManager; +import org.dhis2.fhir.adapter.model.ValueType; +import org.hl7.fhir.r4.model.Coding; +import org.hl7.fhir.r4.model.Questionnaire; +import org.junit.Assert; +import org.junit.Rule; +import org.junit.Test; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.junit.MockitoJUnit; +import org.mockito.junit.MockitoRule; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; + +/** + * Unit tests for {@link R4ProgramStageMetadataToFhirQuestionnaireTransformer}. + * + * @author volsch + */ +public class R4ProgramStageMetadataToFhirQuestionnaireTransformerTest +{ + @Mock + private ScriptExecutor scriptExecutor; + + @Mock + private LockManager lockManager; + + @Mock + private SystemRepository systemRepository; + + @Mock + private FhirResourceRepository fhirResourceRepository; + + @Mock + private FhirDhisAssignmentRepository fhirDhisAssignmentRepository; + + @Mock + private OrganizationUnitService organizationUnitService; + + @Mock + private DhisToFhirTransformerContext context; + + @Mock + private FhirClient fhirClient; + + @Mock + private ScriptExecutionContext scriptExecutionContext; + + @InjectMocks + private R4ProgramStageMetadataToFhirQuestionnaireTransformer transformer; + + @Rule + public MockitoRule rule = MockitoJUnit.rule(); + + @Test + public void getFhirVersions() + { + Assert.assertEquals( FhirVersion.R4_ONLY, transformer.getFhirVersions() ); + } + + @Test + public void transformInternal() + { + final WritableProgramStage programStage = new WritableProgramStage(); + programStage.setId( "b1234567890" ); + programStage.setName( "Test Stage 1" ); + programStage.setDescription( "Test Description 1" ); + programStage.setRepeatable( false ); + programStage.setDataElements( new ArrayList<>() ); + + WritableProgramStageDataElement programStageDataElement = new WritableProgramStageDataElement(); + WritableDataElement dataElement = new WritableDataElement(); + dataElement.setId( "d0123456789" ); + dataElement.setName( "Value 1" ); + dataElement.setValueType( ValueType.TEXT ); + programStageDataElement.setCompulsory( false ); + programStageDataElement.setElement( dataElement ); + programStage.getDataElements().add( programStageDataElement ); + + WritableOptionSet optionSet = new WritableOptionSet(); + optionSet.setOptions( new ArrayList<>() ); + optionSet.getOptions().add( new WritableOption( "5", "Test Value 1" ) ); + optionSet.getOptions().add( new WritableOption( "7", "Test Value 2" ) ); + + programStageDataElement = new WritableProgramStageDataElement(); + dataElement = new WritableDataElement(); + dataElement.setId( "d1123456789" ); + dataElement.setName( "Value 2" ); + dataElement.setValueType( ValueType.INTEGER ); + dataElement.setOptionSetValue( true ); + dataElement.setOptionSet( optionSet ); + programStageDataElement.setCompulsory( true ); + programStageDataElement.setElement( dataElement ); + programStage.getDataElements().add( programStageDataElement ); + + final Questionnaire fhirQuestionnaire = new Questionnaire(); + + transformer.transformInternal( fhirClient, context, new RuleInfo<>( new ProgramStageMetadataRule(), Collections.emptyList() ), new HashMap<>(), + new WritableScriptedDhisMetadata( programStage, scriptExecutionContext ), fhirQuestionnaire ); + + Assert.assertEquals( "b1234567890", fhirQuestionnaire.getUrl() ); + Assert.assertEquals( "Test Stage 1", fhirQuestionnaire.getTitle() ); + Assert.assertEquals( "Test Description 1", fhirQuestionnaire.getDescription() ); + Assert.assertEquals( 2, fhirQuestionnaire.getItem().size() ); + + Assert.assertEquals( "d0123456789", fhirQuestionnaire.getItem().get( 0 ).getLinkId() ); + Assert.assertEquals( "Value 1", fhirQuestionnaire.getItem().get( 0 ).getText() ); + Assert.assertEquals( 0, fhirQuestionnaire.getItem().get( 0 ).getAnswerOption().size() ); + Assert.assertFalse( fhirQuestionnaire.getItem().get( 0 ).getRequired() ); + + Assert.assertEquals( "d1123456789", fhirQuestionnaire.getItem().get( 1 ).getLinkId() ); + Assert.assertEquals( "Value 2", fhirQuestionnaire.getItem().get( 1 ).getText() ); + Assert.assertEquals( 2, fhirQuestionnaire.getItem().get( 1 ).getAnswerOption().size() ); + Assert.assertTrue( fhirQuestionnaire.getItem().get( 1 ).getAnswerOption().get( 0 ).getValue() instanceof Coding ); + Assert.assertEquals( "5", ( (Coding) fhirQuestionnaire.getItem().get( 1 ).getAnswerOption().get( 0 ).getValue() ).getCode() ); + Assert.assertEquals( "Test Value 1", ( (Coding) fhirQuestionnaire.getItem().get( 1 ).getAnswerOption().get( 0 ).getValue() ).getDisplay() ); + Assert.assertEquals( "7", ( (Coding) fhirQuestionnaire.getItem().get( 1 ).getAnswerOption().get( 1 ).getValue() ).getCode() ); + Assert.assertEquals( "Test Value 2", ( (Coding) fhirQuestionnaire.getItem().get( 1 ).getAnswerOption().get( 1 ).getValue() ).getDisplay() ); + Assert.assertTrue( fhirQuestionnaire.getItem().get( 1 ).getRequired() ); + } +} diff --git a/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/metadata/program/AbstractProgramStageMetadataToFhirDataProvider.java b/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/metadata/program/AbstractProgramStageMetadataToFhirDataProvider.java new file mode 100644 index 00000000..05062a36 --- /dev/null +++ b/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/metadata/program/AbstractProgramStageMetadataToFhirDataProvider.java @@ -0,0 +1,66 @@ +package org.dhis2.fhir.adapter.fhir.transform.dhis.impl.metadata.program; + +/* + * Copyright (c) 2004-2019, University of Oslo + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * Neither the name of the HISP project nor the names of its contributors may + * be used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +import org.dhis2.fhir.adapter.dhis.model.DhisResourceType; +import org.dhis2.fhir.adapter.dhis.tracker.program.ProgramStage; +import org.dhis2.fhir.adapter.dhis.tracker.program.ProgramStageMetadataService; +import org.dhis2.fhir.adapter.fhir.metadata.model.ProgramStageMetadataRule; +import org.dhis2.fhir.adapter.fhir.script.ScriptExecutor; +import org.dhis2.fhir.adapter.fhir.transform.dhis.DhisToFhirDataProvider; +import org.dhis2.fhir.adapter.fhir.transform.dhis.impl.metadata.AbstractDhisMetadataToFhirDataProvider; + +import javax.annotation.Nonnull; + +/** + * Abstract implementation of {@link DhisToFhirDataProvider} for DHIS2 Program Stage Metadata. + * + * @author volsch + */ +public abstract class AbstractProgramStageMetadataToFhirDataProvider extends AbstractDhisMetadataToFhirDataProvider +{ + public AbstractProgramStageMetadataToFhirDataProvider( @Nonnull ScriptExecutor scriptExecutor, @Nonnull ProgramStageMetadataService programStageMetadataService ) + { + super( scriptExecutor, programStageMetadataService ); + } + + @Nonnull + @Override + public DhisResourceType getDhisResourceType() + { + return DhisResourceType.PROGRAM_STAGE_METADATA; + } + + @Nonnull + @Override + protected Class getRuleClass() + { + return ProgramStageMetadataRule.class; + } +} diff --git a/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/metadata/program/AbstractProgramStageMetadataToFhirQuestionnaireTransformer.java b/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/metadata/program/AbstractProgramStageMetadataToFhirQuestionnaireTransformer.java new file mode 100644 index 00000000..0bc8ba45 --- /dev/null +++ b/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/metadata/program/AbstractProgramStageMetadataToFhirQuestionnaireTransformer.java @@ -0,0 +1,93 @@ +package org.dhis2.fhir.adapter.fhir.transform.dhis.impl.metadata.program; + +/* + * Copyright (c) 2004-2019, University of Oslo + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * Neither the name of the HISP project nor the names of its contributors may + * be used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +import org.dhis2.fhir.adapter.dhis.model.DhisResourceType; +import org.dhis2.fhir.adapter.dhis.orgunit.OrganizationUnitService; +import org.dhis2.fhir.adapter.fhir.data.repository.FhirDhisAssignmentRepository; +import org.dhis2.fhir.adapter.fhir.metadata.model.FhirResourceType; +import org.dhis2.fhir.adapter.fhir.metadata.model.ProgramStageMetadataRule; +import org.dhis2.fhir.adapter.fhir.metadata.repository.SystemRepository; +import org.dhis2.fhir.adapter.fhir.repository.FhirResourceRepository; +import org.dhis2.fhir.adapter.fhir.script.ScriptExecutor; +import org.dhis2.fhir.adapter.fhir.transform.dhis.impl.DhisToFhirTransformer; +import org.dhis2.fhir.adapter.fhir.transform.dhis.impl.metadata.AbstractReadOnlyDhisMetadataToTypedFhirTransformer; +import org.dhis2.fhir.adapter.fhir.transform.scripted.AccessibleScriptedDhisMetadata; +import org.dhis2.fhir.adapter.lock.LockManager; +import org.hl7.fhir.instance.model.api.IBaseResource; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import javax.annotation.Nonnull; + +/** + * Implementation of {@link DhisToFhirTransformer} for transforming DHIS2 + * program stage metadata to FHIR Questionnaire. + * + * @param the concrete type of the FHIR resource into which the DHIS2 resource should be transformed. + * @author volsch + */ +public abstract class AbstractProgramStageMetadataToFhirQuestionnaireTransformer extends AbstractReadOnlyDhisMetadataToTypedFhirTransformer +{ + private final Logger logger = LoggerFactory.getLogger( getClass() ); + + public AbstractProgramStageMetadataToFhirQuestionnaireTransformer( @Nonnull ScriptExecutor scriptExecutor, @Nonnull LockManager lockManager, @Nonnull SystemRepository systemRepository, @Nonnull FhirResourceRepository fhirResourceRepository, + @Nonnull FhirDhisAssignmentRepository fhirDhisAssignmentRepository, @Nonnull OrganizationUnitService organizationUnitService ) + { + super( scriptExecutor, lockManager, systemRepository, fhirResourceRepository, fhirDhisAssignmentRepository ); + } + + @Nonnull + @Override + public DhisResourceType getDhisResourceType() + { + return DhisResourceType.PROGRAM_STAGE_METADATA; + } + + @Nonnull + @Override + protected FhirResourceType getFhirResourceType() + { + return FhirResourceType.QUESTIONNAIRE; + } + + @Nonnull + @Override + public Class getDhisResourceClass() + { + return AccessibleScriptedDhisMetadata.class; + } + + @Nonnull + @Override + public Class getRuleClass() + { + return ProgramStageMetadataRule.class; + } +} diff --git a/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/metadata/program/ProgramStageMetadataToFhirRequestResolver.java b/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/metadata/program/ProgramStageMetadataToFhirRequestResolver.java new file mode 100644 index 00000000..4186ebae --- /dev/null +++ b/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/metadata/program/ProgramStageMetadataToFhirRequestResolver.java @@ -0,0 +1,76 @@ +package org.dhis2.fhir.adapter.fhir.transform.dhis.impl.metadata.program; + +/* + * Copyright (c) 2004-2019, University of Oslo + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * Neither the name of the HISP project nor the names of its contributors may + * be used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +import org.dhis2.fhir.adapter.dhis.model.DhisMetadata; +import org.dhis2.fhir.adapter.dhis.model.DhisResource; +import org.dhis2.fhir.adapter.dhis.model.DhisResourceType; +import org.dhis2.fhir.adapter.fhir.metadata.repository.FhirClientRepository; +import org.dhis2.fhir.adapter.fhir.metadata.repository.RuleRepository; +import org.dhis2.fhir.adapter.fhir.script.ScriptExecutionContext; +import org.dhis2.fhir.adapter.fhir.transform.dhis.impl.DhisToFhirRequestResolver; +import org.dhis2.fhir.adapter.fhir.transform.dhis.impl.metadata.AbstractDhisMetadataToFhirRequestResolver; +import org.dhis2.fhir.adapter.fhir.transform.dhis.model.DhisRequest; +import org.dhis2.fhir.adapter.fhir.transform.scripted.ImmutableScriptedDhisMetadata; +import org.dhis2.fhir.adapter.fhir.transform.scripted.ScriptedDhisResource; +import org.dhis2.fhir.adapter.fhir.transform.scripted.WritableScriptedDhisMetadata; +import org.springframework.stereotype.Component; + +import javax.annotation.Nonnull; + +/** + * Implementation of {@link DhisToFhirRequestResolver} for DHIS2 program stage metadata. + * + * @author volsch + */ +@Component +public class ProgramStageMetadataToFhirRequestResolver extends AbstractDhisMetadataToFhirRequestResolver +{ + private final ScriptExecutionContext scriptExecutionContext; + + public ProgramStageMetadataToFhirRequestResolver( @Nonnull FhirClientRepository fhirClientRepository, @Nonnull RuleRepository ruleRepository, @Nonnull ScriptExecutionContext scriptExecutionContext ) + { + super( fhirClientRepository, ruleRepository ); + this.scriptExecutionContext = scriptExecutionContext; + } + + @Nonnull + @Override + public DhisResourceType getDhisResourceType() + { + return DhisResourceType.PROGRAM_STAGE_METADATA; + } + + @Nonnull + @Override + public ScriptedDhisResource convert( @Nonnull DhisResource dhisResource, @Nonnull DhisRequest dhisRequest ) + { + return new ImmutableScriptedDhisMetadata( new WritableScriptedDhisMetadata( (DhisMetadata) dhisResource, scriptExecutionContext ) ); + } +} diff --git a/fhir/src/main/resources/db/migration/sample/V1.1.0.41_10_0__Plan_Definition_Sample_Data.sql b/fhir/src/main/resources/db/migration/sample/V1.1.0.41_10_0__Plan_Definition_Sample_Data.sql index e21ec8fd..e3918522 100644 --- a/fhir/src/main/resources/db/migration/sample/V1.1.0.41_10_0__Plan_Definition_Sample_Data.sql +++ b/fhir/src/main/resources/db/migration/sample/V1.1.0.41_10_0__Plan_Definition_Sample_Data.sql @@ -34,5 +34,7 @@ WHERE id='73cd99c5-0ca8-42ad-a53b-1891fccce08f'; -- virtual subscription for FHIR organizations INSERT INTO fhir_client_resource (id, version, fhir_client_id, fhir_resource_type, exp_only, fhir_criteria_parameters, description) VALUES ('2520acc5-86b4-4716-ae0f-ea0531eb885a', 0, '73cd99c5-0ca8-42ad-a53b-1891fccce08f', 'PLAN_DEFINITION', TRUE, NULL, 'Virtual subscription for all Plan Definitions.'); +INSERT INTO fhir_client_resource (id, version, fhir_client_id, fhir_resource_type, exp_only, fhir_criteria_parameters, description) +VALUES ('0918c4f3-995c-4130-b607-b28023a1a3a0', 0, '73cd99c5-0ca8-42ad-a53b-1891fccce08f', 'QUESTIONNAIRE', TRUE, NULL, 'Virtual subscription for all Questionnaires.'); INSERT INTO fhir_client_resource_update(id) VALUES ('2520acc5-86b4-4716-ae0f-ea0531eb885a'); From c8dec4877db4765b5e3c8ea21c201d93ee0d7906 Mon Sep 17 00:00:00 2001 From: Volker Schmidt Date: Wed, 31 Jul 2019 12:06:35 +0200 Subject: [PATCH 09/21] Fixes issue with generated FHIR server resource IDs. --- .../adapter/dhis/model/DhisResourceType.java | 9 +- .../fhir/metadata/model/FhirResourceType.java | 46 ++-- .../repository/FhirResourceRepository.java | 2 +- .../repository/impl/DhisRepositoryImpl.java | 5 +- .../impl/FhirResourceRepositoryImpl.java | 33 ++- .../impl/FhirResourceRepositoryImplTest.java | 207 ++++++++++++++++++ 6 files changed, 273 insertions(+), 29 deletions(-) create mode 100644 fhir/src/test/java/org/dhis2/fhir/adapter/fhir/repository/impl/FhirResourceRepositoryImplTest.java diff --git a/dhis/src/main/java/org/dhis2/fhir/adapter/dhis/model/DhisResourceType.java b/dhis/src/main/java/org/dhis2/fhir/adapter/dhis/model/DhisResourceType.java index 30e669e9..8b6ca64a 100644 --- a/dhis/src/main/java/org/dhis2/fhir/adapter/dhis/model/DhisResourceType.java +++ b/dhis/src/main/java/org/dhis2/fhir/adapter/dhis/model/DhisResourceType.java @@ -47,16 +47,15 @@ public enum DhisResourceType */ ORGANIZATION_UNIT( "organisationUnits", "ou", "OrganizationUnitRule" ), - /** - * The program metadata. - */ - PROGRAM_METADATA( "programs", "pm", "ProgramMetadataRule" ), - /** * The program stage metadata. */ PROGRAM_STAGE_METADATA( "programStages", "sm", "ProgramStageMetadataRule" ), + /** + * The program metadata. + */ + PROGRAM_METADATA( "programs", "pm", "ProgramMetadataRule" ), /** * Resource is a tracked entity type. diff --git a/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/metadata/model/FhirResourceType.java b/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/metadata/model/FhirResourceType.java index 6bf41c17..ed0b04c6 100644 --- a/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/metadata/model/FhirResourceType.java +++ b/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/metadata/model/FhirResourceType.java @@ -58,22 +58,22 @@ */ public enum FhirResourceType { - CONDITION( FhirVersion.ALL, "Condition", 19, Collections.emptySet(), Collections.singleton( "Condition" ) ), - DIAGNOSTIC_REPORT( FhirVersion.ALL, "DiagnosticReport", 30, Collections.emptySet(), Collections.singleton( "DiagnosticReport" ) ), - ENCOUNTER( FhirVersion.ALL, "Encounter", 4, Collections.emptySet(), Collections.singleton( "Encounter" ) ), - IMMUNIZATION( FhirVersion.ALL, "Immunization", 22, Collections.emptySet(), Collections.singleton( "Immunization" ) ), - LOCATION( FhirVersion.ALL, "Location", 2, Collections.emptySet(), Collections.singleton( "Location" ) ), - MEDICATION_REQUEST( FhirVersion.ALL, "MedicationRequest", 21, Collections.emptySet(), Collections.singleton( "MedicationRequest" ) ), - OBSERVATION( FhirVersion.ALL, "Observation", 20, Collections.emptySet(), Collections.singleton( "Observation" ) ), - ORGANIZATION( FhirVersion.ALL, "Organization", 1, Collections.emptySet(), Collections.singleton( "Organization" ) ), - PATIENT( FhirVersion.ALL, "Patient", 10, Collections.emptySet(), Collections.singleton( "Patient" ) ), - RELATED_PERSON( FhirVersion.ALL, "RelatedPerson", 11, Collections.emptySet(), Collections.singleton( "RelatedPerson" ) ), - PRACTITIONER( FhirVersion.ALL, "Practitioner", 9, Collections.emptySet(), Collections.singleton( "Practitioner" ) ), - MEASURE_REPORT( FhirVersion.ALL, "MeasureReport", 30, Collections.singleton( "MeasureReport" ), Collections.singleton( "MeasureReport" ) ), - PLAN_DEFINITION( FhirVersion.R4_ONLY, "PlanDefinition", 30, Collections.emptySet(), Collections.singleton( "PlanDefinition" ) ), - QUESTIONNAIRE( FhirVersion.R4_ONLY, "Questionnaire", 31, Collections.emptySet(), Collections.singleton( "Questionnaire" ) ), - CARE_PLAN( FhirVersion.R4_ONLY, "CarePlan", 35, Collections.emptySet(), Collections.singleton( "CarePlan" ) ), - QUESTIONNAIRE_RESPONSE( FhirVersion.R4_ONLY, "QuestionnaireResponse", 40, Collections.emptySet(), Collections.singleton( "QuestionnaireResponse" ) ); + CONDITION( FhirVersion.ALL, "Condition", false, 19, Collections.emptySet(), Collections.singleton( "Condition" ) ), + DIAGNOSTIC_REPORT( FhirVersion.ALL, "DiagnosticReport", false, 30, Collections.emptySet(), Collections.singleton( "DiagnosticReport" ) ), + ENCOUNTER( FhirVersion.ALL, "Encounter", false, 4, Collections.emptySet(), Collections.singleton( "Encounter" ) ), + IMMUNIZATION( FhirVersion.ALL, "Immunization", false, 22, Collections.emptySet(), Collections.singleton( "Immunization" ) ), + LOCATION( FhirVersion.ALL, "Location", false, 2, Collections.emptySet(), Collections.singleton( "Location" ) ), + MEDICATION_REQUEST( FhirVersion.ALL, "MedicationRequest", false, 21, Collections.emptySet(), Collections.singleton( "MedicationRequest" ) ), + OBSERVATION( FhirVersion.ALL, "Observation", false, 20, Collections.emptySet(), Collections.singleton( "Observation" ) ), + ORGANIZATION( FhirVersion.ALL, "Organization", false, 1, Collections.emptySet(), Collections.singleton( "Organization" ) ), + PATIENT( FhirVersion.ALL, "Patient", false, 10, Collections.emptySet(), Collections.singleton( "Patient" ) ), + RELATED_PERSON( FhirVersion.ALL, "RelatedPerson", false, 11, Collections.emptySet(), Collections.singleton( "RelatedPerson" ) ), + PRACTITIONER( FhirVersion.ALL, "Practitioner", false, 9, Collections.emptySet(), Collections.singleton( "Practitioner" ) ), + MEASURE_REPORT( FhirVersion.ALL, "MeasureReport", false, 30, Collections.singleton( "MeasureReport" ), Collections.singleton( "MeasureReport" ) ), + PLAN_DEFINITION( FhirVersion.R4_ONLY, "PlanDefinition", true, 30, Collections.emptySet(), Collections.singleton( "PlanDefinition" ) ), + QUESTIONNAIRE( FhirVersion.R4_ONLY, "Questionnaire", true, 31, Collections.emptySet(), Collections.singleton( "Questionnaire" ) ), + CARE_PLAN( FhirVersion.R4_ONLY, "CarePlan", false, 35, Collections.emptySet(), Collections.singleton( "CarePlan" ) ), + QUESTIONNAIRE_RESPONSE( FhirVersion.R4_ONLY, "QuestionnaireResponse", false, 40, Collections.emptySet(), Collections.singleton( "QuestionnaireResponse" ) ); private static final Map resourcesBySimpleClassName = Arrays.stream( values() ).flatMap( v -> v.getSimpleClassNames().stream().map( scn -> new SimpleEntry<>( scn, v ) ) ) .collect( Collectors.toMap( SimpleEntry::getKey, SimpleEntry::getValue ) ); @@ -85,17 +85,21 @@ public static FhirResourceType getByResource( @Nullable IBaseResource resource ) { return null; } + FhirResourceType frt; Class c = resource.getClass(); + do { frt = resourcesBySimpleClassName.get( c.getSimpleName() ); + if ( frt == null ) { c = c.getSuperclass(); } } while ( (frt == null) && (c != null) && (c != Object.class) ); + return frt; } @@ -115,6 +119,8 @@ public static FhirResourceType getByResourceTypeName( @Nullable String resourceT private final String resourceTypeName; + private final boolean syncDhisId; + private final int order; private final Set transactionalWith; @@ -123,10 +129,11 @@ public static FhirResourceType getByResourceTypeName( @Nullable String resourceT private volatile Set transactionalWithTypes; - FhirResourceType( Set fhirVersions, String resourceTypeName, int order, Collection transactionalWith, Collection simpleClassNames ) + FhirResourceType( Set fhirVersions, String resourceTypeName, boolean syncDhisId, int order, Collection transactionalWith, Collection simpleClassNames ) { this.fhirVersions = fhirVersions; this.resourceTypeName = resourceTypeName; + this.syncDhisId = syncDhisId; this.order = order; this.transactionalWith = new HashSet<>( transactionalWith ); this.simpleClassNames = Collections.unmodifiableSet( new HashSet<>( simpleClassNames ) ); @@ -144,6 +151,11 @@ public String getResourceTypeName() return resourceTypeName; } + public boolean isSyncDhisId() + { + return syncDhisId; + } + public int getOrder() { return order; diff --git a/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/repository/FhirResourceRepository.java b/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/repository/FhirResourceRepository.java index 48cfc16d..8ca0374a 100644 --- a/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/repository/FhirResourceRepository.java +++ b/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/repository/FhirResourceRepository.java @@ -75,7 +75,7 @@ public interface FhirResourceRepository IBaseResource transform( @Nonnull UUID fhirClientId, @Nonnull FhirVersion fhirVersion, @Nullable IBaseResource resource ); @Nonnull - IBaseResource save( @Nonnull FhirClient fhirClient, @Nonnull IBaseResource resource ); + IBaseResource save( @Nonnull FhirClient fhirClient, @Nonnull IBaseResource resource, @Nullable String dhisResourceId ); boolean delete( @Nonnull FhirClient fhirClient, @Nonnull IBaseResource resource ); } diff --git a/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/repository/impl/DhisRepositoryImpl.java b/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/repository/impl/DhisRepositoryImpl.java index a613d7a2..b2c17a31 100644 --- a/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/repository/impl/DhisRepositoryImpl.java +++ b/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/repository/impl/DhisRepositoryImpl.java @@ -451,7 +451,8 @@ protected boolean saveInternally( @Nonnull DhisResource resource ) } else { - final IBaseResource resultingResource = fhirResourceRepository.save( transformerRequest.getFhirClient(), outcome.getResource() ); + final IBaseResource resultingResource = fhirResourceRepository + .save( transformerRequest.getFhirClient(), outcome.getResource(), resource.getId() ); // resource may have been set as attribute in transformer context (e.g. shared encounter) outcome.getResource().setId( resultingResource.getIdElement() ); fhirDhisAssignmentRepository.saveFhirResourceId( outcome.getRule(), transformerRequest.getFhirClient(), @@ -482,6 +483,7 @@ public Optional read( @Nonnull FhirClient fhirClient, @Nonnull Fh if ( dhisResource == null ) { logger.debug( "DHIS resource could not be found." ); + return Optional.empty(); } @@ -493,6 +495,7 @@ public Optional read( @Nonnull FhirClient fhirClient, @Nonnull Fh if ( transformerRequest == null ) { logger.debug( "No matching rule has been found." ); + return Optional.empty(); } diff --git a/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/repository/impl/FhirResourceRepositoryImpl.java b/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/repository/impl/FhirResourceRepositoryImpl.java index 086f7b9b..fbbb7f7c 100644 --- a/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/repository/impl/FhirResourceRepositoryImpl.java +++ b/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/repository/impl/FhirResourceRepositoryImpl.java @@ -276,7 +276,7 @@ public boolean delete( @Nonnull FhirClient fhirClient, @Nonnull IBaseResource re "T(org.dhis2.fhir.adapter.fhir.metadata.model.FhirResourceType).getByResource(#resource).getResourceTypeName(), #resource.getIdElement().getIdPart(), true}" ) @Nonnull @Override - public IBaseResource save( @Nonnull FhirClient fhirClient, @Nonnull IBaseResource resource ) + public IBaseResource save( @Nonnull FhirClient fhirClient, @Nonnull IBaseResource resource, @Nullable String dhisResourceId ) { if ( !fhirClient.getFhirEndpoint().isUseRemote() ) { @@ -287,25 +287,28 @@ public IBaseResource save( @Nonnull FhirClient fhirClient, @Nonnull IBaseResourc final FhirContext fhirContext = fhirContexts.get( fhirClient.getFhirVersion() ); final IGenericClient client = FhirClientUtils.createClient( fhirContext, fhirClient.getFhirEndpoint() ); + final IBaseResource preparedResource = prepareResource( resource, dhisResourceId ); final MethodOutcome methodOutcome; + if ( resource.getIdElement().hasIdPart() ) { try { - methodOutcome = client.update().resource( resource ).prefer( PreferReturnEnum.REPRESENTATION ).execute(); + methodOutcome = client.update().resource( preparedResource ).prefer( PreferReturnEnum.REPRESENTATION ).execute(); } catch ( PreconditionFailedException e ) { throw new OptimisticFhirResourceLockException( "Could not update FHIR resource " + - resource.getIdElement() + " because of an optimistic locking failure.", e ); + preparedResource.getIdElement() + " because of an optimistic locking failure.", e ); } } else { - methodOutcome = client.create().resource( resource ).prefer( PreferReturnEnum.REPRESENTATION ).execute(); + methodOutcome = client.create().resource( preparedResource ).prefer( PreferReturnEnum.REPRESENTATION ).execute(); } ProcessedItemInfo processedItemInfo = null; + if ( (methodOutcome.getResource() != null) && (methodOutcome.getResource().getMeta() != null) ) { // resource itself may contain old version ID (even if it should not) @@ -313,7 +316,7 @@ public IBaseResource save( @Nonnull FhirClient fhirClient, @Nonnull IBaseResourc } else if ( (methodOutcome.getId() != null) && methodOutcome.getId().hasVersionIdPart() ) { - processedItemInfo = ProcessedFhirItemInfoUtils.create( resource, methodOutcome.getId() ); + processedItemInfo = ProcessedFhirItemInfoUtils.create( preparedResource, methodOutcome.getId() ); } if ( processedItemInfo == null ) @@ -327,9 +330,11 @@ else if ( (methodOutcome.getId() != null) && methodOutcome.getId().hasVersionIdP } final IBaseResource result; + if ( methodOutcome.getResource() == null ) { result = resource; + if ( methodOutcome.getId() != null ) { result.setId( methodOutcome.getId() ); @@ -343,6 +348,24 @@ else if ( (methodOutcome.getId() != null) && methodOutcome.getId().hasVersionIdP return result; } + @Nonnull + protected T prepareResource( @Nonnull T resource, @Nullable String dhisResourceId ) + { + final FhirResourceType fhirResourceType = FhirResourceType.getByResource( resource ); + + if ( fhirResourceType == null ) + { + throw new FhirResourceTransformationException( "Could not determine FHIR resource type for " + resource.getClass().getSimpleName() ); + } + + if ( fhirResourceType.isSyncDhisId() && dhisResourceId != null && !resource.getIdElement().hasIdPart() ) + { + resource.setId( dhisResourceId ); + } + + return resource; + } + @TransactionalEventListener( phase = TransactionPhase.BEFORE_COMMIT, classes = AutoCreatedFhirClientResourceEvent.class ) public void autoCreatedSubscriptionResource( @Nonnull AutoCreatedFhirClientResourceEvent event ) { diff --git a/fhir/src/test/java/org/dhis2/fhir/adapter/fhir/repository/impl/FhirResourceRepositoryImplTest.java b/fhir/src/test/java/org/dhis2/fhir/adapter/fhir/repository/impl/FhirResourceRepositoryImplTest.java new file mode 100644 index 00000000..6fcd4ef9 --- /dev/null +++ b/fhir/src/test/java/org/dhis2/fhir/adapter/fhir/repository/impl/FhirResourceRepositoryImplTest.java @@ -0,0 +1,207 @@ +package org.dhis2.fhir.adapter.fhir.repository.impl; + +/* + * Copyright (c) 2004-2019, University of Oslo + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * Neither the name of the HISP project nor the names of its contributors may + * be used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +import ca.uhn.fhir.context.FhirContext; +import ca.uhn.fhir.context.FhirVersionEnum; +import ca.uhn.fhir.model.api.IFhirVersion; +import ca.uhn.fhir.model.primitive.IdDt; +import org.dhis2.fhir.adapter.fhir.client.StoredFhirResourceService; +import org.dhis2.fhir.adapter.fhir.metadata.repository.FhirClientResourceRepository; +import org.dhis2.fhir.adapter.fhir.script.ScriptExecutor; +import org.dhis2.fhir.adapter.spring.StaticObjectProvider; +import org.hl7.fhir.instance.model.api.IBaseMetaType; +import org.hl7.fhir.instance.model.api.IBaseResource; +import org.hl7.fhir.instance.model.api.IIdType; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.mockito.Mock; +import org.mockito.Mockito; +import org.mockito.junit.MockitoJUnit; +import org.mockito.junit.MockitoRule; + +import java.util.Arrays; +import java.util.Collections; +import java.util.List; + +/** + * Implementation of {@link FhirResourceRepositoryImpl}. + * + * @author volsch + */ +public class FhirResourceRepositoryImplTest +{ + @Mock + private ScriptExecutor scriptExecutor; + + @Mock + private StoredFhirResourceService storedItemService; + + @Mock + private FhirClientResourceRepository fhirClientResourceRepository; + + @Mock + private FhirContext fhirContextDstu3; + + @Mock + private IFhirVersion fhirVersionDstu3; + + @Mock + private FhirContext fhirContextR4; + + @Mock + private IFhirVersion fhirVersionR4; + + private FhirResourceRepositoryImpl repository; + + @Rule + public MockitoRule mockitoRule = MockitoJUnit.rule(); + + @Before + public void setUp() + { + Mockito.when( fhirContextDstu3.getVersion() ).thenReturn( fhirVersionDstu3 ); + Mockito.when( fhirVersionDstu3.getVersion() ).thenReturn( FhirVersionEnum.DSTU3 ); + Mockito.when( fhirContextR4.getVersion() ).thenReturn( fhirVersionR4 ); + Mockito.when( fhirVersionR4.getVersion() ).thenReturn( FhirVersionEnum.R4 ); + + repository = new FhirResourceRepositoryImpl( scriptExecutor, storedItemService, fhirClientResourceRepository, + new StaticObjectProvider<>( Arrays.asList( fhirContextDstu3, fhirContextR4 ) ), new StaticObjectProvider<>( Collections.emptyList() ) ); + } + + @Test + public void prepareResourceWithoutAnyId() + { + Patient patient = new Patient(); + patient = repository.prepareResource( patient, null ); + Assert.assertTrue( patient.getIdElement().isEmpty() ); + } + + @Test + public void prepareResourceNonMatchingWithDhisId() + { + Patient patient = new Patient(); + patient = repository.prepareResource( patient, "a0123456789" ); + Assert.assertTrue( patient.getIdElement().isEmpty() ); + } + + @Test + public void prepareResourceWithDhisId() + { + PlanDefinition planDefinition = new PlanDefinition(); + planDefinition = repository.prepareResource( planDefinition, "a0123456789" ); + Assert.assertEquals( "a0123456789", planDefinition.getIdElement().getIdPart() ); + } + + @Test + public void prepareResourceWithAllIds() + { + PlanDefinition planDefinition = new PlanDefinition(); + planDefinition.setId( "d0123456789" ); + planDefinition = repository.prepareResource( planDefinition, "a0123456789" ); + Assert.assertEquals( "d0123456789", planDefinition.getIdElement().getIdPart() ); + } + + public static class Patient extends AbstractBaseResource + { + private static final long serialVersionUID = -1428885428508171576L; + } + + public static class PlanDefinition extends AbstractBaseResource + { + private static final long serialVersionUID = -1428885428508171576L; + } + + public static abstract class AbstractBaseResource implements IBaseResource + { + private static final long serialVersionUID = 7566894473303706042L; + + private IIdType id = new IdDt(); + + @Override + public IBaseMetaType getMeta() + { + return null; + } + + @Override + public IIdType getIdElement() + { + return id; + } + + @Override + public IBaseResource setId( String theId ) + { + id = new IdDt( theId ); + + return this; + } + + @Override + public IBaseResource setId( IIdType theId ) + { + this.id = theId; + + return this; + } + + @Override + public FhirVersionEnum getStructureFhirVersionEnum() + { + return FhirVersionEnum.R4; + } + + @Override + public boolean isEmpty() + { + return false; + } + + @Override + public boolean hasFormatComment() + { + return false; + } + + @Override + public List getFormatCommentsPre() + { + return null; + } + + @Override + public List getFormatCommentsPost() + { + return null; + } + } +} \ No newline at end of file From f0e7261439d10caf6a1925213d23eec6beed0261 Mon Sep 17 00:00:00 2001 From: Volker Schmidt Date: Wed, 31 Jul 2019 14:31:05 +0200 Subject: [PATCH 10/21] Adds support for tracked entity resource type in Plan Definitions. --- .../r4/R4ProgramMetadataFhirRestAppTest.java | 3 + .../LocationExtension.StructureDefinition.xml | 17 +-- ...ourceTypeExtension.StructureDefinition.xml | 57 ++++++++++ ...gramPlanDefinition.StructureDefinition.xml | 21 ++++ ...tadataToFhirPlanDefinitionTransformer.java | 12 +- ...taToFhirPlanDefinitionTransformerTest.java | 14 ++- .../extension/ResourceTypeExtensionUtils.java | 68 +++++++++++ ...tadataToFhirPlanDefinitionTransformer.java | 33 +++++- .../ResourceTypeExtensionUtilsTest.java | 107 ++++++++++++++++++ ...taToFhirPlanDefinitionTransformerTest.java | 88 ++++++++++---- 10 files changed, 372 insertions(+), 48 deletions(-) create mode 100644 docs/fhir/profiles/ResourceTypeExtension.StructureDefinition.xml create mode 100644 fhir/src/main/java/org/dhis2/fhir/adapter/fhir/extension/ResourceTypeExtensionUtils.java create mode 100644 fhir/src/test/java/org/dhis2/fhir/adapter/fhir/extension/ResourceTypeExtensionUtilsTest.java diff --git a/app/src/test/java/org/dhis2/fhir/adapter/fhir/server/r4/R4ProgramMetadataFhirRestAppTest.java b/app/src/test/java/org/dhis2/fhir/adapter/fhir/server/r4/R4ProgramMetadataFhirRestAppTest.java index e86e33b7..e828ae5b 100644 --- a/app/src/test/java/org/dhis2/fhir/adapter/fhir/server/r4/R4ProgramMetadataFhirRestAppTest.java +++ b/app/src/test/java/org/dhis2/fhir/adapter/fhir/server/r4/R4ProgramMetadataFhirRestAppTest.java @@ -34,6 +34,7 @@ import ca.uhn.fhir.rest.server.exceptions.ResourceNotFoundException; import org.apache.commons.io.IOUtils; import org.dhis2.fhir.adapter.AbstractAppTest; +import org.dhis2.fhir.adapter.fhir.extension.ResourceTypeExtensionUtils; import org.dhis2.fhir.adapter.fhir.model.FhirVersion; import org.hl7.fhir.r4.model.Bundle; import org.hl7.fhir.r4.model.PlanDefinition; @@ -124,6 +125,8 @@ private void getPlanDefinition() throws Exception client.registerInterceptor( new BasicAuthInterceptor( "fhir_client", "fhir_client_1" ) ); PlanDefinition planDefinition = client.read().resource( PlanDefinition.class ).withId( "EPDyQuoRnXk" ).execute(); Assert.assertEquals( "Child Programme", planDefinition.getTitle() ); + Assert.assertNotNull( planDefinition.getExtensionByUrl( ResourceTypeExtensionUtils.URL ) ); + Assert.assertEquals( "Patient", planDefinition.getExtensionByUrl( ResourceTypeExtensionUtils.URL ).getValue().primitiveValue() ); systemDhis2Server.verify(); userDhis2Server.verify(); diff --git a/docs/fhir/profiles/LocationExtension.StructureDefinition.xml b/docs/fhir/profiles/LocationExtension.StructureDefinition.xml index 4211ca46..51891d76 100644 --- a/docs/fhir/profiles/LocationExtension.StructureDefinition.xml +++ b/docs/fhir/profiles/LocationExtension.StructureDefinition.xml @@ -28,7 +28,7 @@ --> - + @@ -47,21 +47,6 @@ - - - - - - - - - - - - - - - diff --git a/docs/fhir/profiles/ResourceTypeExtension.StructureDefinition.xml b/docs/fhir/profiles/ResourceTypeExtension.StructureDefinition.xml new file mode 100644 index 00000000..76f90869 --- /dev/null +++ b/docs/fhir/profiles/ResourceTypeExtension.StructureDefinition.xml @@ -0,0 +1,57 @@ + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/docs/fhir/profiles/TrackerProgramPlanDefinition.StructureDefinition.xml b/docs/fhir/profiles/TrackerProgramPlanDefinition.StructureDefinition.xml index bf33d0bb..25e9ae28 100644 --- a/docs/fhir/profiles/TrackerProgramPlanDefinition.StructureDefinition.xml +++ b/docs/fhir/profiles/TrackerProgramPlanDefinition.StructureDefinition.xml @@ -50,6 +50,27 @@ + + + + + + + + + + + + + + + + + + + + + diff --git a/fhir-r4/src/main/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/metadata/r4/R4ProgramMetadataToFhirPlanDefinitionTransformer.java b/fhir-r4/src/main/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/metadata/r4/R4ProgramMetadataToFhirPlanDefinitionTransformer.java index 44333c79..90f5a217 100644 --- a/fhir-r4/src/main/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/metadata/r4/R4ProgramMetadataToFhirPlanDefinitionTransformer.java +++ b/fhir-r4/src/main/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/metadata/r4/R4ProgramMetadataToFhirPlanDefinitionTransformer.java @@ -28,6 +28,7 @@ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ +import ca.uhn.fhir.model.api.IElement; import org.dhis2.fhir.adapter.dhis.orgunit.OrganizationUnitService; import org.dhis2.fhir.adapter.dhis.tracker.program.Program; import org.dhis2.fhir.adapter.dhis.tracker.trackedentity.TrackedEntityMetadataService; @@ -50,11 +51,13 @@ import org.hl7.fhir.r4.model.Enumerations; import org.hl7.fhir.r4.model.PlanDefinition; import org.hl7.fhir.r4.model.PlanDefinition.PlanDefinitionActionComponent; +import org.hl7.fhir.r4.model.ResourceFactory; import org.springframework.stereotype.Component; import javax.annotation.Nonnull; import java.util.Map; import java.util.Set; +import java.util.function.Function; /** * R4 specific version of DHIS2 Program Metadata to FHIR Plan Definition transformer. @@ -86,7 +89,7 @@ protected boolean transformInternal( @Nonnull FhirClient fhirClient, @Nonnull Dh final Program dhisProgram = (Program) input.getDhisResource(); final PlanDefinition fhirPlanDefinition = (PlanDefinition) output; - if ( !isApplicableProgram( dhisProgram ) ) + if ( !addSubjectResourceType( dhisProgram, fhirPlanDefinition ) ) { return false; } @@ -111,4 +114,11 @@ protected boolean transformInternal( @Nonnull FhirClient fhirClient, @Nonnull Dh return true; } + + @Nonnull + @Override + protected Function getTypeFactory() + { + return ResourceFactory::createType; + } } diff --git a/fhir-r4/src/test/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/metadata/r4/R4ProgramMetadataToFhirPlanDefinitionTransformerTest.java b/fhir-r4/src/test/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/metadata/r4/R4ProgramMetadataToFhirPlanDefinitionTransformerTest.java index adccd703..a795ad71 100644 --- a/fhir-r4/src/test/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/metadata/r4/R4ProgramMetadataToFhirPlanDefinitionTransformerTest.java +++ b/fhir-r4/src/test/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/metadata/r4/R4ProgramMetadataToFhirPlanDefinitionTransformerTest.java @@ -36,9 +36,12 @@ import org.dhis2.fhir.adapter.dhis.tracker.trackedentity.TrackedEntityMetadataService; import org.dhis2.fhir.adapter.dhis.tracker.trackedentity.WritableTrackedEntityType; import org.dhis2.fhir.adapter.fhir.data.repository.FhirDhisAssignmentRepository; +import org.dhis2.fhir.adapter.fhir.extension.ResourceTypeExtensionUtils; import org.dhis2.fhir.adapter.fhir.metadata.model.FhirClient; +import org.dhis2.fhir.adapter.fhir.metadata.model.FhirResourceType; import org.dhis2.fhir.adapter.fhir.metadata.model.ProgramMetadataRule; import org.dhis2.fhir.adapter.fhir.metadata.model.RuleInfo; +import org.dhis2.fhir.adapter.fhir.metadata.model.TrackedEntityRule; import org.dhis2.fhir.adapter.fhir.metadata.repository.SystemRepository; import org.dhis2.fhir.adapter.fhir.metadata.repository.TrackedEntityRuleRepository; import org.dhis2.fhir.adapter.fhir.model.FhirVersion; @@ -120,10 +123,13 @@ public void getFhirVersions() @Test public void transformInternal() { + final TrackedEntityRule rule2 = new TrackedEntityRule(); + rule2.setEvaluationOrder( 100 ); + rule2.setFhirResourceType( FhirResourceType.PATIENT ); + Mockito.doReturn( Optional.of( new WritableTrackedEntityType( "a1234567890", "Test", Collections.emptyList() ) ) ) .when( trackedEntityMetadataService ).findTypeByReference( Mockito.eq( new Reference( "a1234567890", ReferenceType.ID ) ) ); - Mockito.doReturn( Collections.singletonList( new RuleInfo<>( new ProgramMetadataRule(), Collections.emptyList() ) ) ) - .when( trackedEntityRuleRepository ).findByTypeRefs( Mockito.eq( new HashSet<>( + Mockito.doReturn( Collections.singletonList( rule2 ) ).when( trackedEntityRuleRepository ).findByTypeRefs( Mockito.eq( new HashSet<>( Arrays.asList( new Reference( "a1234567890", ReferenceType.ID ), new Reference( "Test", ReferenceType.NAME ) ) ) ) ); final WritableProgram program = new WritableProgram(); @@ -156,6 +162,10 @@ public void transformInternal() Assert.assertEquals( "Test Description", fhirPlanDefinition.getDescription() ); Assert.assertEquals( 2, fhirPlanDefinition.getAction().size() ); + Assert.assertEquals( 1, fhirPlanDefinition.getExtension().size() ); + Assert.assertEquals( ResourceTypeExtensionUtils.URL, fhirPlanDefinition.getExtension().get( 0 ).getUrl() ); + Assert.assertEquals( "Patient", fhirPlanDefinition.getExtension().get( 0 ).getValue().toString() ); + Assert.assertEquals( "b1234567890", fhirPlanDefinition.getAction().get( 0 ).getId() ); Assert.assertEquals( "Test Stage 1", fhirPlanDefinition.getAction().get( 0 ).getTitle() ); Assert.assertEquals( "Test Description 1", fhirPlanDefinition.getAction().get( 0 ).getDescription() ); diff --git a/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/extension/ResourceTypeExtensionUtils.java b/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/extension/ResourceTypeExtensionUtils.java new file mode 100644 index 00000000..595cc28f --- /dev/null +++ b/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/extension/ResourceTypeExtensionUtils.java @@ -0,0 +1,68 @@ +package org.dhis2.fhir.adapter.fhir.extension; + +/* + * Copyright (c) 2004-2019, University of Oslo + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * Neither the name of the HISP project nor the names of its contributors may + * be used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +import ca.uhn.fhir.model.api.IElement; +import org.dhis2.fhir.adapter.fhir.metadata.model.FhirResourceType; +import org.hl7.fhir.instance.model.api.IBaseExtension; +import org.hl7.fhir.instance.model.api.IBaseHasExtensions; +import org.hl7.fhir.instance.model.api.IPrimitiveType; + +import javax.annotation.Nonnull; +import javax.annotation.Nullable; +import java.util.function.Function; + +/** + * Utility class to process FHIR resource type extension. + * + * @author volsch + */ +public abstract class ResourceTypeExtensionUtils +{ + public static final String URL = "http://www.dhis2.org/dhis2-fhir-adapter/fhir/extensions/resource-type"; + + @SuppressWarnings( "unchecked" ) + public static void setValue( @Nonnull IBaseHasExtensions resource, @Nullable FhirResourceType fhirResourceType, @Nonnull Function typeFactory ) + { + resource.getExtension().removeIf( e -> URL.equals( e.getUrl() ) ); + + if ( fhirResourceType != null ) + { + final IBaseExtension extension = resource.addExtension(); + + extension.setUrl( URL ); + extension.setValue( ( (IPrimitiveType) typeFactory.apply( "string" ) ).setValue( fhirResourceType.getResourceTypeName() ) ); + } + } + + private ResourceTypeExtensionUtils() + { + super(); + } +} diff --git a/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/metadata/program/AbstractProgramMetadataToFhirPlanDefinitionTransformer.java b/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/metadata/program/AbstractProgramMetadataToFhirPlanDefinitionTransformer.java index bb7aaefe..a2b48af7 100644 --- a/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/metadata/program/AbstractProgramMetadataToFhirPlanDefinitionTransformer.java +++ b/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/metadata/program/AbstractProgramMetadataToFhirPlanDefinitionTransformer.java @@ -28,6 +28,7 @@ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ +import ca.uhn.fhir.model.api.IElement; import org.dhis2.fhir.adapter.dhis.model.DhisResourceType; import org.dhis2.fhir.adapter.dhis.model.Reference; import org.dhis2.fhir.adapter.dhis.model.ReferenceType; @@ -36,6 +37,8 @@ import org.dhis2.fhir.adapter.dhis.tracker.trackedentity.TrackedEntityMetadataService; import org.dhis2.fhir.adapter.dhis.tracker.trackedentity.TrackedEntityType; import org.dhis2.fhir.adapter.fhir.data.repository.FhirDhisAssignmentRepository; +import org.dhis2.fhir.adapter.fhir.extension.ResourceTypeExtensionUtils; +import org.dhis2.fhir.adapter.fhir.metadata.model.AbstractRule; import org.dhis2.fhir.adapter.fhir.metadata.model.FhirResourceType; import org.dhis2.fhir.adapter.fhir.metadata.model.ProgramMetadataRule; import org.dhis2.fhir.adapter.fhir.metadata.repository.SystemRepository; @@ -46,11 +49,13 @@ import org.dhis2.fhir.adapter.fhir.transform.dhis.impl.metadata.AbstractReadOnlyDhisMetadataToTypedFhirTransformer; import org.dhis2.fhir.adapter.fhir.transform.scripted.AccessibleScriptedDhisMetadata; import org.dhis2.fhir.adapter.lock.LockManager; +import org.hl7.fhir.instance.model.api.IBaseHasExtensions; import org.hl7.fhir.instance.model.api.IBaseResource; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import javax.annotation.Nonnull; +import java.util.function.Function; /** * Implementation of {@link DhisToFhirTransformer} for transforming DHIS2 @@ -63,6 +68,8 @@ public abstract class AbstractProgramMetadataToFhirPlanDefinitionTransformer getRuleClass() return ProgramMetadataRule.class; } - protected boolean isApplicableProgram( @Nonnull Program program ) + protected boolean addSubjectResourceType( @Nonnull Program program, @Nonnull IBaseHasExtensions resource ) { - if ( program.isWithoutRegistration() || program.getTrackedEntityTypeId() == null ) + if ( program.getTrackedEntityTypeId() == null ) { - return false; + ResourceTypeExtensionUtils.setValue( resource, null, getTypeFactory() ); + + return true; } - final TrackedEntityType trackedEntityType = trackedEntityMetadataService.findTypeByReference( new Reference( program.getTrackedEntityTypeId(), ReferenceType.ID ) ).orElse( null ); + final TrackedEntityType trackedEntityType = trackedEntityMetadataService.findTypeByReference( + new Reference( program.getTrackedEntityTypeId(), ReferenceType.ID ) ).orElse( null ); if ( trackedEntityType == null ) { return false; } - return !trackedEntityRuleRepository.findByTypeRefs( trackedEntityType.getAllReferences() ).isEmpty(); + final FhirResourceType fhirResourceType = trackedEntityRuleRepository.findByTypeRefs( trackedEntityType.getAllReferences() ).stream() + .sorted( ( o1, o2 ) -> o2.getEvaluationOrder() - o1.getEvaluationOrder() ).map( AbstractRule::getFhirResourceType ).findFirst().orElse( null ); + + if ( fhirResourceType == null ) + { + return false; + } + + ResourceTypeExtensionUtils.setValue( resource, fhirResourceType, getTypeFactory() ); + + return true; } + + @Nonnull + protected abstract Function getTypeFactory(); } diff --git a/fhir/src/test/java/org/dhis2/fhir/adapter/fhir/extension/ResourceTypeExtensionUtilsTest.java b/fhir/src/test/java/org/dhis2/fhir/adapter/fhir/extension/ResourceTypeExtensionUtilsTest.java new file mode 100644 index 00000000..d245ec1d --- /dev/null +++ b/fhir/src/test/java/org/dhis2/fhir/adapter/fhir/extension/ResourceTypeExtensionUtilsTest.java @@ -0,0 +1,107 @@ +package org.dhis2.fhir.adapter.fhir.extension; + +/* + * Copyright (c) 2004-2019, University of Oslo + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * Neither the name of the HISP project nor the names of its contributors may + * be used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +import ca.uhn.fhir.model.api.ExtensionDt; +import ca.uhn.fhir.model.api.IElement; +import ca.uhn.fhir.model.primitive.StringDt; +import org.dhis2.fhir.adapter.fhir.metadata.model.FhirResourceType; +import org.hl7.fhir.instance.model.api.IBaseExtension; +import org.hl7.fhir.instance.model.api.IBaseHasExtensions; +import org.junit.Assert; +import org.junit.Test; + +import java.util.ArrayList; +import java.util.List; + +/** + * Unit tests for {@link ResourceTypeExtensionUtils}. + * + * @author volsch + */ +public class ResourceTypeExtensionUtilsTest +{ + @Test + public void resetValue() + { + PlanDefinition planDefinition = new PlanDefinition(); + + ResourceTypeExtensionUtils.setValue( planDefinition, FhirResourceType.PATIENT, TypeFactory::createType ); + ResourceTypeExtensionUtils.setValue( planDefinition, null, TypeFactory::createType ); + + Assert.assertTrue( planDefinition.getExtension().isEmpty() ); + } + + @Test + public void setValue() + { + PlanDefinition planDefinition = new PlanDefinition(); + + ResourceTypeExtensionUtils.setValue( planDefinition, FhirResourceType.PATIENT, TypeFactory::createType ); + + Assert.assertEquals( 1, planDefinition.getExtension().size() ); + Assert.assertEquals( ResourceTypeExtensionUtils.URL, planDefinition.getExtension().get( 0 ).getUrl() ); + Assert.assertEquals( "Patient", planDefinition.getExtension().get( 0 ).getValue().toString() ); + } + + public static class PlanDefinition implements IBaseHasExtensions + { + private final List> extensions = new ArrayList<>(); + + @Override + public IBaseExtension addExtension() + { + extensions.add( new ExtensionDt() ); + + return extensions.get( extensions.size() - 1 ); + } + + @Override + public List> getExtension() + { + return extensions; + } + + @Override + public boolean hasExtension() + { + return !extensions.isEmpty(); + } + } + + public static class TypeFactory + { + public static IElement createType( String name ) + { + Assert.assertEquals( "string", name ); + + return new StringDt(); + } + } +} \ No newline at end of file diff --git a/fhir/src/test/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/metadata/program/AbstractProgramMetadataToFhirPlanDefinitionTransformerTest.java b/fhir/src/test/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/metadata/program/AbstractProgramMetadataToFhirPlanDefinitionTransformerTest.java index 8ee2c709..5c473888 100644 --- a/fhir/src/test/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/metadata/program/AbstractProgramMetadataToFhirPlanDefinitionTransformerTest.java +++ b/fhir/src/test/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/metadata/program/AbstractProgramMetadataToFhirPlanDefinitionTransformerTest.java @@ -28,6 +28,8 @@ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ +import ca.uhn.fhir.model.api.ExtensionDt; +import ca.uhn.fhir.model.primitive.StringDt; import org.dhis2.fhir.adapter.dhis.model.DhisResourceType; import org.dhis2.fhir.adapter.dhis.model.Reference; import org.dhis2.fhir.adapter.dhis.model.ReferenceType; @@ -36,15 +38,17 @@ import org.dhis2.fhir.adapter.dhis.tracker.trackedentity.TrackedEntityMetadataService; import org.dhis2.fhir.adapter.dhis.tracker.trackedentity.WritableTrackedEntityType; import org.dhis2.fhir.adapter.fhir.data.repository.FhirDhisAssignmentRepository; +import org.dhis2.fhir.adapter.fhir.extension.ResourceTypeExtensionUtils; import org.dhis2.fhir.adapter.fhir.metadata.model.FhirResourceType; -import org.dhis2.fhir.adapter.fhir.metadata.model.ProgramMetadataRule; -import org.dhis2.fhir.adapter.fhir.metadata.model.RuleInfo; +import org.dhis2.fhir.adapter.fhir.metadata.model.TrackedEntityRule; import org.dhis2.fhir.adapter.fhir.metadata.repository.SystemRepository; import org.dhis2.fhir.adapter.fhir.metadata.repository.TrackedEntityRuleRepository; import org.dhis2.fhir.adapter.fhir.repository.FhirResourceRepository; import org.dhis2.fhir.adapter.fhir.script.ScriptExecutor; import org.dhis2.fhir.adapter.fhir.transform.scripted.AccessibleScriptedDhisMetadata; import org.dhis2.fhir.adapter.lock.LockManager; +import org.hl7.fhir.instance.model.api.IBaseExtension; +import org.hl7.fhir.instance.model.api.IBaseHasExtensions; import org.junit.Assert; import org.junit.Before; import org.junit.Rule; @@ -58,6 +62,7 @@ import java.util.Arrays; import java.util.Collections; import java.util.HashSet; +import java.util.List; import java.util.Optional; import static org.mockito.Mockito.CALLS_REAL_METHODS; @@ -97,7 +102,7 @@ public class AbstractProgramMetadataToFhirPlanDefinitionTransformerTest private AbstractProgramMetadataToFhirPlanDefinitionTransformer transformer; @Rule - public MockitoRule rule = MockitoJUnit.rule(); + public MockitoRule rule = MockitoJUnit.rule().silent(); @Before public void setUp() @@ -105,6 +110,8 @@ public void setUp() transformer = Mockito.mock( AbstractProgramMetadataToFhirPlanDefinitionTransformer.class, withSettings().useConstructor( scriptExecutor, lockManager, systemRepository, fhirResourceRepository, fhirDhisAssignmentRepository, organizationUnitService, trackedEntityMetadataService, trackedEntityRuleRepository ).defaultAnswer( CALLS_REAL_METHODS ) ); + + Mockito.when( transformer.getTypeFactory() ).thenReturn( name -> new StringDt() ); } @Test @@ -126,12 +133,19 @@ public void getDhisResourceClass() } @Test - public void isApplicableProgram() + public void addSubjectResourceType() { + final TrackedEntityRule rule1 = new TrackedEntityRule(); + rule1.setEvaluationOrder( 1 ); + rule1.setFhirResourceType( FhirResourceType.RELATED_PERSON ); + + final TrackedEntityRule rule2 = new TrackedEntityRule(); + rule2.setEvaluationOrder( 100 ); + rule2.setFhirResourceType( FhirResourceType.PATIENT ); + Mockito.doReturn( Optional.of( new WritableTrackedEntityType( "a1234567890", "Test", Collections.emptyList() ) ) ) .when( trackedEntityMetadataService ).findTypeByReference( Mockito.eq( new Reference( "a1234567890", ReferenceType.ID ) ) ); - Mockito.doReturn( Collections.singletonList( new RuleInfo<>( new ProgramMetadataRule(), Collections.emptyList() ) ) ) - .when( trackedEntityRuleRepository ).findByTypeRefs( Mockito.eq( new HashSet<>( + Mockito.doReturn( Arrays.asList( rule1, rule2 ) ).when( trackedEntityRuleRepository ).findByTypeRefs( Mockito.eq( new HashSet<>( Arrays.asList( new Reference( "a1234567890", ReferenceType.ID ), new Reference( "Test", ReferenceType.NAME ) ) ) ) ); final WritableProgram program = new WritableProgram(); @@ -141,24 +155,17 @@ public void isApplicableProgram() program.setTrackedEntityTypeId( "a1234567890" ); program.setStages( new ArrayList<>() ); - Assert.assertTrue( transformer.isApplicableProgram( program ) ); - } + final PlanDefinition planDefinition = new PlanDefinition(); - @Test - public void isApplicableProgramWithoutRegistration() - { - final WritableProgram program = new WritableProgram(); - program.setName( "Test Program" ); - program.setDescription( "Test Description" ); - program.setWithoutRegistration( true ); - program.setTrackedEntityTypeId( "a1234567890" ); - program.setStages( new ArrayList<>() ); + Assert.assertTrue( transformer.addSubjectResourceType( program, planDefinition ) ); - Assert.assertFalse( transformer.isApplicableProgram( program ) ); + Assert.assertEquals( 1, planDefinition.getExtension().size() ); + Assert.assertEquals( ResourceTypeExtensionUtils.URL, planDefinition.getExtension().get( 0 ).getUrl() ); + Assert.assertEquals( "Patient", planDefinition.getExtension().get( 0 ).getValue().toString() ); } @Test - public void isApplicableProgramWithoutTrackedEntityType() + public void addSubjectResourceTypeWithoutTrackedEntityType() { final WritableProgram program = new WritableProgram(); program.setName( "Test Program" ); @@ -166,11 +173,15 @@ public void isApplicableProgramWithoutTrackedEntityType() program.setWithoutRegistration( false ); program.setStages( new ArrayList<>() ); - Assert.assertFalse( transformer.isApplicableProgram( program ) ); + final PlanDefinition planDefinition = new PlanDefinition(); + + Assert.assertTrue( transformer.addSubjectResourceType( program, planDefinition ) ); + + Assert.assertEquals( 0, planDefinition.getExtension().size() ); } @Test - public void isApplicableProgramWithoutTrackedEntityMetadata() + public void addSubjectResourceTypeWithoutTrackedEntityMetadata() { Mockito.doReturn( Optional.empty() ) .when( trackedEntityMetadataService ).findTypeByReference( Mockito.eq( new Reference( "a1234567890", ReferenceType.ID ) ) ); @@ -182,11 +193,13 @@ public void isApplicableProgramWithoutTrackedEntityMetadata() program.setTrackedEntityTypeId( "a1234567890" ); program.setStages( new ArrayList<>() ); - Assert.assertFalse( transformer.isApplicableProgram( program ) ); + final PlanDefinition planDefinition = new PlanDefinition(); + + Assert.assertFalse( transformer.addSubjectResourceType( program, planDefinition ) ); } @Test - public void isApplicableProgramWithoutRules() + public void addSubjectResourceTypeWithoutRules() { Mockito.doReturn( Optional.of( new WritableTrackedEntityType( "a1234567890", "Test", Collections.emptyList() ) ) ) .when( trackedEntityMetadataService ).findTypeByReference( Mockito.eq( new Reference( "a1234567890", ReferenceType.ID ) ) ); @@ -201,6 +214,33 @@ public void isApplicableProgramWithoutRules() program.setTrackedEntityTypeId( "a1234567890" ); program.setStages( new ArrayList<>() ); - Assert.assertFalse( transformer.isApplicableProgram( program ) ); + final PlanDefinition planDefinition = new PlanDefinition(); + + Assert.assertFalse( transformer.addSubjectResourceType( program, planDefinition ) ); + } + + public static class PlanDefinition implements IBaseHasExtensions + { + private final List> extensions = new ArrayList<>(); + + @Override + public IBaseExtension addExtension() + { + extensions.add( new ExtensionDt() ); + + return extensions.get( extensions.size() - 1 ); + } + + @Override + public List> getExtension() + { + return extensions; + } + + @Override + public boolean hasExtension() + { + return !extensions.isEmpty(); + } } } \ No newline at end of file From 5930c6377e8526ed22c88a54c9e9d7b76746dc1c Mon Sep 17 00:00:00 2001 From: Volker Schmidt Date: Wed, 31 Jul 2019 17:54:47 +0200 Subject: [PATCH 11/21] Adds value type extension for Questionnaire items. --- ...ourceTypeExtension.StructureDefinition.xml | 2 +- ...ogramQuestionnaire.StructureDefinition.xml | 20 ++++++ ...ValueTypeExtension.StructureDefinition.xml | 57 ++++++++++++++++ ...etadataToFhirQuestionnaireTransformer.java | 13 ++++ ...ataToFhirQuestionnaireTransformerTest.java | 12 ++++ .../fhir/extension/BaseExtensionUtils.java | 65 ++++++++++++++++++ .../extension/ResourceTypeExtensionUtils.java | 13 +--- .../extension/ValueTypeExtensionUtils.java | 56 ++++++++++++++++ ...etadataToFhirQuestionnaireTransformer.java | 5 ++ .../extension/BaseExtensionUtilsTest.java | 64 ++++++++++++++++++ .../ResourceTypeExtensionUtilsTest.java | 47 +------------ .../fhir/extension/TestPlanDefinition.java | 66 +++++++++++++++++++ .../adapter/fhir/extension/TypeFactory.java | 48 ++++++++++++++ .../ValueTypeExtensionUtilsTest.java | 53 +++++++++++++++ 14 files changed, 463 insertions(+), 58 deletions(-) create mode 100644 docs/fhir/profiles/ValueTypeExtension.StructureDefinition.xml create mode 100644 fhir/src/main/java/org/dhis2/fhir/adapter/fhir/extension/BaseExtensionUtils.java create mode 100644 fhir/src/main/java/org/dhis2/fhir/adapter/fhir/extension/ValueTypeExtensionUtils.java create mode 100644 fhir/src/test/java/org/dhis2/fhir/adapter/fhir/extension/BaseExtensionUtilsTest.java create mode 100644 fhir/src/test/java/org/dhis2/fhir/adapter/fhir/extension/TestPlanDefinition.java create mode 100644 fhir/src/test/java/org/dhis2/fhir/adapter/fhir/extension/TypeFactory.java create mode 100644 fhir/src/test/java/org/dhis2/fhir/adapter/fhir/extension/ValueTypeExtensionUtilsTest.java diff --git a/docs/fhir/profiles/ResourceTypeExtension.StructureDefinition.xml b/docs/fhir/profiles/ResourceTypeExtension.StructureDefinition.xml index 76f90869..fc42e483 100644 --- a/docs/fhir/profiles/ResourceTypeExtension.StructureDefinition.xml +++ b/docs/fhir/profiles/ResourceTypeExtension.StructureDefinition.xml @@ -36,7 +36,7 @@ - + diff --git a/docs/fhir/profiles/TrackerProgramQuestionnaire.StructureDefinition.xml b/docs/fhir/profiles/TrackerProgramQuestionnaire.StructureDefinition.xml index eeef7fc0..f7fa6224 100644 --- a/docs/fhir/profiles/TrackerProgramQuestionnaire.StructureDefinition.xml +++ b/docs/fhir/profiles/TrackerProgramQuestionnaire.StructureDefinition.xml @@ -164,6 +164,26 @@ + + + + + + + + + + + + + + + + + + + + diff --git a/docs/fhir/profiles/ValueTypeExtension.StructureDefinition.xml b/docs/fhir/profiles/ValueTypeExtension.StructureDefinition.xml new file mode 100644 index 00000000..5fae7e76 --- /dev/null +++ b/docs/fhir/profiles/ValueTypeExtension.StructureDefinition.xml @@ -0,0 +1,57 @@ + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/fhir-r4/src/main/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/metadata/r4/R4ProgramStageMetadataToFhirQuestionnaireTransformer.java b/fhir-r4/src/main/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/metadata/r4/R4ProgramStageMetadataToFhirQuestionnaireTransformer.java index 2575b8ee..8b113b97 100644 --- a/fhir-r4/src/main/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/metadata/r4/R4ProgramStageMetadataToFhirQuestionnaireTransformer.java +++ b/fhir-r4/src/main/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/metadata/r4/R4ProgramStageMetadataToFhirQuestionnaireTransformer.java @@ -28,9 +28,11 @@ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ +import ca.uhn.fhir.model.api.IElement; import org.dhis2.fhir.adapter.dhis.orgunit.OrganizationUnitService; import org.dhis2.fhir.adapter.dhis.tracker.program.ProgramStage; import org.dhis2.fhir.adapter.fhir.data.repository.FhirDhisAssignmentRepository; +import org.dhis2.fhir.adapter.fhir.extension.ValueTypeExtensionUtils; import org.dhis2.fhir.adapter.fhir.metadata.model.FhirClient; import org.dhis2.fhir.adapter.fhir.metadata.model.ProgramStageMetadataRule; import org.dhis2.fhir.adapter.fhir.metadata.model.RuleInfo; @@ -49,12 +51,14 @@ import org.hl7.fhir.r4.model.Questionnaire; import org.hl7.fhir.r4.model.Questionnaire.QuestionnaireItemComponent; import org.hl7.fhir.r4.model.Questionnaire.QuestionnaireItemType; +import org.hl7.fhir.r4.model.ResourceFactory; import org.springframework.stereotype.Component; import javax.annotation.Nonnull; import javax.annotation.Nullable; import java.util.Map; import java.util.Set; +import java.util.function.Function; import static org.hl7.fhir.r4.model.Questionnaire.QuestionnaireItemType.*; @@ -104,6 +108,8 @@ protected boolean transformInternal( @Nonnull FhirClient fhirClient, @Nonnull Dh itemComponent.setRequired( dataElement.isCompulsory() ); itemComponent.setType( type ); + ValueTypeExtensionUtils.setValue( itemComponent, dataElement.getElement().getValueType(), getTypeFactory() ); + if ( dataElement.getElement().isOptionSetValue() ) { dataElement.getElement().getOptionSet().getOptions().forEach( option -> itemComponent.addAnswerOption().setValue( @@ -155,4 +161,11 @@ protected QuestionnaireItemType convertValueType( @Nonnull ValueType valueType ) // unhandled data type return null; } + + @Nonnull + @Override + protected Function getTypeFactory() + { + return ResourceFactory::createType; + } } diff --git a/fhir-r4/src/test/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/metadata/r4/R4ProgramStageMetadataToFhirQuestionnaireTransformerTest.java b/fhir-r4/src/test/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/metadata/r4/R4ProgramStageMetadataToFhirQuestionnaireTransformerTest.java index 3cf0b25c..c27859e3 100644 --- a/fhir-r4/src/test/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/metadata/r4/R4ProgramStageMetadataToFhirQuestionnaireTransformerTest.java +++ b/fhir-r4/src/test/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/metadata/r4/R4ProgramStageMetadataToFhirQuestionnaireTransformerTest.java @@ -35,6 +35,7 @@ import org.dhis2.fhir.adapter.dhis.tracker.program.WritableProgramStage; import org.dhis2.fhir.adapter.dhis.tracker.program.WritableProgramStageDataElement; import org.dhis2.fhir.adapter.fhir.data.repository.FhirDhisAssignmentRepository; +import org.dhis2.fhir.adapter.fhir.extension.ValueTypeExtensionUtils; import org.dhis2.fhir.adapter.fhir.metadata.model.FhirClient; import org.dhis2.fhir.adapter.fhir.metadata.model.ProgramStageMetadataRule; import org.dhis2.fhir.adapter.fhir.metadata.model.RuleInfo; @@ -49,6 +50,7 @@ import org.dhis2.fhir.adapter.model.ValueType; import org.hl7.fhir.r4.model.Coding; import org.hl7.fhir.r4.model.Questionnaire; +import org.hl7.fhir.r4.model.StringType; import org.junit.Assert; import org.junit.Rule; import org.junit.Test; @@ -157,6 +159,11 @@ public void transformInternal() Assert.assertEquals( 0, fhirQuestionnaire.getItem().get( 0 ).getAnswerOption().size() ); Assert.assertFalse( fhirQuestionnaire.getItem().get( 0 ).getRequired() ); + Assert.assertEquals( 1, fhirQuestionnaire.getItem().get( 0 ).getExtension().size() ); + Assert.assertEquals( ValueTypeExtensionUtils.URL, fhirQuestionnaire.getItem().get( 0 ).getExtension().get( 0 ).getUrl() ); + Assert.assertTrue( fhirQuestionnaire.getItem().get( 0 ).getExtension().get( 0 ).getValue() instanceof StringType ); + Assert.assertEquals( "TEXT", ( (StringType) fhirQuestionnaire.getItem().get( 0 ).getExtension().get( 0 ).getValue() ).getValue() ); + Assert.assertEquals( "d1123456789", fhirQuestionnaire.getItem().get( 1 ).getLinkId() ); Assert.assertEquals( "Value 2", fhirQuestionnaire.getItem().get( 1 ).getText() ); Assert.assertEquals( 2, fhirQuestionnaire.getItem().get( 1 ).getAnswerOption().size() ); @@ -166,5 +173,10 @@ public void transformInternal() Assert.assertEquals( "7", ( (Coding) fhirQuestionnaire.getItem().get( 1 ).getAnswerOption().get( 1 ).getValue() ).getCode() ); Assert.assertEquals( "Test Value 2", ( (Coding) fhirQuestionnaire.getItem().get( 1 ).getAnswerOption().get( 1 ).getValue() ).getDisplay() ); Assert.assertTrue( fhirQuestionnaire.getItem().get( 1 ).getRequired() ); + + Assert.assertEquals( 1, fhirQuestionnaire.getItem().get( 1 ).getExtension().size() ); + Assert.assertEquals( ValueTypeExtensionUtils.URL, fhirQuestionnaire.getItem().get( 1 ).getExtension().get( 0 ).getUrl() ); + Assert.assertTrue( fhirQuestionnaire.getItem().get( 1 ).getExtension().get( 0 ).getValue() instanceof StringType ); + Assert.assertEquals( "INTEGER", ( (StringType) fhirQuestionnaire.getItem().get( 1 ).getExtension().get( 0 ).getValue() ).getValue() ); } } diff --git a/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/extension/BaseExtensionUtils.java b/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/extension/BaseExtensionUtils.java new file mode 100644 index 00000000..6811ec79 --- /dev/null +++ b/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/extension/BaseExtensionUtils.java @@ -0,0 +1,65 @@ +package org.dhis2.fhir.adapter.fhir.extension; + +/* + * Copyright (c) 2004-2019, University of Oslo + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * Neither the name of the HISP project nor the names of its contributors may + * be used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +import ca.uhn.fhir.model.api.IElement; +import org.hl7.fhir.instance.model.api.IBaseExtension; +import org.hl7.fhir.instance.model.api.IBaseHasExtensions; +import org.hl7.fhir.instance.model.api.IPrimitiveType; + +import javax.annotation.Nonnull; +import javax.annotation.Nullable; +import java.util.function.Function; + +/** + * Utility class to process FHIR resource extensions. + * + * @author volsch + */ +abstract class BaseExtensionUtils +{ + @SuppressWarnings( "unchecked" ) + protected static void setStringValue( @Nonnull String url, @Nonnull IBaseHasExtensions resource, @Nullable String value, @Nonnull Function typeFactory ) + { + resource.getExtension().removeIf( e -> url.equals( e.getUrl() ) ); + + if ( value != null ) + { + final IBaseExtension extension = resource.addExtension(); + + extension.setUrl( url ); + extension.setValue( ( (IPrimitiveType) typeFactory.apply( "string" ) ).setValue( value ) ); + } + } + + private BaseExtensionUtils() + { + super(); + } +} diff --git a/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/extension/ResourceTypeExtensionUtils.java b/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/extension/ResourceTypeExtensionUtils.java index 595cc28f..6a316e7d 100644 --- a/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/extension/ResourceTypeExtensionUtils.java +++ b/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/extension/ResourceTypeExtensionUtils.java @@ -30,9 +30,7 @@ import ca.uhn.fhir.model.api.IElement; import org.dhis2.fhir.adapter.fhir.metadata.model.FhirResourceType; -import org.hl7.fhir.instance.model.api.IBaseExtension; import org.hl7.fhir.instance.model.api.IBaseHasExtensions; -import org.hl7.fhir.instance.model.api.IPrimitiveType; import javax.annotation.Nonnull; import javax.annotation.Nullable; @@ -47,18 +45,9 @@ public abstract class ResourceTypeExtensionUtils { public static final String URL = "http://www.dhis2.org/dhis2-fhir-adapter/fhir/extensions/resource-type"; - @SuppressWarnings( "unchecked" ) public static void setValue( @Nonnull IBaseHasExtensions resource, @Nullable FhirResourceType fhirResourceType, @Nonnull Function typeFactory ) { - resource.getExtension().removeIf( e -> URL.equals( e.getUrl() ) ); - - if ( fhirResourceType != null ) - { - final IBaseExtension extension = resource.addExtension(); - - extension.setUrl( URL ); - extension.setValue( ( (IPrimitiveType) typeFactory.apply( "string" ) ).setValue( fhirResourceType.getResourceTypeName() ) ); - } + BaseExtensionUtils.setStringValue( URL, resource, fhirResourceType == null ? null : fhirResourceType.getResourceTypeName(), typeFactory ); } private ResourceTypeExtensionUtils() diff --git a/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/extension/ValueTypeExtensionUtils.java b/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/extension/ValueTypeExtensionUtils.java new file mode 100644 index 00000000..127b9c7c --- /dev/null +++ b/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/extension/ValueTypeExtensionUtils.java @@ -0,0 +1,56 @@ +package org.dhis2.fhir.adapter.fhir.extension; + +/* + * Copyright (c) 2004-2019, University of Oslo + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * Neither the name of the HISP project nor the names of its contributors may + * be used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +import ca.uhn.fhir.model.api.IElement; +import org.dhis2.fhir.adapter.model.ValueType; +import org.hl7.fhir.instance.model.api.IBaseHasExtensions; + +import javax.annotation.Nonnull; +import java.util.function.Function; + +/** + * Utility class to process FHIR value type extension. + * + * @author volsch + */ +public abstract class ValueTypeExtensionUtils +{ + public static final String URL = "http://www.dhis2.org/dhis2-fhir-adapter/fhir/extensions/value-type"; + + public static void setValue( @Nonnull IBaseHasExtensions resource, @Nonnull ValueType valueType, @Nonnull Function typeFactory ) + { + BaseExtensionUtils.setStringValue( URL, resource, valueType.name(), typeFactory ); + } + + private ValueTypeExtensionUtils() + { + super(); + } +} diff --git a/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/metadata/program/AbstractProgramStageMetadataToFhirQuestionnaireTransformer.java b/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/metadata/program/AbstractProgramStageMetadataToFhirQuestionnaireTransformer.java index 0bc8ba45..2252165a 100644 --- a/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/metadata/program/AbstractProgramStageMetadataToFhirQuestionnaireTransformer.java +++ b/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/metadata/program/AbstractProgramStageMetadataToFhirQuestionnaireTransformer.java @@ -28,6 +28,7 @@ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ +import ca.uhn.fhir.model.api.IElement; import org.dhis2.fhir.adapter.dhis.model.DhisResourceType; import org.dhis2.fhir.adapter.dhis.orgunit.OrganizationUnitService; import org.dhis2.fhir.adapter.fhir.data.repository.FhirDhisAssignmentRepository; @@ -45,6 +46,7 @@ import org.slf4j.LoggerFactory; import javax.annotation.Nonnull; +import java.util.function.Function; /** * Implementation of {@link DhisToFhirTransformer} for transforming DHIS2 @@ -90,4 +92,7 @@ public Class getRuleClass() { return ProgramStageMetadataRule.class; } + + @Nonnull + protected abstract Function getTypeFactory(); } diff --git a/fhir/src/test/java/org/dhis2/fhir/adapter/fhir/extension/BaseExtensionUtilsTest.java b/fhir/src/test/java/org/dhis2/fhir/adapter/fhir/extension/BaseExtensionUtilsTest.java new file mode 100644 index 00000000..b6c8758a --- /dev/null +++ b/fhir/src/test/java/org/dhis2/fhir/adapter/fhir/extension/BaseExtensionUtilsTest.java @@ -0,0 +1,64 @@ +package org.dhis2.fhir.adapter.fhir.extension; + +/* + * Copyright (c) 2004-2019, University of Oslo + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * Neither the name of the HISP project nor the names of its contributors may + * be used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +import org.junit.Assert; +import org.junit.Test; + +/** + * Unit tests for {@link BaseExtensionUtils}. + * + * @author volsch + */ +public class BaseExtensionUtilsTest +{ + @Test + public void resetValue() + { + TestPlanDefinition planDefinition = new TestPlanDefinition(); + + BaseExtensionUtils.setStringValue( "testUrl", planDefinition, "testValue", TypeFactory::createType ); + BaseExtensionUtils.setStringValue( "testUrl", planDefinition, null, TypeFactory::createType ); + + Assert.assertTrue( planDefinition.getExtension().isEmpty() ); + } + + @Test + public void setValue() + { + TestPlanDefinition planDefinition = new TestPlanDefinition(); + + BaseExtensionUtils.setStringValue( "testUrl", planDefinition, "testValue", TypeFactory::createType ); + + Assert.assertEquals( 1, planDefinition.getExtension().size() ); + Assert.assertEquals( "testUrl", planDefinition.getExtension().get( 0 ).getUrl() ); + Assert.assertEquals( "testValue", planDefinition.getExtension().get( 0 ).getValue().toString() ); + } + +} \ No newline at end of file diff --git a/fhir/src/test/java/org/dhis2/fhir/adapter/fhir/extension/ResourceTypeExtensionUtilsTest.java b/fhir/src/test/java/org/dhis2/fhir/adapter/fhir/extension/ResourceTypeExtensionUtilsTest.java index d245ec1d..b45787a9 100644 --- a/fhir/src/test/java/org/dhis2/fhir/adapter/fhir/extension/ResourceTypeExtensionUtilsTest.java +++ b/fhir/src/test/java/org/dhis2/fhir/adapter/fhir/extension/ResourceTypeExtensionUtilsTest.java @@ -28,18 +28,10 @@ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -import ca.uhn.fhir.model.api.ExtensionDt; -import ca.uhn.fhir.model.api.IElement; -import ca.uhn.fhir.model.primitive.StringDt; import org.dhis2.fhir.adapter.fhir.metadata.model.FhirResourceType; -import org.hl7.fhir.instance.model.api.IBaseExtension; -import org.hl7.fhir.instance.model.api.IBaseHasExtensions; import org.junit.Assert; import org.junit.Test; -import java.util.ArrayList; -import java.util.List; - /** * Unit tests for {@link ResourceTypeExtensionUtils}. * @@ -50,7 +42,7 @@ public class ResourceTypeExtensionUtilsTest @Test public void resetValue() { - PlanDefinition planDefinition = new PlanDefinition(); + TestPlanDefinition planDefinition = new TestPlanDefinition(); ResourceTypeExtensionUtils.setValue( planDefinition, FhirResourceType.PATIENT, TypeFactory::createType ); ResourceTypeExtensionUtils.setValue( planDefinition, null, TypeFactory::createType ); @@ -61,7 +53,7 @@ public void resetValue() @Test public void setValue() { - PlanDefinition planDefinition = new PlanDefinition(); + TestPlanDefinition planDefinition = new TestPlanDefinition(); ResourceTypeExtensionUtils.setValue( planDefinition, FhirResourceType.PATIENT, TypeFactory::createType ); @@ -69,39 +61,4 @@ public void setValue() Assert.assertEquals( ResourceTypeExtensionUtils.URL, planDefinition.getExtension().get( 0 ).getUrl() ); Assert.assertEquals( "Patient", planDefinition.getExtension().get( 0 ).getValue().toString() ); } - - public static class PlanDefinition implements IBaseHasExtensions - { - private final List> extensions = new ArrayList<>(); - - @Override - public IBaseExtension addExtension() - { - extensions.add( new ExtensionDt() ); - - return extensions.get( extensions.size() - 1 ); - } - - @Override - public List> getExtension() - { - return extensions; - } - - @Override - public boolean hasExtension() - { - return !extensions.isEmpty(); - } - } - - public static class TypeFactory - { - public static IElement createType( String name ) - { - Assert.assertEquals( "string", name ); - - return new StringDt(); - } - } } \ No newline at end of file diff --git a/fhir/src/test/java/org/dhis2/fhir/adapter/fhir/extension/TestPlanDefinition.java b/fhir/src/test/java/org/dhis2/fhir/adapter/fhir/extension/TestPlanDefinition.java new file mode 100644 index 00000000..b2f01a17 --- /dev/null +++ b/fhir/src/test/java/org/dhis2/fhir/adapter/fhir/extension/TestPlanDefinition.java @@ -0,0 +1,66 @@ +package org.dhis2.fhir.adapter.fhir.extension; + +/* + * Copyright (c) 2004-2019, University of Oslo + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * Neither the name of the HISP project nor the names of its contributors may + * be used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +import ca.uhn.fhir.model.api.ExtensionDt; +import org.hl7.fhir.instance.model.api.IBaseExtension; +import org.hl7.fhir.instance.model.api.IBaseHasExtensions; + +import java.util.ArrayList; +import java.util.List; + +/** + * Test plan definition FHIR resource. + * + * @author volsch + */ +class TestPlanDefinition implements IBaseHasExtensions +{ + private final List> extensions = new ArrayList<>(); + + @Override + public IBaseExtension addExtension() + { + extensions.add( new ExtensionDt() ); + + return extensions.get( extensions.size() - 1 ); + } + + @Override + public List> getExtension() + { + return extensions; + } + + @Override + public boolean hasExtension() + { + return !extensions.isEmpty(); + } +} diff --git a/fhir/src/test/java/org/dhis2/fhir/adapter/fhir/extension/TypeFactory.java b/fhir/src/test/java/org/dhis2/fhir/adapter/fhir/extension/TypeFactory.java new file mode 100644 index 00000000..32329ee4 --- /dev/null +++ b/fhir/src/test/java/org/dhis2/fhir/adapter/fhir/extension/TypeFactory.java @@ -0,0 +1,48 @@ +package org.dhis2.fhir.adapter.fhir.extension; + +/* + * Copyright (c) 2004-2019, University of Oslo + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * Neither the name of the HISP project nor the names of its contributors may + * be used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +import ca.uhn.fhir.model.api.IElement; +import ca.uhn.fhir.model.primitive.StringDt; +import org.junit.Assert; + +/** + * Test type factory. + * + * @author volsch + */ +class TypeFactory +{ + public static IElement createType( String name ) + { + Assert.assertEquals( "string", name ); + + return new StringDt(); + } +} diff --git a/fhir/src/test/java/org/dhis2/fhir/adapter/fhir/extension/ValueTypeExtensionUtilsTest.java b/fhir/src/test/java/org/dhis2/fhir/adapter/fhir/extension/ValueTypeExtensionUtilsTest.java new file mode 100644 index 00000000..6593491a --- /dev/null +++ b/fhir/src/test/java/org/dhis2/fhir/adapter/fhir/extension/ValueTypeExtensionUtilsTest.java @@ -0,0 +1,53 @@ +package org.dhis2.fhir.adapter.fhir.extension; + +/* + * Copyright (c) 2004-2019, University of Oslo + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * Neither the name of the HISP project nor the names of its contributors may + * be used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +import org.dhis2.fhir.adapter.model.ValueType; +import org.junit.Assert; +import org.junit.Test; + +/** + * Unit tests for {@link ValueTypeExtensionUtils}. + * + * @author volsch + */ +public class ValueTypeExtensionUtilsTest +{ + @Test + public void setValue() + { + TestPlanDefinition planDefinition = new TestPlanDefinition(); + + ValueTypeExtensionUtils.setValue( planDefinition, ValueType.INTEGER_POSITIVE, TypeFactory::createType ); + + Assert.assertEquals( 1, planDefinition.getExtension().size() ); + Assert.assertEquals( ValueTypeExtensionUtils.URL, planDefinition.getExtension().get( 0 ).getUrl() ); + Assert.assertEquals( "INTEGER_POSITIVE", planDefinition.getExtension().get( 0 ).getValue().toString() ); + } +} \ No newline at end of file From 262442e35b47fb57c01f0366a3de2611cbfb7e4c Mon Sep 17 00:00:00 2001 From: Volker Schmidt Date: Sat, 3 Aug 2019 22:28:07 +0200 Subject: [PATCH 12/21] Adds support for converting DHIS2 values to FHIR values and vice versa. --- .../dhis/model/ImmutableOptionSet.java | 12 +- .../fhir/adapter/dhis/model/OptionSet.java | 14 +- .../adapter/dhis/model/WritableOptionSet.java | 29 ++- ...u3ValueTypeDhisToFhirTransformerUtils.java | 135 +++++++++++++ ...u3ValueTypeFhirToDhisTransformerUtils.java | 165 ++++++++++++++++ ...lueTypeDhisToFhirTransformerUtilsTest.java | 185 +++++++++++++++++ ...lueTypeFhirToDhisTransformerUtilsTest.java | 179 +++++++++++++++++ ...R4ValueTypeFhirToDhisTransformerUtils.java | 165 ++++++++++++++++ ...R4ValueTypeDhisToFhirTransformerUtils.java | 135 +++++++++++++ ...lueTypeDhisToFhirTransformerUtilsTest.java | 186 ++++++++++++++++++ ...lueTypeFhirToDhisTransformerUtilsTest.java | 180 +++++++++++++++++ ...ctValueTypeDhisToFhirTransformerUtils.java | 129 ++++++++++++ ...ctValueTypeFhirToDhisTransformerUtils.java | 86 ++++++++ ...lueTypeDhisToFhirTransformerUtilsTest.java | 78 ++++++++ 14 files changed, 1675 insertions(+), 3 deletions(-) create mode 100644 fhir-dstu3/src/main/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/util/dstu3/Dstu3ValueTypeDhisToFhirTransformerUtils.java create mode 100644 fhir-dstu3/src/main/java/org/dhis2/fhir/adapter/fhir/transform/fhir/impl/util/dstu3/Dstu3ValueTypeFhirToDhisTransformerUtils.java create mode 100644 fhir-dstu3/src/test/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/util/dstu3/Dstu3ValueTypeDhisToFhirTransformerUtilsTest.java create mode 100644 fhir-dstu3/src/test/java/org/dhis2/fhir/adapter/fhir/transform/fhir/impl/util/dstu3/Dstu3ValueTypeFhirToDhisTransformerUtilsTest.java create mode 100644 fhir-r4/src/main/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/util/r4/R4ValueTypeFhirToDhisTransformerUtils.java create mode 100644 fhir-r4/src/main/java/org/dhis2/fhir/adapter/fhir/transform/fhir/impl/util/r4/R4ValueTypeDhisToFhirTransformerUtils.java create mode 100644 fhir-r4/src/test/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/util/r4/R4ValueTypeDhisToFhirTransformerUtilsTest.java create mode 100644 fhir-r4/src/test/java/org/dhis2/fhir/adapter/fhir/transform/fhir/impl/util/r4/R4ValueTypeFhirToDhisTransformerUtilsTest.java create mode 100644 fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/util/AbstractValueTypeDhisToFhirTransformerUtils.java create mode 100644 fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/fhir/impl/util/AbstractValueTypeFhirToDhisTransformerUtils.java create mode 100644 fhir/src/test/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/util/AbstractValueTypeDhisToFhirTransformerUtilsTest.java diff --git a/dhis/src/main/java/org/dhis2/fhir/adapter/dhis/model/ImmutableOptionSet.java b/dhis/src/main/java/org/dhis2/fhir/adapter/dhis/model/ImmutableOptionSet.java index f88fb968..b229d988 100644 --- a/dhis/src/main/java/org/dhis2/fhir/adapter/dhis/model/ImmutableOptionSet.java +++ b/dhis/src/main/java/org/dhis2/fhir/adapter/dhis/model/ImmutableOptionSet.java @@ -1,7 +1,7 @@ package org.dhis2.fhir.adapter.dhis.model; /* - * Copyright (c) 2004-2018, University of Oslo + * Copyright (c) 2004-2019, University of Oslo * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -33,8 +33,10 @@ import com.fasterxml.jackson.annotation.JsonProperty; import javax.annotation.Nonnull; +import javax.annotation.Nullable; import java.io.Serializable; import java.util.List; +import java.util.Optional; import java.util.stream.Collectors; /** @@ -77,4 +79,12 @@ public List - - - - - + diff --git a/docs/fhir/profiles/TrackerProgramQuestionnaire.StructureDefinition.xml b/docs/fhir/profiles/TrackerProgramQuestionnaire.StructureDefinition.xml index 0702ebad..eeef7fc0 100644 --- a/docs/fhir/profiles/TrackerProgramQuestionnaire.StructureDefinition.xml +++ b/docs/fhir/profiles/TrackerProgramQuestionnaire.StructureDefinition.xml @@ -96,9 +96,13 @@ + + + + - + @@ -186,6 +190,10 @@ + + + + @@ -200,6 +208,12 @@ + + + + + + diff --git a/fhir-r4/src/main/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/metadata/r4/R4ProgramMetadataToFhirDataProvider.java b/fhir-r4/src/main/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/metadata/r4/R4ProgramMetadataToFhirDataProvider.java index 180a4560..da5594af 100644 --- a/fhir-r4/src/main/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/metadata/r4/R4ProgramMetadataToFhirDataProvider.java +++ b/fhir-r4/src/main/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/metadata/r4/R4ProgramMetadataToFhirDataProvider.java @@ -65,6 +65,7 @@ public Set getFhirVersions() @Override protected void initSearchFilter( @Nonnull FhirVersion fhirVersion, @Nonnull RuleInfo ruleInfo, @Nonnull SearchFilter searchFilter ) { - searchFilter.add( PlanDefinition.SP_NAME, SearchParamType.STRING, "name" ); + searchFilter.add( PlanDefinition.SP_NAME, SearchParamType.STRING, "code" ); + searchFilter.add( PlanDefinition.SP_TITLE, SearchParamType.STRING, "name" ); } } diff --git a/fhir-r4/src/main/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/metadata/r4/R4ProgramMetadataToFhirPlanDefinitionTransformer.java b/fhir-r4/src/main/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/metadata/r4/R4ProgramMetadataToFhirPlanDefinitionTransformer.java index 6002c267..44333c79 100644 --- a/fhir-r4/src/main/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/metadata/r4/R4ProgramMetadataToFhirPlanDefinitionTransformer.java +++ b/fhir-r4/src/main/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/metadata/r4/R4ProgramMetadataToFhirPlanDefinitionTransformer.java @@ -92,7 +92,8 @@ protected boolean transformInternal( @Nonnull FhirClient fhirClient, @Nonnull Dh } fhirPlanDefinition.setUrl( dhisProgram.getId() ); - fhirPlanDefinition.setName( dhisProgram.getName() ); + fhirPlanDefinition.setName( dhisProgram.getCode() ); + fhirPlanDefinition.setTitle( dhisProgram.getName() ); fhirPlanDefinition.setStatus( Enumerations.PublicationStatus.ACTIVE ); fhirPlanDefinition.setDescription( dhisProgram.getDescription() ); fhirPlanDefinition.setAction( null ); diff --git a/fhir-r4/src/main/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/metadata/r4/R4ProgramStageMetadataToFhirDataProvider.java b/fhir-r4/src/main/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/metadata/r4/R4ProgramStageMetadataToFhirDataProvider.java new file mode 100644 index 00000000..5801cc6b --- /dev/null +++ b/fhir-r4/src/main/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/metadata/r4/R4ProgramStageMetadataToFhirDataProvider.java @@ -0,0 +1,70 @@ +package org.dhis2.fhir.adapter.fhir.transform.dhis.impl.metadata.r4; + +/* + * Copyright (c) 2004-2019, University of Oslo + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * Neither the name of the HISP project nor the names of its contributors may + * be used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +import org.dhis2.fhir.adapter.dhis.tracker.program.ProgramStageMetadataService; +import org.dhis2.fhir.adapter.fhir.metadata.model.ProgramStageMetadataRule; +import org.dhis2.fhir.adapter.fhir.metadata.model.RuleInfo; +import org.dhis2.fhir.adapter.fhir.model.FhirVersion; +import org.dhis2.fhir.adapter.fhir.script.ScriptExecutor; +import org.dhis2.fhir.adapter.fhir.transform.dhis.impl.metadata.program.AbstractProgramStageMetadataToFhirDataProvider; +import org.dhis2.fhir.adapter.fhir.transform.dhis.search.SearchFilter; +import org.dhis2.fhir.adapter.fhir.transform.dhis.search.SearchParamType; +import org.hl7.fhir.r4.model.Questionnaire; +import org.springframework.stereotype.Component; + +import javax.annotation.Nonnull; +import java.util.Set; + +/** + * R4 specific version of DHIS2 Program Stage Metadata data provider. + * + * @author volsch + */ +@Component +public class R4ProgramStageMetadataToFhirDataProvider extends AbstractProgramStageMetadataToFhirDataProvider +{ + public R4ProgramStageMetadataToFhirDataProvider( @Nonnull ScriptExecutor scriptExecutor, @Nonnull ProgramStageMetadataService programStageMetadataService ) + { + super( scriptExecutor, programStageMetadataService ); + } + + @Nonnull + @Override + public Set getFhirVersions() + { + return FhirVersion.R4_ONLY; + } + + @Override + protected void initSearchFilter( @Nonnull FhirVersion fhirVersion, @Nonnull RuleInfo ruleInfo, @Nonnull SearchFilter searchFilter ) + { + searchFilter.add( Questionnaire.SP_TITLE, SearchParamType.STRING, "name" ); + } +} diff --git a/fhir-r4/src/main/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/metadata/r4/R4ProgramStageMetadataToFhirQuestionnaireTransformer.java b/fhir-r4/src/main/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/metadata/r4/R4ProgramStageMetadataToFhirQuestionnaireTransformer.java new file mode 100644 index 00000000..2575b8ee --- /dev/null +++ b/fhir-r4/src/main/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/metadata/r4/R4ProgramStageMetadataToFhirQuestionnaireTransformer.java @@ -0,0 +1,158 @@ +package org.dhis2.fhir.adapter.fhir.transform.dhis.impl.metadata.r4; + +/* + * Copyright (c) 2004-2019, University of Oslo + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * Neither the name of the HISP project nor the names of its contributors may + * be used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +import org.dhis2.fhir.adapter.dhis.orgunit.OrganizationUnitService; +import org.dhis2.fhir.adapter.dhis.tracker.program.ProgramStage; +import org.dhis2.fhir.adapter.fhir.data.repository.FhirDhisAssignmentRepository; +import org.dhis2.fhir.adapter.fhir.metadata.model.FhirClient; +import org.dhis2.fhir.adapter.fhir.metadata.model.ProgramStageMetadataRule; +import org.dhis2.fhir.adapter.fhir.metadata.model.RuleInfo; +import org.dhis2.fhir.adapter.fhir.metadata.repository.SystemRepository; +import org.dhis2.fhir.adapter.fhir.model.FhirVersion; +import org.dhis2.fhir.adapter.fhir.repository.FhirResourceRepository; +import org.dhis2.fhir.adapter.fhir.script.ScriptExecutor; +import org.dhis2.fhir.adapter.fhir.transform.dhis.DhisToFhirTransformerContext; +import org.dhis2.fhir.adapter.fhir.transform.dhis.impl.metadata.program.AbstractProgramStageMetadataToFhirQuestionnaireTransformer; +import org.dhis2.fhir.adapter.fhir.transform.scripted.AccessibleScriptedDhisMetadata; +import org.dhis2.fhir.adapter.lock.LockManager; +import org.dhis2.fhir.adapter.model.ValueType; +import org.hl7.fhir.instance.model.api.IBaseResource; +import org.hl7.fhir.r4.model.Coding; +import org.hl7.fhir.r4.model.Enumerations; +import org.hl7.fhir.r4.model.Questionnaire; +import org.hl7.fhir.r4.model.Questionnaire.QuestionnaireItemComponent; +import org.hl7.fhir.r4.model.Questionnaire.QuestionnaireItemType; +import org.springframework.stereotype.Component; + +import javax.annotation.Nonnull; +import javax.annotation.Nullable; +import java.util.Map; +import java.util.Set; + +import static org.hl7.fhir.r4.model.Questionnaire.QuestionnaireItemType.*; + +/** + * R4 specific version of DHIS2 Program Stage Metadata to FHIR Questionnaire transformer. + * + * @author volsch + */ +@Component +public class R4ProgramStageMetadataToFhirQuestionnaireTransformer extends AbstractProgramStageMetadataToFhirQuestionnaireTransformer +{ + public R4ProgramStageMetadataToFhirQuestionnaireTransformer( @Nonnull ScriptExecutor scriptExecutor, @Nonnull LockManager lockManager, @Nonnull SystemRepository systemRepository, @Nonnull FhirResourceRepository fhirResourceRepository, + @Nonnull FhirDhisAssignmentRepository fhirDhisAssignmentRepository, @Nonnull OrganizationUnitService organizationUnitService ) + { + super( scriptExecutor, lockManager, systemRepository, fhirResourceRepository, fhirDhisAssignmentRepository, organizationUnitService ); + } + + @Nonnull + @Override + public Set getFhirVersions() + { + return FhirVersion.R4_ONLY; + } + + @Override + protected boolean transformInternal( @Nonnull FhirClient fhirClient, @Nonnull DhisToFhirTransformerContext context, @Nonnull RuleInfo ruleInfo, @Nonnull Map scriptVariables, + @Nonnull AccessibleScriptedDhisMetadata input, @Nonnull IBaseResource output ) + { + final ProgramStage dhisProgramStage = (ProgramStage) input.getDhisResource(); + final Questionnaire fhirQuestionnaire = (Questionnaire) output; + + fhirQuestionnaire.setUrl( dhisProgramStage.getId() ); + fhirQuestionnaire.setTitle( dhisProgramStage.getName() ); + fhirQuestionnaire.setStatus( Enumerations.PublicationStatus.ACTIVE ); + fhirQuestionnaire.setDescription( dhisProgramStage.getDescription() ); + fhirQuestionnaire.setItem( null ); + + dhisProgramStage.getDataElements().forEach( dataElement -> { + final QuestionnaireItemType type = convertValueType( dataElement.getElement().getValueType() ); + + if ( type != null ) + { + final QuestionnaireItemComponent itemComponent = fhirQuestionnaire.addItem(); + + itemComponent.setLinkId( dataElement.getElementId() ); + itemComponent.setText( dataElement.getElement().getName() ); + itemComponent.setRequired( dataElement.isCompulsory() ); + itemComponent.setType( type ); + + if ( dataElement.getElement().isOptionSetValue() ) + { + dataElement.getElement().getOptionSet().getOptions().forEach( option -> itemComponent.addAnswerOption().setValue( + new Coding().setCode( option.getCode() ).setDisplay( option.getName() ) ) ); + } + } + } ); + + return true; + } + + @Nullable + protected QuestionnaireItemType convertValueType( @Nonnull ValueType valueType ) + { + switch ( valueType ) + { + case TEXT: + case EMAIL: + case LETTER: + case ORGANISATION_UNIT: + case PHONE_NUMBER: + case TRACKER_ASSOCIATE: + case URL: + case USERNAME: + return STRING; + case LONG_TEXT: + return TEXT; + case INTEGER: + case INTEGER_POSITIVE: + case INTEGER_NEGATIVE: + case INTEGER_ZERO_OR_POSITIVE: + return INTEGER; + case NUMBER: + case PERCENTAGE: + case UNIT_INTERVAL: + return DECIMAL; + case DATETIME: + case AGE: + return DATETIME; + case DATE: + return DATE; + case TIME: + return TIME; + case BOOLEAN: + case TRUE_ONLY: + return BOOLEAN; + } + + // unhandled data type + return null; + } +} diff --git a/fhir-r4/src/test/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/metadata/r4/R4ProgramMetadataToFhirDataProviderTest.java b/fhir-r4/src/test/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/metadata/r4/R4ProgramMetadataToFhirDataProviderTest.java index 4798edd1..6ed3a090 100644 --- a/fhir-r4/src/test/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/metadata/r4/R4ProgramMetadataToFhirDataProviderTest.java +++ b/fhir-r4/src/test/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/metadata/r4/R4ProgramMetadataToFhirDataProviderTest.java @@ -79,7 +79,7 @@ public void getFhirVersions() @Test public void searchFilterName() throws URISyntaxException { - final SearchFilterCollector searchFilterCollector = new SearchFilterCollector( Collections.singletonMap( "name", Collections.singletonList( "Child Programme" ) ) ); + final SearchFilterCollector searchFilterCollector = new SearchFilterCollector( Collections.singletonMap( "title", Collections.singletonList( "Child Programme" ) ) ); final SearchFilter searchFilter = new SearchFilter( searchFilterCollector, false ); provider.initSearchFilter( FhirVersion.R4, new RuleInfo<>( new ProgramMetadataRule(), Collections.emptyList() ), searchFilter ); diff --git a/fhir-r4/src/test/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/metadata/r4/R4ProgramMetadataToFhirPlanDefinitionTransformerTest.java b/fhir-r4/src/test/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/metadata/r4/R4ProgramMetadataToFhirPlanDefinitionTransformerTest.java index 3d14b07b..adccd703 100644 --- a/fhir-r4/src/test/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/metadata/r4/R4ProgramMetadataToFhirPlanDefinitionTransformerTest.java +++ b/fhir-r4/src/test/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/metadata/r4/R4ProgramMetadataToFhirPlanDefinitionTransformerTest.java @@ -152,7 +152,7 @@ public void transformInternal() transformer.transformInternal( fhirClient, context, new RuleInfo<>( new ProgramMetadataRule(), Collections.emptyList() ), new HashMap<>(), new WritableScriptedDhisMetadata( program, scriptExecutionContext ), fhirPlanDefinition ); - Assert.assertEquals( "Test Program", fhirPlanDefinition.getName() ); + Assert.assertEquals( "Test Program", fhirPlanDefinition.getTitle() ); Assert.assertEquals( "Test Description", fhirPlanDefinition.getDescription() ); Assert.assertEquals( 2, fhirPlanDefinition.getAction().size() ); diff --git a/fhir-r4/src/test/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/metadata/r4/R4ProgramStageMetadataToFhirDataProviderTest.java b/fhir-r4/src/test/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/metadata/r4/R4ProgramStageMetadataToFhirDataProviderTest.java new file mode 100644 index 00000000..8b2c37f7 --- /dev/null +++ b/fhir-r4/src/test/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/metadata/r4/R4ProgramStageMetadataToFhirDataProviderTest.java @@ -0,0 +1,93 @@ +package org.dhis2.fhir.adapter.fhir.transform.dhis.impl.metadata.r4; + +/* + * Copyright (c) 2004-2019, University of Oslo + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * Neither the name of the HISP project nor the names of its contributors may + * be used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +import org.dhis2.fhir.adapter.dhis.tracker.program.ProgramStageMetadataService; +import org.dhis2.fhir.adapter.fhir.metadata.model.ProgramStageMetadataRule; +import org.dhis2.fhir.adapter.fhir.metadata.model.RuleInfo; +import org.dhis2.fhir.adapter.fhir.model.FhirVersion; +import org.dhis2.fhir.adapter.fhir.script.ScriptExecutor; +import org.dhis2.fhir.adapter.fhir.transform.dhis.search.SearchFilter; +import org.dhis2.fhir.adapter.fhir.transform.dhis.search.SearchFilterCollector; +import org.junit.Assert; +import org.junit.Rule; +import org.junit.Test; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.junit.MockitoJUnit; +import org.mockito.junit.MockitoRule; +import org.springframework.web.util.DefaultUriBuilderFactory; +import org.springframework.web.util.UriBuilder; + +import java.net.URI; +import java.net.URISyntaxException; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +/** + * Unit tests for {@link R4ProgramStageMetadataToFhirDataProvider}. + * + * @author volsch + */ +public class R4ProgramStageMetadataToFhirDataProviderTest +{ + @Mock + private ScriptExecutor scriptExecutor; + + @Mock + private ProgramStageMetadataService programStageMetadataService; + + @InjectMocks + private R4ProgramStageMetadataToFhirDataProvider provider; + + @Rule + public MockitoRule rule = MockitoJUnit.rule(); + + @Test + public void getFhirVersions() + { + Assert.assertEquals( FhirVersion.R4_ONLY, provider.getFhirVersions() ); + } + + @Test + public void searchFilterName() throws URISyntaxException + { + final SearchFilterCollector searchFilterCollector = new SearchFilterCollector( Collections.singletonMap( "title", Collections.singletonList( "Birth" ) ) ); + final SearchFilter searchFilter = new SearchFilter( searchFilterCollector, false ); + + provider.initSearchFilter( FhirVersion.R4, new RuleInfo<>( new ProgramStageMetadataRule(), Collections.emptyList() ), searchFilter ); + + final List variables = new ArrayList<>(); + UriBuilder uriBuilder = new DefaultUriBuilderFactory().builder(); + uriBuilder = searchFilterCollector.add( uriBuilder, variables ); + + Assert.assertEquals( new URI( "?filter=%5Bname:$ilike:Birth%5D" ), uriBuilder.build( variables ) ); + } +} diff --git a/fhir-r4/src/test/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/metadata/r4/R4ProgramStageMetadataToFhirQuestionnaireTransformerTest.java b/fhir-r4/src/test/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/metadata/r4/R4ProgramStageMetadataToFhirQuestionnaireTransformerTest.java new file mode 100644 index 00000000..3cf0b25c --- /dev/null +++ b/fhir-r4/src/test/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/metadata/r4/R4ProgramStageMetadataToFhirQuestionnaireTransformerTest.java @@ -0,0 +1,170 @@ +package org.dhis2.fhir.adapter.fhir.transform.dhis.impl.metadata.r4; + +/* + * Copyright (c) 2004-2019, University of Oslo + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * Neither the name of the HISP project nor the names of its contributors may + * be used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +import org.dhis2.fhir.adapter.dhis.model.WritableDataElement; +import org.dhis2.fhir.adapter.dhis.model.WritableOption; +import org.dhis2.fhir.adapter.dhis.model.WritableOptionSet; +import org.dhis2.fhir.adapter.dhis.orgunit.OrganizationUnitService; +import org.dhis2.fhir.adapter.dhis.tracker.program.WritableProgramStage; +import org.dhis2.fhir.adapter.dhis.tracker.program.WritableProgramStageDataElement; +import org.dhis2.fhir.adapter.fhir.data.repository.FhirDhisAssignmentRepository; +import org.dhis2.fhir.adapter.fhir.metadata.model.FhirClient; +import org.dhis2.fhir.adapter.fhir.metadata.model.ProgramStageMetadataRule; +import org.dhis2.fhir.adapter.fhir.metadata.model.RuleInfo; +import org.dhis2.fhir.adapter.fhir.metadata.repository.SystemRepository; +import org.dhis2.fhir.adapter.fhir.model.FhirVersion; +import org.dhis2.fhir.adapter.fhir.repository.FhirResourceRepository; +import org.dhis2.fhir.adapter.fhir.script.ScriptExecutionContext; +import org.dhis2.fhir.adapter.fhir.script.ScriptExecutor; +import org.dhis2.fhir.adapter.fhir.transform.dhis.DhisToFhirTransformerContext; +import org.dhis2.fhir.adapter.fhir.transform.scripted.WritableScriptedDhisMetadata; +import org.dhis2.fhir.adapter.lock.LockManager; +import org.dhis2.fhir.adapter.model.ValueType; +import org.hl7.fhir.r4.model.Coding; +import org.hl7.fhir.r4.model.Questionnaire; +import org.junit.Assert; +import org.junit.Rule; +import org.junit.Test; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.junit.MockitoJUnit; +import org.mockito.junit.MockitoRule; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; + +/** + * Unit tests for {@link R4ProgramStageMetadataToFhirQuestionnaireTransformer}. + * + * @author volsch + */ +public class R4ProgramStageMetadataToFhirQuestionnaireTransformerTest +{ + @Mock + private ScriptExecutor scriptExecutor; + + @Mock + private LockManager lockManager; + + @Mock + private SystemRepository systemRepository; + + @Mock + private FhirResourceRepository fhirResourceRepository; + + @Mock + private FhirDhisAssignmentRepository fhirDhisAssignmentRepository; + + @Mock + private OrganizationUnitService organizationUnitService; + + @Mock + private DhisToFhirTransformerContext context; + + @Mock + private FhirClient fhirClient; + + @Mock + private ScriptExecutionContext scriptExecutionContext; + + @InjectMocks + private R4ProgramStageMetadataToFhirQuestionnaireTransformer transformer; + + @Rule + public MockitoRule rule = MockitoJUnit.rule(); + + @Test + public void getFhirVersions() + { + Assert.assertEquals( FhirVersion.R4_ONLY, transformer.getFhirVersions() ); + } + + @Test + public void transformInternal() + { + final WritableProgramStage programStage = new WritableProgramStage(); + programStage.setId( "b1234567890" ); + programStage.setName( "Test Stage 1" ); + programStage.setDescription( "Test Description 1" ); + programStage.setRepeatable( false ); + programStage.setDataElements( new ArrayList<>() ); + + WritableProgramStageDataElement programStageDataElement = new WritableProgramStageDataElement(); + WritableDataElement dataElement = new WritableDataElement(); + dataElement.setId( "d0123456789" ); + dataElement.setName( "Value 1" ); + dataElement.setValueType( ValueType.TEXT ); + programStageDataElement.setCompulsory( false ); + programStageDataElement.setElement( dataElement ); + programStage.getDataElements().add( programStageDataElement ); + + WritableOptionSet optionSet = new WritableOptionSet(); + optionSet.setOptions( new ArrayList<>() ); + optionSet.getOptions().add( new WritableOption( "5", "Test Value 1" ) ); + optionSet.getOptions().add( new WritableOption( "7", "Test Value 2" ) ); + + programStageDataElement = new WritableProgramStageDataElement(); + dataElement = new WritableDataElement(); + dataElement.setId( "d1123456789" ); + dataElement.setName( "Value 2" ); + dataElement.setValueType( ValueType.INTEGER ); + dataElement.setOptionSetValue( true ); + dataElement.setOptionSet( optionSet ); + programStageDataElement.setCompulsory( true ); + programStageDataElement.setElement( dataElement ); + programStage.getDataElements().add( programStageDataElement ); + + final Questionnaire fhirQuestionnaire = new Questionnaire(); + + transformer.transformInternal( fhirClient, context, new RuleInfo<>( new ProgramStageMetadataRule(), Collections.emptyList() ), new HashMap<>(), + new WritableScriptedDhisMetadata( programStage, scriptExecutionContext ), fhirQuestionnaire ); + + Assert.assertEquals( "b1234567890", fhirQuestionnaire.getUrl() ); + Assert.assertEquals( "Test Stage 1", fhirQuestionnaire.getTitle() ); + Assert.assertEquals( "Test Description 1", fhirQuestionnaire.getDescription() ); + Assert.assertEquals( 2, fhirQuestionnaire.getItem().size() ); + + Assert.assertEquals( "d0123456789", fhirQuestionnaire.getItem().get( 0 ).getLinkId() ); + Assert.assertEquals( "Value 1", fhirQuestionnaire.getItem().get( 0 ).getText() ); + Assert.assertEquals( 0, fhirQuestionnaire.getItem().get( 0 ).getAnswerOption().size() ); + Assert.assertFalse( fhirQuestionnaire.getItem().get( 0 ).getRequired() ); + + Assert.assertEquals( "d1123456789", fhirQuestionnaire.getItem().get( 1 ).getLinkId() ); + Assert.assertEquals( "Value 2", fhirQuestionnaire.getItem().get( 1 ).getText() ); + Assert.assertEquals( 2, fhirQuestionnaire.getItem().get( 1 ).getAnswerOption().size() ); + Assert.assertTrue( fhirQuestionnaire.getItem().get( 1 ).getAnswerOption().get( 0 ).getValue() instanceof Coding ); + Assert.assertEquals( "5", ( (Coding) fhirQuestionnaire.getItem().get( 1 ).getAnswerOption().get( 0 ).getValue() ).getCode() ); + Assert.assertEquals( "Test Value 1", ( (Coding) fhirQuestionnaire.getItem().get( 1 ).getAnswerOption().get( 0 ).getValue() ).getDisplay() ); + Assert.assertEquals( "7", ( (Coding) fhirQuestionnaire.getItem().get( 1 ).getAnswerOption().get( 1 ).getValue() ).getCode() ); + Assert.assertEquals( "Test Value 2", ( (Coding) fhirQuestionnaire.getItem().get( 1 ).getAnswerOption().get( 1 ).getValue() ).getDisplay() ); + Assert.assertTrue( fhirQuestionnaire.getItem().get( 1 ).getRequired() ); + } +} diff --git a/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/metadata/program/AbstractProgramStageMetadataToFhirDataProvider.java b/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/metadata/program/AbstractProgramStageMetadataToFhirDataProvider.java new file mode 100644 index 00000000..05062a36 --- /dev/null +++ b/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/metadata/program/AbstractProgramStageMetadataToFhirDataProvider.java @@ -0,0 +1,66 @@ +package org.dhis2.fhir.adapter.fhir.transform.dhis.impl.metadata.program; + +/* + * Copyright (c) 2004-2019, University of Oslo + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * Neither the name of the HISP project nor the names of its contributors may + * be used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +import org.dhis2.fhir.adapter.dhis.model.DhisResourceType; +import org.dhis2.fhir.adapter.dhis.tracker.program.ProgramStage; +import org.dhis2.fhir.adapter.dhis.tracker.program.ProgramStageMetadataService; +import org.dhis2.fhir.adapter.fhir.metadata.model.ProgramStageMetadataRule; +import org.dhis2.fhir.adapter.fhir.script.ScriptExecutor; +import org.dhis2.fhir.adapter.fhir.transform.dhis.DhisToFhirDataProvider; +import org.dhis2.fhir.adapter.fhir.transform.dhis.impl.metadata.AbstractDhisMetadataToFhirDataProvider; + +import javax.annotation.Nonnull; + +/** + * Abstract implementation of {@link DhisToFhirDataProvider} for DHIS2 Program Stage Metadata. + * + * @author volsch + */ +public abstract class AbstractProgramStageMetadataToFhirDataProvider extends AbstractDhisMetadataToFhirDataProvider +{ + public AbstractProgramStageMetadataToFhirDataProvider( @Nonnull ScriptExecutor scriptExecutor, @Nonnull ProgramStageMetadataService programStageMetadataService ) + { + super( scriptExecutor, programStageMetadataService ); + } + + @Nonnull + @Override + public DhisResourceType getDhisResourceType() + { + return DhisResourceType.PROGRAM_STAGE_METADATA; + } + + @Nonnull + @Override + protected Class getRuleClass() + { + return ProgramStageMetadataRule.class; + } +} diff --git a/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/metadata/program/AbstractProgramStageMetadataToFhirQuestionnaireTransformer.java b/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/metadata/program/AbstractProgramStageMetadataToFhirQuestionnaireTransformer.java new file mode 100644 index 00000000..0bc8ba45 --- /dev/null +++ b/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/metadata/program/AbstractProgramStageMetadataToFhirQuestionnaireTransformer.java @@ -0,0 +1,93 @@ +package org.dhis2.fhir.adapter.fhir.transform.dhis.impl.metadata.program; + +/* + * Copyright (c) 2004-2019, University of Oslo + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * Neither the name of the HISP project nor the names of its contributors may + * be used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +import org.dhis2.fhir.adapter.dhis.model.DhisResourceType; +import org.dhis2.fhir.adapter.dhis.orgunit.OrganizationUnitService; +import org.dhis2.fhir.adapter.fhir.data.repository.FhirDhisAssignmentRepository; +import org.dhis2.fhir.adapter.fhir.metadata.model.FhirResourceType; +import org.dhis2.fhir.adapter.fhir.metadata.model.ProgramStageMetadataRule; +import org.dhis2.fhir.adapter.fhir.metadata.repository.SystemRepository; +import org.dhis2.fhir.adapter.fhir.repository.FhirResourceRepository; +import org.dhis2.fhir.adapter.fhir.script.ScriptExecutor; +import org.dhis2.fhir.adapter.fhir.transform.dhis.impl.DhisToFhirTransformer; +import org.dhis2.fhir.adapter.fhir.transform.dhis.impl.metadata.AbstractReadOnlyDhisMetadataToTypedFhirTransformer; +import org.dhis2.fhir.adapter.fhir.transform.scripted.AccessibleScriptedDhisMetadata; +import org.dhis2.fhir.adapter.lock.LockManager; +import org.hl7.fhir.instance.model.api.IBaseResource; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import javax.annotation.Nonnull; + +/** + * Implementation of {@link DhisToFhirTransformer} for transforming DHIS2 + * program stage metadata to FHIR Questionnaire. + * + * @param the concrete type of the FHIR resource into which the DHIS2 resource should be transformed. + * @author volsch + */ +public abstract class AbstractProgramStageMetadataToFhirQuestionnaireTransformer extends AbstractReadOnlyDhisMetadataToTypedFhirTransformer +{ + private final Logger logger = LoggerFactory.getLogger( getClass() ); + + public AbstractProgramStageMetadataToFhirQuestionnaireTransformer( @Nonnull ScriptExecutor scriptExecutor, @Nonnull LockManager lockManager, @Nonnull SystemRepository systemRepository, @Nonnull FhirResourceRepository fhirResourceRepository, + @Nonnull FhirDhisAssignmentRepository fhirDhisAssignmentRepository, @Nonnull OrganizationUnitService organizationUnitService ) + { + super( scriptExecutor, lockManager, systemRepository, fhirResourceRepository, fhirDhisAssignmentRepository ); + } + + @Nonnull + @Override + public DhisResourceType getDhisResourceType() + { + return DhisResourceType.PROGRAM_STAGE_METADATA; + } + + @Nonnull + @Override + protected FhirResourceType getFhirResourceType() + { + return FhirResourceType.QUESTIONNAIRE; + } + + @Nonnull + @Override + public Class getDhisResourceClass() + { + return AccessibleScriptedDhisMetadata.class; + } + + @Nonnull + @Override + public Class getRuleClass() + { + return ProgramStageMetadataRule.class; + } +} diff --git a/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/metadata/program/ProgramStageMetadataToFhirRequestResolver.java b/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/metadata/program/ProgramStageMetadataToFhirRequestResolver.java new file mode 100644 index 00000000..4186ebae --- /dev/null +++ b/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/metadata/program/ProgramStageMetadataToFhirRequestResolver.java @@ -0,0 +1,76 @@ +package org.dhis2.fhir.adapter.fhir.transform.dhis.impl.metadata.program; + +/* + * Copyright (c) 2004-2019, University of Oslo + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * Neither the name of the HISP project nor the names of its contributors may + * be used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +import org.dhis2.fhir.adapter.dhis.model.DhisMetadata; +import org.dhis2.fhir.adapter.dhis.model.DhisResource; +import org.dhis2.fhir.adapter.dhis.model.DhisResourceType; +import org.dhis2.fhir.adapter.fhir.metadata.repository.FhirClientRepository; +import org.dhis2.fhir.adapter.fhir.metadata.repository.RuleRepository; +import org.dhis2.fhir.adapter.fhir.script.ScriptExecutionContext; +import org.dhis2.fhir.adapter.fhir.transform.dhis.impl.DhisToFhirRequestResolver; +import org.dhis2.fhir.adapter.fhir.transform.dhis.impl.metadata.AbstractDhisMetadataToFhirRequestResolver; +import org.dhis2.fhir.adapter.fhir.transform.dhis.model.DhisRequest; +import org.dhis2.fhir.adapter.fhir.transform.scripted.ImmutableScriptedDhisMetadata; +import org.dhis2.fhir.adapter.fhir.transform.scripted.ScriptedDhisResource; +import org.dhis2.fhir.adapter.fhir.transform.scripted.WritableScriptedDhisMetadata; +import org.springframework.stereotype.Component; + +import javax.annotation.Nonnull; + +/** + * Implementation of {@link DhisToFhirRequestResolver} for DHIS2 program stage metadata. + * + * @author volsch + */ +@Component +public class ProgramStageMetadataToFhirRequestResolver extends AbstractDhisMetadataToFhirRequestResolver +{ + private final ScriptExecutionContext scriptExecutionContext; + + public ProgramStageMetadataToFhirRequestResolver( @Nonnull FhirClientRepository fhirClientRepository, @Nonnull RuleRepository ruleRepository, @Nonnull ScriptExecutionContext scriptExecutionContext ) + { + super( fhirClientRepository, ruleRepository ); + this.scriptExecutionContext = scriptExecutionContext; + } + + @Nonnull + @Override + public DhisResourceType getDhisResourceType() + { + return DhisResourceType.PROGRAM_STAGE_METADATA; + } + + @Nonnull + @Override + public ScriptedDhisResource convert( @Nonnull DhisResource dhisResource, @Nonnull DhisRequest dhisRequest ) + { + return new ImmutableScriptedDhisMetadata( new WritableScriptedDhisMetadata( (DhisMetadata) dhisResource, scriptExecutionContext ) ); + } +} diff --git a/fhir/src/main/resources/db/migration/sample/V1.1.0.41_10_0__Plan_Definition_Sample_Data.sql b/fhir/src/main/resources/db/migration/sample/V1.1.0.41_10_0__Plan_Definition_Sample_Data.sql index e21ec8fd..e3918522 100644 --- a/fhir/src/main/resources/db/migration/sample/V1.1.0.41_10_0__Plan_Definition_Sample_Data.sql +++ b/fhir/src/main/resources/db/migration/sample/V1.1.0.41_10_0__Plan_Definition_Sample_Data.sql @@ -34,5 +34,7 @@ WHERE id='73cd99c5-0ca8-42ad-a53b-1891fccce08f'; -- virtual subscription for FHIR organizations INSERT INTO fhir_client_resource (id, version, fhir_client_id, fhir_resource_type, exp_only, fhir_criteria_parameters, description) VALUES ('2520acc5-86b4-4716-ae0f-ea0531eb885a', 0, '73cd99c5-0ca8-42ad-a53b-1891fccce08f', 'PLAN_DEFINITION', TRUE, NULL, 'Virtual subscription for all Plan Definitions.'); +INSERT INTO fhir_client_resource (id, version, fhir_client_id, fhir_resource_type, exp_only, fhir_criteria_parameters, description) +VALUES ('0918c4f3-995c-4130-b607-b28023a1a3a0', 0, '73cd99c5-0ca8-42ad-a53b-1891fccce08f', 'QUESTIONNAIRE', TRUE, NULL, 'Virtual subscription for all Questionnaires.'); INSERT INTO fhir_client_resource_update(id) VALUES ('2520acc5-86b4-4716-ae0f-ea0531eb885a'); From bf65003480550c55712e7dcce128eb90ca454be8 Mon Sep 17 00:00:00 2001 From: Volker Schmidt Date: Wed, 31 Jul 2019 12:06:35 +0200 Subject: [PATCH 19/21] Fixes issue with generated FHIR server resource IDs. --- .../adapter/dhis/model/DhisResourceType.java | 9 +- .../fhir/metadata/model/FhirResourceType.java | 46 ++-- .../repository/FhirResourceRepository.java | 2 +- .../repository/impl/DhisRepositoryImpl.java | 5 +- .../impl/FhirResourceRepositoryImpl.java | 33 ++- .../impl/FhirResourceRepositoryImplTest.java | 207 ++++++++++++++++++ 6 files changed, 273 insertions(+), 29 deletions(-) create mode 100644 fhir/src/test/java/org/dhis2/fhir/adapter/fhir/repository/impl/FhirResourceRepositoryImplTest.java diff --git a/dhis/src/main/java/org/dhis2/fhir/adapter/dhis/model/DhisResourceType.java b/dhis/src/main/java/org/dhis2/fhir/adapter/dhis/model/DhisResourceType.java index 30e669e9..8b6ca64a 100644 --- a/dhis/src/main/java/org/dhis2/fhir/adapter/dhis/model/DhisResourceType.java +++ b/dhis/src/main/java/org/dhis2/fhir/adapter/dhis/model/DhisResourceType.java @@ -47,16 +47,15 @@ public enum DhisResourceType */ ORGANIZATION_UNIT( "organisationUnits", "ou", "OrganizationUnitRule" ), - /** - * The program metadata. - */ - PROGRAM_METADATA( "programs", "pm", "ProgramMetadataRule" ), - /** * The program stage metadata. */ PROGRAM_STAGE_METADATA( "programStages", "sm", "ProgramStageMetadataRule" ), + /** + * The program metadata. + */ + PROGRAM_METADATA( "programs", "pm", "ProgramMetadataRule" ), /** * Resource is a tracked entity type. diff --git a/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/metadata/model/FhirResourceType.java b/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/metadata/model/FhirResourceType.java index 6bf41c17..ed0b04c6 100644 --- a/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/metadata/model/FhirResourceType.java +++ b/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/metadata/model/FhirResourceType.java @@ -58,22 +58,22 @@ */ public enum FhirResourceType { - CONDITION( FhirVersion.ALL, "Condition", 19, Collections.emptySet(), Collections.singleton( "Condition" ) ), - DIAGNOSTIC_REPORT( FhirVersion.ALL, "DiagnosticReport", 30, Collections.emptySet(), Collections.singleton( "DiagnosticReport" ) ), - ENCOUNTER( FhirVersion.ALL, "Encounter", 4, Collections.emptySet(), Collections.singleton( "Encounter" ) ), - IMMUNIZATION( FhirVersion.ALL, "Immunization", 22, Collections.emptySet(), Collections.singleton( "Immunization" ) ), - LOCATION( FhirVersion.ALL, "Location", 2, Collections.emptySet(), Collections.singleton( "Location" ) ), - MEDICATION_REQUEST( FhirVersion.ALL, "MedicationRequest", 21, Collections.emptySet(), Collections.singleton( "MedicationRequest" ) ), - OBSERVATION( FhirVersion.ALL, "Observation", 20, Collections.emptySet(), Collections.singleton( "Observation" ) ), - ORGANIZATION( FhirVersion.ALL, "Organization", 1, Collections.emptySet(), Collections.singleton( "Organization" ) ), - PATIENT( FhirVersion.ALL, "Patient", 10, Collections.emptySet(), Collections.singleton( "Patient" ) ), - RELATED_PERSON( FhirVersion.ALL, "RelatedPerson", 11, Collections.emptySet(), Collections.singleton( "RelatedPerson" ) ), - PRACTITIONER( FhirVersion.ALL, "Practitioner", 9, Collections.emptySet(), Collections.singleton( "Practitioner" ) ), - MEASURE_REPORT( FhirVersion.ALL, "MeasureReport", 30, Collections.singleton( "MeasureReport" ), Collections.singleton( "MeasureReport" ) ), - PLAN_DEFINITION( FhirVersion.R4_ONLY, "PlanDefinition", 30, Collections.emptySet(), Collections.singleton( "PlanDefinition" ) ), - QUESTIONNAIRE( FhirVersion.R4_ONLY, "Questionnaire", 31, Collections.emptySet(), Collections.singleton( "Questionnaire" ) ), - CARE_PLAN( FhirVersion.R4_ONLY, "CarePlan", 35, Collections.emptySet(), Collections.singleton( "CarePlan" ) ), - QUESTIONNAIRE_RESPONSE( FhirVersion.R4_ONLY, "QuestionnaireResponse", 40, Collections.emptySet(), Collections.singleton( "QuestionnaireResponse" ) ); + CONDITION( FhirVersion.ALL, "Condition", false, 19, Collections.emptySet(), Collections.singleton( "Condition" ) ), + DIAGNOSTIC_REPORT( FhirVersion.ALL, "DiagnosticReport", false, 30, Collections.emptySet(), Collections.singleton( "DiagnosticReport" ) ), + ENCOUNTER( FhirVersion.ALL, "Encounter", false, 4, Collections.emptySet(), Collections.singleton( "Encounter" ) ), + IMMUNIZATION( FhirVersion.ALL, "Immunization", false, 22, Collections.emptySet(), Collections.singleton( "Immunization" ) ), + LOCATION( FhirVersion.ALL, "Location", false, 2, Collections.emptySet(), Collections.singleton( "Location" ) ), + MEDICATION_REQUEST( FhirVersion.ALL, "MedicationRequest", false, 21, Collections.emptySet(), Collections.singleton( "MedicationRequest" ) ), + OBSERVATION( FhirVersion.ALL, "Observation", false, 20, Collections.emptySet(), Collections.singleton( "Observation" ) ), + ORGANIZATION( FhirVersion.ALL, "Organization", false, 1, Collections.emptySet(), Collections.singleton( "Organization" ) ), + PATIENT( FhirVersion.ALL, "Patient", false, 10, Collections.emptySet(), Collections.singleton( "Patient" ) ), + RELATED_PERSON( FhirVersion.ALL, "RelatedPerson", false, 11, Collections.emptySet(), Collections.singleton( "RelatedPerson" ) ), + PRACTITIONER( FhirVersion.ALL, "Practitioner", false, 9, Collections.emptySet(), Collections.singleton( "Practitioner" ) ), + MEASURE_REPORT( FhirVersion.ALL, "MeasureReport", false, 30, Collections.singleton( "MeasureReport" ), Collections.singleton( "MeasureReport" ) ), + PLAN_DEFINITION( FhirVersion.R4_ONLY, "PlanDefinition", true, 30, Collections.emptySet(), Collections.singleton( "PlanDefinition" ) ), + QUESTIONNAIRE( FhirVersion.R4_ONLY, "Questionnaire", true, 31, Collections.emptySet(), Collections.singleton( "Questionnaire" ) ), + CARE_PLAN( FhirVersion.R4_ONLY, "CarePlan", false, 35, Collections.emptySet(), Collections.singleton( "CarePlan" ) ), + QUESTIONNAIRE_RESPONSE( FhirVersion.R4_ONLY, "QuestionnaireResponse", false, 40, Collections.emptySet(), Collections.singleton( "QuestionnaireResponse" ) ); private static final Map resourcesBySimpleClassName = Arrays.stream( values() ).flatMap( v -> v.getSimpleClassNames().stream().map( scn -> new SimpleEntry<>( scn, v ) ) ) .collect( Collectors.toMap( SimpleEntry::getKey, SimpleEntry::getValue ) ); @@ -85,17 +85,21 @@ public static FhirResourceType getByResource( @Nullable IBaseResource resource ) { return null; } + FhirResourceType frt; Class c = resource.getClass(); + do { frt = resourcesBySimpleClassName.get( c.getSimpleName() ); + if ( frt == null ) { c = c.getSuperclass(); } } while ( (frt == null) && (c != null) && (c != Object.class) ); + return frt; } @@ -115,6 +119,8 @@ public static FhirResourceType getByResourceTypeName( @Nullable String resourceT private final String resourceTypeName; + private final boolean syncDhisId; + private final int order; private final Set transactionalWith; @@ -123,10 +129,11 @@ public static FhirResourceType getByResourceTypeName( @Nullable String resourceT private volatile Set transactionalWithTypes; - FhirResourceType( Set fhirVersions, String resourceTypeName, int order, Collection transactionalWith, Collection simpleClassNames ) + FhirResourceType( Set fhirVersions, String resourceTypeName, boolean syncDhisId, int order, Collection transactionalWith, Collection simpleClassNames ) { this.fhirVersions = fhirVersions; this.resourceTypeName = resourceTypeName; + this.syncDhisId = syncDhisId; this.order = order; this.transactionalWith = new HashSet<>( transactionalWith ); this.simpleClassNames = Collections.unmodifiableSet( new HashSet<>( simpleClassNames ) ); @@ -144,6 +151,11 @@ public String getResourceTypeName() return resourceTypeName; } + public boolean isSyncDhisId() + { + return syncDhisId; + } + public int getOrder() { return order; diff --git a/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/repository/FhirResourceRepository.java b/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/repository/FhirResourceRepository.java index 48cfc16d..8ca0374a 100644 --- a/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/repository/FhirResourceRepository.java +++ b/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/repository/FhirResourceRepository.java @@ -75,7 +75,7 @@ public interface FhirResourceRepository IBaseResource transform( @Nonnull UUID fhirClientId, @Nonnull FhirVersion fhirVersion, @Nullable IBaseResource resource ); @Nonnull - IBaseResource save( @Nonnull FhirClient fhirClient, @Nonnull IBaseResource resource ); + IBaseResource save( @Nonnull FhirClient fhirClient, @Nonnull IBaseResource resource, @Nullable String dhisResourceId ); boolean delete( @Nonnull FhirClient fhirClient, @Nonnull IBaseResource resource ); } diff --git a/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/repository/impl/DhisRepositoryImpl.java b/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/repository/impl/DhisRepositoryImpl.java index a613d7a2..b2c17a31 100644 --- a/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/repository/impl/DhisRepositoryImpl.java +++ b/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/repository/impl/DhisRepositoryImpl.java @@ -451,7 +451,8 @@ protected boolean saveInternally( @Nonnull DhisResource resource ) } else { - final IBaseResource resultingResource = fhirResourceRepository.save( transformerRequest.getFhirClient(), outcome.getResource() ); + final IBaseResource resultingResource = fhirResourceRepository + .save( transformerRequest.getFhirClient(), outcome.getResource(), resource.getId() ); // resource may have been set as attribute in transformer context (e.g. shared encounter) outcome.getResource().setId( resultingResource.getIdElement() ); fhirDhisAssignmentRepository.saveFhirResourceId( outcome.getRule(), transformerRequest.getFhirClient(), @@ -482,6 +483,7 @@ public Optional read( @Nonnull FhirClient fhirClient, @Nonnull Fh if ( dhisResource == null ) { logger.debug( "DHIS resource could not be found." ); + return Optional.empty(); } @@ -493,6 +495,7 @@ public Optional read( @Nonnull FhirClient fhirClient, @Nonnull Fh if ( transformerRequest == null ) { logger.debug( "No matching rule has been found." ); + return Optional.empty(); } diff --git a/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/repository/impl/FhirResourceRepositoryImpl.java b/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/repository/impl/FhirResourceRepositoryImpl.java index 086f7b9b..fbbb7f7c 100644 --- a/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/repository/impl/FhirResourceRepositoryImpl.java +++ b/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/repository/impl/FhirResourceRepositoryImpl.java @@ -276,7 +276,7 @@ public boolean delete( @Nonnull FhirClient fhirClient, @Nonnull IBaseResource re "T(org.dhis2.fhir.adapter.fhir.metadata.model.FhirResourceType).getByResource(#resource).getResourceTypeName(), #resource.getIdElement().getIdPart(), true}" ) @Nonnull @Override - public IBaseResource save( @Nonnull FhirClient fhirClient, @Nonnull IBaseResource resource ) + public IBaseResource save( @Nonnull FhirClient fhirClient, @Nonnull IBaseResource resource, @Nullable String dhisResourceId ) { if ( !fhirClient.getFhirEndpoint().isUseRemote() ) { @@ -287,25 +287,28 @@ public IBaseResource save( @Nonnull FhirClient fhirClient, @Nonnull IBaseResourc final FhirContext fhirContext = fhirContexts.get( fhirClient.getFhirVersion() ); final IGenericClient client = FhirClientUtils.createClient( fhirContext, fhirClient.getFhirEndpoint() ); + final IBaseResource preparedResource = prepareResource( resource, dhisResourceId ); final MethodOutcome methodOutcome; + if ( resource.getIdElement().hasIdPart() ) { try { - methodOutcome = client.update().resource( resource ).prefer( PreferReturnEnum.REPRESENTATION ).execute(); + methodOutcome = client.update().resource( preparedResource ).prefer( PreferReturnEnum.REPRESENTATION ).execute(); } catch ( PreconditionFailedException e ) { throw new OptimisticFhirResourceLockException( "Could not update FHIR resource " + - resource.getIdElement() + " because of an optimistic locking failure.", e ); + preparedResource.getIdElement() + " because of an optimistic locking failure.", e ); } } else { - methodOutcome = client.create().resource( resource ).prefer( PreferReturnEnum.REPRESENTATION ).execute(); + methodOutcome = client.create().resource( preparedResource ).prefer( PreferReturnEnum.REPRESENTATION ).execute(); } ProcessedItemInfo processedItemInfo = null; + if ( (methodOutcome.getResource() != null) && (methodOutcome.getResource().getMeta() != null) ) { // resource itself may contain old version ID (even if it should not) @@ -313,7 +316,7 @@ public IBaseResource save( @Nonnull FhirClient fhirClient, @Nonnull IBaseResourc } else if ( (methodOutcome.getId() != null) && methodOutcome.getId().hasVersionIdPart() ) { - processedItemInfo = ProcessedFhirItemInfoUtils.create( resource, methodOutcome.getId() ); + processedItemInfo = ProcessedFhirItemInfoUtils.create( preparedResource, methodOutcome.getId() ); } if ( processedItemInfo == null ) @@ -327,9 +330,11 @@ else if ( (methodOutcome.getId() != null) && methodOutcome.getId().hasVersionIdP } final IBaseResource result; + if ( methodOutcome.getResource() == null ) { result = resource; + if ( methodOutcome.getId() != null ) { result.setId( methodOutcome.getId() ); @@ -343,6 +348,24 @@ else if ( (methodOutcome.getId() != null) && methodOutcome.getId().hasVersionIdP return result; } + @Nonnull + protected T prepareResource( @Nonnull T resource, @Nullable String dhisResourceId ) + { + final FhirResourceType fhirResourceType = FhirResourceType.getByResource( resource ); + + if ( fhirResourceType == null ) + { + throw new FhirResourceTransformationException( "Could not determine FHIR resource type for " + resource.getClass().getSimpleName() ); + } + + if ( fhirResourceType.isSyncDhisId() && dhisResourceId != null && !resource.getIdElement().hasIdPart() ) + { + resource.setId( dhisResourceId ); + } + + return resource; + } + @TransactionalEventListener( phase = TransactionPhase.BEFORE_COMMIT, classes = AutoCreatedFhirClientResourceEvent.class ) public void autoCreatedSubscriptionResource( @Nonnull AutoCreatedFhirClientResourceEvent event ) { diff --git a/fhir/src/test/java/org/dhis2/fhir/adapter/fhir/repository/impl/FhirResourceRepositoryImplTest.java b/fhir/src/test/java/org/dhis2/fhir/adapter/fhir/repository/impl/FhirResourceRepositoryImplTest.java new file mode 100644 index 00000000..6fcd4ef9 --- /dev/null +++ b/fhir/src/test/java/org/dhis2/fhir/adapter/fhir/repository/impl/FhirResourceRepositoryImplTest.java @@ -0,0 +1,207 @@ +package org.dhis2.fhir.adapter.fhir.repository.impl; + +/* + * Copyright (c) 2004-2019, University of Oslo + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * Neither the name of the HISP project nor the names of its contributors may + * be used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +import ca.uhn.fhir.context.FhirContext; +import ca.uhn.fhir.context.FhirVersionEnum; +import ca.uhn.fhir.model.api.IFhirVersion; +import ca.uhn.fhir.model.primitive.IdDt; +import org.dhis2.fhir.adapter.fhir.client.StoredFhirResourceService; +import org.dhis2.fhir.adapter.fhir.metadata.repository.FhirClientResourceRepository; +import org.dhis2.fhir.adapter.fhir.script.ScriptExecutor; +import org.dhis2.fhir.adapter.spring.StaticObjectProvider; +import org.hl7.fhir.instance.model.api.IBaseMetaType; +import org.hl7.fhir.instance.model.api.IBaseResource; +import org.hl7.fhir.instance.model.api.IIdType; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.mockito.Mock; +import org.mockito.Mockito; +import org.mockito.junit.MockitoJUnit; +import org.mockito.junit.MockitoRule; + +import java.util.Arrays; +import java.util.Collections; +import java.util.List; + +/** + * Implementation of {@link FhirResourceRepositoryImpl}. + * + * @author volsch + */ +public class FhirResourceRepositoryImplTest +{ + @Mock + private ScriptExecutor scriptExecutor; + + @Mock + private StoredFhirResourceService storedItemService; + + @Mock + private FhirClientResourceRepository fhirClientResourceRepository; + + @Mock + private FhirContext fhirContextDstu3; + + @Mock + private IFhirVersion fhirVersionDstu3; + + @Mock + private FhirContext fhirContextR4; + + @Mock + private IFhirVersion fhirVersionR4; + + private FhirResourceRepositoryImpl repository; + + @Rule + public MockitoRule mockitoRule = MockitoJUnit.rule(); + + @Before + public void setUp() + { + Mockito.when( fhirContextDstu3.getVersion() ).thenReturn( fhirVersionDstu3 ); + Mockito.when( fhirVersionDstu3.getVersion() ).thenReturn( FhirVersionEnum.DSTU3 ); + Mockito.when( fhirContextR4.getVersion() ).thenReturn( fhirVersionR4 ); + Mockito.when( fhirVersionR4.getVersion() ).thenReturn( FhirVersionEnum.R4 ); + + repository = new FhirResourceRepositoryImpl( scriptExecutor, storedItemService, fhirClientResourceRepository, + new StaticObjectProvider<>( Arrays.asList( fhirContextDstu3, fhirContextR4 ) ), new StaticObjectProvider<>( Collections.emptyList() ) ); + } + + @Test + public void prepareResourceWithoutAnyId() + { + Patient patient = new Patient(); + patient = repository.prepareResource( patient, null ); + Assert.assertTrue( patient.getIdElement().isEmpty() ); + } + + @Test + public void prepareResourceNonMatchingWithDhisId() + { + Patient patient = new Patient(); + patient = repository.prepareResource( patient, "a0123456789" ); + Assert.assertTrue( patient.getIdElement().isEmpty() ); + } + + @Test + public void prepareResourceWithDhisId() + { + PlanDefinition planDefinition = new PlanDefinition(); + planDefinition = repository.prepareResource( planDefinition, "a0123456789" ); + Assert.assertEquals( "a0123456789", planDefinition.getIdElement().getIdPart() ); + } + + @Test + public void prepareResourceWithAllIds() + { + PlanDefinition planDefinition = new PlanDefinition(); + planDefinition.setId( "d0123456789" ); + planDefinition = repository.prepareResource( planDefinition, "a0123456789" ); + Assert.assertEquals( "d0123456789", planDefinition.getIdElement().getIdPart() ); + } + + public static class Patient extends AbstractBaseResource + { + private static final long serialVersionUID = -1428885428508171576L; + } + + public static class PlanDefinition extends AbstractBaseResource + { + private static final long serialVersionUID = -1428885428508171576L; + } + + public static abstract class AbstractBaseResource implements IBaseResource + { + private static final long serialVersionUID = 7566894473303706042L; + + private IIdType id = new IdDt(); + + @Override + public IBaseMetaType getMeta() + { + return null; + } + + @Override + public IIdType getIdElement() + { + return id; + } + + @Override + public IBaseResource setId( String theId ) + { + id = new IdDt( theId ); + + return this; + } + + @Override + public IBaseResource setId( IIdType theId ) + { + this.id = theId; + + return this; + } + + @Override + public FhirVersionEnum getStructureFhirVersionEnum() + { + return FhirVersionEnum.R4; + } + + @Override + public boolean isEmpty() + { + return false; + } + + @Override + public boolean hasFormatComment() + { + return false; + } + + @Override + public List getFormatCommentsPre() + { + return null; + } + + @Override + public List getFormatCommentsPost() + { + return null; + } + } +} \ No newline at end of file From 46299cede66daae51d6e3df38f6fc2c40e55c716 Mon Sep 17 00:00:00 2001 From: Volker Schmidt Date: Wed, 31 Jul 2019 14:31:05 +0200 Subject: [PATCH 20/21] Adds support for tracked entity resource type in Plan Definitions. --- .../r4/R4ProgramMetadataFhirRestAppTest.java | 3 + .../LocationExtension.StructureDefinition.xml | 17 +-- ...ourceTypeExtension.StructureDefinition.xml | 57 ++++++++++ ...gramPlanDefinition.StructureDefinition.xml | 21 ++++ ...tadataToFhirPlanDefinitionTransformer.java | 12 +- ...taToFhirPlanDefinitionTransformerTest.java | 14 ++- .../extension/ResourceTypeExtensionUtils.java | 68 +++++++++++ ...tadataToFhirPlanDefinitionTransformer.java | 33 +++++- .../ResourceTypeExtensionUtilsTest.java | 107 ++++++++++++++++++ ...taToFhirPlanDefinitionTransformerTest.java | 88 ++++++++++---- 10 files changed, 372 insertions(+), 48 deletions(-) create mode 100644 docs/fhir/profiles/ResourceTypeExtension.StructureDefinition.xml create mode 100644 fhir/src/main/java/org/dhis2/fhir/adapter/fhir/extension/ResourceTypeExtensionUtils.java create mode 100644 fhir/src/test/java/org/dhis2/fhir/adapter/fhir/extension/ResourceTypeExtensionUtilsTest.java diff --git a/app/src/test/java/org/dhis2/fhir/adapter/fhir/server/r4/R4ProgramMetadataFhirRestAppTest.java b/app/src/test/java/org/dhis2/fhir/adapter/fhir/server/r4/R4ProgramMetadataFhirRestAppTest.java index e86e33b7..e828ae5b 100644 --- a/app/src/test/java/org/dhis2/fhir/adapter/fhir/server/r4/R4ProgramMetadataFhirRestAppTest.java +++ b/app/src/test/java/org/dhis2/fhir/adapter/fhir/server/r4/R4ProgramMetadataFhirRestAppTest.java @@ -34,6 +34,7 @@ import ca.uhn.fhir.rest.server.exceptions.ResourceNotFoundException; import org.apache.commons.io.IOUtils; import org.dhis2.fhir.adapter.AbstractAppTest; +import org.dhis2.fhir.adapter.fhir.extension.ResourceTypeExtensionUtils; import org.dhis2.fhir.adapter.fhir.model.FhirVersion; import org.hl7.fhir.r4.model.Bundle; import org.hl7.fhir.r4.model.PlanDefinition; @@ -124,6 +125,8 @@ private void getPlanDefinition() throws Exception client.registerInterceptor( new BasicAuthInterceptor( "fhir_client", "fhir_client_1" ) ); PlanDefinition planDefinition = client.read().resource( PlanDefinition.class ).withId( "EPDyQuoRnXk" ).execute(); Assert.assertEquals( "Child Programme", planDefinition.getTitle() ); + Assert.assertNotNull( planDefinition.getExtensionByUrl( ResourceTypeExtensionUtils.URL ) ); + Assert.assertEquals( "Patient", planDefinition.getExtensionByUrl( ResourceTypeExtensionUtils.URL ).getValue().primitiveValue() ); systemDhis2Server.verify(); userDhis2Server.verify(); diff --git a/docs/fhir/profiles/LocationExtension.StructureDefinition.xml b/docs/fhir/profiles/LocationExtension.StructureDefinition.xml index 4211ca46..51891d76 100644 --- a/docs/fhir/profiles/LocationExtension.StructureDefinition.xml +++ b/docs/fhir/profiles/LocationExtension.StructureDefinition.xml @@ -28,7 +28,7 @@ --> - + @@ -47,21 +47,6 @@ - - - - - - - - - - - - - - - diff --git a/docs/fhir/profiles/ResourceTypeExtension.StructureDefinition.xml b/docs/fhir/profiles/ResourceTypeExtension.StructureDefinition.xml new file mode 100644 index 00000000..76f90869 --- /dev/null +++ b/docs/fhir/profiles/ResourceTypeExtension.StructureDefinition.xml @@ -0,0 +1,57 @@ + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/docs/fhir/profiles/TrackerProgramPlanDefinition.StructureDefinition.xml b/docs/fhir/profiles/TrackerProgramPlanDefinition.StructureDefinition.xml index bf33d0bb..25e9ae28 100644 --- a/docs/fhir/profiles/TrackerProgramPlanDefinition.StructureDefinition.xml +++ b/docs/fhir/profiles/TrackerProgramPlanDefinition.StructureDefinition.xml @@ -50,6 +50,27 @@ + + + + + + + + + + + + + + + + + + + + + diff --git a/fhir-r4/src/main/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/metadata/r4/R4ProgramMetadataToFhirPlanDefinitionTransformer.java b/fhir-r4/src/main/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/metadata/r4/R4ProgramMetadataToFhirPlanDefinitionTransformer.java index 44333c79..90f5a217 100644 --- a/fhir-r4/src/main/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/metadata/r4/R4ProgramMetadataToFhirPlanDefinitionTransformer.java +++ b/fhir-r4/src/main/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/metadata/r4/R4ProgramMetadataToFhirPlanDefinitionTransformer.java @@ -28,6 +28,7 @@ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ +import ca.uhn.fhir.model.api.IElement; import org.dhis2.fhir.adapter.dhis.orgunit.OrganizationUnitService; import org.dhis2.fhir.adapter.dhis.tracker.program.Program; import org.dhis2.fhir.adapter.dhis.tracker.trackedentity.TrackedEntityMetadataService; @@ -50,11 +51,13 @@ import org.hl7.fhir.r4.model.Enumerations; import org.hl7.fhir.r4.model.PlanDefinition; import org.hl7.fhir.r4.model.PlanDefinition.PlanDefinitionActionComponent; +import org.hl7.fhir.r4.model.ResourceFactory; import org.springframework.stereotype.Component; import javax.annotation.Nonnull; import java.util.Map; import java.util.Set; +import java.util.function.Function; /** * R4 specific version of DHIS2 Program Metadata to FHIR Plan Definition transformer. @@ -86,7 +89,7 @@ protected boolean transformInternal( @Nonnull FhirClient fhirClient, @Nonnull Dh final Program dhisProgram = (Program) input.getDhisResource(); final PlanDefinition fhirPlanDefinition = (PlanDefinition) output; - if ( !isApplicableProgram( dhisProgram ) ) + if ( !addSubjectResourceType( dhisProgram, fhirPlanDefinition ) ) { return false; } @@ -111,4 +114,11 @@ protected boolean transformInternal( @Nonnull FhirClient fhirClient, @Nonnull Dh return true; } + + @Nonnull + @Override + protected Function getTypeFactory() + { + return ResourceFactory::createType; + } } diff --git a/fhir-r4/src/test/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/metadata/r4/R4ProgramMetadataToFhirPlanDefinitionTransformerTest.java b/fhir-r4/src/test/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/metadata/r4/R4ProgramMetadataToFhirPlanDefinitionTransformerTest.java index adccd703..a795ad71 100644 --- a/fhir-r4/src/test/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/metadata/r4/R4ProgramMetadataToFhirPlanDefinitionTransformerTest.java +++ b/fhir-r4/src/test/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/metadata/r4/R4ProgramMetadataToFhirPlanDefinitionTransformerTest.java @@ -36,9 +36,12 @@ import org.dhis2.fhir.adapter.dhis.tracker.trackedentity.TrackedEntityMetadataService; import org.dhis2.fhir.adapter.dhis.tracker.trackedentity.WritableTrackedEntityType; import org.dhis2.fhir.adapter.fhir.data.repository.FhirDhisAssignmentRepository; +import org.dhis2.fhir.adapter.fhir.extension.ResourceTypeExtensionUtils; import org.dhis2.fhir.adapter.fhir.metadata.model.FhirClient; +import org.dhis2.fhir.adapter.fhir.metadata.model.FhirResourceType; import org.dhis2.fhir.adapter.fhir.metadata.model.ProgramMetadataRule; import org.dhis2.fhir.adapter.fhir.metadata.model.RuleInfo; +import org.dhis2.fhir.adapter.fhir.metadata.model.TrackedEntityRule; import org.dhis2.fhir.adapter.fhir.metadata.repository.SystemRepository; import org.dhis2.fhir.adapter.fhir.metadata.repository.TrackedEntityRuleRepository; import org.dhis2.fhir.adapter.fhir.model.FhirVersion; @@ -120,10 +123,13 @@ public void getFhirVersions() @Test public void transformInternal() { + final TrackedEntityRule rule2 = new TrackedEntityRule(); + rule2.setEvaluationOrder( 100 ); + rule2.setFhirResourceType( FhirResourceType.PATIENT ); + Mockito.doReturn( Optional.of( new WritableTrackedEntityType( "a1234567890", "Test", Collections.emptyList() ) ) ) .when( trackedEntityMetadataService ).findTypeByReference( Mockito.eq( new Reference( "a1234567890", ReferenceType.ID ) ) ); - Mockito.doReturn( Collections.singletonList( new RuleInfo<>( new ProgramMetadataRule(), Collections.emptyList() ) ) ) - .when( trackedEntityRuleRepository ).findByTypeRefs( Mockito.eq( new HashSet<>( + Mockito.doReturn( Collections.singletonList( rule2 ) ).when( trackedEntityRuleRepository ).findByTypeRefs( Mockito.eq( new HashSet<>( Arrays.asList( new Reference( "a1234567890", ReferenceType.ID ), new Reference( "Test", ReferenceType.NAME ) ) ) ) ); final WritableProgram program = new WritableProgram(); @@ -156,6 +162,10 @@ public void transformInternal() Assert.assertEquals( "Test Description", fhirPlanDefinition.getDescription() ); Assert.assertEquals( 2, fhirPlanDefinition.getAction().size() ); + Assert.assertEquals( 1, fhirPlanDefinition.getExtension().size() ); + Assert.assertEquals( ResourceTypeExtensionUtils.URL, fhirPlanDefinition.getExtension().get( 0 ).getUrl() ); + Assert.assertEquals( "Patient", fhirPlanDefinition.getExtension().get( 0 ).getValue().toString() ); + Assert.assertEquals( "b1234567890", fhirPlanDefinition.getAction().get( 0 ).getId() ); Assert.assertEquals( "Test Stage 1", fhirPlanDefinition.getAction().get( 0 ).getTitle() ); Assert.assertEquals( "Test Description 1", fhirPlanDefinition.getAction().get( 0 ).getDescription() ); diff --git a/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/extension/ResourceTypeExtensionUtils.java b/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/extension/ResourceTypeExtensionUtils.java new file mode 100644 index 00000000..595cc28f --- /dev/null +++ b/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/extension/ResourceTypeExtensionUtils.java @@ -0,0 +1,68 @@ +package org.dhis2.fhir.adapter.fhir.extension; + +/* + * Copyright (c) 2004-2019, University of Oslo + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * Neither the name of the HISP project nor the names of its contributors may + * be used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +import ca.uhn.fhir.model.api.IElement; +import org.dhis2.fhir.adapter.fhir.metadata.model.FhirResourceType; +import org.hl7.fhir.instance.model.api.IBaseExtension; +import org.hl7.fhir.instance.model.api.IBaseHasExtensions; +import org.hl7.fhir.instance.model.api.IPrimitiveType; + +import javax.annotation.Nonnull; +import javax.annotation.Nullable; +import java.util.function.Function; + +/** + * Utility class to process FHIR resource type extension. + * + * @author volsch + */ +public abstract class ResourceTypeExtensionUtils +{ + public static final String URL = "http://www.dhis2.org/dhis2-fhir-adapter/fhir/extensions/resource-type"; + + @SuppressWarnings( "unchecked" ) + public static void setValue( @Nonnull IBaseHasExtensions resource, @Nullable FhirResourceType fhirResourceType, @Nonnull Function typeFactory ) + { + resource.getExtension().removeIf( e -> URL.equals( e.getUrl() ) ); + + if ( fhirResourceType != null ) + { + final IBaseExtension extension = resource.addExtension(); + + extension.setUrl( URL ); + extension.setValue( ( (IPrimitiveType) typeFactory.apply( "string" ) ).setValue( fhirResourceType.getResourceTypeName() ) ); + } + } + + private ResourceTypeExtensionUtils() + { + super(); + } +} diff --git a/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/metadata/program/AbstractProgramMetadataToFhirPlanDefinitionTransformer.java b/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/metadata/program/AbstractProgramMetadataToFhirPlanDefinitionTransformer.java index bb7aaefe..a2b48af7 100644 --- a/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/metadata/program/AbstractProgramMetadataToFhirPlanDefinitionTransformer.java +++ b/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/metadata/program/AbstractProgramMetadataToFhirPlanDefinitionTransformer.java @@ -28,6 +28,7 @@ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ +import ca.uhn.fhir.model.api.IElement; import org.dhis2.fhir.adapter.dhis.model.DhisResourceType; import org.dhis2.fhir.adapter.dhis.model.Reference; import org.dhis2.fhir.adapter.dhis.model.ReferenceType; @@ -36,6 +37,8 @@ import org.dhis2.fhir.adapter.dhis.tracker.trackedentity.TrackedEntityMetadataService; import org.dhis2.fhir.adapter.dhis.tracker.trackedentity.TrackedEntityType; import org.dhis2.fhir.adapter.fhir.data.repository.FhirDhisAssignmentRepository; +import org.dhis2.fhir.adapter.fhir.extension.ResourceTypeExtensionUtils; +import org.dhis2.fhir.adapter.fhir.metadata.model.AbstractRule; import org.dhis2.fhir.adapter.fhir.metadata.model.FhirResourceType; import org.dhis2.fhir.adapter.fhir.metadata.model.ProgramMetadataRule; import org.dhis2.fhir.adapter.fhir.metadata.repository.SystemRepository; @@ -46,11 +49,13 @@ import org.dhis2.fhir.adapter.fhir.transform.dhis.impl.metadata.AbstractReadOnlyDhisMetadataToTypedFhirTransformer; import org.dhis2.fhir.adapter.fhir.transform.scripted.AccessibleScriptedDhisMetadata; import org.dhis2.fhir.adapter.lock.LockManager; +import org.hl7.fhir.instance.model.api.IBaseHasExtensions; import org.hl7.fhir.instance.model.api.IBaseResource; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import javax.annotation.Nonnull; +import java.util.function.Function; /** * Implementation of {@link DhisToFhirTransformer} for transforming DHIS2 @@ -63,6 +68,8 @@ public abstract class AbstractProgramMetadataToFhirPlanDefinitionTransformer getRuleClass() return ProgramMetadataRule.class; } - protected boolean isApplicableProgram( @Nonnull Program program ) + protected boolean addSubjectResourceType( @Nonnull Program program, @Nonnull IBaseHasExtensions resource ) { - if ( program.isWithoutRegistration() || program.getTrackedEntityTypeId() == null ) + if ( program.getTrackedEntityTypeId() == null ) { - return false; + ResourceTypeExtensionUtils.setValue( resource, null, getTypeFactory() ); + + return true; } - final TrackedEntityType trackedEntityType = trackedEntityMetadataService.findTypeByReference( new Reference( program.getTrackedEntityTypeId(), ReferenceType.ID ) ).orElse( null ); + final TrackedEntityType trackedEntityType = trackedEntityMetadataService.findTypeByReference( + new Reference( program.getTrackedEntityTypeId(), ReferenceType.ID ) ).orElse( null ); if ( trackedEntityType == null ) { return false; } - return !trackedEntityRuleRepository.findByTypeRefs( trackedEntityType.getAllReferences() ).isEmpty(); + final FhirResourceType fhirResourceType = trackedEntityRuleRepository.findByTypeRefs( trackedEntityType.getAllReferences() ).stream() + .sorted( ( o1, o2 ) -> o2.getEvaluationOrder() - o1.getEvaluationOrder() ).map( AbstractRule::getFhirResourceType ).findFirst().orElse( null ); + + if ( fhirResourceType == null ) + { + return false; + } + + ResourceTypeExtensionUtils.setValue( resource, fhirResourceType, getTypeFactory() ); + + return true; } + + @Nonnull + protected abstract Function getTypeFactory(); } diff --git a/fhir/src/test/java/org/dhis2/fhir/adapter/fhir/extension/ResourceTypeExtensionUtilsTest.java b/fhir/src/test/java/org/dhis2/fhir/adapter/fhir/extension/ResourceTypeExtensionUtilsTest.java new file mode 100644 index 00000000..d245ec1d --- /dev/null +++ b/fhir/src/test/java/org/dhis2/fhir/adapter/fhir/extension/ResourceTypeExtensionUtilsTest.java @@ -0,0 +1,107 @@ +package org.dhis2.fhir.adapter.fhir.extension; + +/* + * Copyright (c) 2004-2019, University of Oslo + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * Neither the name of the HISP project nor the names of its contributors may + * be used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +import ca.uhn.fhir.model.api.ExtensionDt; +import ca.uhn.fhir.model.api.IElement; +import ca.uhn.fhir.model.primitive.StringDt; +import org.dhis2.fhir.adapter.fhir.metadata.model.FhirResourceType; +import org.hl7.fhir.instance.model.api.IBaseExtension; +import org.hl7.fhir.instance.model.api.IBaseHasExtensions; +import org.junit.Assert; +import org.junit.Test; + +import java.util.ArrayList; +import java.util.List; + +/** + * Unit tests for {@link ResourceTypeExtensionUtils}. + * + * @author volsch + */ +public class ResourceTypeExtensionUtilsTest +{ + @Test + public void resetValue() + { + PlanDefinition planDefinition = new PlanDefinition(); + + ResourceTypeExtensionUtils.setValue( planDefinition, FhirResourceType.PATIENT, TypeFactory::createType ); + ResourceTypeExtensionUtils.setValue( planDefinition, null, TypeFactory::createType ); + + Assert.assertTrue( planDefinition.getExtension().isEmpty() ); + } + + @Test + public void setValue() + { + PlanDefinition planDefinition = new PlanDefinition(); + + ResourceTypeExtensionUtils.setValue( planDefinition, FhirResourceType.PATIENT, TypeFactory::createType ); + + Assert.assertEquals( 1, planDefinition.getExtension().size() ); + Assert.assertEquals( ResourceTypeExtensionUtils.URL, planDefinition.getExtension().get( 0 ).getUrl() ); + Assert.assertEquals( "Patient", planDefinition.getExtension().get( 0 ).getValue().toString() ); + } + + public static class PlanDefinition implements IBaseHasExtensions + { + private final List> extensions = new ArrayList<>(); + + @Override + public IBaseExtension addExtension() + { + extensions.add( new ExtensionDt() ); + + return extensions.get( extensions.size() - 1 ); + } + + @Override + public List> getExtension() + { + return extensions; + } + + @Override + public boolean hasExtension() + { + return !extensions.isEmpty(); + } + } + + public static class TypeFactory + { + public static IElement createType( String name ) + { + Assert.assertEquals( "string", name ); + + return new StringDt(); + } + } +} \ No newline at end of file diff --git a/fhir/src/test/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/metadata/program/AbstractProgramMetadataToFhirPlanDefinitionTransformerTest.java b/fhir/src/test/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/metadata/program/AbstractProgramMetadataToFhirPlanDefinitionTransformerTest.java index 8ee2c709..5c473888 100644 --- a/fhir/src/test/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/metadata/program/AbstractProgramMetadataToFhirPlanDefinitionTransformerTest.java +++ b/fhir/src/test/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/metadata/program/AbstractProgramMetadataToFhirPlanDefinitionTransformerTest.java @@ -28,6 +28,8 @@ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ +import ca.uhn.fhir.model.api.ExtensionDt; +import ca.uhn.fhir.model.primitive.StringDt; import org.dhis2.fhir.adapter.dhis.model.DhisResourceType; import org.dhis2.fhir.adapter.dhis.model.Reference; import org.dhis2.fhir.adapter.dhis.model.ReferenceType; @@ -36,15 +38,17 @@ import org.dhis2.fhir.adapter.dhis.tracker.trackedentity.TrackedEntityMetadataService; import org.dhis2.fhir.adapter.dhis.tracker.trackedentity.WritableTrackedEntityType; import org.dhis2.fhir.adapter.fhir.data.repository.FhirDhisAssignmentRepository; +import org.dhis2.fhir.adapter.fhir.extension.ResourceTypeExtensionUtils; import org.dhis2.fhir.adapter.fhir.metadata.model.FhirResourceType; -import org.dhis2.fhir.adapter.fhir.metadata.model.ProgramMetadataRule; -import org.dhis2.fhir.adapter.fhir.metadata.model.RuleInfo; +import org.dhis2.fhir.adapter.fhir.metadata.model.TrackedEntityRule; import org.dhis2.fhir.adapter.fhir.metadata.repository.SystemRepository; import org.dhis2.fhir.adapter.fhir.metadata.repository.TrackedEntityRuleRepository; import org.dhis2.fhir.adapter.fhir.repository.FhirResourceRepository; import org.dhis2.fhir.adapter.fhir.script.ScriptExecutor; import org.dhis2.fhir.adapter.fhir.transform.scripted.AccessibleScriptedDhisMetadata; import org.dhis2.fhir.adapter.lock.LockManager; +import org.hl7.fhir.instance.model.api.IBaseExtension; +import org.hl7.fhir.instance.model.api.IBaseHasExtensions; import org.junit.Assert; import org.junit.Before; import org.junit.Rule; @@ -58,6 +62,7 @@ import java.util.Arrays; import java.util.Collections; import java.util.HashSet; +import java.util.List; import java.util.Optional; import static org.mockito.Mockito.CALLS_REAL_METHODS; @@ -97,7 +102,7 @@ public class AbstractProgramMetadataToFhirPlanDefinitionTransformerTest private AbstractProgramMetadataToFhirPlanDefinitionTransformer transformer; @Rule - public MockitoRule rule = MockitoJUnit.rule(); + public MockitoRule rule = MockitoJUnit.rule().silent(); @Before public void setUp() @@ -105,6 +110,8 @@ public void setUp() transformer = Mockito.mock( AbstractProgramMetadataToFhirPlanDefinitionTransformer.class, withSettings().useConstructor( scriptExecutor, lockManager, systemRepository, fhirResourceRepository, fhirDhisAssignmentRepository, organizationUnitService, trackedEntityMetadataService, trackedEntityRuleRepository ).defaultAnswer( CALLS_REAL_METHODS ) ); + + Mockito.when( transformer.getTypeFactory() ).thenReturn( name -> new StringDt() ); } @Test @@ -126,12 +133,19 @@ public void getDhisResourceClass() } @Test - public void isApplicableProgram() + public void addSubjectResourceType() { + final TrackedEntityRule rule1 = new TrackedEntityRule(); + rule1.setEvaluationOrder( 1 ); + rule1.setFhirResourceType( FhirResourceType.RELATED_PERSON ); + + final TrackedEntityRule rule2 = new TrackedEntityRule(); + rule2.setEvaluationOrder( 100 ); + rule2.setFhirResourceType( FhirResourceType.PATIENT ); + Mockito.doReturn( Optional.of( new WritableTrackedEntityType( "a1234567890", "Test", Collections.emptyList() ) ) ) .when( trackedEntityMetadataService ).findTypeByReference( Mockito.eq( new Reference( "a1234567890", ReferenceType.ID ) ) ); - Mockito.doReturn( Collections.singletonList( new RuleInfo<>( new ProgramMetadataRule(), Collections.emptyList() ) ) ) - .when( trackedEntityRuleRepository ).findByTypeRefs( Mockito.eq( new HashSet<>( + Mockito.doReturn( Arrays.asList( rule1, rule2 ) ).when( trackedEntityRuleRepository ).findByTypeRefs( Mockito.eq( new HashSet<>( Arrays.asList( new Reference( "a1234567890", ReferenceType.ID ), new Reference( "Test", ReferenceType.NAME ) ) ) ) ); final WritableProgram program = new WritableProgram(); @@ -141,24 +155,17 @@ public void isApplicableProgram() program.setTrackedEntityTypeId( "a1234567890" ); program.setStages( new ArrayList<>() ); - Assert.assertTrue( transformer.isApplicableProgram( program ) ); - } + final PlanDefinition planDefinition = new PlanDefinition(); - @Test - public void isApplicableProgramWithoutRegistration() - { - final WritableProgram program = new WritableProgram(); - program.setName( "Test Program" ); - program.setDescription( "Test Description" ); - program.setWithoutRegistration( true ); - program.setTrackedEntityTypeId( "a1234567890" ); - program.setStages( new ArrayList<>() ); + Assert.assertTrue( transformer.addSubjectResourceType( program, planDefinition ) ); - Assert.assertFalse( transformer.isApplicableProgram( program ) ); + Assert.assertEquals( 1, planDefinition.getExtension().size() ); + Assert.assertEquals( ResourceTypeExtensionUtils.URL, planDefinition.getExtension().get( 0 ).getUrl() ); + Assert.assertEquals( "Patient", planDefinition.getExtension().get( 0 ).getValue().toString() ); } @Test - public void isApplicableProgramWithoutTrackedEntityType() + public void addSubjectResourceTypeWithoutTrackedEntityType() { final WritableProgram program = new WritableProgram(); program.setName( "Test Program" ); @@ -166,11 +173,15 @@ public void isApplicableProgramWithoutTrackedEntityType() program.setWithoutRegistration( false ); program.setStages( new ArrayList<>() ); - Assert.assertFalse( transformer.isApplicableProgram( program ) ); + final PlanDefinition planDefinition = new PlanDefinition(); + + Assert.assertTrue( transformer.addSubjectResourceType( program, planDefinition ) ); + + Assert.assertEquals( 0, planDefinition.getExtension().size() ); } @Test - public void isApplicableProgramWithoutTrackedEntityMetadata() + public void addSubjectResourceTypeWithoutTrackedEntityMetadata() { Mockito.doReturn( Optional.empty() ) .when( trackedEntityMetadataService ).findTypeByReference( Mockito.eq( new Reference( "a1234567890", ReferenceType.ID ) ) ); @@ -182,11 +193,13 @@ public void isApplicableProgramWithoutTrackedEntityMetadata() program.setTrackedEntityTypeId( "a1234567890" ); program.setStages( new ArrayList<>() ); - Assert.assertFalse( transformer.isApplicableProgram( program ) ); + final PlanDefinition planDefinition = new PlanDefinition(); + + Assert.assertFalse( transformer.addSubjectResourceType( program, planDefinition ) ); } @Test - public void isApplicableProgramWithoutRules() + public void addSubjectResourceTypeWithoutRules() { Mockito.doReturn( Optional.of( new WritableTrackedEntityType( "a1234567890", "Test", Collections.emptyList() ) ) ) .when( trackedEntityMetadataService ).findTypeByReference( Mockito.eq( new Reference( "a1234567890", ReferenceType.ID ) ) ); @@ -201,6 +214,33 @@ public void isApplicableProgramWithoutRules() program.setTrackedEntityTypeId( "a1234567890" ); program.setStages( new ArrayList<>() ); - Assert.assertFalse( transformer.isApplicableProgram( program ) ); + final PlanDefinition planDefinition = new PlanDefinition(); + + Assert.assertFalse( transformer.addSubjectResourceType( program, planDefinition ) ); + } + + public static class PlanDefinition implements IBaseHasExtensions + { + private final List> extensions = new ArrayList<>(); + + @Override + public IBaseExtension addExtension() + { + extensions.add( new ExtensionDt() ); + + return extensions.get( extensions.size() - 1 ); + } + + @Override + public List> getExtension() + { + return extensions; + } + + @Override + public boolean hasExtension() + { + return !extensions.isEmpty(); + } } } \ No newline at end of file From 054de1fcbb2d94605343cd7dca7bf55be8405e7b Mon Sep 17 00:00:00 2001 From: Volker Schmidt Date: Wed, 31 Jul 2019 17:54:47 +0200 Subject: [PATCH 21/21] Adds value type extension for Questionnaire items. --- ...ourceTypeExtension.StructureDefinition.xml | 2 +- ...ogramQuestionnaire.StructureDefinition.xml | 20 ++++++ ...ValueTypeExtension.StructureDefinition.xml | 57 ++++++++++++++++ ...etadataToFhirQuestionnaireTransformer.java | 13 ++++ ...ataToFhirQuestionnaireTransformerTest.java | 12 ++++ .../fhir/extension/BaseExtensionUtils.java | 65 ++++++++++++++++++ .../extension/ResourceTypeExtensionUtils.java | 13 +--- .../extension/ValueTypeExtensionUtils.java | 56 ++++++++++++++++ ...etadataToFhirQuestionnaireTransformer.java | 5 ++ .../extension/BaseExtensionUtilsTest.java | 64 ++++++++++++++++++ .../ResourceTypeExtensionUtilsTest.java | 47 +------------ .../fhir/extension/TestPlanDefinition.java | 66 +++++++++++++++++++ .../adapter/fhir/extension/TypeFactory.java | 48 ++++++++++++++ .../ValueTypeExtensionUtilsTest.java | 53 +++++++++++++++ 14 files changed, 463 insertions(+), 58 deletions(-) create mode 100644 docs/fhir/profiles/ValueTypeExtension.StructureDefinition.xml create mode 100644 fhir/src/main/java/org/dhis2/fhir/adapter/fhir/extension/BaseExtensionUtils.java create mode 100644 fhir/src/main/java/org/dhis2/fhir/adapter/fhir/extension/ValueTypeExtensionUtils.java create mode 100644 fhir/src/test/java/org/dhis2/fhir/adapter/fhir/extension/BaseExtensionUtilsTest.java create mode 100644 fhir/src/test/java/org/dhis2/fhir/adapter/fhir/extension/TestPlanDefinition.java create mode 100644 fhir/src/test/java/org/dhis2/fhir/adapter/fhir/extension/TypeFactory.java create mode 100644 fhir/src/test/java/org/dhis2/fhir/adapter/fhir/extension/ValueTypeExtensionUtilsTest.java diff --git a/docs/fhir/profiles/ResourceTypeExtension.StructureDefinition.xml b/docs/fhir/profiles/ResourceTypeExtension.StructureDefinition.xml index 76f90869..fc42e483 100644 --- a/docs/fhir/profiles/ResourceTypeExtension.StructureDefinition.xml +++ b/docs/fhir/profiles/ResourceTypeExtension.StructureDefinition.xml @@ -36,7 +36,7 @@ - + diff --git a/docs/fhir/profiles/TrackerProgramQuestionnaire.StructureDefinition.xml b/docs/fhir/profiles/TrackerProgramQuestionnaire.StructureDefinition.xml index eeef7fc0..f7fa6224 100644 --- a/docs/fhir/profiles/TrackerProgramQuestionnaire.StructureDefinition.xml +++ b/docs/fhir/profiles/TrackerProgramQuestionnaire.StructureDefinition.xml @@ -164,6 +164,26 @@ + + + + + + + + + + + + + + + + + + + + diff --git a/docs/fhir/profiles/ValueTypeExtension.StructureDefinition.xml b/docs/fhir/profiles/ValueTypeExtension.StructureDefinition.xml new file mode 100644 index 00000000..5fae7e76 --- /dev/null +++ b/docs/fhir/profiles/ValueTypeExtension.StructureDefinition.xml @@ -0,0 +1,57 @@ + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/fhir-r4/src/main/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/metadata/r4/R4ProgramStageMetadataToFhirQuestionnaireTransformer.java b/fhir-r4/src/main/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/metadata/r4/R4ProgramStageMetadataToFhirQuestionnaireTransformer.java index 2575b8ee..8b113b97 100644 --- a/fhir-r4/src/main/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/metadata/r4/R4ProgramStageMetadataToFhirQuestionnaireTransformer.java +++ b/fhir-r4/src/main/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/metadata/r4/R4ProgramStageMetadataToFhirQuestionnaireTransformer.java @@ -28,9 +28,11 @@ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ +import ca.uhn.fhir.model.api.IElement; import org.dhis2.fhir.adapter.dhis.orgunit.OrganizationUnitService; import org.dhis2.fhir.adapter.dhis.tracker.program.ProgramStage; import org.dhis2.fhir.adapter.fhir.data.repository.FhirDhisAssignmentRepository; +import org.dhis2.fhir.adapter.fhir.extension.ValueTypeExtensionUtils; import org.dhis2.fhir.adapter.fhir.metadata.model.FhirClient; import org.dhis2.fhir.adapter.fhir.metadata.model.ProgramStageMetadataRule; import org.dhis2.fhir.adapter.fhir.metadata.model.RuleInfo; @@ -49,12 +51,14 @@ import org.hl7.fhir.r4.model.Questionnaire; import org.hl7.fhir.r4.model.Questionnaire.QuestionnaireItemComponent; import org.hl7.fhir.r4.model.Questionnaire.QuestionnaireItemType; +import org.hl7.fhir.r4.model.ResourceFactory; import org.springframework.stereotype.Component; import javax.annotation.Nonnull; import javax.annotation.Nullable; import java.util.Map; import java.util.Set; +import java.util.function.Function; import static org.hl7.fhir.r4.model.Questionnaire.QuestionnaireItemType.*; @@ -104,6 +108,8 @@ protected boolean transformInternal( @Nonnull FhirClient fhirClient, @Nonnull Dh itemComponent.setRequired( dataElement.isCompulsory() ); itemComponent.setType( type ); + ValueTypeExtensionUtils.setValue( itemComponent, dataElement.getElement().getValueType(), getTypeFactory() ); + if ( dataElement.getElement().isOptionSetValue() ) { dataElement.getElement().getOptionSet().getOptions().forEach( option -> itemComponent.addAnswerOption().setValue( @@ -155,4 +161,11 @@ protected QuestionnaireItemType convertValueType( @Nonnull ValueType valueType ) // unhandled data type return null; } + + @Nonnull + @Override + protected Function getTypeFactory() + { + return ResourceFactory::createType; + } } diff --git a/fhir-r4/src/test/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/metadata/r4/R4ProgramStageMetadataToFhirQuestionnaireTransformerTest.java b/fhir-r4/src/test/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/metadata/r4/R4ProgramStageMetadataToFhirQuestionnaireTransformerTest.java index 3cf0b25c..c27859e3 100644 --- a/fhir-r4/src/test/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/metadata/r4/R4ProgramStageMetadataToFhirQuestionnaireTransformerTest.java +++ b/fhir-r4/src/test/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/metadata/r4/R4ProgramStageMetadataToFhirQuestionnaireTransformerTest.java @@ -35,6 +35,7 @@ import org.dhis2.fhir.adapter.dhis.tracker.program.WritableProgramStage; import org.dhis2.fhir.adapter.dhis.tracker.program.WritableProgramStageDataElement; import org.dhis2.fhir.adapter.fhir.data.repository.FhirDhisAssignmentRepository; +import org.dhis2.fhir.adapter.fhir.extension.ValueTypeExtensionUtils; import org.dhis2.fhir.adapter.fhir.metadata.model.FhirClient; import org.dhis2.fhir.adapter.fhir.metadata.model.ProgramStageMetadataRule; import org.dhis2.fhir.adapter.fhir.metadata.model.RuleInfo; @@ -49,6 +50,7 @@ import org.dhis2.fhir.adapter.model.ValueType; import org.hl7.fhir.r4.model.Coding; import org.hl7.fhir.r4.model.Questionnaire; +import org.hl7.fhir.r4.model.StringType; import org.junit.Assert; import org.junit.Rule; import org.junit.Test; @@ -157,6 +159,11 @@ public void transformInternal() Assert.assertEquals( 0, fhirQuestionnaire.getItem().get( 0 ).getAnswerOption().size() ); Assert.assertFalse( fhirQuestionnaire.getItem().get( 0 ).getRequired() ); + Assert.assertEquals( 1, fhirQuestionnaire.getItem().get( 0 ).getExtension().size() ); + Assert.assertEquals( ValueTypeExtensionUtils.URL, fhirQuestionnaire.getItem().get( 0 ).getExtension().get( 0 ).getUrl() ); + Assert.assertTrue( fhirQuestionnaire.getItem().get( 0 ).getExtension().get( 0 ).getValue() instanceof StringType ); + Assert.assertEquals( "TEXT", ( (StringType) fhirQuestionnaire.getItem().get( 0 ).getExtension().get( 0 ).getValue() ).getValue() ); + Assert.assertEquals( "d1123456789", fhirQuestionnaire.getItem().get( 1 ).getLinkId() ); Assert.assertEquals( "Value 2", fhirQuestionnaire.getItem().get( 1 ).getText() ); Assert.assertEquals( 2, fhirQuestionnaire.getItem().get( 1 ).getAnswerOption().size() ); @@ -166,5 +173,10 @@ public void transformInternal() Assert.assertEquals( "7", ( (Coding) fhirQuestionnaire.getItem().get( 1 ).getAnswerOption().get( 1 ).getValue() ).getCode() ); Assert.assertEquals( "Test Value 2", ( (Coding) fhirQuestionnaire.getItem().get( 1 ).getAnswerOption().get( 1 ).getValue() ).getDisplay() ); Assert.assertTrue( fhirQuestionnaire.getItem().get( 1 ).getRequired() ); + + Assert.assertEquals( 1, fhirQuestionnaire.getItem().get( 1 ).getExtension().size() ); + Assert.assertEquals( ValueTypeExtensionUtils.URL, fhirQuestionnaire.getItem().get( 1 ).getExtension().get( 0 ).getUrl() ); + Assert.assertTrue( fhirQuestionnaire.getItem().get( 1 ).getExtension().get( 0 ).getValue() instanceof StringType ); + Assert.assertEquals( "INTEGER", ( (StringType) fhirQuestionnaire.getItem().get( 1 ).getExtension().get( 0 ).getValue() ).getValue() ); } } diff --git a/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/extension/BaseExtensionUtils.java b/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/extension/BaseExtensionUtils.java new file mode 100644 index 00000000..6811ec79 --- /dev/null +++ b/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/extension/BaseExtensionUtils.java @@ -0,0 +1,65 @@ +package org.dhis2.fhir.adapter.fhir.extension; + +/* + * Copyright (c) 2004-2019, University of Oslo + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * Neither the name of the HISP project nor the names of its contributors may + * be used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +import ca.uhn.fhir.model.api.IElement; +import org.hl7.fhir.instance.model.api.IBaseExtension; +import org.hl7.fhir.instance.model.api.IBaseHasExtensions; +import org.hl7.fhir.instance.model.api.IPrimitiveType; + +import javax.annotation.Nonnull; +import javax.annotation.Nullable; +import java.util.function.Function; + +/** + * Utility class to process FHIR resource extensions. + * + * @author volsch + */ +abstract class BaseExtensionUtils +{ + @SuppressWarnings( "unchecked" ) + protected static void setStringValue( @Nonnull String url, @Nonnull IBaseHasExtensions resource, @Nullable String value, @Nonnull Function typeFactory ) + { + resource.getExtension().removeIf( e -> url.equals( e.getUrl() ) ); + + if ( value != null ) + { + final IBaseExtension extension = resource.addExtension(); + + extension.setUrl( url ); + extension.setValue( ( (IPrimitiveType) typeFactory.apply( "string" ) ).setValue( value ) ); + } + } + + private BaseExtensionUtils() + { + super(); + } +} diff --git a/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/extension/ResourceTypeExtensionUtils.java b/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/extension/ResourceTypeExtensionUtils.java index 595cc28f..6a316e7d 100644 --- a/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/extension/ResourceTypeExtensionUtils.java +++ b/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/extension/ResourceTypeExtensionUtils.java @@ -30,9 +30,7 @@ import ca.uhn.fhir.model.api.IElement; import org.dhis2.fhir.adapter.fhir.metadata.model.FhirResourceType; -import org.hl7.fhir.instance.model.api.IBaseExtension; import org.hl7.fhir.instance.model.api.IBaseHasExtensions; -import org.hl7.fhir.instance.model.api.IPrimitiveType; import javax.annotation.Nonnull; import javax.annotation.Nullable; @@ -47,18 +45,9 @@ public abstract class ResourceTypeExtensionUtils { public static final String URL = "http://www.dhis2.org/dhis2-fhir-adapter/fhir/extensions/resource-type"; - @SuppressWarnings( "unchecked" ) public static void setValue( @Nonnull IBaseHasExtensions resource, @Nullable FhirResourceType fhirResourceType, @Nonnull Function typeFactory ) { - resource.getExtension().removeIf( e -> URL.equals( e.getUrl() ) ); - - if ( fhirResourceType != null ) - { - final IBaseExtension extension = resource.addExtension(); - - extension.setUrl( URL ); - extension.setValue( ( (IPrimitiveType) typeFactory.apply( "string" ) ).setValue( fhirResourceType.getResourceTypeName() ) ); - } + BaseExtensionUtils.setStringValue( URL, resource, fhirResourceType == null ? null : fhirResourceType.getResourceTypeName(), typeFactory ); } private ResourceTypeExtensionUtils() diff --git a/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/extension/ValueTypeExtensionUtils.java b/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/extension/ValueTypeExtensionUtils.java new file mode 100644 index 00000000..127b9c7c --- /dev/null +++ b/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/extension/ValueTypeExtensionUtils.java @@ -0,0 +1,56 @@ +package org.dhis2.fhir.adapter.fhir.extension; + +/* + * Copyright (c) 2004-2019, University of Oslo + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * Neither the name of the HISP project nor the names of its contributors may + * be used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +import ca.uhn.fhir.model.api.IElement; +import org.dhis2.fhir.adapter.model.ValueType; +import org.hl7.fhir.instance.model.api.IBaseHasExtensions; + +import javax.annotation.Nonnull; +import java.util.function.Function; + +/** + * Utility class to process FHIR value type extension. + * + * @author volsch + */ +public abstract class ValueTypeExtensionUtils +{ + public static final String URL = "http://www.dhis2.org/dhis2-fhir-adapter/fhir/extensions/value-type"; + + public static void setValue( @Nonnull IBaseHasExtensions resource, @Nonnull ValueType valueType, @Nonnull Function typeFactory ) + { + BaseExtensionUtils.setStringValue( URL, resource, valueType.name(), typeFactory ); + } + + private ValueTypeExtensionUtils() + { + super(); + } +} diff --git a/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/metadata/program/AbstractProgramStageMetadataToFhirQuestionnaireTransformer.java b/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/metadata/program/AbstractProgramStageMetadataToFhirQuestionnaireTransformer.java index 0bc8ba45..2252165a 100644 --- a/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/metadata/program/AbstractProgramStageMetadataToFhirQuestionnaireTransformer.java +++ b/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/metadata/program/AbstractProgramStageMetadataToFhirQuestionnaireTransformer.java @@ -28,6 +28,7 @@ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ +import ca.uhn.fhir.model.api.IElement; import org.dhis2.fhir.adapter.dhis.model.DhisResourceType; import org.dhis2.fhir.adapter.dhis.orgunit.OrganizationUnitService; import org.dhis2.fhir.adapter.fhir.data.repository.FhirDhisAssignmentRepository; @@ -45,6 +46,7 @@ import org.slf4j.LoggerFactory; import javax.annotation.Nonnull; +import java.util.function.Function; /** * Implementation of {@link DhisToFhirTransformer} for transforming DHIS2 @@ -90,4 +92,7 @@ public Class getRuleClass() { return ProgramStageMetadataRule.class; } + + @Nonnull + protected abstract Function getTypeFactory(); } diff --git a/fhir/src/test/java/org/dhis2/fhir/adapter/fhir/extension/BaseExtensionUtilsTest.java b/fhir/src/test/java/org/dhis2/fhir/adapter/fhir/extension/BaseExtensionUtilsTest.java new file mode 100644 index 00000000..b6c8758a --- /dev/null +++ b/fhir/src/test/java/org/dhis2/fhir/adapter/fhir/extension/BaseExtensionUtilsTest.java @@ -0,0 +1,64 @@ +package org.dhis2.fhir.adapter.fhir.extension; + +/* + * Copyright (c) 2004-2019, University of Oslo + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * Neither the name of the HISP project nor the names of its contributors may + * be used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +import org.junit.Assert; +import org.junit.Test; + +/** + * Unit tests for {@link BaseExtensionUtils}. + * + * @author volsch + */ +public class BaseExtensionUtilsTest +{ + @Test + public void resetValue() + { + TestPlanDefinition planDefinition = new TestPlanDefinition(); + + BaseExtensionUtils.setStringValue( "testUrl", planDefinition, "testValue", TypeFactory::createType ); + BaseExtensionUtils.setStringValue( "testUrl", planDefinition, null, TypeFactory::createType ); + + Assert.assertTrue( planDefinition.getExtension().isEmpty() ); + } + + @Test + public void setValue() + { + TestPlanDefinition planDefinition = new TestPlanDefinition(); + + BaseExtensionUtils.setStringValue( "testUrl", planDefinition, "testValue", TypeFactory::createType ); + + Assert.assertEquals( 1, planDefinition.getExtension().size() ); + Assert.assertEquals( "testUrl", planDefinition.getExtension().get( 0 ).getUrl() ); + Assert.assertEquals( "testValue", planDefinition.getExtension().get( 0 ).getValue().toString() ); + } + +} \ No newline at end of file diff --git a/fhir/src/test/java/org/dhis2/fhir/adapter/fhir/extension/ResourceTypeExtensionUtilsTest.java b/fhir/src/test/java/org/dhis2/fhir/adapter/fhir/extension/ResourceTypeExtensionUtilsTest.java index d245ec1d..b45787a9 100644 --- a/fhir/src/test/java/org/dhis2/fhir/adapter/fhir/extension/ResourceTypeExtensionUtilsTest.java +++ b/fhir/src/test/java/org/dhis2/fhir/adapter/fhir/extension/ResourceTypeExtensionUtilsTest.java @@ -28,18 +28,10 @@ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -import ca.uhn.fhir.model.api.ExtensionDt; -import ca.uhn.fhir.model.api.IElement; -import ca.uhn.fhir.model.primitive.StringDt; import org.dhis2.fhir.adapter.fhir.metadata.model.FhirResourceType; -import org.hl7.fhir.instance.model.api.IBaseExtension; -import org.hl7.fhir.instance.model.api.IBaseHasExtensions; import org.junit.Assert; import org.junit.Test; -import java.util.ArrayList; -import java.util.List; - /** * Unit tests for {@link ResourceTypeExtensionUtils}. * @@ -50,7 +42,7 @@ public class ResourceTypeExtensionUtilsTest @Test public void resetValue() { - PlanDefinition planDefinition = new PlanDefinition(); + TestPlanDefinition planDefinition = new TestPlanDefinition(); ResourceTypeExtensionUtils.setValue( planDefinition, FhirResourceType.PATIENT, TypeFactory::createType ); ResourceTypeExtensionUtils.setValue( planDefinition, null, TypeFactory::createType ); @@ -61,7 +53,7 @@ public void resetValue() @Test public void setValue() { - PlanDefinition planDefinition = new PlanDefinition(); + TestPlanDefinition planDefinition = new TestPlanDefinition(); ResourceTypeExtensionUtils.setValue( planDefinition, FhirResourceType.PATIENT, TypeFactory::createType ); @@ -69,39 +61,4 @@ public void setValue() Assert.assertEquals( ResourceTypeExtensionUtils.URL, planDefinition.getExtension().get( 0 ).getUrl() ); Assert.assertEquals( "Patient", planDefinition.getExtension().get( 0 ).getValue().toString() ); } - - public static class PlanDefinition implements IBaseHasExtensions - { - private final List> extensions = new ArrayList<>(); - - @Override - public IBaseExtension addExtension() - { - extensions.add( new ExtensionDt() ); - - return extensions.get( extensions.size() - 1 ); - } - - @Override - public List> getExtension() - { - return extensions; - } - - @Override - public boolean hasExtension() - { - return !extensions.isEmpty(); - } - } - - public static class TypeFactory - { - public static IElement createType( String name ) - { - Assert.assertEquals( "string", name ); - - return new StringDt(); - } - } } \ No newline at end of file diff --git a/fhir/src/test/java/org/dhis2/fhir/adapter/fhir/extension/TestPlanDefinition.java b/fhir/src/test/java/org/dhis2/fhir/adapter/fhir/extension/TestPlanDefinition.java new file mode 100644 index 00000000..b2f01a17 --- /dev/null +++ b/fhir/src/test/java/org/dhis2/fhir/adapter/fhir/extension/TestPlanDefinition.java @@ -0,0 +1,66 @@ +package org.dhis2.fhir.adapter.fhir.extension; + +/* + * Copyright (c) 2004-2019, University of Oslo + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * Neither the name of the HISP project nor the names of its contributors may + * be used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +import ca.uhn.fhir.model.api.ExtensionDt; +import org.hl7.fhir.instance.model.api.IBaseExtension; +import org.hl7.fhir.instance.model.api.IBaseHasExtensions; + +import java.util.ArrayList; +import java.util.List; + +/** + * Test plan definition FHIR resource. + * + * @author volsch + */ +class TestPlanDefinition implements IBaseHasExtensions +{ + private final List> extensions = new ArrayList<>(); + + @Override + public IBaseExtension addExtension() + { + extensions.add( new ExtensionDt() ); + + return extensions.get( extensions.size() - 1 ); + } + + @Override + public List> getExtension() + { + return extensions; + } + + @Override + public boolean hasExtension() + { + return !extensions.isEmpty(); + } +} diff --git a/fhir/src/test/java/org/dhis2/fhir/adapter/fhir/extension/TypeFactory.java b/fhir/src/test/java/org/dhis2/fhir/adapter/fhir/extension/TypeFactory.java new file mode 100644 index 00000000..32329ee4 --- /dev/null +++ b/fhir/src/test/java/org/dhis2/fhir/adapter/fhir/extension/TypeFactory.java @@ -0,0 +1,48 @@ +package org.dhis2.fhir.adapter.fhir.extension; + +/* + * Copyright (c) 2004-2019, University of Oslo + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * Neither the name of the HISP project nor the names of its contributors may + * be used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +import ca.uhn.fhir.model.api.IElement; +import ca.uhn.fhir.model.primitive.StringDt; +import org.junit.Assert; + +/** + * Test type factory. + * + * @author volsch + */ +class TypeFactory +{ + public static IElement createType( String name ) + { + Assert.assertEquals( "string", name ); + + return new StringDt(); + } +} diff --git a/fhir/src/test/java/org/dhis2/fhir/adapter/fhir/extension/ValueTypeExtensionUtilsTest.java b/fhir/src/test/java/org/dhis2/fhir/adapter/fhir/extension/ValueTypeExtensionUtilsTest.java new file mode 100644 index 00000000..6593491a --- /dev/null +++ b/fhir/src/test/java/org/dhis2/fhir/adapter/fhir/extension/ValueTypeExtensionUtilsTest.java @@ -0,0 +1,53 @@ +package org.dhis2.fhir.adapter.fhir.extension; + +/* + * Copyright (c) 2004-2019, University of Oslo + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * Neither the name of the HISP project nor the names of its contributors may + * be used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +import org.dhis2.fhir.adapter.model.ValueType; +import org.junit.Assert; +import org.junit.Test; + +/** + * Unit tests for {@link ValueTypeExtensionUtils}. + * + * @author volsch + */ +public class ValueTypeExtensionUtilsTest +{ + @Test + public void setValue() + { + TestPlanDefinition planDefinition = new TestPlanDefinition(); + + ValueTypeExtensionUtils.setValue( planDefinition, ValueType.INTEGER_POSITIVE, TypeFactory::createType ); + + Assert.assertEquals( 1, planDefinition.getExtension().size() ); + Assert.assertEquals( ValueTypeExtensionUtils.URL, planDefinition.getExtension().get( 0 ).getUrl() ); + Assert.assertEquals( "INTEGER_POSITIVE", planDefinition.getExtension().get( 0 ).getValue().toString() ); + } +} \ No newline at end of file