diff --git a/app/src/test/java/org/dhis2/fhir/adapter/AbstractAppTest.java b/app/src/test/java/org/dhis2/fhir/adapter/AbstractAppTest.java index fbfc3de4..8167925e 100644 --- a/app/src/test/java/org/dhis2/fhir/adapter/AbstractAppTest.java +++ b/app/src/test/java/org/dhis2/fhir/adapter/AbstractAppTest.java @@ -41,8 +41,14 @@ import org.apache.activemq.artemis.jms.client.ActiveMQSession; import org.apache.commons.io.IOUtils; import org.apache.commons.lang3.StringUtils; +import org.dhis2.fhir.adapter.dhis.model.DhisResourceId; +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.repository.FhirClientRepository; +import org.dhis2.fhir.adapter.fhir.metadata.repository.RuleRepository; import org.dhis2.fhir.adapter.fhir.model.FhirVersion; +import org.dhis2.fhir.adapter.fhir.security.AdapterSystemAuthenticationToken; +import org.hl7.fhir.instance.model.api.IIdType; import org.junit.Assert; import org.junit.Before; import org.junit.runner.RunWith; @@ -59,6 +65,8 @@ import org.springframework.http.MediaType; import org.springframework.jms.UncategorizedJmsException; import org.springframework.jms.core.JmsTemplate; +import org.springframework.security.core.Authentication; +import org.springframework.security.core.context.SecurityContextHolder; import org.springframework.security.test.context.TestSecurityContextHolder; import org.springframework.test.context.TestPropertySource; import org.springframework.test.web.client.MockRestServiceServer; @@ -74,6 +82,7 @@ import javax.persistence.EntityManager; import java.nio.charset.StandardCharsets; import java.util.Objects; +import java.util.UUID; import static com.github.tomakehurst.wiremock.client.WireMock.*; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; @@ -173,6 +182,15 @@ public abstract class AbstractAppTest @Autowired protected PlatformTransactionManager transactionManager; + @Autowired + protected FhirDhisAssignmentRepository assignmentRepository; + + @Autowired + protected RuleRepository ruleRepository; + + @Autowired + protected FhirClientRepository fhirClientRepository; + private long resourceDlQueueCount; @Nonnull @@ -250,9 +268,33 @@ protected HttpHeaders createDefaultHeaders() { final HttpHeaders headers = new HttpHeaders(); headers.setDate( System.currentTimeMillis() ); + return headers; } + protected void createAssignment( @Nonnull UUID ruleId, @Nonnull UUID fhirClientId, @Nonnull DhisResourceId dhisResourceId, @Nonnull IIdType fhirResourceId ) + { + final Authentication authentication = SecurityContextHolder.getContext().getAuthentication(); + SecurityContextHolder.getContext().setAuthentication( new AdapterSystemAuthenticationToken() ); + try + { + assignmentRepository.saveFhirResourceId( + ruleRepository.findById( ruleId ).orElseGet( () -> { + Assert.fail( "Rule could not be found: " + ruleId ); + return null; + } ), + fhirClientRepository.findById( fhirClientId ).orElseGet( () -> { + Assert.fail( "FHIR client could not be found: " + fhirClientId ); + return null; + } ), + dhisResourceId, fhirResourceId ); + } + finally + { + SecurityContextHolder.getContext().setAuthentication( authentication ); + } + } + protected void notifyResource( @Nonnull FhirResourceType resourceType, @Nullable String resourceSearchResponse, @Nullable String resourceId, @Nullable String resourceResponse, boolean payload ) throws Exception { @@ -261,6 +303,7 @@ protected void notifyResource( @Nonnull FhirResourceType resourceType, @Nullable fhirMockServer.removeStub( previousResourceSearchStubMapping ); previousResourceSearchStubMapping = null; } + if ( StringUtils.isNotBlank( resourceSearchResponse ) ) { final UrlPattern urlPattern = urlMatching( getBaseFhirContext() + @@ -271,6 +314,7 @@ protected void notifyResource( @Nonnull FhirResourceType resourceType, @Nullable .withHeader( "Content-Type", "application/fhir+json" ) .withBody( resourceSearchResponse ) ) ); } + if ( StringUtils.isNotBlank( resourceId ) && StringUtils.isNotBlank( resourceResponse ) ) { fhirMockServer.stubFor( WireMock.get( urlEqualTo( getBaseFhirContext() + @@ -306,16 +350,19 @@ protected void waitForEmptyResourceQueue() throws Exception { messageCount = getQueueMessageCount( fhirRestHookRequestQueueJmsTemplate, null ) + getQueueMessageCount( fhirResourceQueueJmsTemplate, null ); + if ( messageCount > 0 ) { if ( (System.currentTimeMillis() - begin) > MAX_COMPLETED_POLL_TIME ) { Assert.fail( "Waited more than " + MAX_COMPLETED_POLL_TIME + " ms, but there are still " + messageCount + " messages in the queues." ); } + Thread.sleep( 100L ); } } while ( messageCount > 0 ); + Assert.assertEquals( "Resource dead letter queue contains messages.", resourceDlQueueCount, getQueueMessageCount( fhirResourceQueueJmsTemplate, RESOURCE_DL_QUEUE_NAME ) ); } 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 1cfadd70..0f4eeed1 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 @@ -51,13 +51,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,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" ) ) + "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/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," + @@ -73,10 +69,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,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" ) ) + "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/AbstractQuestionnaireResponseToProgramStageTransformationAppTest.java b/app/src/test/java/org/dhis2/fhir/adapter/fhir/transform/fhir/impl/programstage/AbstractQuestionnaireResponseToProgramStageTransformationAppTest.java index 36f6e54f..ce14bdfa 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 @@ -28,15 +28,20 @@ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ +import ca.uhn.fhir.model.primitive.IdDt; import com.github.tomakehurst.wiremock.client.WireMock; import org.apache.commons.io.IOUtils; +import org.dhis2.fhir.adapter.dhis.model.DhisResourceId; +import org.dhis2.fhir.adapter.dhis.model.DhisResourceType; import org.dhis2.fhir.adapter.fhir.metadata.model.FhirResourceType; +import org.dhis2.fhir.adapter.fhir.model.FhirVersion; import org.junit.Test; import org.springframework.http.HttpMethod; import org.springframework.http.MediaType; import org.springframework.test.web.client.ExpectedCount; import java.nio.charset.StandardCharsets; +import java.util.UUID; import static com.github.tomakehurst.wiremock.client.WireMock.aResponse; import static com.github.tomakehurst.wiremock.client.WireMock.urlPathEqualTo; @@ -52,8 +57,11 @@ public abstract class AbstractQuestionnaireResponseToProgramStageTransformationA extends AbstractProgramStageTransformationAppTest { @Test - public void createEnrollment() throws Exception + public void createQuestionnaireResponse() throws Exception { + createAssignment( UUID.fromString( "c4e17e7d-880e-45b5-9bc5-568da8c79742" ), testConfiguration.getFhirClientId( FhirVersion.R4 ), + new DhisResourceId( DhisResourceType.ENROLLMENT, "ieR4nl4muff" ), new IdDt( "CarePlan", "90" ) ); + expectMetadataRequests(); fhirMockServer.stubFor( WireMock.get( urlPathEqualTo( getBaseFhirContext() + "/Patient/15" ) ).willReturn( aResponse() .withHeader( "Content-Type", "application/fhir+json" ) @@ -62,27 +70,30 @@ public void createEnrollment() throws Exception .withHeader( "Content-Type", "application/fhir+json" ) .withBody( IOUtils.resourceToString( "/org/dhis2/fhir/adapter/fhir/test/" + getResourceDir() + "/get-organization-20.json", StandardCharsets.UTF_8 ) ) ) ); + systemDhis2Server.expect( ExpectedCount.once(), method( HttpMethod.GET ) ).andExpect( header( "Authorization", testConfiguration.getDhis2SystemAuthorization() ) ) + .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 ) ); + 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," + + "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 + "/organisationUnits.json?paging=false&fields=lastUpdated,id,code,name,shortName,displayName,level,openingDate,closedDate,coordinates,leaf,parent%5Bid%5D&filter=code:eq:OU_1357" ) ) - .andRespond( withSuccess( IOUtils.resourceToString( "/org/dhis2/fhir/adapter/dhis/test/default-org-unit-OU_1357.json", StandardCharsets.UTF_8 ), MediaType.APPLICATION_JSON ) ); + .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( dhis2BaseUrl + "/api/" + dhis2ApiVersion + "/trackedEntityInstances.json?trackedEntityType=MCPQUTHX1Ze&ouMode=ACCESSIBLE&filter=jD1NGmSntCt:EQ:PT_88589&pageSize=2&fields=" + "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-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 ) ); - - 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( dhis2BaseUrl + "/api/" + dhis2ApiVersion + "/enrollments.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", 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 ) ) ) - .andRespond( withSuccess( IOUtils.resourceToString( "/org/dhis2/fhir/adapter/dhis/test/default-enrollment-91-create-response.json", StandardCharsets.UTF_8 ), MediaType.APPLICATION_JSON ) + .andExpect( content().json( IOUtils.resourceToString( "/org/dhis2/fhir/adapter/dhis/test/default-event-91-create.json", StandardCharsets.UTF_8 ) ) ) + .andRespond( withSuccess( IOUtils.resourceToString( "/org/dhis2/fhir/adapter/dhis/test/default-event-91-create-response.json", StandardCharsets.UTF_8 ), MediaType.APPLICATION_JSON ) .headers( createDefaultHeaders() ) ); notifyResource( FhirResourceType.QUESTIONNAIRE_RESPONSE, null, diff --git a/app/src/test/resources/data.sql b/app/src/test/resources/data.sql index 855c3e92..f3a14822 100644 --- a/app/src/test/resources/data.sql +++ b/app/src/test/resources/data.sql @@ -2682,11 +2682,6 @@ imp_enrollment_org_lookup_script_id, imp_enrollment_date_lookup_script_id) VALUES('417d0db376bc48bebc4223e7a7e663b0', 0, 'QUESTIONNAIRE_RESPONSE', 'PATIENT', '762b4137a98b4b10a0f5629d93e23461', '25a97bb47b394ed48677db4bcaa28ccf', '521b3a008ecc487ba7e3fe12b68e388a', '385e52d28674403db42586b5d4e9faf0', '25a97bb47b394ed48677db4bcaa28ccf', '521b3a008ecc487ba7e3fe12b68e388a'); -INSERT INTO fhir_rule (id, version, name, description, enabled, evaluation_order, fhir_resource_type, dhis_resource_type, transform_imp_script_id, exp_enabled, fhir_create_enabled, fhir_update_enabled, fhir_delete_enabled) -VALUES ('e113ec6ec61048aa9df2cf7739f40985', 0, 'Child Programme Birth Stage Questionnaire Response', NULL, TRUE, 0, 'QUESTIONNAIRE_RESPONSE', 'PROGRAM_STAGE_EVENT', '2d5ec1318fbc44d395c52544f7ff284f', TRUE, TRUE, TRUE, TRUE); -INSERT INTO fhir_program_stage_rule (id, program_stage_id, enrollment_creation_enabled,event_creation_enabled) -VALUES ('e113ec6ec61048aa9df2cf7739f40985','4c074c85be494b9d89739e16b9615dad', TRUE, TRUE); - -- Tracker Program Child Programme, Birth: OPV Dose INSERT INTO fhir_rule_dhis_data_ref(id, version, rule_id, data_ref, script_arg_name, required) SELECT 'e66efbcb7142445a8e1ad1ef524977c1', 0, id, 'CODE:DE_2006104', 'dataElement', true diff --git a/app/src/test/resources/org/dhis2/fhir/adapter/dhis/test/default-event-91-create-response.json b/app/src/test/resources/org/dhis2/fhir/adapter/dhis/test/default-event-91-create-response.json new file mode 100644 index 00000000..fb4ea45f --- /dev/null +++ b/app/src/test/resources/org/dhis2/fhir/adapter/dhis/test/default-event-91-create-response.json @@ -0,0 +1,11 @@ +{ + "status": "OK", + "response": { + "importSummaries": [ + { + "status": "SUCCESS", + "reference": "deR4kl4mnf7" + } + ] + } +} diff --git a/app/src/test/resources/org/dhis2/fhir/adapter/dhis/test/default-event-91-create.json b/app/src/test/resources/org/dhis2/fhir/adapter/dhis/test/default-event-91-create.json new file mode 100644 index 00000000..4b4cec56 --- /dev/null +++ b/app/src/test/resources/org/dhis2/fhir/adapter/dhis/test/default-event-91-create.json @@ -0,0 +1,22 @@ +{ + "status": "ACTIVE", + "eventDate": "2018-11-10T00:00:00+01:00", + "dueDate": "2018-11-12T00:00:00+01:00", + "coordinate": null, + "dataValues": [ + { + "value": "3666", + "providedElsewhere": false, + "dataElement": "BnplxU2jGvX" + }, + { + "value": "1", + "providedElsewhere": false, + "dataElement": "ft7iD5ZzPxJ" + } + ], + "orgUnit": "ldXIdLNUNEn", + "program": "EPDyQuoRnXk", + "trackedEntityInstance": "JeR2Ul4mZfx", + "programStage": "MsWxkiY6tMS" +} \ No newline at end of file diff --git a/app/src/test/resources/org/dhis2/fhir/adapter/fhir/test/r4/get-questionnaire-response-91.json b/app/src/test/resources/org/dhis2/fhir/adapter/fhir/test/r4/get-questionnaire-response-91.json index 85ed3b85..dae88066 100644 --- a/app/src/test/resources/org/dhis2/fhir/adapter/fhir/test/r4/get-questionnaire-response-91.json +++ b/app/src/test/resources/org/dhis2/fhir/adapter/fhir/test/r4/get-questionnaire-response-91.json @@ -5,15 +5,15 @@ "versionId": "6", "lastUpdated": "2018-11-19T21:31:14.644+01:00" }, - "questionnaire": "MsWxkiY6tMS", + "questionnaire": "Questionnaire/MsWxkiY6tMS", + "partOf": { + "reference": "CarePlan/90" + }, "status": "in-progress", "subject": { "reference": "Patient/15" }, "authored": "2018-11-10", - "author": { - "reference": "Organization/20" - }, "item": [ { "linkId": "BnplxU2jGvX", @@ -31,6 +31,25 @@ } ] } + ], + "extension": [ + { + "url": "http://www.dhis2.org/dhis2-fhir-adapter/fhir/extensions/location", + "valueReference": { + "identifier": { + "system": "http://www.dhis2.org/dhis2-fhir-adapter/systems/DHIS2-FHIR-Identifier", + "value": "ldXIdLNUNEn" + } + } + }, + { + "url": "http://www.dhis2.org/dhis2-fhir-adapter/fhir/extensions/due-date", + "valueDate": "2018-11-12" + }, + { + "url": "http://www.dhis2.org/dhis2-fhir-adapter/fhir/extensions/event-status", + "valueString": "ACTIVE" + } ] } 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 24c14251..5eb75de0 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 @@ -205,7 +205,7 @@ private boolean saveEvent( @Nonnull Event event ) { boolean updated = false; - if ( (event.getTrackedEntityInstance() != null) && event.getTrackedEntityInstance().isModified() ) + if ( event.getTrackedEntityInstance() != null && event.getTrackedEntityInstance().isModified() ) { logger.debug( "Persisting tracked entity instance." ); trackedEntityService.createOrUpdate( event.getTrackedEntityInstance() ); @@ -213,18 +213,21 @@ private boolean saveEvent( @Nonnull Event event ) updated = true; } - if ( event.getEnrollment().isNewResource() ) + final Enrollment enrollment = event.getEnrollment(); + + if ( enrollment != null && enrollment.isNewResource() ) { logger.info( "Creating new enrollment." ); - event.getEnrollment().setEvents( Collections.singletonList( event ) ); - enrollmentService.createOrUpdate( event.getEnrollment() ); - logger.info( "Created new enrollment {} with new event {}.", event.getEnrollment().getId(), event.getId() ); + enrollment.setEvents( Collections.singletonList( event ) ); + enrollmentService.createOrUpdate( enrollment ); + logger.info( "Created new enrollment {} with new event {}.", enrollment.getId(), event.getId() ); updated = true; } else { - final List events = event.getEnrollment().getEvents(); - if ( event.getEnrollment().isModified() ) + final List events = enrollment == null ? Collections.singletonList( event ) : enrollment.getEvents(); + + if ( enrollment != null && enrollment.isModified() ) { logger.info( "Updating existing enrollment." ); event.setEnrollment( enrollmentService.createOrUpdate( event.getEnrollment() ) ); @@ -305,14 +308,9 @@ else if ( dataValueSet.isModified() ) private boolean validateDataValueSet( @Nonnull DataValueSet dataValueSet ) { - if ( dataValueSet.getDataSetId() != null && !dataValueSet.getDataSetId().isEmpty() && + return dataValueSet.getDataSetId() != null && !dataValueSet.getDataSetId().isEmpty() && dataValueSet.getOrgUnitId() != null && !dataValueSet.getOrgUnitId().isEmpty() && dataValueSet.getPeriod() != null && !dataValueSet.getPeriod().isEmpty() && - dataValueSet.getDataValues() != null && !dataValueSet.getDataValues().isEmpty() ) - { - return true; - } - - return false; + dataValueSet.getDataValues() != null && !dataValueSet.getDataValues().isEmpty(); } } diff --git a/dhis/src/main/java/org/dhis2/fhir/adapter/dhis/tracker/program/Event.java b/dhis/src/main/java/org/dhis2/fhir/adapter/dhis/tracker/program/Event.java index 0267fc9a..098c4746 100644 --- a/dhis/src/main/java/org/dhis2/fhir/adapter/dhis/tracker/program/Event.java +++ b/dhis/src/main/java/org/dhis2/fhir/adapter/dhis/tracker/program/Event.java @@ -335,9 +335,9 @@ public boolean isModified() return modified; } - public void setModified( boolean modified ) + public void setModified() { - this.modified = modified; + this.modified = true; } @Nonnull @@ -347,13 +347,16 @@ public WritableDataValue getDataValue( @Nonnull String dataElementId ) { dataValues = new ArrayList<>(); } + WritableDataValue dataValue = dataValues.stream().filter( dv -> Objects.equals( dataElementId, dv.getDataElementId() ) ).findFirst().orElse( null ); + if ( dataValue == null ) { dataValue = new WritableDataValue( dataElementId, true ); dataValues.add( dataValue ); } + return dataValue; } 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 ab114dd4..411597e4 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 @@ -308,7 +308,7 @@ public void update() throws IOException .andExpect( method( HttpMethod.PUT ) ).andRespond( withSuccess( IOUtils.resourceToByteArray( "/org/dhis2/fhir/adapter/dhis/tracker/program/impl/updateEvent-response.json" ), MediaType.APPLICATION_JSON ) ); final Event event = new Event(); - event.setModified( true ); + event.setModified(); event.setId( "jShdkweusi2" ); event.setEnrollmentId( "Jskdsjeua1s" ); event.setEventDate( ZonedDateTime.of( 2018, 10, 5, 22, 12, 34, 998000000, ZoneId.systemDefault() ) ); diff --git a/fhir-r4/src/main/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/program/r4/R4ProgramStageToFhirQuestionnaireResponseTransformer.java b/fhir-r4/src/main/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/program/r4/R4ProgramStageToFhirQuestionnaireResponseTransformer.java index 468895dc..45754b7c 100644 --- a/fhir-r4/src/main/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/program/r4/R4ProgramStageToFhirQuestionnaireResponseTransformer.java +++ b/fhir-r4/src/main/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/program/r4/R4ProgramStageToFhirQuestionnaireResponseTransformer.java @@ -61,6 +61,7 @@ import org.hl7.fhir.r4.model.CanonicalType; import org.hl7.fhir.r4.model.QuestionnaireResponse; import org.hl7.fhir.r4.model.QuestionnaireResponse.QuestionnaireResponseItemComponent; +import org.hl7.fhir.r4.model.QuestionnaireResponse.QuestionnaireResponseStatus; import org.hl7.fhir.r4.model.Reference; import org.hl7.fhir.r4.model.ResourceFactory; import org.hl7.fhir.r4.model.Type; @@ -122,11 +123,11 @@ protected boolean transformInternal( @Nonnull FhirClient fhirClient, @Nonnull Dh if ( event.getStatus() == EventStatus.COMPLETED ) { - fhirQuestionnaireResponse.setStatus( QuestionnaireResponse.QuestionnaireResponseStatus.COMPLETED ); + fhirQuestionnaireResponse.setStatus( QuestionnaireResponseStatus.COMPLETED ); } else { - fhirQuestionnaireResponse.setStatus( QuestionnaireResponse.QuestionnaireResponseStatus.INPROGRESS ); + fhirQuestionnaireResponse.setStatus( QuestionnaireResponseStatus.INPROGRESS ); } final AbstractValueTypeDhisToFhirTransformerUtils valueTypeUtils = diff --git a/fhir-r4/src/main/java/org/dhis2/fhir/adapter/fhir/transform/fhir/impl/enrollment/r4/R4FhirCarePlanToEnrollmentTransformer.java b/fhir-r4/src/main/java/org/dhis2/fhir/adapter/fhir/transform/fhir/impl/enrollment/r4/R4FhirCarePlanToEnrollmentTransformer.java index c366397a..96e656fe 100644 --- a/fhir-r4/src/main/java/org/dhis2/fhir/adapter/fhir/transform/fhir/impl/enrollment/r4/R4FhirCarePlanToEnrollmentTransformer.java +++ b/fhir-r4/src/main/java/org/dhis2/fhir/adapter/fhir/transform/fhir/impl/enrollment/r4/R4FhirCarePlanToEnrollmentTransformer.java @@ -30,7 +30,6 @@ import org.dhis2.fhir.adapter.dhis.converter.ValueConverter; 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.program.Enrollment; import org.dhis2.fhir.adapter.dhis.tracker.program.EnrollmentService; @@ -54,7 +53,7 @@ import org.dhis2.fhir.adapter.fhir.transform.TransformerException; import org.dhis2.fhir.adapter.fhir.transform.TransformerMappingException; import org.dhis2.fhir.adapter.fhir.transform.fhir.FhirToDhisTransformerContext; -import org.dhis2.fhir.adapter.fhir.transform.fhir.impl.program.AbstractFhirCarePlanToEnrollmentTransformer; +import org.dhis2.fhir.adapter.fhir.transform.fhir.impl.enrollment.AbstractFhirCarePlanToEnrollmentTransformer; import org.dhis2.fhir.adapter.fhir.transform.scripted.ScriptedTrackedEntityInstance; import org.dhis2.fhir.adapter.fhir.transform.scripted.WritableScriptedEnrollment; import org.dhis2.fhir.adapter.fhir.util.FhirUriUtils; @@ -105,8 +104,8 @@ protected void transformInternal( @Nonnull FhirToDhisTransformerContext context, enrollment.setProgramId( program.getId() ); enrollment.setStatus( convertStatus( fhirCarePlan.getStatus() ) ); - enrollment.setOrgUnitId( getOrgUnit( context, ruleInfo, fhirResourceMapping.getImpEnrollmentOrgLookupScript(), scriptVariables ) - .map( OrganizationUnit::getId ).orElseThrow( () -> new TransformerMappingException( "Care plan contains location that cannot be mapped." ) ) ); + enrollment.setOrgUnitId( getOrgUnitId( context, ruleInfo, fhirResourceMapping.getImpEnrollmentOrgLookupScript(), scriptVariables ) + .orElseThrow( () -> new TransformerMappingException( "Care plan contains location that cannot be mapped." ) ) ); enrollment.setTrackedEntityInstanceId( scriptedTrackedEntityInstance.getId() ); if ( fhirCarePlan.getPeriod().hasStart() ) @@ -151,7 +150,7 @@ protected EnrollmentStatus convertStatus( @Nullable CarePlan.CarePlanStatus care protected Reference getProgramRef( @Nonnull FhirToDhisTransformerContext context, @Nonnull RuleInfo ruleInfo, @Nonnull Map scriptVariables, @Nonnull IBaseResource carePlan ) { final CarePlan fhirCarePlan = (CarePlan) carePlan; - final String uri; + String uri = null; if ( !fhirCarePlan.getInstantiatesUri().isEmpty() ) { @@ -161,7 +160,8 @@ else if ( !fhirCarePlan.getInstantiatesCanonical().isEmpty() ) { uri = fhirCarePlan.getInstantiatesCanonical().get( 0 ).getValueAsString(); } - else + + if ( uri == null ) { throw new TransformerDataException( "No reference to a plan definition that is instantiated by this care plan has been given." ); } diff --git a/fhir-r4/src/main/java/org/dhis2/fhir/adapter/fhir/transform/fhir/impl/program/r4/R4FhirQuestionnaireResponseToProgramStageTransformer.java b/fhir-r4/src/main/java/org/dhis2/fhir/adapter/fhir/transform/fhir/impl/program/r4/R4FhirQuestionnaireResponseToProgramStageTransformer.java new file mode 100644 index 00000000..eab382ab --- /dev/null +++ b/fhir-r4/src/main/java/org/dhis2/fhir/adapter/fhir/transform/fhir/impl/program/r4/R4FhirQuestionnaireResponseToProgramStageTransformer.java @@ -0,0 +1,214 @@ +package org.dhis2.fhir.adapter.fhir.transform.fhir.impl.program.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.converter.ValueConverter; +import org.dhis2.fhir.adapter.dhis.model.Reference; +import org.dhis2.fhir.adapter.dhis.model.WritableDataValue; +import org.dhis2.fhir.adapter.dhis.orgunit.OrganizationUnitService; +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.EventStatus; +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.program.ProgramStage; +import org.dhis2.fhir.adapter.dhis.tracker.program.ProgramStageDataElement; +import org.dhis2.fhir.adapter.dhis.tracker.program.ProgramStageMetadataService; +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.extension.DueDateExtensionUtils; +import org.dhis2.fhir.adapter.fhir.extension.EventStatusExtensionUtils; +import org.dhis2.fhir.adapter.fhir.metadata.model.FhirResourceMapping; +import org.dhis2.fhir.adapter.fhir.metadata.model.FhirResourceType; +import org.dhis2.fhir.adapter.fhir.metadata.model.ProgramStageRule; +import org.dhis2.fhir.adapter.fhir.metadata.model.RuleInfo; +import org.dhis2.fhir.adapter.fhir.metadata.model.ScriptVariable; +import org.dhis2.fhir.adapter.fhir.metadata.repository.FhirResourceMappingRepository; +import org.dhis2.fhir.adapter.fhir.metadata.repository.TrackedEntityRuleRepository; +import org.dhis2.fhir.adapter.fhir.model.FhirVersion; +import org.dhis2.fhir.adapter.fhir.script.ScriptExecutionContext; +import org.dhis2.fhir.adapter.fhir.script.ScriptExecutor; +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.FhirToDhisTransformerContext; +import org.dhis2.fhir.adapter.fhir.transform.fhir.impl.program.AbstractFhirQuestionnaireResponseToProgramStageTransformer; +import org.dhis2.fhir.adapter.fhir.transform.fhir.impl.util.AbstractValueTypeFhirToDhisTransformerUtils; +import org.dhis2.fhir.adapter.fhir.transform.scripted.ScriptedTrackedEntityInstance; +import org.dhis2.fhir.adapter.fhir.transform.scripted.WritableScriptedEvent; +import org.dhis2.fhir.adapter.fhir.transform.util.TransformerUtils; +import org.dhis2.fhir.adapter.fhir.util.FhirUriUtils; +import org.hl7.fhir.instance.model.api.IBaseResource; +import org.hl7.fhir.instance.model.api.IIdType; +import org.hl7.fhir.r4.model.QuestionnaireResponse; +import org.hl7.fhir.r4.model.QuestionnaireResponse.QuestionnaireResponseStatus; +import org.springframework.stereotype.Component; + +import javax.annotation.Nonnull; +import java.time.ZoneId; +import java.time.ZonedDateTime; +import java.util.Date; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; + +/** + * R4 specific implementation of {@link AbstractFhirQuestionnaireResponseToProgramStageTransformer}. + * + * @author volsch + */ +@Component +public class R4FhirQuestionnaireResponseToProgramStageTransformer extends AbstractFhirQuestionnaireResponseToProgramStageTransformer +{ + private final ZoneId zoneId = ZoneId.systemDefault(); + + public R4FhirQuestionnaireResponseToProgramStageTransformer( @Nonnull ScriptExecutor scriptExecutor, @Nonnull TrackedEntityMetadataService trackedEntityMetadataService, @Nonnull TrackedEntityService trackedEntityService, + @Nonnull TrackedEntityRuleRepository trackedEntityRuleRepository, @Nonnull OrganizationUnitService organizationUnitService, @Nonnull ProgramMetadataService programMetadataService, @Nonnull ProgramStageMetadataService programStageMetadataService, + @Nonnull EventService eventService, @Nonnull FhirResourceMappingRepository resourceMappingRepository, @Nonnull FhirDhisAssignmentRepository fhirDhisAssignmentRepository, @Nonnull ScriptExecutionContext scriptExecutionContext, + @Nonnull ValueConverter valueConverter ) + { + super( scriptExecutor, trackedEntityMetadataService, trackedEntityService, trackedEntityRuleRepository, organizationUnitService, programMetadataService, programStageMetadataService, eventService, resourceMappingRepository, + fhirDhisAssignmentRepository, scriptExecutionContext, valueConverter ); + } + + @Nonnull + @Override + public Set getFhirVersions() + { + return FhirVersion.R4_ONLY; + } + + @Override + protected void transformInternal( @Nonnull FhirToDhisTransformerContext context, @Nonnull RuleInfo ruleInfo, @Nonnull Map scriptVariables, @Nonnull FhirResourceMapping fhirResourceMapping, @Nonnull Program program, + @Nonnull ProgramStage programStage, @Nonnull IBaseResource questionnaireResponse, @Nonnull ScriptedTrackedEntityInstance scriptedTrackedEntityInstance, @Nonnull WritableScriptedEvent scriptedEvent ) throws TransformerException + { + final QuestionnaireResponse fhirQuestionnaireResponse = (QuestionnaireResponse) questionnaireResponse; + final Event event = (Event) scriptedEvent.getDhisResource(); + final String enrollmentId = getAssignedDhisId( context, ruleInfo, scriptVariables, fhirQuestionnaireResponse.getPartOfFirstRep().getReferenceElement() ); + + if ( enrollmentId == null ) + { + throw new TransformerDataException( "Enrollment could not be found: " + + fhirQuestionnaireResponse.getPartOfFirstRep().getReferenceElement().getValue() ); + } + + event.setEnrollmentId( enrollmentId ); + event.setProgramId( program.getId() ); + event.setProgramStageId( programStage.getId() ); + event.setStatus( convertStatus( fhirQuestionnaireResponse ) ); + event.setOrgUnitId( getOrgUnitId( context, ruleInfo, fhirResourceMapping.getImpEnrollmentOrgLookupScript(), scriptVariables ) + .orElseThrow( () -> new TransformerMappingException( "Care plan contains location that cannot be mapped." ) ) ); + event.setTrackedEntityInstanceId( scriptedTrackedEntityInstance.getId() ); + event.setStatus( convertStatus( fhirQuestionnaireResponse ) ); + + final Date eventDate = fhirQuestionnaireResponse.hasAuthored() ? fhirQuestionnaireResponse.getAuthored() : null; + event.setEventDate( eventDate == null ? null : ZonedDateTime.ofInstant( eventDate.toInstant(), zoneId ) ); + final Date dueDate = DueDateExtensionUtils.getValue( fhirQuestionnaireResponse ); + event.setDueDate( dueDate == null ? null : ZonedDateTime.ofInstant( dueDate.toInstant(), zoneId ) ); + + final AbstractValueTypeFhirToDhisTransformerUtils valueTypeUtils = + TransformerUtils.getScriptVariable( scriptVariables, ScriptVariable.VALUE_TYPE_UTILS, AbstractValueTypeFhirToDhisTransformerUtils.class ); + final Set dataElementIds = new HashSet<>(); + + fhirQuestionnaireResponse.getItem().forEach( item -> { + final ProgramStageDataElement dataElement = programStage.getOptionalDataElement( Reference.createIdReference( item.getLinkId() ) ) + .orElseThrow( () -> new TransformerDataException( "Data element ID " + item.getLinkId() + " has not been defined for program stage ID " + programStage.getId() ) ); + + final WritableDataValue dataValue = event.getDataValue( dataElement.getElementId() ); + dataValue.setValue( valueTypeUtils.convert( item.getAnswerFirstRep().getValue(), dataElement.getElement().getValueType() ) ); + dataValue.setModified(); + + dataElementIds.add( item.getLinkId() ); + } ); + + event.getDataValues().removeIf( dataValue -> !dataElementIds.contains( dataValue.getDataElementId() ) ); + event.setModified(); + } + + @Nonnull + protected EventStatus convertStatus( @Nonnull QuestionnaireResponse questionnaireResponse ) + { + try + { + final EventStatus eventStatus = EventStatusExtensionUtils.getValue( questionnaireResponse ); + + if ( eventStatus != null ) + { + return eventStatus; + } + } + catch ( IllegalArgumentException e ) + { + throw new TransformerDataException( e.getMessage() ); + } + + final QuestionnaireResponseStatus questionnaireResponseStatus = questionnaireResponse.getStatus(); + + if ( questionnaireResponseStatus == null ) + { + throw new TransformerDataException( "No questionnaire response status has been given." ); + } + + switch ( questionnaireResponseStatus ) + { + case COMPLETED: + return EventStatus.COMPLETED; + case INPROGRESS: + return EventStatus.ACTIVE; + default: + throw new TransformerDataException( "Questionnaire response status is not allowed: " + questionnaireResponseStatus ); + } + } + + @Nonnull + @Override + protected Reference getProgramStageRef( @Nonnull FhirToDhisTransformerContext context, @Nonnull RuleInfo ruleInfo, @Nonnull Map scriptVariables, @Nonnull IBaseResource questionnaireResponse ) + { + final QuestionnaireResponse fhirQuestionnaireResponse = (QuestionnaireResponse) questionnaireResponse; + final String uri = fhirQuestionnaireResponse.getQuestionnaire(); + + if ( uri == null ) + { + throw new TransformerDataException( "No reference to a questionnaire that is instantiated by this questionnaire response has been given." ); + } + + final IIdType id; + try + { + id = FhirUriUtils.createIdFromUri( uri, FhirResourceType.QUESTIONNAIRE ); + } + catch ( IllegalArgumentException e ) + { + throw new TransformerDataException( e.getMessage(), e ); + } + + return Reference.createIdReference( id.getIdPart() ); + } +} diff --git a/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/data/model/FhirDhisAssignment.java b/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/data/model/FhirDhisAssignment.java index 1ac136df..230974b4 100644 --- a/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/data/model/FhirDhisAssignment.java +++ b/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/data/model/FhirDhisAssignment.java @@ -54,19 +54,25 @@ @Entity @Table( name = "fhir_dhis_assignment" ) @NamedQueries( { - @NamedQuery( name = FhirDhisAssignment.FIND_FIRST_ID_BY_FHIR_NAMED_QUERY, query = "SELECT a.dhisResourceId FROM FhirDhisAssignment a WHERE a.rule.id=:ruleId AND a.fhirClient.id=:subscriptionId AND a.fhirResourceId=:fhirResourceId" ), - @NamedQuery( name = FhirDhisAssignment.FIND_FIRST_BY_FHIR_NAMED_QUERY, query = "SELECT a FROM FhirDhisAssignment a WHERE a.rule.id=:ruleId AND a.fhirClient.id=:subscriptionId AND a.fhirResourceId=:fhirResourceId" ), - @NamedQuery( name = FhirDhisAssignment.FIND_FIRST_ID_BY_DHIS_NAMED_QUERY, query = "SELECT a.fhirResourceId FROM FhirDhisAssignment a WHERE a.rule.id=:ruleId AND a.fhirClient.id=:subscriptionId AND a.dhisResourceId=:dhisResourceId" ), - @NamedQuery( name = FhirDhisAssignment.FIND_FIRST_BY_DHIS_NAMED_QUERY, query = "SELECT a FROM FhirDhisAssignment a WHERE a.rule.id=:ruleId AND a.fhirClient.id=:subscriptionId AND a.dhisResourceId=:dhisResourceId" ), + @NamedQuery( name = FhirDhisAssignment.FIND_FIRST_RULED_ID_BY_FHIR_NAMED_QUERY, query = "SELECT a.dhisResourceId FROM FhirDhisAssignment a WHERE a.rule.id=:ruleId AND a.fhirClient.id=:subscriptionId AND a.fhirResourceId=:fhirResourceId ORDER BY a.id" ), + @NamedQuery( name = FhirDhisAssignment.FIND_FIRST_ID_BY_FHIR_NAMED_QUERY, query = "SELECT a.dhisResourceId FROM FhirDhisAssignment a WHERE a.fhirClient.id=:fhirClientId AND a.fhirResourceId=:fhirResourceId ORDER BY a.id" ), + @NamedQuery( name = FhirDhisAssignment.FIND_FIRST_BY_FHIR_NAMED_QUERY, query = "SELECT a FROM FhirDhisAssignment a WHERE a.rule.id=:ruleId AND a.fhirClient.id=:subscriptionId AND a.fhirResourceId=:fhirResourceId ORDER BY a.id" ), + @NamedQuery( name = FhirDhisAssignment.FIND_FIRST_RULED_ID_BY_DHIS_NAMED_QUERY, query = "SELECT a.fhirResourceId FROM FhirDhisAssignment a WHERE a.rule.id=:ruleId AND a.fhirClient.id=:subscriptionId AND a.dhisResourceId=:dhisResourceId ORDER BY a.id" ), + @NamedQuery( name = FhirDhisAssignment.FIND_FIRST_ID_BY_DHIS_NAMED_QUERY, query = "SELECT a.fhirResourceId FROM FhirDhisAssignment a WHERE a.fhirClient.id=:fhirClientId AND a.dhisResourceId=:dhisResourceId ORDER BY a.id" ), + @NamedQuery( name = FhirDhisAssignment.FIND_FIRST_BY_DHIS_NAMED_QUERY, query = "SELECT a FROM FhirDhisAssignment a WHERE a.rule.id=:ruleId AND a.fhirClient.id=:subscriptionId AND a.dhisResourceId=:dhisResourceId ORDER BY a.id" ) } ) public class FhirDhisAssignment implements Serializable { private static final long serialVersionUID = 5203344475315981090L; + public static final String FIND_FIRST_RULED_ID_BY_FHIR_NAMED_QUERY = "FhirDhisAssignment.findFirstRuledIdByFhir"; + public static final String FIND_FIRST_ID_BY_FHIR_NAMED_QUERY = "FhirDhisAssignment.findFirstIdByFhir"; public static final String FIND_FIRST_BY_FHIR_NAMED_QUERY = "FhirDhisAssignment.findFirstByFhir"; + public static final String FIND_FIRST_RULED_ID_BY_DHIS_NAMED_QUERY = "FhirDhisAssignment.findFirstRuledIdByDhis"; + public static final String FIND_FIRST_ID_BY_DHIS_NAMED_QUERY = "FhirDhisAssignment.findFirstIdByDhis"; public static final String FIND_FIRST_BY_DHIS_NAMED_QUERY = "FhirDhisAssignment.findFirstByDhis"; diff --git a/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/data/repository/CustomFhirDhisAssignmentRepository.java b/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/data/repository/CustomFhirDhisAssignmentRepository.java index 7ced4964..4c495331 100644 --- a/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/data/repository/CustomFhirDhisAssignmentRepository.java +++ b/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/data/repository/CustomFhirDhisAssignmentRepository.java @@ -47,9 +47,15 @@ public interface CustomFhirDhisAssignmentRepository @Nullable String findFirstDhisResourceId( @Nonnull AbstractRule rule, @Nonnull FhirClient fhirClient, @Nonnull IIdType fhirResourceId ); + @Nullable + String findFirstDhisResourceId( @Nonnull FhirClient fhirClient, @Nonnull IIdType fhirResourceId ); + @Nullable String findFirstFhirResourceId( @Nonnull AbstractRule rule, @Nonnull FhirClient fhirClient, @Nonnull DhisResourceId dhisResourceId ); + @Nullable + String findFirstFhirResourceId( @Nonnull FhirClient fhirClient, @Nonnull DhisResourceId dhisResourceId ); + boolean saveDhisResourceId( @Nonnull AbstractRule rule, @Nonnull FhirClient fhirClient, @Nonnull IIdType fhirResourceId, @Nonnull DhisResourceId dhisResourceId ); boolean saveFhirResourceId( @Nonnull AbstractRule rule, @Nonnull FhirClient fhirClient, @Nonnull DhisResourceId dhisResourceId, @Nonnull IIdType fhirResourceId ); diff --git a/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/data/repository/impl/CustomFhirDhisAssignmentRepositoryImpl.java b/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/data/repository/impl/CustomFhirDhisAssignmentRepositoryImpl.java index a6ad792b..3f6cda7f 100644 --- a/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/data/repository/impl/CustomFhirDhisAssignmentRepositoryImpl.java +++ b/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/data/repository/impl/CustomFhirDhisAssignmentRepositoryImpl.java @@ -84,21 +84,37 @@ public String findFirstDhisResourceId( @Nonnull AbstractRule rule, @Nonnull Fhir protected String findFirstDhisResourceId( @Nonnull AbstractRule rule, @Nonnull FhirClient subscription, @Nonnull IIdType fhirResourceId, boolean locked ) { - return entityManager.createNamedQuery( FhirDhisAssignment.FIND_FIRST_ID_BY_FHIR_NAMED_QUERY, String.class ) + return entityManager.createNamedQuery( FhirDhisAssignment.FIND_FIRST_RULED_ID_BY_FHIR_NAMED_QUERY, String.class ) .setLockMode( locked ? LockModeType.PESSIMISTIC_WRITE : LockModeType.NONE ) .setParameter( "ruleId", rule.getId() ).setParameter( "subscriptionId", subscription.getId() ) .setParameter( "fhirResourceId", fhirResourceId.getIdPart() ).getResultList().stream().findFirst().orElse( null ); } + @Nullable + @Override + public String findFirstDhisResourceId( @Nonnull FhirClient fhirClient, @Nonnull IIdType fhirResourceId ) + { + return entityManager.createNamedQuery( FhirDhisAssignment.FIND_FIRST_ID_BY_FHIR_NAMED_QUERY, String.class ) + .setParameter( "fhirClientId", fhirClient.getId() ).setParameter( "fhirResourceId", fhirResourceId.getIdPart() ).getResultList().stream().findFirst().orElse( null ); + } + @Nullable @Override public String findFirstFhirResourceId( @Nonnull AbstractRule rule, @Nonnull FhirClient subscription, @Nonnull DhisResourceId dhisResourceId ) { - return entityManager.createNamedQuery( FhirDhisAssignment.FIND_FIRST_ID_BY_DHIS_NAMED_QUERY, String.class ) + return entityManager.createNamedQuery( FhirDhisAssignment.FIND_FIRST_RULED_ID_BY_DHIS_NAMED_QUERY, String.class ) .setParameter( "ruleId", rule.getId() ).setParameter( "subscriptionId", subscription.getId() ) .setParameter( "dhisResourceId", dhisResourceId.getId() ).getResultList().stream().findFirst().orElse( null ); } + @Nullable + @Override + public String findFirstFhirResourceId( @Nonnull FhirClient fhirClient, @Nonnull DhisResourceId dhisResourceId ) + { + return entityManager.createNamedQuery( FhirDhisAssignment.FIND_FIRST_ID_BY_DHIS_NAMED_QUERY, String.class ) + .setParameter( "fhirClientId", fhirClient.getId() ).setParameter( "dhisResourceId", dhisResourceId.getId() ).getResultList().stream().findFirst().orElse( null ); + } + @Override @Transactional( propagation = Propagation.NOT_SUPPORTED ) public boolean saveDhisResourceId( @Nonnull AbstractRule rule, @Nonnull FhirClient fhirClient, @Nonnull IIdType fhirResourceId, @Nonnull DhisResourceId dhisResourceId ) @@ -180,6 +196,7 @@ private boolean persist( @Nonnull AbstractRule rule, @Nonnull FhirClient subscri assignment.setFhirClient( entityManager.getReference( FhirClient.class, subscription.getId() ) ); assignment.setFhirResourceId( fhirResourceId.getIdPart() ); assignment.setDhisResourceId( dhisResourceId.getId() ); + try { entityManager.persist( assignment ); 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 index 025e9f70..8cc33690 100644 --- 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 @@ -47,6 +47,19 @@ */ abstract class BaseExtensionUtils { + @Nullable + protected static String getStringValue( @Nonnull String url, @Nonnull IBaseHasExtensions resource ) + { + final IBaseDatatype value = getValue( url, resource ); + + if ( value instanceof IPrimitiveType ) + { + return ( (IPrimitiveType) value ).getValueAsString(); + } + + return null; + } + @SuppressWarnings( "unchecked" ) protected static void setStringValue( @Nonnull String url, @Nonnull IBaseHasExtensions resource, @Nullable String value, @Nonnull Function typeFactory ) { diff --git a/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/extension/DueDateExtensionUtils.java b/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/extension/DueDateExtensionUtils.java index 814238f9..88c5e108 100644 --- a/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/extension/DueDateExtensionUtils.java +++ b/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/extension/DueDateExtensionUtils.java @@ -50,6 +50,12 @@ public static void setValue( @Nonnull IBaseHasExtensions resource, @Nullable Dat BaseExtensionUtils.setDateValue( URL, resource, date, typeFactory ); } + @Nullable + public static Date getValue( @Nonnull IBaseHasExtensions resource ) + { + return BaseExtensionUtils.getDateValue( URL, resource ); + } + private DueDateExtensionUtils() { super(); diff --git a/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/extension/EventStatusExtensionUtils.java b/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/extension/EventStatusExtensionUtils.java index eae6b470..cdeaf23a 100644 --- a/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/extension/EventStatusExtensionUtils.java +++ b/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/extension/EventStatusExtensionUtils.java @@ -50,6 +50,26 @@ public static void setValue( @Nonnull IBaseHasExtensions resource, @Nullable Eve BaseExtensionUtils.setStringValue( URL, resource, eventStatus == null ? null : eventStatus.name(), typeFactory ); } + @Nullable + public static EventStatus getValue( @Nonnull IBaseHasExtensions resource ) throws IllegalArgumentException + { + final String stringValue = BaseExtensionUtils.getStringValue( URL, resource ); + + if ( stringValue == null ) + { + return null; + } + + try + { + return EventStatus.valueOf( stringValue ); + } + catch ( IllegalArgumentException e ) + { + throw new IllegalArgumentException( "Invalid event status: " + stringValue ); + } + } + private EventStatusExtensionUtils() { super(); diff --git a/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/metadata/model/EventStatusUpdate.java b/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/metadata/model/EventStatusUpdate.java index 4ef7dbbd..60b1a361 100644 --- a/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/metadata/model/EventStatusUpdate.java +++ b/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/metadata/model/EventStatusUpdate.java @@ -113,7 +113,7 @@ public void update( @Nonnull Event event ) if ( event.getStatus() != resulting ) { event.setStatus( resulting ); - event.setModified( true ); + event.setModified(); } } } diff --git a/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/repository/impl/FhirRepositoryImpl.java b/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/repository/impl/FhirRepositoryImpl.java index 0ba9831b..65090102 100644 --- a/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/repository/impl/FhirRepositoryImpl.java +++ b/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/repository/impl/FhirRepositoryImpl.java @@ -344,8 +344,9 @@ protected FhirRepositoryOperationOutcome saveInternally( @Nonnull FhirClientReso final WritableFhirRequest fhirRequest = new WritableFhirRequest(); fhirRequest.setResourceType( FhirResourceType.getByResource( resource ) ); fhirRequest.setLastUpdated( getLastUpdated( resource ) ); - fhirRequest.setResourceId( (resource.getMeta() == null) ? null : resource.getIdElement().getIdPart() ); - fhirRequest.setResourceVersionId( (resource.getMeta() == null) ? null : resource.getMeta().getVersionId() ); + fhirRequest.setResourceId( resource.getMeta() == null ? null : resource.getIdElement().getIdPart() ); + fhirRequest.setResourceVersionId( resource.getMeta() == null ? null : resource.getMeta().getVersionId() ); + fhirRequest.setFhirClientId( fhirClientResource.getFhirClient().getId() ); fhirRequest.setFhirClientResourceId( fhirClientResource.getId() ); fhirRequest.setVersion( fhirClientResource.getFhirClient().getFhirVersion() ); fhirRequest.setParameters( ArrayListMultimap.create() ); @@ -365,6 +366,7 @@ protected FhirRepositoryOperationOutcome saveInternally( @Nonnull FhirClientReso fhirRequest.setRequestMethod( getRequestMethod( fhirRepositoryOperation ) ); fhirRequest.setDhisFhirId( true ); fhirRequest.setFirstRuleOnly( true ); + if ( fhirRepositoryOperation.getDhisFhirResourceId() != null ) { fhirRequest.setDhisResourceType( fhirRepositoryOperation.getDhisFhirResourceId().getType() ); diff --git a/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/util/AbstractAssignmentDhisToFhirTransformerUtils.java b/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/util/AbstractAssignmentDhisToFhirTransformerUtils.java index 0bbc6bae..a1399235 100644 --- a/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/util/AbstractAssignmentDhisToFhirTransformerUtils.java +++ b/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/util/AbstractAssignmentDhisToFhirTransformerUtils.java @@ -89,7 +89,8 @@ public IBaseReference getMappedFhirId( @Nonnull DhisToFhirTransformerContext con final FhirClient fhirClient = fhirClientRepository.findOneByIdCached( context.getFhirClientId() ) .orElseThrow( () -> new FatalTransformerException( "FHIR Client with ID " + context.getFhirClientId() + " could not be found." ) ); - final String fhirId = assignmentRepository.findFirstFhirResourceId( rule, fhirClient, new DhisResourceId( dhisResourceType, dhisId ) ); + // rule must not be used to determine different assigment + final String fhirId = assignmentRepository.findFirstFhirResourceId( fhirClient, new DhisResourceId( dhisResourceType, dhisId ) ); if ( fhirId == null ) { 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 05813faf..13b8766b 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 @@ -32,6 +32,7 @@ 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.model.ReferenceType; 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; @@ -64,12 +65,14 @@ import org.dhis2.fhir.adapter.fhir.transform.fhir.TrackedEntityInstanceNotFoundException; 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.impl.util.AssignmentFhirToDhisTransformerUtils; 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; import org.hl7.fhir.instance.model.api.IDomainResource; +import org.hl7.fhir.instance.model.api.IIdType; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.ObjectProvider; @@ -286,6 +289,15 @@ protected String getDhisId( @Nonnull FhirToDhisTransformerContext context, @Nonn return context.getFhirRequest().getDhisResourceId(); } + @Nullable + protected String getAssignedDhisId( @Nonnull FhirToDhisTransformerContext context, @Nonnull RuleInfo ruleInfo, @Nonnull Map scriptVariables, @Nullable IIdType fhirId ) + { + final AssignmentFhirToDhisTransformerUtils assignmentTransformerUtils = TransformerUtils.getScriptVariable( + scriptVariables, ScriptVariable.ASSIGNMENT_UTILS, AssignmentFhirToDhisTransformerUtils.class ); + + return assignmentTransformerUtils.getMappedDhisId( context, ruleInfo.getRule(), fhirId ); + } + protected boolean transform( @Nonnull FhirToDhisTransformerContext context, @Nonnull RuleInfo ruleInfo, @Nonnull Map scriptVariables ) throws TransformerException { if ( ruleInfo.getRule().getTransformImpScript() == null ) @@ -300,14 +312,37 @@ protected boolean transform( @Nonnull FhirToDhisTransformerContext context, @Non protected Optional getOrgUnit( @Nonnull FhirToDhisTransformerContext context, @Nonnull RuleInfo ruleInfo, @Nonnull ExecutableScript lookupScript, @Nonnull Map scriptVariables ) { final Reference orgUnitReference = executeScript( context, ruleInfo, lookupScript, scriptVariables, Reference.class ); + if ( orgUnitReference == null ) { logger.info( "Could not extract organization unit reference." ); + return Optional.empty(); } + return getOrgUnit( context, orgUnitReference, scriptVariables ); } + @Nonnull + protected Optional getOrgUnitId( @Nonnull FhirToDhisTransformerContext context, @Nonnull RuleInfo ruleInfo, @Nonnull ExecutableScript lookupScript, @Nonnull Map scriptVariables ) + { + final Reference orgUnitReference = executeScript( context, ruleInfo, lookupScript, scriptVariables, Reference.class ); + + if ( orgUnitReference == null ) + { + logger.info( "Could not extract organization unit reference." ); + + return Optional.empty(); + } + + if ( orgUnitReference.getType() == ReferenceType.ID ) + { + return Optional.of( orgUnitReference.getValue() ); + } + + return getOrgUnit( context, orgUnitReference, scriptVariables ).map( OrganizationUnit::getId ); + } + @Nonnull protected Optional getOrgUnit( @Nonnull FhirToDhisTransformerContext context, @Nonnull Reference orgUnitReference, @Nonnull Map scriptVariables ) { diff --git a/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/fhir/impl/program/AbstractFhirCarePlanToEnrollmentTransformer.java b/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/fhir/impl/enrollment/AbstractFhirCarePlanToEnrollmentTransformer.java similarity index 96% rename from fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/fhir/impl/program/AbstractFhirCarePlanToEnrollmentTransformer.java rename to fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/fhir/impl/enrollment/AbstractFhirCarePlanToEnrollmentTransformer.java index 66485f5e..e2844023 100644 --- a/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/fhir/impl/program/AbstractFhirCarePlanToEnrollmentTransformer.java +++ b/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/fhir/impl/enrollment/AbstractFhirCarePlanToEnrollmentTransformer.java @@ -1,4 +1,4 @@ -package org.dhis2.fhir.adapter.fhir.transform.fhir.impl.program; +package org.dhis2.fhir.adapter.fhir.transform.fhir.impl.enrollment; /* * Copyright (c) 2004-2019, University of Oslo @@ -29,7 +29,6 @@ */ import org.dhis2.fhir.adapter.dhis.converter.ValueConverter; -import org.dhis2.fhir.adapter.dhis.model.Reference; import org.dhis2.fhir.adapter.dhis.orgunit.OrganizationUnitService; import org.dhis2.fhir.adapter.dhis.tracker.program.Enrollment; import org.dhis2.fhir.adapter.dhis.tracker.program.EnrollmentService; @@ -160,7 +159,4 @@ protected void transformInternal( @Nonnull FhirToDhisTransformerContext context, { // method can be overridden } - - @Nonnull - protected abstract Reference getProgramRef( @Nonnull FhirToDhisTransformerContext context, @Nonnull RuleInfo ruleInfo, @Nonnull Map scriptVariables, @Nonnull IBaseResource carePlan ); } diff --git a/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/fhir/impl/program/AbstractFhirToEnrollmentTransformer.java b/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/fhir/impl/enrollment/AbstractFhirToEnrollmentTransformer.java similarity index 99% rename from fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/fhir/impl/program/AbstractFhirToEnrollmentTransformer.java rename to fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/fhir/impl/enrollment/AbstractFhirToEnrollmentTransformer.java index 03f8f73e..89ccf3a1 100644 --- a/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/fhir/impl/program/AbstractFhirToEnrollmentTransformer.java +++ b/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/fhir/impl/enrollment/AbstractFhirToEnrollmentTransformer.java @@ -1,4 +1,4 @@ -package org.dhis2.fhir.adapter.fhir.transform.fhir.impl.program; +package org.dhis2.fhir.adapter.fhir.transform.fhir.impl.enrollment; /* * Copyright (c) 2004-2019, University of Oslo 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/enrollment/FhirToEnrollmentTransformer.java similarity index 99% rename from fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/fhir/impl/program/FhirToEnrollmentTransformer.java rename to fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/fhir/impl/enrollment/FhirToEnrollmentTransformer.java index 84727692..930c5f01 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/enrollment/FhirToEnrollmentTransformer.java @@ -1,4 +1,4 @@ -package org.dhis2.fhir.adapter.fhir.transform.fhir.impl.program; +package org.dhis2.fhir.adapter.fhir.transform.fhir.impl.enrollment; /* * Copyright (c) 2004-2019, University of Oslo diff --git a/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/fhir/impl/program/AbstractFhirQuestionnaireResponseToProgramStageTransformer.java b/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/fhir/impl/program/AbstractFhirQuestionnaireResponseToProgramStageTransformer.java new file mode 100644 index 00000000..b96ac64d --- /dev/null +++ b/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/fhir/impl/program/AbstractFhirQuestionnaireResponseToProgramStageTransformer.java @@ -0,0 +1,327 @@ +package org.dhis2.fhir.adapter.fhir.transform.fhir.impl.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.converter.ValueConverter; +import org.dhis2.fhir.adapter.dhis.model.DhisResourceType; +import org.dhis2.fhir.adapter.dhis.model.Reference; +import org.dhis2.fhir.adapter.dhis.orgunit.OrganizationUnitService; +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.EventStatus; +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.program.ProgramStage; +import org.dhis2.fhir.adapter.dhis.tracker.program.ProgramStageMetadataService; +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.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; +import org.dhis2.fhir.adapter.fhir.metadata.model.FhirClientResource; +import org.dhis2.fhir.adapter.fhir.metadata.model.FhirResourceMapping; +import org.dhis2.fhir.adapter.fhir.metadata.model.FhirResourceType; +import org.dhis2.fhir.adapter.fhir.metadata.model.ProgramStageRule; +import org.dhis2.fhir.adapter.fhir.metadata.model.RuleInfo; +import org.dhis2.fhir.adapter.fhir.metadata.model.ScriptVariable; +import org.dhis2.fhir.adapter.fhir.metadata.model.TrackedEntityRule; +import org.dhis2.fhir.adapter.fhir.metadata.repository.FhirResourceMappingRepository; +import org.dhis2.fhir.adapter.fhir.metadata.repository.TrackedEntityRuleRepository; +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.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.FhirToDhisDeleteTransformOutcome; +import org.dhis2.fhir.adapter.fhir.transform.fhir.FhirToDhisTransformOutcome; +import org.dhis2.fhir.adapter.fhir.transform.fhir.FhirToDhisTransformerContext; +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.WritableScriptedEvent; +import org.dhis2.fhir.adapter.fhir.transform.util.TransformerUtils; +import org.dhis2.fhir.adapter.spring.StaticObjectProvider; +import org.hl7.fhir.instance.model.api.IBaseResource; + +import javax.annotation.Nonnull; +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; +import java.util.Optional; + +/** + * Abstract implementation of a transformer that transforms from FHIR Questionnaire Response to a DHIS2 event. + * + * @author volsch + */ +public abstract class AbstractFhirQuestionnaireResponseToProgramStageTransformer extends AbstractFhirToDhisTransformer +{ + protected final EventService eventService; + + protected final TrackedEntityMetadataService trackedEntityMetadataService; + + protected final TrackedEntityRuleRepository trackedEntityRuleRepository; + + protected final ScriptExecutionContext scriptExecutionContext; + + protected final ValueConverter valueConverter; + + protected final ProgramMetadataService programMetadataService; + + protected final ProgramStageMetadataService programStageMetadataService; + + protected final FhirResourceMappingRepository resourceMappingRepository; + + public AbstractFhirQuestionnaireResponseToProgramStageTransformer( @Nonnull ScriptExecutor scriptExecutor, @Nonnull TrackedEntityMetadataService trackedEntityMetadataService, @Nonnull TrackedEntityService trackedEntityService, + @Nonnull TrackedEntityRuleRepository trackedEntityRuleRepository, @Nonnull OrganizationUnitService organizationUnitService, + @Nonnull ProgramMetadataService programMetadataService, @Nonnull ProgramStageMetadataService programStageMetadataService, @Nonnull EventService eventService, + @Nonnull FhirResourceMappingRepository resourceMappingRepository, @Nonnull FhirDhisAssignmentRepository fhirDhisAssignmentRepository, @Nonnull ScriptExecutionContext scriptExecutionContext, @Nonnull ValueConverter valueConverter ) + { + super( scriptExecutor, organizationUnitService, new StaticObjectProvider<>( trackedEntityMetadataService ), new StaticObjectProvider<>( trackedEntityService ), + fhirDhisAssignmentRepository, scriptExecutionContext, valueConverter ); + + this.programMetadataService = programMetadataService; + this.programStageMetadataService = programStageMetadataService; + this.eventService = eventService; + this.trackedEntityMetadataService = trackedEntityMetadataService; + this.trackedEntityRuleRepository = trackedEntityRuleRepository; + this.resourceMappingRepository = resourceMappingRepository; + this.scriptExecutionContext = scriptExecutionContext; + this.valueConverter = valueConverter; + } + + @Override + @Nonnull + public DhisResourceType getDhisResourceType() + { + return DhisResourceType.PROGRAM_STAGE_EVENT; + } + + @Override + @Nonnull + public Class getDhisResourceClass() + { + return Event.class; + } + + @Override + @Nonnull + public Class getRuleClass() + { + return ProgramStageRule.class; + } + + @Override + public int getPriority() + { + return 10; + } + + @Override + protected boolean isSyncRequired( @Nonnull FhirToDhisTransformerContext context, @Nonnull RuleInfo ruleInfo, @Nonnull Map scriptVariables ) throws TransformerException + { + return context.getFhirRequest().isSync(); + } + + @Override + @Nonnull + protected Optional getResourceById( String id ) throws TransformerException + { + return id == null ? Optional.empty() : eventService.findOneById( id ); + } + + @Override + @Nonnull + protected Optional findResourceById( @Nonnull FhirToDhisTransformerContext context, @Nonnull RuleInfo ruleInfo, @Nonnull String id, @Nonnull Map scriptVariables ) + { + return eventService.findOneById( id ); + } + + @Nonnull + @Override + protected Optional getActiveResource( @Nonnull FhirToDhisTransformerContext context, @Nonnull RuleInfo ruleInfo, @Nonnull Map scriptVariables, boolean sync, boolean refreshed ) throws TransformerException + { + return Optional.empty(); + } + + @Override + protected Event createResource( @Nonnull FhirToDhisTransformerContext context, @Nonnull RuleInfo ruleInfo, String id, @Nonnull Map scriptVariables, boolean sync, boolean refreshed ) throws TransformerException + { + final Event event = new Event( true ); + event.setStatus( EventStatus.ACTIVE ); + + return event; + } + + @Override + public FhirToDhisTransformOutcome transform( @Nonnull FhirClientResource fhirClientResource, @Nonnull FhirToDhisTransformerContext context, + @Nonnull IBaseResource input, @Nonnull RuleInfo ruleInfo, @Nonnull Map scriptVariables ) throws TransformerException + { + if ( ruleInfo.getRule().getFhirResourceType() != FhirResourceType.QUESTIONNAIRE_RESPONSE || ruleInfo.getRule().getProgramStage() != null ) + { + return null; + } + + final Map variables = new HashMap<>( scriptVariables ); + + addBasicScriptVariables( context, ruleInfo, variables, input ); + + final RuleInfo trackedEntityRuleInfo = getTrackedEntityRuleInfo( context, ruleInfo, scriptVariables, + 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 IdentifiedTrackedEntityInstance trackedEntityInstance = getTrackedEntityInstance( context, trackedEntityRuleInfo, resourceMapping, variables, false ) + .orElseThrow( () -> new TransformerDataException( "Tracked entity instance could not be found." ) ); + + addScriptVariables( context, variables, ruleInfo, trackedEntityRuleInfo, trackedEntityInstance ); + + final Event event = getResource( fhirClientResource, context, ruleInfo, variables ) + .orElseThrow( () -> new TransformerDataException( "Event could not be determined." ) ); + final ScriptedTrackedEntityInstance scriptedTrackedEntityInstance = TransformerUtils.getScriptVariable( variables, ScriptVariable.TRACKED_ENTITY_INSTANCE, ScriptedTrackedEntityInstance.class ); + final Program program = TransformerUtils.getScriptVariable( variables, ScriptVariable.PROGRAM, Program.class ); + final ProgramStage programStage = TransformerUtils.getScriptVariable( variables, ScriptVariable.PROGRAM_STAGE, ProgramStage.class ); + + final WritableScriptedEvent scriptedEvent = new WritableScriptedEvent( program, programStage, event, scriptedTrackedEntityInstance, scriptExecutionContext, valueConverter ); + variables.put( ScriptVariable.OUTPUT.getVariableName(), scriptedEvent ); + + transformInternal( context, ruleInfo, scriptVariables, resourceMapping, + program, programStage, input, scriptedTrackedEntityInstance, scriptedEvent ); + if ( !transform( context, ruleInfo, variables ) ) + { + throw new TransformerDataException( "Care plan data could not be transformed to enrollment." ); + } + + if ( event.isModified() ) + { + scriptedEvent.validate(); + } + + return new FhirToDhisTransformOutcome<>( ruleInfo.getRule(), event, event.isNewResource() ); + } + + protected void transformInternal( @Nonnull FhirToDhisTransformerContext context, @Nonnull RuleInfo ruleInfo, @Nonnull Map scriptVariables, @Nonnull FhirResourceMapping fhirResourceMapping, + @Nonnull Program program, @Nonnull ProgramStage programStage, @Nonnull IBaseResource questionnaireResponse, @Nonnull ScriptedTrackedEntityInstance scriptedTrackedEntityInstance, @Nonnull WritableScriptedEvent scriptedEvent ) throws TransformerException + { + // method can be overridden + } + + @Override + public FhirToDhisDeleteTransformOutcome transformDeletion( @Nonnull FhirClientResource fhirClientResource, @Nonnull RuleInfo ruleInfo, @Nonnull DhisFhirResourceId dhisFhirResourceId ) throws TransformerException + { + if ( ruleInfo.getRule().getFhirResourceType() != FhirResourceType.QUESTIONNAIRE_RESPONSE || ruleInfo.getRule().getProgramStage() != null ) + { + return null; + } + + final Event event = new Event(); + event.setId( dhisFhirResourceId.getId() ); + + return new FhirToDhisDeleteTransformOutcome<>( ruleInfo.getRule(), event, true ); + } + + protected void addBasicScriptVariables( @Nonnull FhirToDhisTransformerContext context, @Nonnull RuleInfo ruleInfo, @Nonnull Map scriptVariables, @Nonnull IBaseResource questionnaireResponse ) throws TransformerException + { + final ProgramStage programStage = getProgramStage( context, ruleInfo, scriptVariables, questionnaireResponse ); + scriptVariables.put( ScriptVariable.PROGRAM_STAGE.getVariableName(), programStage ); + + final Program program = getProgram( context, ruleInfo, scriptVariables, programStage ); + scriptVariables.put( ScriptVariable.PROGRAM.getVariableName(), 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 ); + } + + protected void addScriptVariables( @Nonnull FhirToDhisTransformerContext context, @Nonnull Map variables, @Nonnull RuleInfo ruleInfo, + @Nonnull RuleInfo trackedEntityRuleRuleInfo, @Nonnull IdentifiedTrackedEntityInstance trackedEntityInstance ) throws TransformerException + { + variables.put( ScriptVariable.TRACKED_ENTITY_RESOURCE_TYPE.getVariableName(), trackedEntityRuleRuleInfo.getRule().getFhirResourceType() ); + addTrackedEntityScriptVariables( context, ruleInfo, trackedEntityInstance, variables ); + } + + @Nonnull + protected FhirResourceMapping getResourceMapping( @Nonnull RuleInfo ruleInfo, @Nonnull FhirResourceType trackedEntityFhirResourceType ) + { + return resourceMappingRepository.findOneByFhirResourceType( ruleInfo.getRule().getFhirResourceType(), trackedEntityFhirResourceType ) + .orElseThrow( () -> new TransformerMappingException( "No FHIR resource mapping has been defined for " + ruleInfo.getRule().getFhirResourceType() + + " and tracked entity FHIR resource " + trackedEntityFhirResourceType + "." ) ); + } + + @Override + protected boolean isAlwaysActiveResource( @Nonnull RuleInfo ruleInfo ) + { + return false; + } + + @Nonnull + protected TrackedEntityType getTrackedEntityType( @Nonnull FhirToDhisTransformerContext context, @Nonnull RuleInfo ruleInfo, @Nonnull Map scriptVariables, + @Nonnull Program program ) + { + return trackedEntityMetadataService.findTypeByReference( Reference.createIdReference( program.getTrackedEntityTypeId() ) ) + .orElseThrow( () -> new TransformerMappingException( "Tracked entity type " + program.getTrackedEntityTypeId() + " of tracker program " + program.getId() + " does not exist." ) ); + } + + @Nonnull + protected RuleInfo getTrackedEntityRuleInfo( @Nonnull FhirToDhisTransformerContext context, @Nonnull RuleInfo ruleInfo, @Nonnull Map scriptVariables, + @Nonnull Program program, @Nonnull TrackedEntityType trackedEntityType ) + { + return trackedEntityRuleRepository.findByTypeRefs( trackedEntityType.getAllReferences() ) + .stream().findFirst().map( r -> new RuleInfo<>( r, Collections.emptyList() ) ) + .orElseThrow( () -> new TransformerMappingException( "Tracked entity type " + program.getTrackedEntityTypeId() + " is not associated with a tracked entity rule." ) ); + } + + @Nonnull + protected abstract Reference getProgramStageRef( @Nonnull FhirToDhisTransformerContext context, @Nonnull RuleInfo ruleInfo, @Nonnull Map scriptVariables, @Nonnull IBaseResource questionnaireResponse ); + + @Nonnull + protected ProgramStage getProgramStage( @Nonnull FhirToDhisTransformerContext context, @Nonnull RuleInfo ruleInfo, @Nonnull Map scriptVariables, @Nonnull IBaseResource questionnaireResponse ) + { + final Reference programStageRef = getProgramStageRef( context, ruleInfo, scriptVariables, questionnaireResponse ); + + return programStageMetadataService.findMetadataByReference( programStageRef ) + .orElseThrow( () -> new TransformerDataException( "Tracker program stage \"" + programStageRef + "\" does not exist." ) ); + } + + @Nonnull + protected Program getProgram( @Nonnull FhirToDhisTransformerContext context, @Nonnull RuleInfo ruleInfo, @Nonnull Map scriptVariables, @Nonnull ProgramStage programStage ) + { + final Program program = programMetadataService.findMetadataByReference( Reference.createIdReference( programStage.getProgramId() ) ) + .orElseThrow( () -> new TransformerDataException( "Tracker program \"" + programStage.getProgramId() + "\" does not exist." ) ); + + if ( !program.isRegistration() ) + { + throw new TransformerDataException( "Tracker program \"" + programStage.getProgramId() + "\" does not require registration." ); + } + + return program; + } +} 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 d1ee066c..f5d6d3ce 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 @@ -302,6 +302,11 @@ protected void updateTrackedEntityInstance( @Nonnull Event event, @Nonnull Map transformDeletion( @Nonnull FhirClientResource fhirClientResource, @Nonnull RuleInfo ruleInfo, @Nonnull DhisFhirResourceId dhisFhirResourceId ) throws TransformerException { + if ( ruleInfo.getRule().getProgramStage() == null ) + { + return null; + } + if ( ruleInfo.getRule().isGrouping() && ruleInfo.getDhisDataReferences().isEmpty() ) { return new FhirToDhisDeleteTransformOutcome<>( @@ -657,7 +662,7 @@ protected void updateEventDate( @Nonnull FhirToDhisTransformerContext context, @ if ( !Objects.equals( event.getEventDate(), eventDate ) ) { event.setEventDate( eventDate ); - event.setModified( true ); + event.setModified(); logger.info( "Updated event date to {}.", eventDate ); } } diff --git a/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/fhir/impl/util/AbstractValueTypeFhirToDhisTransformerUtils.java b/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/fhir/impl/util/AbstractValueTypeFhirToDhisTransformerUtils.java index 364d3a57..9f325761 100644 --- a/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/fhir/impl/util/AbstractValueTypeFhirToDhisTransformerUtils.java +++ b/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/fhir/impl/util/AbstractValueTypeFhirToDhisTransformerUtils.java @@ -29,7 +29,6 @@ */ import org.dhis2.fhir.adapter.fhir.script.ScriptExecutionContext; -import org.dhis2.fhir.adapter.fhir.transform.dhis.impl.util.DhisToFhirTransformerUtils; import org.dhis2.fhir.adapter.fhir.transform.util.AbstractTransformerUtils; import org.dhis2.fhir.adapter.model.ValueType; import org.dhis2.fhir.adapter.scriptable.ScriptMethod; @@ -51,7 +50,7 @@ @Scriptable @ScriptType( value = "ValueTypeUtils", transformType = ScriptTransformType.IMP, var = AbstractValueTypeFhirToDhisTransformerUtils.SCRIPT_ATTR_NAME, description = "Utilities for handling conversion of DHIS2 values with a DHIS2 value type." ) -public abstract class AbstractValueTypeFhirToDhisTransformerUtils extends AbstractTransformerUtils implements DhisToFhirTransformerUtils +public abstract class AbstractValueTypeFhirToDhisTransformerUtils extends AbstractTransformerUtils implements FhirToDhisTransformerUtils { public static final String SCRIPT_ATTR_NAME = "valueTypeUtils"; diff --git a/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/fhir/impl/util/AssignmentFhirToDhisTransformerUtils.java b/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/fhir/impl/util/AssignmentFhirToDhisTransformerUtils.java new file mode 100644 index 00000000..2db78fa8 --- /dev/null +++ b/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/fhir/impl/util/AssignmentFhirToDhisTransformerUtils.java @@ -0,0 +1,116 @@ +package org.dhis2.fhir.adapter.fhir.transform.fhir.impl.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.data.repository.FhirDhisAssignmentRepository; +import org.dhis2.fhir.adapter.fhir.metadata.model.AbstractRule; +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.repository.FhirClientRepository; +import org.dhis2.fhir.adapter.fhir.model.FhirVersion; +import org.dhis2.fhir.adapter.fhir.script.ScriptExecutionContext; +import org.dhis2.fhir.adapter.fhir.transform.FatalTransformerException; +import org.dhis2.fhir.adapter.fhir.transform.TransformerMappingException; +import org.dhis2.fhir.adapter.fhir.transform.fhir.FhirToDhisTransformerContext; +import org.hl7.fhir.instance.model.api.IIdType; +import org.springframework.stereotype.Component; + +import javax.annotation.Nonnull; +import javax.annotation.Nullable; +import java.util.Set; + +/** + * FHIR to DHIS2 transformer utility methods for assignment of IDs. + * + * @author volsch + */ +@Component +public class AssignmentFhirToDhisTransformerUtils extends AbstractFhirToDhisTransformerUtils +{ + public static final String SCRIPT_ATTR_NAME = "assignmentUtils"; + + private final FhirDhisAssignmentRepository assignmentRepository; + + private final FhirClientRepository fhirClientRepository; + + protected AssignmentFhirToDhisTransformerUtils( @Nonnull ScriptExecutionContext scriptExecutionContext, + @Nonnull FhirDhisAssignmentRepository assignmentRepository, @Nonnull FhirClientRepository fhirClientRepository ) + { + super( scriptExecutionContext ); + + this.assignmentRepository = assignmentRepository; + this.fhirClientRepository = fhirClientRepository; + } + + @Nonnull + @Override + public final String getScriptAttrName() + { + return SCRIPT_ATTR_NAME; + } + + @Nonnull + @Override + public Set getFhirVersions() + { + return FhirVersion.ALL; + } + + @Nullable + public String getMappedDhisId( @Nonnull FhirToDhisTransformerContext context, @Nonnull AbstractRule rule, @Nullable IIdType fhirId ) + { + if ( fhirId == null || !fhirId.hasIdPart() ) + { + return null; + } + + if ( fhirId.getResourceType() == null ) + { + throw new IllegalArgumentException( "FHIR resource type is not included: " + fhirId.getValue() ); + } + + final FhirResourceType fhirResourceType = FhirResourceType.getByResourceTypeName( fhirId.getResourceType() ); + + if ( fhirResourceType == null ) + { + throw new TransformerMappingException( "FHIR ID contains unsupported resource type: " + fhirId.getResourceType() ); + } + + if ( fhirResourceType.isSyncDhisId() || context.getFhirRequest().isDhisFhirId() ) + { + return fhirId.getIdPart(); + } + + final FhirClient fhirClient = fhirClientRepository.findOneByIdCached( context.getFhirRequest().getFhirClientId() ) + .orElseThrow( () -> new FatalTransformerException( "FHIR Client with ID " + context.getFhirRequest().getFhirClientId() + " could not be found." ) ); + + // rule must not be used to determine different assignment + return assignmentRepository.findFirstDhisResourceId( fhirClient, fhirId ); + } +} diff --git a/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/fhir/model/FhirRequest.java b/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/fhir/model/FhirRequest.java index a7119353..7e829489 100644 --- a/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/fhir/model/FhirRequest.java +++ b/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/fhir/model/FhirRequest.java @@ -98,6 +98,9 @@ public interface FhirRequest boolean isSync(); + @Nullable + UUID getFhirClientId(); + @Nullable UUID getFhirClientResourceId(); diff --git a/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/fhir/model/ImmutableFhirRequest.java b/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/fhir/model/ImmutableFhirRequest.java index 7cff8549..ecc14301 100644 --- a/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/fhir/model/ImmutableFhirRequest.java +++ b/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/fhir/model/ImmutableFhirRequest.java @@ -147,6 +147,13 @@ public String getDhisUsername() return delegate.getDhisUsername(); } + @Nullable + @Override + public UUID getFhirClientId() + { + return delegate.getFhirClientId(); + } + @Nullable @Override public String getFhirClientCode() diff --git a/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/fhir/model/WritableFhirRequest.java b/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/fhir/model/WritableFhirRequest.java index 440e180c..0f4cb1b0 100644 --- a/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/fhir/model/WritableFhirRequest.java +++ b/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/fhir/model/WritableFhirRequest.java @@ -79,6 +79,8 @@ public class WritableFhirRequest implements FhirRequest, Serializable private String dhisUsername; + private UUID fhirClientId; + private String fhirClientCode; private UUID fhirClientResourceId; @@ -260,6 +262,18 @@ public void setFhirClientResourceId( UUID fhirClientResourceId ) this.fhirClientResourceId = fhirClientResourceId; } + @Nullable + @Override + public UUID getFhirClientId() + { + return fhirClientId; + } + + public void setFhirClientId( UUID fhirClientId ) + { + this.fhirClientId = fhirClientId; + } + @Nullable @Override public String getFhirClientCode() diff --git a/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/scripted/WritableScriptedEvent.java b/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/scripted/WritableScriptedEvent.java index 20bdb088..99e511c7 100644 --- a/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/scripted/WritableScriptedEvent.java +++ b/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/scripted/WritableScriptedEvent.java @@ -152,7 +152,7 @@ public boolean setEventDate( @Nullable Object eventDate ) final ZonedDateTime zonedDateTime = ScriptedDateTimeUtils.toZonedDateTime( eventDate, valueConverter ); if ( !Objects.equals( event.getEventDate(), zonedDateTime ) ) { - event.setModified( true ); + event.setModified(); } event.setEventDate( zonedDateTime ); return (eventDate != null); @@ -175,7 +175,7 @@ public boolean setDueDate( @Nullable Object dueDate ) if ( !Objects.equals( event.getDueDate(), zonedDateTime ) ) { - event.setModified( true ); + event.setModified(); } event.setDueDate( zonedDateTime ); @@ -208,7 +208,7 @@ public boolean setStatus( @Nullable Object status ) if ( !Objects.equals( event.getStatus(), convertedStatus ) ) { - event.setModified( true ); + event.setModified(); } event.setStatus( convertedStatus ); @@ -233,7 +233,7 @@ public boolean setCoordinate( @Nullable Object coordinate ) if ( !Objects.equals( event.getCoordinate(), convertedCoordinate ) ) { - event.setModified( true ); + event.setModified(); } event.setCoordinate( convertedCoordinate ); @@ -563,6 +563,7 @@ protected WritableDataValue getDataValue( @Nonnull ProgramStageDataElement dataE { return new WritableDataValue( dataElement.getElementId(), true ); } + return event.getDataValue( dataElement.getElementId() ); } 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 3b8d60a9..8958b5fa 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 @@ -84,10 +84,12 @@ public static Reference createReference( @Nullable String value, @Nonnull Object public static T getScriptVariable( @Nonnull Map scriptVariables, @Nonnull ScriptVariable scriptVariable, @Nonnull Class type ) throws FatalTransformerException { final T value = type.cast( scriptVariables.get( scriptVariable.getVariableName() ) ); + if ( value == null ) { - throw new FatalTransformerException( "Script variable is not included: " + scriptVariable ); + throw new FatalTransformerException( "Script variable is not included: " + scriptVariable.getVariableName() ); } + return value; } 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 index 23f6ffc0..c2290b2b 100644 --- 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 @@ -65,6 +65,15 @@ public void setStringValue() Assert.assertEquals( "testValue", planDefinition.getExtension().get( 0 ).getValue().toString() ); } + @Test + public void getStringValue() + { + TestPlanDefinition planDefinition = new TestPlanDefinition(); + + BaseExtensionUtils.setStringValue( "testUrl", planDefinition, "My Test", TypeFactory::createType ); + Assert.assertEquals( "My Test", BaseExtensionUtils.getStringValue( "testUrl", planDefinition ) ); + } + @Test public void resetDateValue() { diff --git a/fhir/src/test/java/org/dhis2/fhir/adapter/fhir/extension/DueDateExtensionUtilsTest.java b/fhir/src/test/java/org/dhis2/fhir/adapter/fhir/extension/DueDateExtensionUtilsTest.java index 31acdef5..a0c35676 100644 --- a/fhir/src/test/java/org/dhis2/fhir/adapter/fhir/extension/DueDateExtensionUtilsTest.java +++ b/fhir/src/test/java/org/dhis2/fhir/adapter/fhir/extension/DueDateExtensionUtilsTest.java @@ -65,4 +65,15 @@ public void setValue() Assert.assertEquals( DueDateExtensionUtils.URL, planDefinition.getExtension().get( 0 ).getUrl() ); Assert.assertSame( date, ( (DateDt) planDefinition.getExtension().get( 0 ).getValue() ).getValue() ); } + + @Test + public void getValue() + { + TestPlanDefinition planDefinition = new TestPlanDefinition(); + + final Date date = new Date(); + + DueDateExtensionUtils.setValue( planDefinition, date, TypeFactory::createType ); + Assert.assertEquals( date, DueDateExtensionUtils.getValue( planDefinition ) ); + } } \ No newline at end of file diff --git a/fhir/src/test/java/org/dhis2/fhir/adapter/fhir/extension/EventStatusExtensionUtilsTest.java b/fhir/src/test/java/org/dhis2/fhir/adapter/fhir/extension/EventStatusExtensionUtilsTest.java index 23b5be4d..1c8aca09 100644 --- a/fhir/src/test/java/org/dhis2/fhir/adapter/fhir/extension/EventStatusExtensionUtilsTest.java +++ b/fhir/src/test/java/org/dhis2/fhir/adapter/fhir/extension/EventStatusExtensionUtilsTest.java @@ -50,4 +50,22 @@ public void setValue() Assert.assertEquals( EventStatusExtensionUtils.URL, planDefinition.getExtension().get( 0 ).getUrl() ); Assert.assertEquals( "OVERDUE", planDefinition.getExtension().get( 0 ).getValue().toString() ); } + + @Test + public void getValue() + { + TestPlanDefinition planDefinition = new TestPlanDefinition(); + + EventStatusExtensionUtils.setValue( planDefinition, EventStatus.OVERDUE, TypeFactory::createType ); + Assert.assertEquals( EventStatus.OVERDUE, EventStatusExtensionUtils.getValue( planDefinition ) ); + } + + @Test( expected = IllegalArgumentException.class ) + public void getValueInvalid() + { + TestPlanDefinition planDefinition = new TestPlanDefinition(); + + BaseExtensionUtils.setStringValue( EventStatusExtensionUtils.URL, planDefinition, "ABC", TypeFactory::createType ); + EventStatusExtensionUtils.getValue( planDefinition ); + } } \ No newline at end of file diff --git a/fhir/src/test/java/org/dhis2/fhir/adapter/fhir/extension/IncidentDateExtensionUtilsTest.java b/fhir/src/test/java/org/dhis2/fhir/adapter/fhir/extension/IncidentDateExtensionUtilsTest.java index 97edca03..3d5d0572 100644 --- a/fhir/src/test/java/org/dhis2/fhir/adapter/fhir/extension/IncidentDateExtensionUtilsTest.java +++ b/fhir/src/test/java/org/dhis2/fhir/adapter/fhir/extension/IncidentDateExtensionUtilsTest.java @@ -67,7 +67,7 @@ public void setValue() } @Test - public void getDateValue() + public void getValue() { TestPlanDefinition planDefinition = new TestPlanDefinition(); 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/enrollment/FhirToEnrollmentTransformerTest.java similarity index 99% rename from fhir/src/test/java/org/dhis2/fhir/adapter/fhir/transform/fhir/impl/program/FhirToEnrollmentTransformerTest.java rename to fhir/src/test/java/org/dhis2/fhir/adapter/fhir/transform/fhir/impl/enrollment/FhirToEnrollmentTransformerTest.java index def5dbff..edcdea6f 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/enrollment/FhirToEnrollmentTransformerTest.java @@ -1,4 +1,4 @@ -package org.dhis2.fhir.adapter.fhir.transform.fhir.impl.program; +package org.dhis2.fhir.adapter.fhir.transform.fhir.impl.enrollment; /* * Copyright (c) 2004-2019, University of Oslo