From 3ff40b1ef6a95252fd587ac26c6a1ccf55a76061 Mon Sep 17 00:00:00 2001 From: Volker Schmidt Date: Thu, 25 Jul 2019 12:45:04 +0200 Subject: [PATCH 01/16] Adds tests for transformation of DHIS2 Program to FHIR Plan Definition. --- .../AbstractProgramStageFhirRestAppTest.java | 5 +- .../r4/R4ProgramMetadataFhirRestAppTest.java | 335 ++++++++++++++++++ .../adapter/dhis/model/DhisResourceType.java | 25 +- ...adataToFhirPlanDefinitionTransformer.java} | 6 +- .../fhir/metadata/model/AbstractRule.java | 5 +- .../repository/CustomRuleRepository.java | 2 +- .../impl/CustomRuleRepositoryImpl.java | 7 +- ...ractDhisMetadataToFhirRequestResolver.java | 2 +- ...adataToFhirPlanDefinitionTransformer.java} | 4 +- 9 files changed, 366 insertions(+), 25 deletions(-) create mode 100644 app/src/test/java/org/dhis2/fhir/adapter/fhir/server/r4/R4ProgramMetadataFhirRestAppTest.java rename fhir-r4/src/main/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/metadata/r4/{R4ProgramMetadataToFhirCarePlanTransformer.java => R4ProgramMetadataToFhirPlanDefinitionTransformer.java} (92%) rename fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/metadata/program/{AbstractProgramMetadataToFhirCarePlanTransformer.java => AbstractProgramMetadataToFhirPlanDefinitionTransformer.java} (92%) diff --git a/app/src/test/java/org/dhis2/fhir/adapter/fhir/server/AbstractProgramStageFhirRestAppTest.java b/app/src/test/java/org/dhis2/fhir/adapter/fhir/server/AbstractProgramStageFhirRestAppTest.java index fba6ac0b..8962a7e8 100644 --- a/app/src/test/java/org/dhis2/fhir/adapter/fhir/server/AbstractProgramStageFhirRestAppTest.java +++ b/app/src/test/java/org/dhis2/fhir/adapter/fhir/server/AbstractProgramStageFhirRestAppTest.java @@ -74,9 +74,8 @@ protected void expectProgramStageMetadataRequests() throws Exception systemDhis2Server.expect( ExpectedCount.between( 0, 1 ), method( HttpMethod.GET ) ).andExpect( header( "Authorization", testConfiguration.getDhis2SystemAuthorization() ) ) .andExpect( requestTo( dhis2BaseUrl + "/api/" + dhis2ApiVersion + "/programs.json?paging=false&fields=id,name,code,description,selectIncidentDatesInFuture,selectEnrollmentDatesInFuture,displayIncidentDate,registration," + "withoutRegistration,captureCoordinates,trackedEntityType%5Bid%5D,programTrackedEntityAttributes%5Bid,name,valueType,mandatory,allowFutureDate,trackedEntityAttribute%5Bid,name,code,valueType,generated%5D%5D,programStages%5Bid,name," + - "description," + - "repeatable,captureCoordinates,generatedByEnrollmentDate,minDaysFromStart,programStageDataElements%5Bid,compulsory,allowProvidedElsewhere,dataElement%5Bid,name,code,formName,valueType,optionSetValue,optionSet%5Bid,name,options%5Bcode," + - "name%5D%5D%5D%5D%5D&filter=name:eq:Child%20Programme" ) ) + "description,repeatable,captureCoordinates,generatedByEnrollmentDate,minDaysFromStart,programStageDataElements%5Bid,compulsory,allowProvidedElsewhere,dataElement%5Bid,name,code,formName,valueType,optionSetValue,optionSet%5Bid,name," + + "options%5Bcode,name%5D%5D%5D%5D%5D&filter=name:eq:Child%20Programme" ) ) .andRespond( withSuccess( IOUtils.resourceToString( "/org/dhis2/fhir/adapter/dhis/test/default-program.json", StandardCharsets.UTF_8 ), MediaType.APPLICATION_JSON ) ); } } diff --git a/app/src/test/java/org/dhis2/fhir/adapter/fhir/server/r4/R4ProgramMetadataFhirRestAppTest.java b/app/src/test/java/org/dhis2/fhir/adapter/fhir/server/r4/R4ProgramMetadataFhirRestAppTest.java new file mode 100644 index 00000000..e7d1c616 --- /dev/null +++ b/app/src/test/java/org/dhis2/fhir/adapter/fhir/server/r4/R4ProgramMetadataFhirRestAppTest.java @@ -0,0 +1,335 @@ +package org.dhis2.fhir.adapter.fhir.server.r4; + +/* + * Copyright (c) 2004-2019, University of Oslo + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * Neither the name of the HISP project nor the names of its contributors may + * be used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +import ca.uhn.fhir.rest.client.api.IGenericClient; +import ca.uhn.fhir.rest.client.interceptor.BasicAuthInterceptor; +import ca.uhn.fhir.rest.server.exceptions.AuthenticationException; +import ca.uhn.fhir.rest.server.exceptions.ResourceNotFoundException; +import org.apache.commons.io.IOUtils; +import org.dhis2.fhir.adapter.AbstractAppTest; +import org.dhis2.fhir.adapter.fhir.model.FhirVersion; +import org.hl7.fhir.r4.model.Bundle; +import org.hl7.fhir.r4.model.PlanDefinition; +import org.junit.Assert; +import org.junit.Ignore; +import org.junit.Test; +import org.springframework.http.HttpMethod; +import org.springframework.http.HttpStatus; +import org.springframework.http.MediaType; +import org.springframework.test.web.client.ExpectedCount; + +import javax.annotation.Nonnull; +import java.nio.charset.StandardCharsets; + +import static org.springframework.test.web.client.match.MockRestRequestMatchers.*; +import static org.springframework.test.web.client.response.MockRestResponseCreators.withStatus; +import static org.springframework.test.web.client.response.MockRestResponseCreators.withSuccess; + +/** + * R4 tests for rest interfaces that access + * DHIS2 program metadata. + * + * @author volsch + */ +@Ignore +public class R4ProgramMetadataFhirRestAppTest extends AbstractAppTest +{ + @Nonnull + @Override + protected FhirVersion getFhirVersion() + { + return FhirVersion.R4; + } + + @Test( expected = AuthenticationException.class ) + public void getPlanDefinitionWithoutAuthorization() + { + final IGenericClient client = createGenericClient(); + client.read().resource( PlanDefinition.class ).withId( "ldXIdLNUNEn" ).execute(); + } + + @Test( expected = AuthenticationException.class ) + public void getPlanDefinitionWithInvalidAuthorization() + { + userDhis2Server.expect( ExpectedCount.once(), method( HttpMethod.GET ) ).andExpect( header( "Authorization", "Basic Zmhpcl9jbGllbnQ6aW52YWxpZF8x" ) ) + .andExpect( requestTo( dhis2BaseUrl + "/api/" + dhis2ApiVersion + "/programs/EPDyQuoRnXk.json?" + + "fields=id,name,code,description,selectIncidentDatesInFuture,selectEnrollmentDatesInFuture,displayIncidentDate,registration,withoutRegistration,captureCoordinates,trackedEntityType%5Bid%5D,programTrackedEntityAttributes%5Bid,name,valueType," + + "mandatory,allowFutureDate,trackedEntityAttribute%5Bid,name,code,valueType,generated%5D%5D,programStages%5Bid,name,description,repeatable,captureCoordinates,generatedByEnrollmentDate,minDaysFromStart,programStageDataElements%5Bid,compulsory," + + "allowProvidedElsewhere,dataElement%5Bid,name,code,formName,valueType,optionSetValue,optionSet%5Bid,name,options%5Bcode,name%5D%5D%5D%5D%5D" ) ) + .andRespond( withStatus( HttpStatus.UNAUTHORIZED ) ); + + final IGenericClient client = createGenericClient(); + client.registerInterceptor( new BasicAuthInterceptor( "fhir_client", "invalid_1" ) ); + client.read().resource( PlanDefinition.class ).withId( "EPDyQuoRnXk" ).execute(); + } + + @Test + public void getPlanDefinitionRepeated() throws Exception + { + getPlanDefinition(); + getPlanDefinition(); + } + + private void getPlanDefinition() throws Exception + { + systemDhis2Server.reset(); + userDhis2Server.reset(); + + systemDhis2Server.expect( ExpectedCount.between( 0, 1 ), method( HttpMethod.GET ) ).andExpect( header( "Authorization", testConfiguration.getDhis2SystemAuthorization() ) ) + .andExpect( requestTo( dhis2BaseUrl + "/api/" + dhis2ApiVersion + "/trackedEntityTypes/MCPQUTHX1Ze.json?" + + "fields=id,name,trackedEntityTypeAttributes%5Bid,name,valueType,mandatory,trackedEntityAttribute%5Bid,name,code,valueType,generated,optionSetValue,optionSet%5Bid,name,options%5Bcode,name%5D%5D%5D%5D" ) ) + .andRespond( withSuccess( IOUtils.resourceToString( "/org/dhis2/fhir/adapter/dhis/test/single-tracked-entity-type.json", StandardCharsets.UTF_8 ), MediaType.APPLICATION_JSON ) ); + userDhis2Server.expect( ExpectedCount.once(), method( HttpMethod.GET ) ).andExpect( header( "Authorization", "Basic Zmhpcl9jbGllbnQ6Zmhpcl9jbGllbnRfMQ==" ) ) + .andExpect( requestTo( dhis2BaseUrl + "/api/" + dhis2ApiVersion + "/programs/EPDyQuoRnXk.json?fields=id,name,code,description,selectIncidentDatesInFuture,selectEnrollmentDatesInFuture,displayIncidentDate,registration," + + "withoutRegistration,captureCoordinates,trackedEntityType%5Bid%5D,programTrackedEntityAttributes%5Bid,name,valueType,mandatory,allowFutureDate,trackedEntityAttribute%5Bid,name,code,valueType,generated%5D%5D,programStages%5Bid,name," + + "description,repeatable,captureCoordinates,generatedByEnrollmentDate,minDaysFromStart,programStageDataElements%5Bid,compulsory,allowProvidedElsewhere,dataElement%5Bid,name,code,formName,valueType,optionSetValue,optionSet%5Bid,name," + + "options%5Bcode,name%5D%5D%5D%5D%5D" ) ) + .andRespond( withSuccess( IOUtils.resourceToString( "/org/dhis2/fhir/adapter/dhis/test/single-program.json", StandardCharsets.UTF_8 ), MediaType.APPLICATION_JSON ) ); + userDhis2Server.expect( ExpectedCount.between( 0, 1 ), method( HttpMethod.GET ) ).andExpect( header( "Authorization", "Basic Zmhpcl9jbGllbnQ6Zmhpcl9jbGllbnRfMQ==" ) ) + .andExpect( requestTo( dhis2BaseUrl + "/api/" + dhis2ApiVersion + "/programs.json?paging=false&fields=id,name,code,description,selectIncidentDatesInFuture,selectEnrollmentDatesInFuture,displayIncidentDate,registration," + + "withoutRegistration,captureCoordinates,trackedEntityType%5Bid%5D,programTrackedEntityAttributes%5Bid,name,valueType,mandatory,allowFutureDate,trackedEntityAttribute%5Bid,name,code,valueType,generated%5D%5D,programStages%5Bid,name," + + "description,repeatable,captureCoordinates,generatedByEnrollmentDate,minDaysFromStart,programStageDataElements%5Bid,compulsory,allowProvidedElsewhere,dataElement%5Bid,name,code,formName,valueType,optionSetValue,optionSet%5Bid,name," + + "options%5Bcode,name%5D%5D%5D%5D%5D" ) ) + .andRespond( withSuccess( IOUtils.resourceToString( "/org/dhis2/fhir/adapter/dhis/test/all-programs.json", StandardCharsets.UTF_8 ), MediaType.APPLICATION_JSON ) ); + + final IGenericClient client = createGenericClient(); + client.registerInterceptor( new BasicAuthInterceptor( "fhir_client", "fhir_client_1" ) ); + PlanDefinition planDefinition = client.read().resource( PlanDefinition.class ).withId( "EPDyQuoRnXk" ).execute(); + Assert.assertEquals( "Child Programme", planDefinition.getName() ); + + systemDhis2Server.verify(); + userDhis2Server.verify(); + } + + @Test( expected = ResourceNotFoundException.class ) + public void getPlanDefinitionNotExistsRepeated() + { + try + { + getPlanDefinitionNotExists(); + Assert.fail( "Exception expected also an first invocation." ); + } + catch ( ResourceNotFoundException e ) + { + getPlanDefinitionNotExists(); + } + } + + private void getPlanDefinitionNotExists() + { + systemDhis2Server.reset(); + userDhis2Server.reset(); + + userDhis2Server.expect( ExpectedCount.once(), method( HttpMethod.GET ) ).andExpect( header( "Authorization", "Basic Zmhpcl9jbGllbnQ6Zmhpcl9jbGllbnRfMQ==" ) ) + .andExpect( requestTo( dhis2BaseUrl + "/api/" + dhis2ApiVersion + "/programs/0dXIdLNUNEn.json?fields=id,name,code,description,selectIncidentDatesInFuture,selectEnrollmentDatesInFuture,displayIncidentDate,registration," + + "withoutRegistration,captureCoordinates,trackedEntityType%5Bid%5D,programTrackedEntityAttributes%5Bid,name,valueType,mandatory,allowFutureDate,trackedEntityAttribute%5Bid,name,code,valueType,generated%5D%5D,programStages%5Bid,name," + + "description,repeatable,captureCoordinates,generatedByEnrollmentDate,minDaysFromStart,programStageDataElements%5Bid,compulsory,allowProvidedElsewhere,dataElement%5Bid,name,code,formName,valueType,optionSetValue,optionSet%5Bid,name," + + "options%5Bcode,name%5D%5D%5D%5D%5D" ) ) + .andRespond( withStatus( HttpStatus.NOT_FOUND ).contentType( MediaType.APPLICATION_JSON ).body( "{}" ) ); + + try + { + final IGenericClient client = createGenericClient(); + client.registerInterceptor( new BasicAuthInterceptor( "fhir_client", "fhir_client_1" ) ); + client.read().resource( PlanDefinition.class ).withId( "0dXIdLNUNEn" ).execute(); + } + catch ( ResourceNotFoundException e ) + { + systemDhis2Server.verify(); + userDhis2Server.verify(); + throw e; + } + } + + @Test( expected = ResourceNotFoundException.class ) + public void getPlanDefinitionRuleNotFoundRepeated() + { + try + { + getPlanDefinitionNotExists(); + Assert.fail( "Exception expected also an first invocation." ); + } + catch ( ResourceNotFoundException e ) + { + getPlanDefinitionNotExists(); + } + } + + private void getPlanDefinitionRuleNotFound() + { + systemDhis2Server.reset(); + userDhis2Server.reset(); + + try + { + final IGenericClient client = createGenericClient(); + client.registerInterceptor( new BasicAuthInterceptor( "fhir_client", "fhir_client_1" ) ); + client.read().resource( PlanDefinition.class ).withId( "ldXIdLNUNEn" ).execute(); + } + catch ( ResourceNotFoundException e ) + { + systemDhis2Server.verify(); + userDhis2Server.verify(); + throw e; + } + } + + @Test( expected = AuthenticationException.class ) + public void getPlanDefinitionByIdentifierWithoutAuthorization() + { + final IGenericClient client = createGenericClient(); + client.search().forResource( PlanDefinition.class ).where( PlanDefinition.IDENTIFIER.exactly().systemAndIdentifier( "http://www.dhis2.org/dhis2fhiradapter/systems/plan-definition-identifier", "XX_1234" ) ).returnBundle( Bundle.class ).execute(); + } + + @Test( expected = AuthenticationException.class ) + public void getPlanDefinitionByIdentifierInvalidAuthorization() + { + userDhis2Server.expect( ExpectedCount.once(), method( HttpMethod.GET ) ).andExpect( header( "Authorization", "Basic Zmhpcl9jbGllbnQ6aW52YWxpZF8x" ) ) + .andExpect( requestTo( dhis2BaseUrl + "/api/" + dhis2ApiVersion + "/organisationUnits.json?paging=false&fields=lastUpdated,id,code,name,shortName,displayName,level,openingDate,closedDate,coordinates,leaf,parent%5Bid%5D&filter=code:eq:OU_1234" ) ) + .andRespond( withStatus( HttpStatus.UNAUTHORIZED ) ); + + final IGenericClient client = createGenericClient(); + client.registerInterceptor( new BasicAuthInterceptor( "fhir_client", "invalid_1" ) ); + client.search().forResource( PlanDefinition.class ).where( PlanDefinition.IDENTIFIER.exactly().systemAndIdentifier( "http://www.dhis2.org/dhis2fhiradapter/systems/planDefinition-identifier", "OU_1234" ) ).returnBundle( Bundle.class ).execute(); + } + + @Test + public void getPlanDefinitionByIdentifierRepeated() throws Exception + { + getPlanDefinitionByIdentifier(); + getPlanDefinitionByIdentifier(); + } + + private void getPlanDefinitionByIdentifier() throws Exception + { + systemDhis2Server.reset(); + userDhis2Server.reset(); + + userDhis2Server.expect( ExpectedCount.between( 1, 2 ), method( HttpMethod.GET ) ).andExpect( header( "Authorization", "Basic Zmhpcl9jbGllbnQ6Zmhpcl9jbGllbnRfMQ==" ) ) + .andExpect( requestTo( dhis2BaseUrl + "/api/" + dhis2ApiVersion + "/organisationUnits.json?paging=false&fields=lastUpdated,id,code,name,shortName,displayName,level,openingDate,closedDate,coordinates,leaf,parent%5Bid%5D&filter=code:eq:OU_1234" ) ) + .andRespond( withSuccess( IOUtils.resourceToString( "/org/dhis2/fhir/adapter/dhis/test/default-org-unit-OU_1234.json", StandardCharsets.UTF_8 ), MediaType.APPLICATION_JSON ) ); + + final IGenericClient client = createGenericClient(); + client.registerInterceptor( new BasicAuthInterceptor( "fhir_client", "fhir_client_1" ) ); + Bundle bundle = + client.search().forResource( PlanDefinition.class ).where( PlanDefinition.IDENTIFIER.exactly().systemAndIdentifier( "http://www.dhis2.org/dhis2fhiradapter/systems/planDefinition-identifier", "OU_1234" ) ).returnBundle( Bundle.class ).execute(); + Assert.assertEquals( 1, bundle.getEntry().size() ); + PlanDefinition planDefinition = (PlanDefinition) bundle.getEntry().get( 0 ).getResource(); + Assert.assertEquals( "Test Hospital", planDefinition.getName() ); + Assert.assertEquals( 1, planDefinition.getIdentifier().size() ); + Assert.assertEquals( "http://www.dhis2.org/dhis2fhiradapter/systems/planDefinition-identifier", planDefinition.getIdentifier().get( 0 ).getSystem() ); + Assert.assertEquals( "OU_1234", planDefinition.getIdentifier().get( 0 ).getValue() ); + + systemDhis2Server.verify(); + userDhis2Server.verify(); + } + + @Test( expected = AuthenticationException.class ) + public void searchPlanDefinitionWithoutAuthorization() + { + final IGenericClient client = createGenericClient(); + client.search().forResource( PlanDefinition.class ).where( PlanDefinition.NAME.matches().value( "Test" ) ).returnBundle( Bundle.class ).execute(); + } + + @Test( expected = AuthenticationException.class ) + public void searchPlanDefinitionInvalidAuthorization() + { + userDhis2Server.expect( ExpectedCount.once(), method( HttpMethod.GET ) ).andExpect( header( "Authorization", "Basic Zmhpcl9jbGllbnQ6aW52YWxpZF8x" ) ) + .andExpect( requestTo( dhis2BaseUrl + "/api/" + dhis2ApiVersion + "/programs.json?filter=name:$ilike:Test&paging=true&page=1&pageSize=10&order=id&fields=id,name,code,description,selectIncidentDatesInFuture,selectEnrollmentDatesInFuture," + + "displayIncidentDate,registration,withoutRegistration,captureCoordinates,trackedEntityType%5Bid%5D,programTrackedEntityAttributes%5Bid,name,valueType,mandatory,allowFutureDate,trackedEntityAttribute%5Bid,name,code,valueType,generated%5D%5D," + + "programStages%5Bid,name,description,repeatable,captureCoordinates,generatedByEnrollmentDate,minDaysFromStart,programStageDataElements%5Bid,compulsory,allowProvidedElsewhere,dataElement%5Bid,name,code,formName,valueType,optionSetValue," + + "optionSet%5Bid,name,options%5Bcode,name%5D%5D%5D%5D%5D" ) ) + .andRespond( withStatus( HttpStatus.UNAUTHORIZED ) ); + + final IGenericClient client = createGenericClient(); + client.registerInterceptor( new BasicAuthInterceptor( "fhir_client", "invalid_1" ) ); + client.search().forResource( PlanDefinition.class ).where( PlanDefinition.NAME.matches().value( "Test" ) ).returnBundle( Bundle.class ).execute(); + } + + @Test + public void searchPlanDefinition() throws Exception + { + userDhis2Server.expect( ExpectedCount.once(), method( HttpMethod.GET ) ).andExpect( header( "Authorization", "Basic Zmhpcl9jbGllbnQ6Zmhpcl9jbGllbnRfMQ==" ) ) + .andExpect( requestTo( dhis2BaseUrl + "/api/" + dhis2ApiVersion + "/organisationUnits.json?filter=name:$ilike:Test&paging=true&page=1&pageSize=10&order=id" + + "&fields=lastUpdated,id,code,name,shortName,displayName,level,openingDate,closedDate,coordinates,leaf,parent%5Bid%5D" ) ) + .andRespond( withSuccess( IOUtils.resourceToString( "/org/dhis2/fhir/adapter/dhis/test/multi-org-unit.json", StandardCharsets.UTF_8 ), MediaType.APPLICATION_JSON ) ); + userDhis2Server.expect( ExpectedCount.between( 0, 1 ), method( HttpMethod.GET ) ).andExpect( header( "Authorization", "Basic Zmhpcl9jbGllbnQ6Zmhpcl9jbGllbnRfMQ==" ) ) + .andExpect( requestTo( dhis2BaseUrl + "/api/" + dhis2ApiVersion + "/organisationUnits.json?paging=false&fields=lastUpdated,id,code,name,shortName,displayName,level,openingDate,closedDate,coordinates,leaf,parent%5Bid%5D&filter=code:eq:OU_1234" ) ) + .andRespond( withSuccess( IOUtils.resourceToString( "/org/dhis2/fhir/adapter/dhis/test/default-org-unit-OU_1234.json", StandardCharsets.UTF_8 ), MediaType.APPLICATION_JSON ) ); + userDhis2Server.expect( ExpectedCount.between( 0, 1 ), method( HttpMethod.GET ) ).andExpect( header( "Authorization", "Basic Zmhpcl9jbGllbnQ6Zmhpcl9jbGllbnRfMQ==" ) ) + .andExpect( requestTo( dhis2BaseUrl + "/api/" + dhis2ApiVersion + "/organisationUnits.json?paging=false&fields=lastUpdated,id,code,name,shortName,displayName,level,openingDate,closedDate,coordinates,leaf,parent%5Bid%5D&filter=code:eq:OU_4567" ) ) + .andRespond( withSuccess( IOUtils.resourceToString( "/org/dhis2/fhir/adapter/dhis/test/default-org-unit-OU_4567.json", StandardCharsets.UTF_8 ), MediaType.APPLICATION_JSON ) ); + + final IGenericClient client = createGenericClient(); + client.registerInterceptor( new BasicAuthInterceptor( "fhir_client", "fhir_client_1" ) ); + Bundle bundle = client.search().forResource( PlanDefinition.class ).where( PlanDefinition.NAME.matches().value( "Test" ) ).returnBundle( Bundle.class ).execute(); + Assert.assertEquals( 2, bundle.getEntry().size() ); + PlanDefinition planDefinition = (PlanDefinition) bundle.getEntry().get( 0 ).getResource(); + Assert.assertEquals( "Test Hospital", planDefinition.getName() ); + Assert.assertEquals( 1, planDefinition.getIdentifier().size() ); + Assert.assertEquals( "http://www.dhis2.org/dhis2fhiradapter/systems/planDefinition-identifier", planDefinition.getIdentifier().get( 0 ).getSystem() ); + Assert.assertEquals( "OU_1234", planDefinition.getIdentifier().get( 0 ).getValue() ); + planDefinition = (PlanDefinition) bundle.getEntry().get( 1 ).getResource(); + Assert.assertEquals( "Test Hospital 2", planDefinition.getName() ); + Assert.assertEquals( 1, planDefinition.getIdentifier().size() ); + Assert.assertEquals( "http://www.dhis2.org/dhis2fhiradapter/systems/planDefinition-identifier", planDefinition.getIdentifier().get( 0 ).getSystem() ); + Assert.assertEquals( "OU_4567", planDefinition.getIdentifier().get( 0 ).getValue() ); + } + + @Test + public void searchParentPlanDefinition() throws Exception + { + userDhis2Server.expect( ExpectedCount.once(), method( HttpMethod.GET ) ).andExpect( header( "Authorization", "Basic Zmhpcl9jbGllbnQ6Zmhpcl9jbGllbnRfMQ==" ) ) + .andExpect( requestTo( dhis2BaseUrl + "/api/" + dhis2ApiVersion + "/organisationUnits.json?filter=parent.id:eq:bdXIaLNUNEp&paging=true&page=1&pageSize=10&order=id" + + "&fields=lastUpdated,id,code,name,shortName,displayName,level,openingDate,closedDate,coordinates,leaf,parent%5Bid%5D" ) ) + .andRespond( withSuccess( IOUtils.resourceToString( "/org/dhis2/fhir/adapter/dhis/test/multi-org-unit.json", StandardCharsets.UTF_8 ), MediaType.APPLICATION_JSON ) ); + userDhis2Server.expect( ExpectedCount.between( 0, 1 ), method( HttpMethod.GET ) ).andExpect( header( "Authorization", "Basic Zmhpcl9jbGllbnQ6Zmhpcl9jbGllbnRfMQ==" ) ) + .andExpect( requestTo( dhis2BaseUrl + "/api/" + dhis2ApiVersion + "/organisationUnits.json?paging=false&fields=lastUpdated,id,code,name,shortName,displayName,level,openingDate,closedDate,coordinates,leaf,parent%5Bid%5D&filter=code:eq:OU_1234" ) ) + .andRespond( withSuccess( IOUtils.resourceToString( "/org/dhis2/fhir/adapter/dhis/test/default-org-unit-OU_1234.json", StandardCharsets.UTF_8 ), MediaType.APPLICATION_JSON ) ); + userDhis2Server.expect( ExpectedCount.between( 0, 1 ), method( HttpMethod.GET ) ).andExpect( header( "Authorization", "Basic Zmhpcl9jbGllbnQ6Zmhpcl9jbGllbnRfMQ==" ) ) + .andExpect( requestTo( dhis2BaseUrl + "/api/" + dhis2ApiVersion + "/organisationUnits.json?paging=false&fields=lastUpdated,id,code,name,shortName,displayName,level,openingDate,closedDate,coordinates,leaf,parent%5Bid%5D&filter=code:eq:OU_4567" ) ) + .andRespond( withSuccess( IOUtils.resourceToString( "/org/dhis2/fhir/adapter/dhis/test/default-org-unit-OU_4567.json", StandardCharsets.UTF_8 ), MediaType.APPLICATION_JSON ) ); + + final IGenericClient client = createGenericClient(); + client.registerInterceptor( new BasicAuthInterceptor( "fhir_client", "fhir_client_1" ) ); + Bundle bundle = client.search().forResource( PlanDefinition.class ).where( PlanDefinition.NAME.matchesExactly().value( "ChildProgram" ) ).returnBundle( Bundle.class ).execute(); + Assert.assertEquals( 2, bundle.getEntry().size() ); + PlanDefinition planDefinition = (PlanDefinition) bundle.getEntry().get( 0 ).getResource(); + Assert.assertEquals( "Test Hospital", planDefinition.getName() ); + Assert.assertEquals( 1, planDefinition.getIdentifier().size() ); + Assert.assertEquals( "http://www.dhis2.org/dhis2fhiradapter/systems/planDefinition-identifier", planDefinition.getIdentifier().get( 0 ).getSystem() ); + Assert.assertEquals( "OU_1234", planDefinition.getIdentifier().get( 0 ).getValue() ); + planDefinition = (PlanDefinition) bundle.getEntry().get( 1 ).getResource(); + Assert.assertEquals( "Test Hospital 2", planDefinition.getName() ); + Assert.assertEquals( 1, planDefinition.getIdentifier().size() ); + Assert.assertEquals( "http://www.dhis2.org/dhis2fhiradapter/systems/planDefinition-identifier", planDefinition.getIdentifier().get( 0 ).getSystem() ); + Assert.assertEquals( "OU_4567", planDefinition.getIdentifier().get( 0 ).getValue() ); + } +} diff --git a/dhis/src/main/java/org/dhis2/fhir/adapter/dhis/model/DhisResourceType.java b/dhis/src/main/java/org/dhis2/fhir/adapter/dhis/model/DhisResourceType.java index 654008ad..80d8238f 100644 --- a/dhis/src/main/java/org/dhis2/fhir/adapter/dhis/model/DhisResourceType.java +++ b/dhis/src/main/java/org/dhis2/fhir/adapter/dhis/model/DhisResourceType.java @@ -44,37 +44,37 @@ public enum DhisResourceType /** * Resource is a tracked entity instance. */ - TRACKED_ENTITY( "trackedEntityInstances", "te" ), + TRACKED_ENTITY( "trackedEntityInstances", "te", "TrackedEntityRule" ), /** * Resource is a tracked entity type. */ - TRACKED_ENTITY_TYPE( "trackedEntityTypes", "tt" ), + TRACKED_ENTITY_TYPE( "trackedEntityTypes", "tt", "TrackedEntityTypeRule" ), /** * The program metadata. */ - PROGRAM_METADATA( "programs", "pm" ), + PROGRAM_METADATA( "programs", "pm", "ProgramMetadataRule" ), /** * The program stage metadata. */ - PROGRAM_STAGE_METADATA( "programStages", "sm" ), + PROGRAM_STAGE_METADATA( "programStages", "sm", "ProgramStageMetadataRule" ), /** * Resource is a program instance (aka enrollment). */ - ENROLLMENT( "enrollments", "en" ), + ENROLLMENT( "enrollments", "en", "EnrollmentRule" ), /** * Resource is a program stage instance (aka event of a program instance). */ - PROGRAM_STAGE_EVENT( "events", "ps" ), + PROGRAM_STAGE_EVENT( "events", "ps", "ProgramStageRule" ), /** * Resource is a organisation unit. */ - ORGANIZATION_UNIT( "organisationUnits", "ou" ); + ORGANIZATION_UNIT( "organisationUnits", "ou", "OrganizationUnitRule" ); private static final Map byTypeName = Arrays.stream( values() ).collect( Collectors.toMap( DhisResourceType::getTypeName, v -> v ) ); @@ -96,10 +96,13 @@ public static DhisResourceType getByAbbreviation( @Nullable String abbreviation private final String abbreviation; - DhisResourceType( @Nonnull String typeName, @Nonnull String abbreviation ) + private final String ruleType; + + DhisResourceType( @Nonnull String typeName, @Nonnull String abbreviation, @Nonnull String ruleType ) { this.typeName = typeName; this.abbreviation = abbreviation; + this.ruleType = ruleType; } @Nonnull @@ -113,4 +116,10 @@ public String getAbbreviation() { return abbreviation; } + + @Nonnull + public String getRuleType() + { + return ruleType; + } } diff --git a/fhir-r4/src/main/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/metadata/r4/R4ProgramMetadataToFhirCarePlanTransformer.java b/fhir-r4/src/main/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/metadata/r4/R4ProgramMetadataToFhirPlanDefinitionTransformer.java similarity index 92% rename from fhir-r4/src/main/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/metadata/r4/R4ProgramMetadataToFhirCarePlanTransformer.java rename to fhir-r4/src/main/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/metadata/r4/R4ProgramMetadataToFhirPlanDefinitionTransformer.java index 4fbd6e20..6002c267 100644 --- a/fhir-r4/src/main/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/metadata/r4/R4ProgramMetadataToFhirCarePlanTransformer.java +++ b/fhir-r4/src/main/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/metadata/r4/R4ProgramMetadataToFhirPlanDefinitionTransformer.java @@ -42,7 +42,7 @@ import org.dhis2.fhir.adapter.fhir.repository.FhirResourceRepository; import org.dhis2.fhir.adapter.fhir.script.ScriptExecutor; import org.dhis2.fhir.adapter.fhir.transform.dhis.DhisToFhirTransformerContext; -import org.dhis2.fhir.adapter.fhir.transform.dhis.impl.metadata.program.AbstractProgramMetadataToFhirCarePlanTransformer; +import org.dhis2.fhir.adapter.fhir.transform.dhis.impl.metadata.program.AbstractProgramMetadataToFhirPlanDefinitionTransformer; import org.dhis2.fhir.adapter.fhir.transform.scripted.AccessibleScriptedDhisMetadata; import org.dhis2.fhir.adapter.lock.LockManager; import org.hl7.fhir.instance.model.api.IBaseResource; @@ -62,9 +62,9 @@ * @author volsch */ @Component -public class R4ProgramMetadataToFhirCarePlanTransformer extends AbstractProgramMetadataToFhirCarePlanTransformer +public class R4ProgramMetadataToFhirPlanDefinitionTransformer extends AbstractProgramMetadataToFhirPlanDefinitionTransformer { - public R4ProgramMetadataToFhirCarePlanTransformer( @Nonnull ScriptExecutor scriptExecutor, @Nonnull LockManager lockManager, @Nonnull SystemRepository systemRepository, @Nonnull FhirResourceRepository fhirResourceRepository, + public R4ProgramMetadataToFhirPlanDefinitionTransformer( @Nonnull ScriptExecutor scriptExecutor, @Nonnull LockManager lockManager, @Nonnull SystemRepository systemRepository, @Nonnull FhirResourceRepository fhirResourceRepository, @Nonnull FhirDhisAssignmentRepository fhirDhisAssignmentRepository, @Nonnull OrganizationUnitService organizationUnitService, @Nonnull TrackedEntityMetadataService trackedEntityMetadataService, @Nonnull TrackedEntityRuleRepository trackedEntityRuleRepository ) { diff --git a/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/metadata/model/AbstractRule.java b/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/metadata/model/AbstractRule.java index b5bf83f7..14e64cc8 100644 --- a/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/metadata/model/AbstractRule.java +++ b/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/metadata/model/AbstractRule.java @@ -98,8 +98,7 @@ "SELECT r FROM AbstractRule r JOIN r.applicableCodeSet acs WHERE r.fhirResourceType=:fhirResourceType AND r.enabled=true " + "AND r.expEnabled=true AND acs.code IN (:codeSetCodes)" ), @NamedQuery( name = AbstractRule.FIND_IMP_RULE_BY_ID_NAMED_QUERY, query = - "SELECT r FROM AbstractRule r WHERE r.fhirResourceType=:fhirResourceType AND TYPE(r)=:dhisResourceType AND r.id=:ruleId AND r.enabled=true AND r.impEnabled=true" ), - @NamedQuery( name = AbstractRule.FIND_ALL_EXP_NAMED_QUERY, query = "SELECT our FROM OrganizationUnitRule our WHERE our.enabled=true AND our.expEnabled=true AND (our.fhirCreateEnabled=true OR our.fhirUpdateEnabled=true)" ) + "SELECT r FROM AbstractRule r WHERE r.fhirResourceType=:fhirResourceType AND TYPE(r)=:dhisResourceType AND r.id=:ruleId AND r.enabled=true AND r.impEnabled=true" ) } ) @JsonTypeInfo( use = JsonTypeInfo.Id.NAME, property = "dhisResourceType", include = JsonTypeInfo.As.EXISTING_PROPERTY ) @JsonSubTypes( { @@ -129,8 +128,6 @@ public abstract class AbstractRule extends VersionedBaseMetadata implements Seri public static final String FIND_IMP_RULE_BY_ID_NAMED_QUERY = "AbstractRule.findImpById"; - public static final String FIND_ALL_EXP_NAMED_QUERY = "AbstractRule.findAllExp"; - public static final int MAX_NAME_LENGTH = 230; @NotBlank diff --git a/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/metadata/repository/CustomRuleRepository.java b/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/metadata/repository/CustomRuleRepository.java index fcd443a4..f1b83c57 100644 --- a/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/metadata/repository/CustomRuleRepository.java +++ b/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/metadata/repository/CustomRuleRepository.java @@ -67,6 +67,6 @@ public interface CustomRuleRepository @RestResource( exported = false ) @Nonnull - Collection> findAllExp(); + Collection> findAllExp( @Nonnull DhisResourceType dhisResourceType ); } diff --git a/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/metadata/repository/impl/CustomRuleRepositoryImpl.java b/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/metadata/repository/impl/CustomRuleRepositoryImpl.java index 5305b436..5f070628 100644 --- a/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/metadata/repository/impl/CustomRuleRepositoryImpl.java +++ b/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/metadata/repository/impl/CustomRuleRepositoryImpl.java @@ -111,11 +111,12 @@ public List> findAllExpByInputData( @Nonnull Fh @RestResource( exported = false ) @Nonnull - @Cacheable( key = "{#root.methodName}", cacheManager = "metadataCacheManager", cacheNames = "rule" ) + @Cacheable( key = "{#root.methodName, #a0}", cacheManager = "metadataCacheManager", cacheNames = "rule" ) @Transactional( readOnly = true ) - public Collection> findAllExp() + public Collection> findAllExp( @Nonnull DhisResourceType dhisResourceType ) { - final List rules = entityManager.createNamedQuery( AbstractRule.FIND_ALL_EXP_NAMED_QUERY, AbstractRule.class ).getResultList(); + final List rules = entityManager.createQuery( "SELECT r FROM " + dhisResourceType.getRuleType() + + " r WHERE r.enabled=true AND r.expEnabled=true AND (r.fhirCreateEnabled=true OR r.fhirUpdateEnabled=true)", AbstractRule.class ).getResultList(); return rules.stream().map( r -> { Hibernate.initialize( r.getDhisDataReferences() ); diff --git a/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/metadata/AbstractDhisMetadataToFhirRequestResolver.java b/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/metadata/AbstractDhisMetadataToFhirRequestResolver.java index 97782994..574e8695 100644 --- a/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/metadata/AbstractDhisMetadataToFhirRequestResolver.java +++ b/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/metadata/AbstractDhisMetadataToFhirRequestResolver.java @@ -61,7 +61,7 @@ protected AbstractDhisMetadataToFhirRequestResolver( @Nonnull FhirClientReposito @Override public List> resolveRules( @Nonnull ScriptedDhisResource dhisResource ) { - return ruleRepository.findAllExp().stream().sorted().collect( Collectors.toList() ); + return ruleRepository.findAllExp( dhisResource.getResourceType() ).stream().sorted().collect( Collectors.toList() ); } @Nonnull diff --git a/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/metadata/program/AbstractProgramMetadataToFhirCarePlanTransformer.java b/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/metadata/program/AbstractProgramMetadataToFhirPlanDefinitionTransformer.java similarity index 92% rename from fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/metadata/program/AbstractProgramMetadataToFhirCarePlanTransformer.java rename to fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/metadata/program/AbstractProgramMetadataToFhirPlanDefinitionTransformer.java index 8707f71d..bb7aaefe 100644 --- a/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/metadata/program/AbstractProgramMetadataToFhirCarePlanTransformer.java +++ b/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/metadata/program/AbstractProgramMetadataToFhirPlanDefinitionTransformer.java @@ -59,7 +59,7 @@ * @param the concrete type of the FHIR resource into which the DHIS2 resource should be transformed. * @author volsch */ -public abstract class AbstractProgramMetadataToFhirCarePlanTransformer extends AbstractReadOnlyDhisMetadataToTypedFhirTransformer +public abstract class AbstractProgramMetadataToFhirPlanDefinitionTransformer extends AbstractReadOnlyDhisMetadataToTypedFhirTransformer { private final Logger logger = LoggerFactory.getLogger( getClass() ); @@ -67,7 +67,7 @@ public abstract class AbstractProgramMetadataToFhirCarePlanTransformer Date: Mon, 29 Jul 2019 16:30:30 +0200 Subject: [PATCH 02/16] Adds tests for program to plan definition transformation. --- .../main/resources/default-application.yml | 1 + .../r4/R4ProgramMetadataFhirRestAppTest.java | 91 +++----- .../adapter/dhis/test/default-program.json | 1 + ...ProgramMetadataToFhirDataProviderTest.java | 93 ++++++++ ...taToFhirPlanDefinitionTransformerTest.java | 171 +++++++++++++++ .../impl/AbstractDhisToFhirDataProvider.java | 4 +- .../AbstractDhisToFhirRequestResolver.java | 4 +- .../impl/AbstractDhisToFhirTransformer.java | 33 +++ .../dhis/impl/DhisToFhirRuleComparator.java | 6 +- .../DhisToFhirTransformerContextImpl.java | 3 + .../DhisToFhirTransformerRequestImpl.java | 7 +- .../DhisToFhirTransformerServiceImpl.java | 17 ++ .../dhis/impl/TransformerRequestKey.java | 16 +- .../dhis/search/SearchFilterCollector.java | 6 +- ...0.41_10_0__Plan_Definition_Sample_Data.sql | 38 ++++ ...taToFhirPlanDefinitionTransformerTest.java | 206 ++++++++++++++++++ 16 files changed, 624 insertions(+), 73 deletions(-) create mode 100644 fhir-r4/src/test/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/metadata/r4/R4ProgramMetadataToFhirDataProviderTest.java create mode 100644 fhir-r4/src/test/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/metadata/r4/R4ProgramMetadataToFhirPlanDefinitionTransformerTest.java create mode 100644 fhir/src/main/resources/db/migration/sample/V1.1.0.41_10_0__Plan_Definition_Sample_Data.sql create mode 100644 fhir/src/test/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/metadata/program/AbstractProgramMetadataToFhirPlanDefinitionTransformerTest.java diff --git a/app/src/main/resources/default-application.yml b/app/src/main/resources/default-application.yml index e4fc83f5..1feb5c15 100644 --- a/app/src/main/resources/default-application.yml +++ b/app/src/main/resources/default-application.yml @@ -356,6 +356,7 @@ dhis2.fhir-adapter: # DHIS 2 resource types that will be synchronized. resource-types: - ORGANIZATION_UNIT + - PROGRAM_METADATA - TRACKED_ENTITY - PROGRAM_STAGE_EVENT # The queue that is used to store distributed requests to synchronize the data from diff --git a/app/src/test/java/org/dhis2/fhir/adapter/fhir/server/r4/R4ProgramMetadataFhirRestAppTest.java b/app/src/test/java/org/dhis2/fhir/adapter/fhir/server/r4/R4ProgramMetadataFhirRestAppTest.java index e7d1c616..1402d045 100644 --- a/app/src/test/java/org/dhis2/fhir/adapter/fhir/server/r4/R4ProgramMetadataFhirRestAppTest.java +++ b/app/src/test/java/org/dhis2/fhir/adapter/fhir/server/r4/R4ProgramMetadataFhirRestAppTest.java @@ -38,7 +38,6 @@ import org.hl7.fhir.r4.model.Bundle; import org.hl7.fhir.r4.model.PlanDefinition; import org.junit.Assert; -import org.junit.Ignore; import org.junit.Test; import org.springframework.http.HttpMethod; import org.springframework.http.HttpStatus; @@ -58,7 +57,6 @@ * * @author volsch */ -@Ignore public class R4ProgramMetadataFhirRestAppTest extends AbstractAppTest { @Nonnull @@ -205,19 +203,22 @@ private void getPlanDefinitionRuleNotFound() public void getPlanDefinitionByIdentifierWithoutAuthorization() { final IGenericClient client = createGenericClient(); - client.search().forResource( PlanDefinition.class ).where( PlanDefinition.IDENTIFIER.exactly().systemAndIdentifier( "http://www.dhis2.org/dhis2fhiradapter/systems/plan-definition-identifier", "XX_1234" ) ).returnBundle( Bundle.class ).execute(); + client.search().forResource( PlanDefinition.class ).where( PlanDefinition.IDENTIFIER.exactly().systemAndIdentifier( "http://www.dhis2.org/dhis2-fhir-adapter/systems/plan-definition-identifier", "XX_1234" ) ).returnBundle( Bundle.class ).execute(); } @Test( expected = AuthenticationException.class ) public void getPlanDefinitionByIdentifierInvalidAuthorization() { userDhis2Server.expect( ExpectedCount.once(), method( HttpMethod.GET ) ).andExpect( header( "Authorization", "Basic Zmhpcl9jbGllbnQ6aW52YWxpZF8x" ) ) - .andExpect( requestTo( dhis2BaseUrl + "/api/" + dhis2ApiVersion + "/organisationUnits.json?paging=false&fields=lastUpdated,id,code,name,shortName,displayName,level,openingDate,closedDate,coordinates,leaf,parent%5Bid%5D&filter=code:eq:OU_1234" ) ) + .andExpect( requestTo( dhis2BaseUrl + "/api/" + dhis2ApiVersion + "/programs.json?paging=false&fields=id,name,code,description,selectIncidentDatesInFuture,selectEnrollmentDatesInFuture,displayIncidentDate,registration,withoutRegistration," + + "captureCoordinates,trackedEntityType%5Bid%5D,programTrackedEntityAttributes%5Bid,name,valueType,mandatory,allowFutureDate,trackedEntityAttribute%5Bid,name,code,valueType,generated%5D%5D,programStages%5Bid,name,description,repeatable," + + "captureCoordinates,generatedByEnrollmentDate,minDaysFromStart,programStageDataElements%5Bid,compulsory,allowProvidedElsewhere,dataElement%5Bid,name,code,formName,valueType,optionSetValue,optionSet%5Bid,name,options%5Bcode," + + "name%5D%5D%5D%5D%5D&filter=code:eq:OU_1234" ) ) .andRespond( withStatus( HttpStatus.UNAUTHORIZED ) ); final IGenericClient client = createGenericClient(); client.registerInterceptor( new BasicAuthInterceptor( "fhir_client", "invalid_1" ) ); - client.search().forResource( PlanDefinition.class ).where( PlanDefinition.IDENTIFIER.exactly().systemAndIdentifier( "http://www.dhis2.org/dhis2fhiradapter/systems/planDefinition-identifier", "OU_1234" ) ).returnBundle( Bundle.class ).execute(); + client.search().forResource( PlanDefinition.class ).where( PlanDefinition.IDENTIFIER.exactly().systemAndIdentifier( "http://www.dhis2.org/dhis2-fhir-adapter/systems/plan-definition-identifier", "OU_1234" ) ).returnBundle( Bundle.class ).execute(); } @Test @@ -232,20 +233,24 @@ private void getPlanDefinitionByIdentifier() throws Exception systemDhis2Server.reset(); userDhis2Server.reset(); + systemDhis2Server.expect( ExpectedCount.between( 0, 1 ), method( HttpMethod.GET ) ).andExpect( header( "Authorization", testConfiguration.getDhis2SystemAuthorization() ) ) + .andExpect( requestTo( dhis2BaseUrl + "/api/" + dhis2ApiVersion + "/trackedEntityTypes/MCPQUTHX1Ze.json?" + + "fields=id,name,trackedEntityTypeAttributes%5Bid,name,valueType,mandatory,trackedEntityAttribute%5Bid,name,code,valueType,generated,optionSetValue,optionSet%5Bid,name,options%5Bcode,name%5D%5D%5D%5D" ) ) + .andRespond( withSuccess( IOUtils.resourceToString( "/org/dhis2/fhir/adapter/dhis/test/single-tracked-entity-type.json", StandardCharsets.UTF_8 ), MediaType.APPLICATION_JSON ) ); userDhis2Server.expect( ExpectedCount.between( 1, 2 ), method( HttpMethod.GET ) ).andExpect( header( "Authorization", "Basic Zmhpcl9jbGllbnQ6Zmhpcl9jbGllbnRfMQ==" ) ) - .andExpect( requestTo( dhis2BaseUrl + "/api/" + dhis2ApiVersion + "/organisationUnits.json?paging=false&fields=lastUpdated,id,code,name,shortName,displayName,level,openingDate,closedDate,coordinates,leaf,parent%5Bid%5D&filter=code:eq:OU_1234" ) ) - .andRespond( withSuccess( IOUtils.resourceToString( "/org/dhis2/fhir/adapter/dhis/test/default-org-unit-OU_1234.json", StandardCharsets.UTF_8 ), MediaType.APPLICATION_JSON ) ); + .andExpect( requestTo( dhis2BaseUrl + "/api/" + dhis2ApiVersion + "/programs.json?paging=false&fields=id,name,code,description,selectIncidentDatesInFuture,selectEnrollmentDatesInFuture,displayIncidentDate,registration,withoutRegistration," + + "captureCoordinates,trackedEntityType%5Bid%5D,programTrackedEntityAttributes%5Bid,name,valueType,mandatory,allowFutureDate,trackedEntityAttribute%5Bid,name,code,valueType,generated%5D%5D,programStages%5Bid,name,description,repeatable," + + "captureCoordinates,generatedByEnrollmentDate,minDaysFromStart,programStageDataElements%5Bid,compulsory,allowProvidedElsewhere,dataElement%5Bid,name,code,formName,valueType,optionSetValue,optionSet%5Bid,name,options%5Bcode," + + "name%5D%5D%5D%5D%5D&filter=code:eq:PD_1234" ) ) + .andRespond( withSuccess( IOUtils.resourceToString( "/org/dhis2/fhir/adapter/dhis/test/default-program.json", StandardCharsets.UTF_8 ), MediaType.APPLICATION_JSON ) ); final IGenericClient client = createGenericClient(); client.registerInterceptor( new BasicAuthInterceptor( "fhir_client", "fhir_client_1" ) ); Bundle bundle = - client.search().forResource( PlanDefinition.class ).where( PlanDefinition.IDENTIFIER.exactly().systemAndIdentifier( "http://www.dhis2.org/dhis2fhiradapter/systems/planDefinition-identifier", "OU_1234" ) ).returnBundle( Bundle.class ).execute(); + client.search().forResource( PlanDefinition.class ).where( PlanDefinition.IDENTIFIER.exactly().systemAndIdentifier( "http://www.dhis2.org/dhis2-fhir-adapter/systems/plan-definition-identifier", "PD_1234" ) ).returnBundle( Bundle.class ).execute(); Assert.assertEquals( 1, bundle.getEntry().size() ); PlanDefinition planDefinition = (PlanDefinition) bundle.getEntry().get( 0 ).getResource(); - Assert.assertEquals( "Test Hospital", planDefinition.getName() ); - Assert.assertEquals( 1, planDefinition.getIdentifier().size() ); - Assert.assertEquals( "http://www.dhis2.org/dhis2fhiradapter/systems/planDefinition-identifier", planDefinition.getIdentifier().get( 0 ).getSystem() ); - Assert.assertEquals( "OU_1234", planDefinition.getIdentifier().get( 0 ).getValue() ); + Assert.assertEquals( "Child Programme", planDefinition.getName() ); systemDhis2Server.verify(); userDhis2Server.verify(); @@ -276,60 +281,22 @@ public void searchPlanDefinitionInvalidAuthorization() @Test public void searchPlanDefinition() throws Exception { + systemDhis2Server.expect( ExpectedCount.between( 0, 1 ), method( HttpMethod.GET ) ).andExpect( header( "Authorization", testConfiguration.getDhis2SystemAuthorization() ) ) + .andExpect( requestTo( dhis2BaseUrl + "/api/" + dhis2ApiVersion + "/trackedEntityTypes/MCPQUTHX1Ze.json?" + + "fields=id,name,trackedEntityTypeAttributes%5Bid,name,valueType,mandatory,trackedEntityAttribute%5Bid,name,code,valueType,generated,optionSetValue,optionSet%5Bid,name,options%5Bcode,name%5D%5D%5D%5D" ) ) + .andRespond( withSuccess( IOUtils.resourceToString( "/org/dhis2/fhir/adapter/dhis/test/single-tracked-entity-type.json", StandardCharsets.UTF_8 ), MediaType.APPLICATION_JSON ) ); userDhis2Server.expect( ExpectedCount.once(), method( HttpMethod.GET ) ).andExpect( header( "Authorization", "Basic Zmhpcl9jbGllbnQ6Zmhpcl9jbGllbnRfMQ==" ) ) - .andExpect( requestTo( dhis2BaseUrl + "/api/" + dhis2ApiVersion + "/organisationUnits.json?filter=name:$ilike:Test&paging=true&page=1&pageSize=10&order=id" + - "&fields=lastUpdated,id,code,name,shortName,displayName,level,openingDate,closedDate,coordinates,leaf,parent%5Bid%5D" ) ) - .andRespond( withSuccess( IOUtils.resourceToString( "/org/dhis2/fhir/adapter/dhis/test/multi-org-unit.json", StandardCharsets.UTF_8 ), MediaType.APPLICATION_JSON ) ); - userDhis2Server.expect( ExpectedCount.between( 0, 1 ), method( HttpMethod.GET ) ).andExpect( header( "Authorization", "Basic Zmhpcl9jbGllbnQ6Zmhpcl9jbGllbnRfMQ==" ) ) - .andExpect( requestTo( dhis2BaseUrl + "/api/" + dhis2ApiVersion + "/organisationUnits.json?paging=false&fields=lastUpdated,id,code,name,shortName,displayName,level,openingDate,closedDate,coordinates,leaf,parent%5Bid%5D&filter=code:eq:OU_1234" ) ) - .andRespond( withSuccess( IOUtils.resourceToString( "/org/dhis2/fhir/adapter/dhis/test/default-org-unit-OU_1234.json", StandardCharsets.UTF_8 ), MediaType.APPLICATION_JSON ) ); - userDhis2Server.expect( ExpectedCount.between( 0, 1 ), method( HttpMethod.GET ) ).andExpect( header( "Authorization", "Basic Zmhpcl9jbGllbnQ6Zmhpcl9jbGllbnRfMQ==" ) ) - .andExpect( requestTo( dhis2BaseUrl + "/api/" + dhis2ApiVersion + "/organisationUnits.json?paging=false&fields=lastUpdated,id,code,name,shortName,displayName,level,openingDate,closedDate,coordinates,leaf,parent%5Bid%5D&filter=code:eq:OU_4567" ) ) - .andRespond( withSuccess( IOUtils.resourceToString( "/org/dhis2/fhir/adapter/dhis/test/default-org-unit-OU_4567.json", StandardCharsets.UTF_8 ), MediaType.APPLICATION_JSON ) ); - - final IGenericClient client = createGenericClient(); - client.registerInterceptor( new BasicAuthInterceptor( "fhir_client", "fhir_client_1" ) ); - Bundle bundle = client.search().forResource( PlanDefinition.class ).where( PlanDefinition.NAME.matches().value( "Test" ) ).returnBundle( Bundle.class ).execute(); - Assert.assertEquals( 2, bundle.getEntry().size() ); - PlanDefinition planDefinition = (PlanDefinition) bundle.getEntry().get( 0 ).getResource(); - Assert.assertEquals( "Test Hospital", planDefinition.getName() ); - Assert.assertEquals( 1, planDefinition.getIdentifier().size() ); - Assert.assertEquals( "http://www.dhis2.org/dhis2fhiradapter/systems/planDefinition-identifier", planDefinition.getIdentifier().get( 0 ).getSystem() ); - Assert.assertEquals( "OU_1234", planDefinition.getIdentifier().get( 0 ).getValue() ); - planDefinition = (PlanDefinition) bundle.getEntry().get( 1 ).getResource(); - Assert.assertEquals( "Test Hospital 2", planDefinition.getName() ); - Assert.assertEquals( 1, planDefinition.getIdentifier().size() ); - Assert.assertEquals( "http://www.dhis2.org/dhis2fhiradapter/systems/planDefinition-identifier", planDefinition.getIdentifier().get( 0 ).getSystem() ); - Assert.assertEquals( "OU_4567", planDefinition.getIdentifier().get( 0 ).getValue() ); - } - - @Test - public void searchParentPlanDefinition() throws Exception - { - userDhis2Server.expect( ExpectedCount.once(), method( HttpMethod.GET ) ).andExpect( header( "Authorization", "Basic Zmhpcl9jbGllbnQ6Zmhpcl9jbGllbnRfMQ==" ) ) - .andExpect( requestTo( dhis2BaseUrl + "/api/" + dhis2ApiVersion + "/organisationUnits.json?filter=parent.id:eq:bdXIaLNUNEp&paging=true&page=1&pageSize=10&order=id" + - "&fields=lastUpdated,id,code,name,shortName,displayName,level,openingDate,closedDate,coordinates,leaf,parent%5Bid%5D" ) ) - .andRespond( withSuccess( IOUtils.resourceToString( "/org/dhis2/fhir/adapter/dhis/test/multi-org-unit.json", StandardCharsets.UTF_8 ), MediaType.APPLICATION_JSON ) ); - userDhis2Server.expect( ExpectedCount.between( 0, 1 ), method( HttpMethod.GET ) ).andExpect( header( "Authorization", "Basic Zmhpcl9jbGllbnQ6Zmhpcl9jbGllbnRfMQ==" ) ) - .andExpect( requestTo( dhis2BaseUrl + "/api/" + dhis2ApiVersion + "/organisationUnits.json?paging=false&fields=lastUpdated,id,code,name,shortName,displayName,level,openingDate,closedDate,coordinates,leaf,parent%5Bid%5D&filter=code:eq:OU_1234" ) ) - .andRespond( withSuccess( IOUtils.resourceToString( "/org/dhis2/fhir/adapter/dhis/test/default-org-unit-OU_1234.json", StandardCharsets.UTF_8 ), MediaType.APPLICATION_JSON ) ); - userDhis2Server.expect( ExpectedCount.between( 0, 1 ), method( HttpMethod.GET ) ).andExpect( header( "Authorization", "Basic Zmhpcl9jbGllbnQ6Zmhpcl9jbGllbnRfMQ==" ) ) - .andExpect( requestTo( dhis2BaseUrl + "/api/" + dhis2ApiVersion + "/organisationUnits.json?paging=false&fields=lastUpdated,id,code,name,shortName,displayName,level,openingDate,closedDate,coordinates,leaf,parent%5Bid%5D&filter=code:eq:OU_4567" ) ) - .andRespond( withSuccess( IOUtils.resourceToString( "/org/dhis2/fhir/adapter/dhis/test/default-org-unit-OU_4567.json", StandardCharsets.UTF_8 ), MediaType.APPLICATION_JSON ) ); + .andExpect( requestTo( dhis2BaseUrl + "/api/" + dhis2ApiVersion + "/programs.json?filter=name:$ilike:Child%20Programme&paging=true&page=1&pageSize=10&order=id&fields=id,name,code,description,selectIncidentDatesInFuture," + + "selectEnrollmentDatesInFuture,displayIncidentDate,registration,withoutRegistration,captureCoordinates,trackedEntityType%5Bid%5D,programTrackedEntityAttributes%5Bid,name,valueType,mandatory,allowFutureDate,trackedEntityAttribute%5Bid,name," + + "code,valueType,generated%5D%5D,programStages%5Bid,name,description,repeatable,captureCoordinates,generatedByEnrollmentDate,minDaysFromStart,programStageDataElements%5Bid,compulsory,allowProvidedElsewhere,dataElement%5Bid,name,code,formName," + + "valueType,optionSetValue,optionSet%5Bid,name,options%5Bcode,name%5D%5D%5D%5D%5D" ) ) + .andRespond( withSuccess( IOUtils.resourceToString( "/org/dhis2/fhir/adapter/dhis/test/default-program.json", StandardCharsets.UTF_8 ), MediaType.APPLICATION_JSON ) ); final IGenericClient client = createGenericClient(); client.registerInterceptor( new BasicAuthInterceptor( "fhir_client", "fhir_client_1" ) ); - Bundle bundle = client.search().forResource( PlanDefinition.class ).where( PlanDefinition.NAME.matchesExactly().value( "ChildProgram" ) ).returnBundle( Bundle.class ).execute(); - Assert.assertEquals( 2, bundle.getEntry().size() ); + Bundle bundle = client.search().forResource( PlanDefinition.class ).where( PlanDefinition.NAME.matches().value( "Child Programme" ) ).returnBundle( Bundle.class ).execute(); + Assert.assertEquals( 1, bundle.getEntry().size() ); PlanDefinition planDefinition = (PlanDefinition) bundle.getEntry().get( 0 ).getResource(); - Assert.assertEquals( "Test Hospital", planDefinition.getName() ); - Assert.assertEquals( 1, planDefinition.getIdentifier().size() ); - Assert.assertEquals( "http://www.dhis2.org/dhis2fhiradapter/systems/planDefinition-identifier", planDefinition.getIdentifier().get( 0 ).getSystem() ); - Assert.assertEquals( "OU_1234", planDefinition.getIdentifier().get( 0 ).getValue() ); - planDefinition = (PlanDefinition) bundle.getEntry().get( 1 ).getResource(); - Assert.assertEquals( "Test Hospital 2", planDefinition.getName() ); - Assert.assertEquals( 1, planDefinition.getIdentifier().size() ); - Assert.assertEquals( "http://www.dhis2.org/dhis2fhiradapter/systems/planDefinition-identifier", planDefinition.getIdentifier().get( 0 ).getSystem() ); - Assert.assertEquals( "OU_4567", planDefinition.getIdentifier().get( 0 ).getValue() ); + Assert.assertEquals( "Child Programme", planDefinition.getName() ); } } diff --git a/app/src/test/resources/org/dhis2/fhir/adapter/dhis/test/default-program.json b/app/src/test/resources/org/dhis2/fhir/adapter/dhis/test/default-program.json index b1fe9749..1d348852 100644 --- a/app/src/test/resources/org/dhis2/fhir/adapter/dhis/test/default-program.json +++ b/app/src/test/resources/org/dhis2/fhir/adapter/dhis/test/default-program.json @@ -1,4 +1,5 @@ { + "pager": { "page": 1, "pageCount": 1, "total": 0, "pageSize": 1 }, "programs": [ { "name": "Child Programme", diff --git a/fhir-r4/src/test/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/metadata/r4/R4ProgramMetadataToFhirDataProviderTest.java b/fhir-r4/src/test/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/metadata/r4/R4ProgramMetadataToFhirDataProviderTest.java new file mode 100644 index 00000000..4798edd1 --- /dev/null +++ b/fhir-r4/src/test/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/metadata/r4/R4ProgramMetadataToFhirDataProviderTest.java @@ -0,0 +1,93 @@ +package org.dhis2.fhir.adapter.fhir.transform.dhis.impl.metadata.r4; + +/* + * Copyright (c) 2004-2019, University of Oslo + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * Neither the name of the HISP project nor the names of its contributors may + * be used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +import org.dhis2.fhir.adapter.dhis.tracker.program.ProgramMetadataService; +import org.dhis2.fhir.adapter.fhir.metadata.model.ProgramMetadataRule; +import org.dhis2.fhir.adapter.fhir.metadata.model.RuleInfo; +import org.dhis2.fhir.adapter.fhir.model.FhirVersion; +import org.dhis2.fhir.adapter.fhir.script.ScriptExecutor; +import org.dhis2.fhir.adapter.fhir.transform.dhis.search.SearchFilter; +import org.dhis2.fhir.adapter.fhir.transform.dhis.search.SearchFilterCollector; +import org.junit.Assert; +import org.junit.Rule; +import org.junit.Test; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.junit.MockitoJUnit; +import org.mockito.junit.MockitoRule; +import org.springframework.web.util.DefaultUriBuilderFactory; +import org.springframework.web.util.UriBuilder; + +import java.net.URI; +import java.net.URISyntaxException; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +/** + * Unit tests for {@link R4ProgramMetadataToFhirDataProvider}. + * + * @author volsch + */ +public class R4ProgramMetadataToFhirDataProviderTest +{ + @Mock + private ScriptExecutor scriptExecutor; + + @Mock + private ProgramMetadataService programMetadataService; + + @InjectMocks + private R4ProgramMetadataToFhirDataProvider provider; + + @Rule + public MockitoRule rule = MockitoJUnit.rule(); + + @Test + public void getFhirVersions() + { + Assert.assertEquals( FhirVersion.R4_ONLY, provider.getFhirVersions() ); + } + + @Test + public void searchFilterName() throws URISyntaxException + { + final SearchFilterCollector searchFilterCollector = new SearchFilterCollector( Collections.singletonMap( "name", Collections.singletonList( "Child Programme" ) ) ); + final SearchFilter searchFilter = new SearchFilter( searchFilterCollector, false ); + + provider.initSearchFilter( FhirVersion.R4, new RuleInfo<>( new ProgramMetadataRule(), Collections.emptyList() ), searchFilter ); + + final List variables = new ArrayList<>(); + UriBuilder uriBuilder = new DefaultUriBuilderFactory().builder(); + uriBuilder = searchFilterCollector.add( uriBuilder, variables ); + + Assert.assertEquals( new URI( "?filter=%5Bname:$ilike:Child%20Programme%5D" ), uriBuilder.build( variables ) ); + } +} diff --git a/fhir-r4/src/test/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/metadata/r4/R4ProgramMetadataToFhirPlanDefinitionTransformerTest.java b/fhir-r4/src/test/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/metadata/r4/R4ProgramMetadataToFhirPlanDefinitionTransformerTest.java new file mode 100644 index 00000000..3d14b07b --- /dev/null +++ b/fhir-r4/src/test/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/metadata/r4/R4ProgramMetadataToFhirPlanDefinitionTransformerTest.java @@ -0,0 +1,171 @@ +package org.dhis2.fhir.adapter.fhir.transform.dhis.impl.metadata.r4; + +/* + * Copyright (c) 2004-2019, University of Oslo + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * Neither the name of the HISP project nor the names of its contributors may + * be used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +import org.dhis2.fhir.adapter.dhis.model.Reference; +import org.dhis2.fhir.adapter.dhis.model.ReferenceType; +import org.dhis2.fhir.adapter.dhis.orgunit.OrganizationUnitService; +import org.dhis2.fhir.adapter.dhis.tracker.program.WritableProgram; +import org.dhis2.fhir.adapter.dhis.tracker.program.WritableProgramStage; +import org.dhis2.fhir.adapter.dhis.tracker.trackedentity.TrackedEntityMetadataService; +import org.dhis2.fhir.adapter.dhis.tracker.trackedentity.WritableTrackedEntityType; +import org.dhis2.fhir.adapter.fhir.data.repository.FhirDhisAssignmentRepository; +import org.dhis2.fhir.adapter.fhir.metadata.model.FhirClient; +import org.dhis2.fhir.adapter.fhir.metadata.model.ProgramMetadataRule; +import org.dhis2.fhir.adapter.fhir.metadata.model.RuleInfo; +import org.dhis2.fhir.adapter.fhir.metadata.repository.SystemRepository; +import org.dhis2.fhir.adapter.fhir.metadata.repository.TrackedEntityRuleRepository; +import org.dhis2.fhir.adapter.fhir.model.FhirVersion; +import org.dhis2.fhir.adapter.fhir.repository.FhirResourceRepository; +import org.dhis2.fhir.adapter.fhir.script.ScriptExecutionContext; +import org.dhis2.fhir.adapter.fhir.script.ScriptExecutor; +import org.dhis2.fhir.adapter.fhir.transform.dhis.DhisToFhirTransformerContext; +import org.dhis2.fhir.adapter.fhir.transform.scripted.WritableScriptedDhisMetadata; +import org.dhis2.fhir.adapter.lock.LockManager; +import org.hl7.fhir.r4.model.PlanDefinition; +import org.junit.Assert; +import org.junit.Rule; +import org.junit.Test; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.Mockito; +import org.mockito.junit.MockitoJUnit; +import org.mockito.junit.MockitoRule; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Optional; + +/** + * Unit tests for {@link R4ProgramMetadataToFhirPlanDefinitionTransformer}. + * + * @author volsch + */ +public class R4ProgramMetadataToFhirPlanDefinitionTransformerTest +{ + @Mock + private ScriptExecutor scriptExecutor; + + @Mock + private LockManager lockManager; + + @Mock + private SystemRepository systemRepository; + + @Mock + private FhirResourceRepository fhirResourceRepository; + + @Mock + private FhirDhisAssignmentRepository fhirDhisAssignmentRepository; + + @Mock + private OrganizationUnitService organizationUnitService; + + @Mock + private TrackedEntityMetadataService trackedEntityMetadataService; + + @Mock + private TrackedEntityRuleRepository trackedEntityRuleRepository; + + @Mock + private DhisToFhirTransformerContext context; + + @Mock + private FhirClient fhirClient; + + @Mock + private ScriptExecutionContext scriptExecutionContext; + + @InjectMocks + private R4ProgramMetadataToFhirPlanDefinitionTransformer transformer; + + @Rule + public MockitoRule rule = MockitoJUnit.rule(); + + @Test + public void getFhirVersions() + { + Assert.assertEquals( FhirVersion.R4_ONLY, transformer.getFhirVersions() ); + } + + @Test + public void transformInternal() + { + Mockito.doReturn( Optional.of( new WritableTrackedEntityType( "a1234567890", "Test", Collections.emptyList() ) ) ) + .when( trackedEntityMetadataService ).findTypeByReference( Mockito.eq( new Reference( "a1234567890", ReferenceType.ID ) ) ); + Mockito.doReturn( Collections.singletonList( new RuleInfo<>( new ProgramMetadataRule(), Collections.emptyList() ) ) ) + .when( trackedEntityRuleRepository ).findByTypeRefs( Mockito.eq( new HashSet<>( + Arrays.asList( new Reference( "a1234567890", ReferenceType.ID ), new Reference( "Test", ReferenceType.NAME ) ) ) ) ); + + final WritableProgram program = new WritableProgram(); + program.setName( "Test Program" ); + program.setDescription( "Test Description" ); + program.setWithoutRegistration( false ); + program.setTrackedEntityTypeId( "a1234567890" ); + program.setStages( new ArrayList<>() ); + + WritableProgramStage programStage = new WritableProgramStage(); + programStage.setId( "b1234567890" ); + programStage.setName( "Test Stage 1" ); + programStage.setDescription( "Test Description 1" ); + programStage.setRepeatable( false ); + program.getStages().add( programStage ); + + programStage = new WritableProgramStage(); + programStage.setId( "b1234567891" ); + programStage.setName( "Test Stage 2" ); + programStage.setDescription( "Test Description 2" ); + programStage.setRepeatable( true ); + program.getStages().add( programStage ); + + final PlanDefinition fhirPlanDefinition = new PlanDefinition(); + + transformer.transformInternal( fhirClient, context, new RuleInfo<>( new ProgramMetadataRule(), Collections.emptyList() ), new HashMap<>(), + new WritableScriptedDhisMetadata( program, scriptExecutionContext ), fhirPlanDefinition ); + + Assert.assertEquals( "Test Program", fhirPlanDefinition.getName() ); + Assert.assertEquals( "Test Description", fhirPlanDefinition.getDescription() ); + Assert.assertEquals( 2, fhirPlanDefinition.getAction().size() ); + + Assert.assertEquals( "b1234567890", fhirPlanDefinition.getAction().get( 0 ).getId() ); + Assert.assertEquals( "Test Stage 1", fhirPlanDefinition.getAction().get( 0 ).getTitle() ); + Assert.assertEquals( "Test Description 1", fhirPlanDefinition.getAction().get( 0 ).getDescription() ); + Assert.assertEquals( PlanDefinition.ActionCardinalityBehavior.SINGLE, fhirPlanDefinition.getAction().get( 0 ).getCardinalityBehavior() ); + Assert.assertEquals( "Questionnaire/b1234567890", fhirPlanDefinition.getAction().get( 0 ).getDefinition().primitiveValue() ); + + Assert.assertEquals( "b1234567891", fhirPlanDefinition.getAction().get( 1 ).getId() ); + Assert.assertEquals( "Test Stage 2", fhirPlanDefinition.getAction().get( 1 ).getTitle() ); + Assert.assertEquals( "Test Description 2", fhirPlanDefinition.getAction().get( 1 ).getDescription() ); + Assert.assertEquals( PlanDefinition.ActionCardinalityBehavior.MULTIPLE, fhirPlanDefinition.getAction().get( 1 ).getCardinalityBehavior() ); + Assert.assertEquals( "Questionnaire/b1234567891", fhirPlanDefinition.getAction().get( 1 ).getDefinition().primitiveValue() ); + } +} diff --git a/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/AbstractDhisToFhirDataProvider.java b/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/AbstractDhisToFhirDataProvider.java index fb0448d5..ca186c88 100644 --- a/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/AbstractDhisToFhirDataProvider.java +++ b/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/AbstractDhisToFhirDataProvider.java @@ -110,8 +110,8 @@ public DhisResource findByDhisFhirIdentifierCasted( @Nonnull FhirClient fhirClie public PreparedDhisToFhirSearch prepareSearchCasted( @Nonnull FhirVersion fhirVersion, @Nonnull List> ruleInfos, @Nullable Map> filter, @Nullable DateRangeParam lastUpdatedDateRange, int count ) throws DhisToFhirDataProviderException { final Class ruleClass = getRuleClass(); - final List> castedRuleInfos = - ruleInfos.stream().map( r -> new RuleInfo<>( getRuleClass().cast( r.getRule() ), r.getDhisDataReferences() ) ).collect( Collectors.toList() ); + final List> castedRuleInfos = ruleInfos.stream().map( r -> new RuleInfo<>( getRuleClass().cast( r.getRule() ), r.getDhisDataReferences() ) ).collect( Collectors.toList() ); + return prepareSearch( fhirVersion, castedRuleInfos, filter, lastUpdatedDateRange, count ); } } diff --git a/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/AbstractDhisToFhirRequestResolver.java b/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/AbstractDhisToFhirRequestResolver.java index 7a9d2b0f..74ef3c83 100644 --- a/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/AbstractDhisToFhirRequestResolver.java +++ b/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/AbstractDhisToFhirRequestResolver.java @@ -58,11 +58,13 @@ public AbstractDhisToFhirRequestResolver( @Nonnull FhirClientRepository fhirClie @Nonnull public Optional resolveFhirClient( @Nonnull ScriptedDhisResource scriptedDhisResource ) { - Optional fhirClient = fhirClientRepository.findExpEnabledOnly(); + final Optional fhirClient = fhirClientRepository.findExpEnabledOnly(); + if ( fhirClient.isPresent() && fhirClient.get().isEnabled() && fhirClient.get().isExpEnabled() ) { return fhirClient; } + return Optional.empty(); } } diff --git a/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/AbstractDhisToFhirTransformer.java b/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/AbstractDhisToFhirTransformer.java index 222cdbeb..ed19b435 100644 --- a/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/AbstractDhisToFhirTransformer.java +++ b/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/AbstractDhisToFhirTransformer.java @@ -144,47 +144,59 @@ protected Optional getExistingResource( @Nonnull FhirCl if ( context.isUseAdapterIdentifier() ) { final IBaseResource resource = getResourceByAdapterIdentifier( fhirClient, context, ruleInfo, scriptVariables ).orElse( null ); + if ( resource != null ) { if ( !ruleInfo.getRule().isEffectiveFhirUpdateEnable() ) { logger.info( "Existing FHIR resource could be found by adapter identifier, but rule {} does not allow updating FHIR resources.", ruleInfo ); + return Optional.empty(); } + return Optional.of( resource ); } } IBaseResource resource = getResourceBySystemIdentifier( fhirClient, context, ruleInfo, scriptVariables ).orElse( null ); + if ( resource != null ) { if ( !ruleInfo.getRule().isEffectiveFhirUpdateEnable() ) { logger.info( "Existing FHIR resource could be found by system identifier, but rule {} does not allow updating FHIR resources.", ruleInfo ); + return Optional.empty(); } + return Optional.of( resource ); } resource = getResourceByAssignment( fhirClient, context, ruleInfo, scriptVariables ).orElse( null ); + if ( resource != null ) { if ( !ruleInfo.getRule().isEffectiveFhirUpdateEnable() ) { logger.info( "Existing FHIR resource could be found by assigned identifier, but rule {} does not allow updating FHIR resources.", ruleInfo ); + return Optional.empty(); } + return Optional.of( resource ); } resource = getActiveResource( fhirClient, context, ruleInfo, scriptVariables ).orElse( null ); + if ( resource != null ) { if ( !ruleInfo.getRule().isEffectiveFhirUpdateEnable() ) { logger.info( "Existing active FHIR resource could be found, but rule {} does not allow updating FHIR resources.", ruleInfo ); + return Optional.empty(); } + return Optional.of( resource ); } @@ -217,6 +229,7 @@ protected Optional getResource( @Nonnull FhirClient fhi } IBaseResource resource = getExistingResource( fhirClient, context, ruleInfo, scriptVariables ).orElse( null ); + if ( resource != null ) { return Optional.of( resource ); @@ -225,6 +238,7 @@ protected Optional getResource( @Nonnull FhirClient fhi if ( ruleInfo.getRule().isEffectiveFhirCreateEnable() ) { resource = createResource( fhirClient, context, ruleInfo, scriptVariables, sync ); + if ( (resource != null) && !sync ) { lockResource( fhirClient, context, ruleInfo, scriptVariables ); @@ -255,6 +269,7 @@ protected Optional getResourceByAdapterIdentifier( { final R scriptedDhisResource = getDhisResourceClass().cast( TransformerUtils.getScriptVariable( scriptVariables, ScriptVariable.INPUT, ScriptedDhisResource.class ) ); + return getResourceByAdapterIdentifierInternal( fhirClient, context, ruleInfo, scriptedDhisResource, scriptVariables ); } @@ -269,6 +284,7 @@ private Optional clone( context, r ) ); } @@ -279,6 +295,7 @@ protected Optional getResourceBySystemIdentifier( @Nonn { final R scriptedDhisResource = getDhisResourceClass().cast( TransformerUtils.getScriptVariable( scriptVariables, ScriptVariable.INPUT, ScriptedDhisResource.class ) ); + return getResourceBySystemIdentifierInternal( fhirClient, context, ruleInfo, scriptedDhisResource, scriptVariables, new DefaultIdentifierValueProvider() ); } @@ -291,10 +308,12 @@ protected Optional getResourceByAssignment( @Nonnull Fh TransformerUtils.getScriptVariable( scriptVariables, ScriptVariable.INPUT, ScriptedDhisResource.class ) ); final String fhirResourceId = fhirDhisAssignmentRepository.findFirstFhirResourceId( ruleInfo.getRule(), fhirClient, Objects.requireNonNull( scriptedDhisResource.getResourceId() ) ); + if ( fhirResourceId == null ) { return Optional.empty(); } + return getFhirResourceRepository().findRefreshed( fhirClient.getId(), fhirClient.getFhirVersion(), fhirClient.getFhirEndpoint(), ruleInfo.getRule().getFhirResourceType().getResourceTypeName(), fhirResourceId ) .map( r -> clone( context, r ) ); } @@ -305,17 +324,20 @@ private Optional scriptVariables, @Nonnull IdentifierValueProvider identifierValueProvider ) throws TransformerException { final ResourceSystem resourceSystem = context.getResourceSystem( ruleInfo.getRule().getFhirResourceType() ); + if ( resourceSystem == null ) { return Optional.empty(); } final String identifierValue = identifierValueProvider.getIdentifierValue( context, ruleInfo, null, scriptedDhisResource, scriptVariables ); + if ( identifierValue == null ) { logger.info( "FHIR resource type {} defines resource system, but resource does not include an identifier.", ruleInfo.getRule().getFhirResourceType() ); return Optional.empty(); } + if ( StringUtils.isNotBlank( resourceSystem.getCodePrefix() ) && (!identifierValue.startsWith( resourceSystem.getCodePrefix() ) || identifierValue.equals( resourceSystem.getCodePrefix() )) ) { logger.info( "Resource identifier \"{}\" does not start with required prefix \"{}\" for resource type {}.", @@ -324,6 +346,7 @@ private Optional clone( context, r ) ); } @@ -359,6 +382,7 @@ protected boolean updateIdentifiers( @Nonnull DhisToFhirTransformerContext conte if ( resourceSystem != null ) { final String identifierValue = getIdentifierValue( context, ruleInfo, null, scriptedDhisResource, scriptVariables ); + if ( identifierValue == null ) { logger.debug( "FHIR resource type {} defines resource system, but tracked entity does not include an identifier.", ruleInfo.getRule().getFhirResourceType() ); @@ -369,6 +393,7 @@ protected boolean updateIdentifiers( @Nonnull DhisToFhirTransformerContext conte { logger.info( "Tracked entity identifier \"{}\" does not start with required prefix \"{}\" for resource type {}.", identifierValue, resourceSystem.getCodePrefix(), ruleInfo.getRule().getFhirResourceType() ); + return false; } @@ -420,9 +445,11 @@ protected Optional getTrackedEntityFhirResource( @Nonnu @Nonnull ScriptedTrackedEntityInstance scriptedTrackedEntityInstance, @Nonnull Map scriptVariables ) throws TransformerException { IBaseResource resource; + if ( context.getDhisRequest().isDhisFhirId() ) { final IBaseReference reference = context.getDhisFhirResourceReference( scriptedTrackedEntityInstance, trackedEntityResourceType ); + if ( reference == null ) { return Optional.empty(); @@ -436,6 +463,7 @@ protected Optional getTrackedEntityFhirResource( @Nonnu else { resource = getResourceBySystemIdentifierInternal( fhirClient, context, ruleInfo, scriptedTrackedEntityInstance, scriptVariables, new TrackedEntityIdentifierValueProvider() ).orElse( null ); + if ( resource != null ) { return Optional.of( resource ); @@ -445,6 +473,7 @@ protected Optional getTrackedEntityFhirResource( @Nonnu resource = getResourceByAdapterIdentifierInternal( fhirClient, context, ruleInfo, scriptedTrackedEntityInstance, scriptVariables ).orElse( null ); } } + return Optional.ofNullable( resource ); } @@ -471,10 +500,12 @@ protected T cloneToModified( @Nonnull DhisToFhirTransf } final T modifiedResource = clone( context, resource ); + if ( !updateIdentifiers( context, ruleInfo, modifiedResource, scriptVariables, false ) ) { return null; } + return modifiedResource; } @@ -496,6 +527,7 @@ protected boolean evaluateNotModified( @Nonnull DhisToFhirTransformerContext con final AbstractFhirResourceDhisToFhirTransformerUtils fhirResourceUtils = TransformerUtils.getScriptVariable( scriptVariables, ScriptVariable.FHIR_RESOURCE_UTILS, AbstractFhirResourceDhisToFhirTransformerUtils.class ); + return fhirResourceUtils.equalsDeep( base1, base2 ); } @@ -563,6 +595,7 @@ public String getIdentifierValue( @Nonnull DhisToFhirTransformerContext context, { throw new FatalTransformerException( "Identifier value of a tracked entity instance cannot be looked up with an alternative lookup script." ); } + return AbstractDhisToFhirTransformer.this.getTrackedEntityIdentifierValue( context, ruleInfo, scriptedDhisResource, scriptVariables ); } } diff --git a/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/DhisToFhirRuleComparator.java b/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/DhisToFhirRuleComparator.java index 9168e4fd..2fb3fc35 100644 --- a/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/DhisToFhirRuleComparator.java +++ b/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/DhisToFhirRuleComparator.java @@ -1,7 +1,7 @@ package org.dhis2.fhir.adapter.fhir.transform.dhis.impl; /* - * Copyright (c) 2004-2018, University of Oslo + * Copyright (c) 2004-2019, University of Oslo * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -51,11 +51,13 @@ protected DhisToFhirRuleComparator() @Override public int compare( RuleInfo o1, RuleInfo o2 ) { - int value = o1.getRule().getFhirResourceType().getOrder() - o2.getRule().getFhirResourceType().getOrder(); + final int value = o1.getRule().getFhirResourceType().getOrder() - o2.getRule().getFhirResourceType().getOrder(); + if ( value != 0 ) { return value; } + return o1.getRule().compareTo( o2.getRule() ); } } diff --git a/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/DhisToFhirTransformerContextImpl.java b/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/DhisToFhirTransformerContextImpl.java index cc3b73d6..ec19a786 100644 --- a/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/DhisToFhirTransformerContextImpl.java +++ b/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/DhisToFhirTransformerContextImpl.java @@ -208,10 +208,12 @@ public Object getAttribute( @Nonnull String name ) public IBaseReference getDhisFhirResourceReference( @Nullable ScriptedDhisResource dhisResource, Object... fhirResourceTypes ) { final List references = getDhisFhirResourceReferencesLimited( dhisResource, 1, fhirResourceTypes ); + if ( references.isEmpty() ) { return null; } + return references.get( 0 ); } @@ -229,6 +231,7 @@ protected List getDhisFhirResourceReferencesLimited( @Nullable S { return Collections.emptyList(); } + return fhirReferenceResolver.resolveFhirReferences( fhirClient, dhisResource, Stream.of( fhirResourceTypes ).map( frt -> { try { diff --git a/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/DhisToFhirTransformerRequestImpl.java b/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/DhisToFhirTransformerRequestImpl.java index 1248979f..1ca9d5ce 100644 --- a/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/DhisToFhirTransformerRequestImpl.java +++ b/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/DhisToFhirTransformerRequestImpl.java @@ -93,6 +93,7 @@ public ScriptedDhisResource getInput() { throw new FatalTransformerException( "Transformer input has not yet been set." ); } + return input; } @@ -128,6 +129,7 @@ public UUID getRuleId() { throw new FatalTransformerException( "Rule ID is not available." ); } + return rules.get( ruleIndex ).getRule().getId(); } @@ -159,11 +161,14 @@ public RuleInfo nextRule() { return null; } + final RuleInfo ruleInfo = rules.get( ruleIndex++ ); - if ( (input != null) && (ruleInfo.getRule().getDhisResourceType() != input.getResourceType()) ) + + if ( input != null && ruleInfo.getRule().getDhisResourceType() != input.getResourceType() ) { input = null; } + return ruleInfo; } } diff --git a/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/DhisToFhirTransformerServiceImpl.java b/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/DhisToFhirTransformerServiceImpl.java index 64e391ac..5b90e841 100644 --- a/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/DhisToFhirTransformerServiceImpl.java +++ b/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/DhisToFhirTransformerServiceImpl.java @@ -246,6 +246,7 @@ public List resolveFhirReferences( @Nonnull FhirClient fhirClien do { final DhisToFhirTransformOutcome outcome = transform( transformerRequest ); + if ( outcome == null ) { transformerRequest = null; @@ -301,6 +302,7 @@ public DhisToFhirTransformerRequest createTransformerRequest( @Nonnull FhirClien final DhisToFhirRequestResolver requestResolver = getRequestResolver( dhisRequest.getResourceType() ); final ScriptedDhisResource scriptedResource = requestResolver.convert( Objects.requireNonNull( DhisBeanTransformerUtils.clone( resource ) ), dhisRequest ); + return createTransformerRequest( false, fhirClient, dhisRequest, scriptedResource, Collections.singletonList( ruleInfo ) ); } @@ -309,6 +311,7 @@ public DhisToFhirTransformerRequest createTransformerRequest( @Nonnull FhirClien public DhisToFhirTransformerRequest createTransformerRequest( @Nonnull FhirClient fhirClient, @Nonnull DhisRequest dhisRequest, @Nonnull DhisResource resource, @Nonnull List> rules ) { final DhisToFhirRequestResolver requestResolver = getRequestResolver( dhisRequest.getResourceType() ); + return createTransformerRequest( dhisRequest, rr -> rr.convert( resource, dhisRequest ), requestResolver, ( rr, scriptedResource ) -> rr.resolveRules( scriptedResource, rules ), ri -> true, ( si, rr ) -> fhirClient ); } @@ -326,6 +329,7 @@ protected DhisToFhirTransformerRequest createTransformerRequest( @Nonnull DhisRe @Nonnull Predicate> ruleInfoPredicate, @Nonnull BiFunction fhirClientFunction ) { final DhisToFhirRequestResolver requestResolver = getRequestResolver( dhisRequest.getResourceType() ); + return createTransformerRequest( dhisRequest, scriptedDhisResourceFunction, requestResolver, DhisToFhirRequestResolver::resolveRules, ruleInfoPredicate, fhirClientFunction ); } @@ -337,6 +341,7 @@ protected DhisToFhirTransformerRequest createTransformerRequest( @Nonnull DhisRe final ScriptedDhisResource scriptedResource = scriptedDhisResourceFunction.apply( requestResolver ); final List> ruleInfos = ruleResolverFunction.apply( requestResolver, scriptedResource ) .stream().filter( ruleInfoPredicate ).collect( Collectors.toList() ); + if ( ruleInfos.isEmpty() ) { logger.info( "Could not find any rule to process DHIS resource." ); @@ -344,6 +349,7 @@ protected DhisToFhirTransformerRequest createTransformerRequest( @Nonnull DhisRe } final FhirClient fhirClient = fhirClientFunction.apply( scriptedResource, requestResolver ); + if ( fhirClient == null ) { logger.info( "Could not determine FHIR client to process DHIS resource." ); @@ -363,15 +369,19 @@ public DhisToFhirTransformerRequest createTransformerRequest( @Nonnull FhirClien final List> ruleInfos = requestResolver.resolveRules( scriptedDhisResource ); final RuleInfo matchingRuleInfo = ruleInfos.stream().filter( ri -> ri.getRule().getId().equals( ruleId ) ).findFirst().orElse( null ); + if ( (matchingRuleInfo == null) || !fhirResourceType.equals( matchingRuleInfo.getRule().getFhirResourceType() ) ) { logger.info( "Could not find any matching rule to process DHIS resource." ); + return null; } + final List> groupingRuleInfos = ruleInfos.stream() .filter( ri -> ri.getRule().isGrouping() ).collect( Collectors.toList() ); final List> resultingRuleInfos; + if ( groupingRuleInfos.isEmpty() ) { resultingRuleInfos = Collections.singletonList( matchingRuleInfo ); @@ -385,6 +395,7 @@ else if ( matchingRuleInfo.getRule().isGrouping() ) resultingRuleInfos = ruleInfos.stream() .filter( ri -> ri.getRule().isGrouping() || ri.getRule().getId().equals( ruleId ) ).collect( Collectors.toList() ); } + return createTransformerRequest( matchingRuleInfo.getRule().isGrouping(), fhirClient, dhisRequest, scriptedDhisResource, resultingRuleInfos ); } @@ -400,6 +411,7 @@ protected DhisToFhirTransformerRequest createTransformerRequest( boolean groupin .collect( Collectors.toMap( ResourceSystem::getFhirResourceType, rs -> rs ) ); final Map transformerUtils = this.transformerUtils.get( fhirClient.getFhirVersion() ); + if ( transformerUtils == null ) { throw new TransformerMappingException( "No transformer utils can be found for FHIR version " + fhirClient.getFhirVersion() ); @@ -420,6 +432,7 @@ public DhisToFhirTransformerRequest updateTransformerRequest( @Nonnull DhisReque final DhisToFhirTransformerRequestImpl transformerRequestImpl = (DhisToFhirTransformerRequestImpl) transformerRequest; transformerRequestImpl.setInput( scriptedInput ); + return transformerRequest; } @@ -428,6 +441,7 @@ private List> filterAvailableResourceRules( @No { final Map availableResourcesByType = availableResources.stream() .collect( Collectors.toMap( AvailableFhirClientResource::getResourceType, a -> a ) ); + // not all resources may be available on FHIR client return ruleInfos.stream().filter( r -> { final AvailableFhirClientResource availableResource = availableResourcesByType.get( r.getRule().getFhirResourceType() ); @@ -443,10 +457,12 @@ private List> filterAvailableResourceRules( @No private DhisToFhirRequestResolver getRequestResolver( @Nonnull DhisResourceType resourceType ) { final DhisToFhirRequestResolver requestResolver = requestResolvers.get( resourceType ); + if ( requestResolver == null ) { throw new TransformerMappingException( "No request resolver can be found for DHIS resource type " + resourceType ); } + return requestResolver; } @@ -458,6 +474,7 @@ public DhisToFhirTransformOutcome transform( @Nonnull D final boolean firstRule = transformerRequestImpl.isFirstRule(); RuleInfo ruleInfo; + while ( (ruleInfo = transformerRequestImpl.nextRule()) != null ) { Cache transformerRequestCache = null; diff --git a/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/TransformerRequestKey.java b/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/TransformerRequestKey.java index 59f08884..e7fc60e2 100644 --- a/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/TransformerRequestKey.java +++ b/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/TransformerRequestKey.java @@ -57,11 +57,19 @@ public TransformerRequestKey( @Nonnull UUID ruleId, @Nonnull DhisResourceId dhis @Override public boolean equals( Object o ) { - if ( this == o ) return true; - if ( o == null || getClass() != o.getClass() ) return false; + if ( this == o ) + { + return true; + } + + if ( o == null || getClass() != o.getClass() ) + { + return false; + } + TransformerRequestKey that = (TransformerRequestKey) o; - return ruleId.equals( that.ruleId ) && - dhisResourceId.equals( that.dhisResourceId ); + + return ruleId.equals( that.ruleId ) && dhisResourceId.equals( that.dhisResourceId ); } @Override diff --git a/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/dhis/search/SearchFilterCollector.java b/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/dhis/search/SearchFilterCollector.java index a818504b..9a57a55a 100644 --- a/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/dhis/search/SearchFilterCollector.java +++ b/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/dhis/search/SearchFilterCollector.java @@ -79,6 +79,7 @@ public SearchFilterCollector( @Nullable ItemContainerType itemContainerType, @Nu public SearchFilterCollector( @Nullable Function dhisPropertyRefResolver, @Nullable Map> filter ) { this.dhisPropertyRefResolver = dhisPropertyRefResolver; + if ( filter != null ) { filter.forEach( ( key, value ) -> { @@ -142,10 +143,11 @@ public void addQueryParam( @Nonnull String propertyName, @Nullable String value public void addFilterExpression( @Nonnull String propertyName, @Nonnull String operator, @Nullable String value ) { - if ( ( value != null ) && ( value.indexOf( ':' ) >= 0 ) ) + if ( value != null && value.indexOf( ':' ) >= 0 ) { throw new DhisToFhirDataProviderException( "Colon characters in search parameter values are not supported." ); } + filterExpressions.add( propertyName + ':' + operator + ':' + StringUtils.defaultString( value ) ); } @@ -162,11 +164,13 @@ public U add( @Nonnull U uriBuilder, @Nonnull List uriBuilder.queryParam( key, values.toArray() ) ); filterExpressions.forEach( fe -> { uriBuilder.queryParam( "filter", "{sfc" + variables.size() + "}" ); variables.add( fe ); } ); + return uriBuilder; } } diff --git a/fhir/src/main/resources/db/migration/sample/V1.1.0.41_10_0__Plan_Definition_Sample_Data.sql b/fhir/src/main/resources/db/migration/sample/V1.1.0.41_10_0__Plan_Definition_Sample_Data.sql new file mode 100644 index 00000000..e21ec8fd --- /dev/null +++ b/fhir/src/main/resources/db/migration/sample/V1.1.0.41_10_0__Plan_Definition_Sample_Data.sql @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2004-2018, University of Oslo + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * Neither the name of the HISP project nor the names of its contributors may + * be used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO PROGRAM_STAGE_EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +-- @formatter:off + +UPDATE fhir_client SET fhir_version='R4', remote_base_url='http://localhost:8082/hapi-fhir-jpaserver/fhir' +WHERE id='73cd99c5-0ca8-42ad-a53b-1891fccce08f'; + +-- virtual subscription for FHIR organizations +INSERT INTO fhir_client_resource (id, version, fhir_client_id, fhir_resource_type, exp_only, fhir_criteria_parameters, description) +VALUES ('2520acc5-86b4-4716-ae0f-ea0531eb885a', 0, '73cd99c5-0ca8-42ad-a53b-1891fccce08f', 'PLAN_DEFINITION', TRUE, NULL, 'Virtual subscription for all Plan Definitions.'); +INSERT INTO fhir_client_resource_update(id) +VALUES ('2520acc5-86b4-4716-ae0f-ea0531eb885a'); diff --git a/fhir/src/test/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/metadata/program/AbstractProgramMetadataToFhirPlanDefinitionTransformerTest.java b/fhir/src/test/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/metadata/program/AbstractProgramMetadataToFhirPlanDefinitionTransformerTest.java new file mode 100644 index 00000000..8ee2c709 --- /dev/null +++ b/fhir/src/test/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/metadata/program/AbstractProgramMetadataToFhirPlanDefinitionTransformerTest.java @@ -0,0 +1,206 @@ +package org.dhis2.fhir.adapter.fhir.transform.dhis.impl.metadata.program; + +/* + * Copyright (c) 2004-2019, University of Oslo + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * Neither the name of the HISP project nor the names of its contributors may + * be used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +import org.dhis2.fhir.adapter.dhis.model.DhisResourceType; +import org.dhis2.fhir.adapter.dhis.model.Reference; +import org.dhis2.fhir.adapter.dhis.model.ReferenceType; +import org.dhis2.fhir.adapter.dhis.orgunit.OrganizationUnitService; +import org.dhis2.fhir.adapter.dhis.tracker.program.WritableProgram; +import org.dhis2.fhir.adapter.dhis.tracker.trackedentity.TrackedEntityMetadataService; +import org.dhis2.fhir.adapter.dhis.tracker.trackedentity.WritableTrackedEntityType; +import org.dhis2.fhir.adapter.fhir.data.repository.FhirDhisAssignmentRepository; +import org.dhis2.fhir.adapter.fhir.metadata.model.FhirResourceType; +import org.dhis2.fhir.adapter.fhir.metadata.model.ProgramMetadataRule; +import org.dhis2.fhir.adapter.fhir.metadata.model.RuleInfo; +import org.dhis2.fhir.adapter.fhir.metadata.repository.SystemRepository; +import org.dhis2.fhir.adapter.fhir.metadata.repository.TrackedEntityRuleRepository; +import org.dhis2.fhir.adapter.fhir.repository.FhirResourceRepository; +import org.dhis2.fhir.adapter.fhir.script.ScriptExecutor; +import org.dhis2.fhir.adapter.fhir.transform.scripted.AccessibleScriptedDhisMetadata; +import org.dhis2.fhir.adapter.lock.LockManager; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.mockito.Mock; +import org.mockito.Mockito; +import org.mockito.junit.MockitoJUnit; +import org.mockito.junit.MockitoRule; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.HashSet; +import java.util.Optional; + +import static org.mockito.Mockito.CALLS_REAL_METHODS; +import static org.mockito.Mockito.withSettings; + +/** + * Unit tests for {@link AbstractProgramMetadataToFhirPlanDefinitionTransformer}. + * + * @author volsch + */ +public class AbstractProgramMetadataToFhirPlanDefinitionTransformerTest +{ + @Mock + private ScriptExecutor scriptExecutor; + + @Mock + private LockManager lockManager; + + @Mock + private SystemRepository systemRepository; + + @Mock + private FhirResourceRepository fhirResourceRepository; + + @Mock + private FhirDhisAssignmentRepository fhirDhisAssignmentRepository; + + @Mock + private OrganizationUnitService organizationUnitService; + + @Mock + private TrackedEntityMetadataService trackedEntityMetadataService; + + @Mock + private TrackedEntityRuleRepository trackedEntityRuleRepository; + + private AbstractProgramMetadataToFhirPlanDefinitionTransformer transformer; + + @Rule + public MockitoRule rule = MockitoJUnit.rule(); + + @Before + public void setUp() + { + transformer = Mockito.mock( AbstractProgramMetadataToFhirPlanDefinitionTransformer.class, + withSettings().useConstructor( scriptExecutor, lockManager, systemRepository, fhirResourceRepository, fhirDhisAssignmentRepository, organizationUnitService, + trackedEntityMetadataService, trackedEntityRuleRepository ).defaultAnswer( CALLS_REAL_METHODS ) ); + } + + @Test + public void getDhisResourceType() + { + Assert.assertEquals( DhisResourceType.PROGRAM_METADATA, transformer.getDhisResourceType() ); + } + + @Test + public void getFhirResourceType() + { + Assert.assertEquals( FhirResourceType.PLAN_DEFINITION, transformer.getFhirResourceType() ); + } + + @Test + public void getDhisResourceClass() + { + Assert.assertEquals( AccessibleScriptedDhisMetadata.class, transformer.getDhisResourceClass() ); + } + + @Test + public void isApplicableProgram() + { + Mockito.doReturn( Optional.of( new WritableTrackedEntityType( "a1234567890", "Test", Collections.emptyList() ) ) ) + .when( trackedEntityMetadataService ).findTypeByReference( Mockito.eq( new Reference( "a1234567890", ReferenceType.ID ) ) ); + Mockito.doReturn( Collections.singletonList( new RuleInfo<>( new ProgramMetadataRule(), Collections.emptyList() ) ) ) + .when( trackedEntityRuleRepository ).findByTypeRefs( Mockito.eq( new HashSet<>( + Arrays.asList( new Reference( "a1234567890", ReferenceType.ID ), new Reference( "Test", ReferenceType.NAME ) ) ) ) ); + + final WritableProgram program = new WritableProgram(); + program.setName( "Test Program" ); + program.setDescription( "Test Description" ); + program.setWithoutRegistration( false ); + program.setTrackedEntityTypeId( "a1234567890" ); + program.setStages( new ArrayList<>() ); + + Assert.assertTrue( transformer.isApplicableProgram( program ) ); + } + + @Test + public void isApplicableProgramWithoutRegistration() + { + final WritableProgram program = new WritableProgram(); + program.setName( "Test Program" ); + program.setDescription( "Test Description" ); + program.setWithoutRegistration( true ); + program.setTrackedEntityTypeId( "a1234567890" ); + program.setStages( new ArrayList<>() ); + + Assert.assertFalse( transformer.isApplicableProgram( program ) ); + } + + @Test + public void isApplicableProgramWithoutTrackedEntityType() + { + final WritableProgram program = new WritableProgram(); + program.setName( "Test Program" ); + program.setDescription( "Test Description" ); + program.setWithoutRegistration( false ); + program.setStages( new ArrayList<>() ); + + Assert.assertFalse( transformer.isApplicableProgram( program ) ); + } + + @Test + public void isApplicableProgramWithoutTrackedEntityMetadata() + { + Mockito.doReturn( Optional.empty() ) + .when( trackedEntityMetadataService ).findTypeByReference( Mockito.eq( new Reference( "a1234567890", ReferenceType.ID ) ) ); + + final WritableProgram program = new WritableProgram(); + program.setName( "Test Program" ); + program.setDescription( "Test Description" ); + program.setWithoutRegistration( false ); + program.setTrackedEntityTypeId( "a1234567890" ); + program.setStages( new ArrayList<>() ); + + Assert.assertFalse( transformer.isApplicableProgram( program ) ); + } + + @Test + public void isApplicableProgramWithoutRules() + { + Mockito.doReturn( Optional.of( new WritableTrackedEntityType( "a1234567890", "Test", Collections.emptyList() ) ) ) + .when( trackedEntityMetadataService ).findTypeByReference( Mockito.eq( new Reference( "a1234567890", ReferenceType.ID ) ) ); + Mockito.doReturn( Collections.emptyList() ) + .when( trackedEntityRuleRepository ).findByTypeRefs( Mockito.eq( new HashSet<>( + Arrays.asList( new Reference( "a1234567890", ReferenceType.ID ), new Reference( "Test", ReferenceType.NAME ) ) ) ) ); + + final WritableProgram program = new WritableProgram(); + program.setName( "Test Program" ); + program.setDescription( "Test Description" ); + program.setWithoutRegistration( false ); + program.setTrackedEntityTypeId( "a1234567890" ); + program.setStages( new ArrayList<>() ); + + Assert.assertFalse( transformer.isApplicableProgram( program ) ); + } +} \ No newline at end of file From f29bf00647d7584c7ae50617ae0d0c270cd8cd26 Mon Sep 17 00:00:00 2001 From: Volker Schmidt Date: Mon, 29 Jul 2019 19:36:34 +0200 Subject: [PATCH 03/16] Enables export of program metadata. --- .../impl/DhisDataProcessorItemRetrieverImpl.java | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/dhis/src/main/java/org/dhis2/fhir/adapter/dhis/sync/impl/DhisDataProcessorItemRetrieverImpl.java b/dhis/src/main/java/org/dhis2/fhir/adapter/dhis/sync/impl/DhisDataProcessorItemRetrieverImpl.java index cdf131af..6a655afa 100644 --- a/dhis/src/main/java/org/dhis2/fhir/adapter/dhis/sync/impl/DhisDataProcessorItemRetrieverImpl.java +++ b/dhis/src/main/java/org/dhis2/fhir/adapter/dhis/sync/impl/DhisDataProcessorItemRetrieverImpl.java @@ -1,7 +1,7 @@ package org.dhis2.fhir.adapter.dhis.sync.impl; /* - * Copyright (c) 2004-2018, University of Oslo + * Copyright (c) 2004-2019, University of Oslo * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -40,6 +40,7 @@ import org.dhis2.fhir.adapter.dhis.orgunit.OrganizationUnitService; import org.dhis2.fhir.adapter.dhis.sync.SyncExcludedDhisUsernameRetriever; import org.dhis2.fhir.adapter.dhis.tracker.program.EventService; +import org.dhis2.fhir.adapter.dhis.tracker.program.ProgramMetadataService; import org.dhis2.fhir.adapter.dhis.tracker.trackedentity.TrackedEntityService; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.stereotype.Component; @@ -67,6 +68,8 @@ public class DhisDataProcessorItemRetrieverImpl implements DataProcessorItemRetr private final OrganizationUnitService organizationUnitService; + private final ProgramMetadataService programMetadataService; + private final TrackedEntityService trackedEntityService; private final EventService eventService; @@ -78,6 +81,7 @@ public DhisDataProcessorItemRetrieverImpl( @Nonnull @Qualifier( "systemDhis2Authorization" ) Authorization systemDhis2Authorization, @Nonnull SyncExcludedDhisUsernameRetriever excludedDhisUsernameRetriever, @Nonnull OrganizationUnitService organizationUnitService, + @Nonnull ProgramMetadataService programMetadataService, @Nonnull TrackedEntityService trackedEntityService, @Nonnull EventService eventService, @Nonnull DhisSyncProcessorConfig processorConfig, @@ -89,6 +93,7 @@ public DhisDataProcessorItemRetrieverImpl( this.trackedEntityService = trackedEntityService; this.eventService = eventService; this.organizationUnitService = organizationUnitService; + this.programMetadataService = programMetadataService; this.processorConfig = processorConfig; } @@ -110,6 +115,12 @@ public Instant poll( @Nonnull DhisSyncGroup group, @Nonnull Instant lastUpdated, excludedDhisUsernames, consumer ); result = ObjectUtils.min( result, currentResult ); } + if ( resourceTypes.contains( DhisResourceType.PROGRAM_METADATA ) ) + { + final Instant currentResult = programMetadataService.poll( group, lastUpdated, toleranceMillis, maxSearchCount, + excludedDhisUsernames, consumer ); + result = ObjectUtils.min( result, currentResult ); + } if ( resourceTypes.contains( DhisResourceType.TRACKED_ENTITY ) ) { final Instant currentResult = trackedEntityService.poll( group, lastUpdated, toleranceMillis, maxSearchCount, From c6cc85f960adb33843bccddff1bfc9f08fd2707d Mon Sep 17 00:00:00 2001 From: Volker Schmidt Date: Mon, 29 Jul 2019 22:45:12 +0200 Subject: [PATCH 04/16] Provides polling support for plan definitions. --- .../AbstractProgramStageFhirRestAppTest.java | 5 +- .../r4/R4ProgramMetadataFhirRestAppTest.java | 17 ++--- ...stractEnrollmentTransformationAppTest.java | 7 +- ...ractProgramStageTransformationAppTest.java | 5 +- .../adapter/dhis/model/DhisResourceType.java | 28 ++++---- .../impl/OrganizationUnitServiceImpl.java | 2 +- .../dhis/service/DhisMetadataService.java | 12 +--- .../dhis/service/DhisPolledService.java | 52 +++++++++++++++ .../adapter/dhis/service/DhisService.java | 46 +++++++++++++ .../impl/AbstractDhisMetadataServiceImpl.java | 2 +- .../DhisDataProcessorItemRetrieverImpl.java | 66 ++++++------------- .../tracker/program/EnrollmentService.java | 4 +- .../dhis/tracker/program/EventService.java | 13 +--- .../dhis/tracker/program/WritableProgram.java | 4 +- .../program/impl/EnrollmentServiceImpl.java | 8 +++ .../program/impl/EventServiceImpl.java | 8 +++ .../impl/ProgramMetadataServiceImpl.java | 4 +- .../trackedentity/TrackedEntityService.java | 13 +--- .../impl/TrackedEntityServiceImpl.java | 7 ++ .../impl/ProgramMetadataServiceImplTest.java | 15 +++-- .../impl/AbstractDhisToFhirTransformer.java | 7 +- 21 files changed, 204 insertions(+), 121 deletions(-) create mode 100644 dhis/src/main/java/org/dhis2/fhir/adapter/dhis/service/DhisPolledService.java create mode 100644 dhis/src/main/java/org/dhis2/fhir/adapter/dhis/service/DhisService.java diff --git a/app/src/test/java/org/dhis2/fhir/adapter/fhir/server/AbstractProgramStageFhirRestAppTest.java b/app/src/test/java/org/dhis2/fhir/adapter/fhir/server/AbstractProgramStageFhirRestAppTest.java index 8962a7e8..b7ed5cd6 100644 --- a/app/src/test/java/org/dhis2/fhir/adapter/fhir/server/AbstractProgramStageFhirRestAppTest.java +++ b/app/src/test/java/org/dhis2/fhir/adapter/fhir/server/AbstractProgramStageFhirRestAppTest.java @@ -67,12 +67,13 @@ protected void expectProgramStageMetadataRequests() throws Exception .andRespond( withSuccess( IOUtils.resourceToString( "/org/dhis2/fhir/adapter/dhis/test/single-org-unit-OU_1234.json", StandardCharsets.UTF_8 ), MediaType.APPLICATION_JSON ) ); systemDhis2Server.expect( ExpectedCount.between( 0, 1 ), method( HttpMethod.GET ) ).andExpect( header( "Authorization", testConfiguration.getDhis2SystemAuthorization() ) ) .andExpect( requestTo( dhis2BaseUrl + "/api/" + dhis2ApiVersion + "/programs/EPDyQuoRnXk.json?" + - "fields=id,name,code,description,selectIncidentDatesInFuture,selectEnrollmentDatesInFuture,displayIncidentDate,registration,withoutRegistration,captureCoordinates,trackedEntityType%5Bid%5D,programTrackedEntityAttributes%5Bid,name,valueType," + + "fields=id,name,code,description,lastUpdated,selectIncidentDatesInFuture,selectEnrollmentDatesInFuture,displayIncidentDate,registration,withoutRegistration,captureCoordinates,trackedEntityType%5Bid%5D,programTrackedEntityAttributes%5Bid," + + "name,valueType," + "mandatory,allowFutureDate,trackedEntityAttribute%5Bid,name,code,valueType,generated%5D%5D,programStages%5Bid,name,description,repeatable,captureCoordinates,generatedByEnrollmentDate,minDaysFromStart,programStageDataElements%5Bid,compulsory," + "allowProvidedElsewhere,dataElement%5Bid,name,code,formName,valueType,optionSetValue,optionSet%5Bid,name,options%5Bcode,name%5D%5D%5D%5D%5D" ) ) .andRespond( withSuccess( IOUtils.resourceToString( "/org/dhis2/fhir/adapter/dhis/test/single-program.json", StandardCharsets.UTF_8 ), MediaType.APPLICATION_JSON ) ); systemDhis2Server.expect( ExpectedCount.between( 0, 1 ), method( HttpMethod.GET ) ).andExpect( header( "Authorization", testConfiguration.getDhis2SystemAuthorization() ) ) - .andExpect( requestTo( dhis2BaseUrl + "/api/" + dhis2ApiVersion + "/programs.json?paging=false&fields=id,name,code,description,selectIncidentDatesInFuture,selectEnrollmentDatesInFuture,displayIncidentDate,registration," + + .andExpect( requestTo( dhis2BaseUrl + "/api/" + dhis2ApiVersion + "/programs.json?paging=false&fields=id,name,code,description,lastUpdated,selectIncidentDatesInFuture,selectEnrollmentDatesInFuture,displayIncidentDate,registration," + "withoutRegistration,captureCoordinates,trackedEntityType%5Bid%5D,programTrackedEntityAttributes%5Bid,name,valueType,mandatory,allowFutureDate,trackedEntityAttribute%5Bid,name,code,valueType,generated%5D%5D,programStages%5Bid,name," + "description,repeatable,captureCoordinates,generatedByEnrollmentDate,minDaysFromStart,programStageDataElements%5Bid,compulsory,allowProvidedElsewhere,dataElement%5Bid,name,code,formName,valueType,optionSetValue,optionSet%5Bid,name," + "options%5Bcode,name%5D%5D%5D%5D%5D&filter=name:eq:Child%20Programme" ) ) diff --git a/app/src/test/java/org/dhis2/fhir/adapter/fhir/server/r4/R4ProgramMetadataFhirRestAppTest.java b/app/src/test/java/org/dhis2/fhir/adapter/fhir/server/r4/R4ProgramMetadataFhirRestAppTest.java index 1402d045..695d91d2 100644 --- a/app/src/test/java/org/dhis2/fhir/adapter/fhir/server/r4/R4ProgramMetadataFhirRestAppTest.java +++ b/app/src/test/java/org/dhis2/fhir/adapter/fhir/server/r4/R4ProgramMetadataFhirRestAppTest.java @@ -78,7 +78,8 @@ public void getPlanDefinitionWithInvalidAuthorization() { userDhis2Server.expect( ExpectedCount.once(), method( HttpMethod.GET ) ).andExpect( header( "Authorization", "Basic Zmhpcl9jbGllbnQ6aW52YWxpZF8x" ) ) .andExpect( requestTo( dhis2BaseUrl + "/api/" + dhis2ApiVersion + "/programs/EPDyQuoRnXk.json?" + - "fields=id,name,code,description,selectIncidentDatesInFuture,selectEnrollmentDatesInFuture,displayIncidentDate,registration,withoutRegistration,captureCoordinates,trackedEntityType%5Bid%5D,programTrackedEntityAttributes%5Bid,name,valueType," + + "fields=id,name,code,description,lastUpdated,selectIncidentDatesInFuture,selectEnrollmentDatesInFuture,displayIncidentDate,registration,withoutRegistration,captureCoordinates,trackedEntityType%5Bid%5D,programTrackedEntityAttributes%5Bid," + + "name,valueType," + "mandatory,allowFutureDate,trackedEntityAttribute%5Bid,name,code,valueType,generated%5D%5D,programStages%5Bid,name,description,repeatable,captureCoordinates,generatedByEnrollmentDate,minDaysFromStart,programStageDataElements%5Bid,compulsory," + "allowProvidedElsewhere,dataElement%5Bid,name,code,formName,valueType,optionSetValue,optionSet%5Bid,name,options%5Bcode,name%5D%5D%5D%5D%5D" ) ) .andRespond( withStatus( HttpStatus.UNAUTHORIZED ) ); @@ -105,13 +106,13 @@ private void getPlanDefinition() throws Exception "fields=id,name,trackedEntityTypeAttributes%5Bid,name,valueType,mandatory,trackedEntityAttribute%5Bid,name,code,valueType,generated,optionSetValue,optionSet%5Bid,name,options%5Bcode,name%5D%5D%5D%5D" ) ) .andRespond( withSuccess( IOUtils.resourceToString( "/org/dhis2/fhir/adapter/dhis/test/single-tracked-entity-type.json", StandardCharsets.UTF_8 ), MediaType.APPLICATION_JSON ) ); userDhis2Server.expect( ExpectedCount.once(), method( HttpMethod.GET ) ).andExpect( header( "Authorization", "Basic Zmhpcl9jbGllbnQ6Zmhpcl9jbGllbnRfMQ==" ) ) - .andExpect( requestTo( dhis2BaseUrl + "/api/" + dhis2ApiVersion + "/programs/EPDyQuoRnXk.json?fields=id,name,code,description,selectIncidentDatesInFuture,selectEnrollmentDatesInFuture,displayIncidentDate,registration," + + .andExpect( requestTo( dhis2BaseUrl + "/api/" + dhis2ApiVersion + "/programs/EPDyQuoRnXk.json?fields=id,name,code,description,lastUpdated,selectIncidentDatesInFuture,selectEnrollmentDatesInFuture,displayIncidentDate,registration," + "withoutRegistration,captureCoordinates,trackedEntityType%5Bid%5D,programTrackedEntityAttributes%5Bid,name,valueType,mandatory,allowFutureDate,trackedEntityAttribute%5Bid,name,code,valueType,generated%5D%5D,programStages%5Bid,name," + "description,repeatable,captureCoordinates,generatedByEnrollmentDate,minDaysFromStart,programStageDataElements%5Bid,compulsory,allowProvidedElsewhere,dataElement%5Bid,name,code,formName,valueType,optionSetValue,optionSet%5Bid,name," + "options%5Bcode,name%5D%5D%5D%5D%5D" ) ) .andRespond( withSuccess( IOUtils.resourceToString( "/org/dhis2/fhir/adapter/dhis/test/single-program.json", StandardCharsets.UTF_8 ), MediaType.APPLICATION_JSON ) ); userDhis2Server.expect( ExpectedCount.between( 0, 1 ), method( HttpMethod.GET ) ).andExpect( header( "Authorization", "Basic Zmhpcl9jbGllbnQ6Zmhpcl9jbGllbnRfMQ==" ) ) - .andExpect( requestTo( dhis2BaseUrl + "/api/" + dhis2ApiVersion + "/programs.json?paging=false&fields=id,name,code,description,selectIncidentDatesInFuture,selectEnrollmentDatesInFuture,displayIncidentDate,registration," + + .andExpect( requestTo( dhis2BaseUrl + "/api/" + dhis2ApiVersion + "/programs.json?paging=false&fields=id,name,code,description,lastUpdated,selectIncidentDatesInFuture,selectEnrollmentDatesInFuture,displayIncidentDate,registration," + "withoutRegistration,captureCoordinates,trackedEntityType%5Bid%5D,programTrackedEntityAttributes%5Bid,name,valueType,mandatory,allowFutureDate,trackedEntityAttribute%5Bid,name,code,valueType,generated%5D%5D,programStages%5Bid,name," + "description,repeatable,captureCoordinates,generatedByEnrollmentDate,minDaysFromStart,programStageDataElements%5Bid,compulsory,allowProvidedElsewhere,dataElement%5Bid,name,code,formName,valueType,optionSetValue,optionSet%5Bid,name," + "options%5Bcode,name%5D%5D%5D%5D%5D" ) ) @@ -146,7 +147,7 @@ private void getPlanDefinitionNotExists() userDhis2Server.reset(); userDhis2Server.expect( ExpectedCount.once(), method( HttpMethod.GET ) ).andExpect( header( "Authorization", "Basic Zmhpcl9jbGllbnQ6Zmhpcl9jbGllbnRfMQ==" ) ) - .andExpect( requestTo( dhis2BaseUrl + "/api/" + dhis2ApiVersion + "/programs/0dXIdLNUNEn.json?fields=id,name,code,description,selectIncidentDatesInFuture,selectEnrollmentDatesInFuture,displayIncidentDate,registration," + + .andExpect( requestTo( dhis2BaseUrl + "/api/" + dhis2ApiVersion + "/programs/0dXIdLNUNEn.json?fields=id,name,code,description,lastUpdated,selectIncidentDatesInFuture,selectEnrollmentDatesInFuture,displayIncidentDate,registration," + "withoutRegistration,captureCoordinates,trackedEntityType%5Bid%5D,programTrackedEntityAttributes%5Bid,name,valueType,mandatory,allowFutureDate,trackedEntityAttribute%5Bid,name,code,valueType,generated%5D%5D,programStages%5Bid,name," + "description,repeatable,captureCoordinates,generatedByEnrollmentDate,minDaysFromStart,programStageDataElements%5Bid,compulsory,allowProvidedElsewhere,dataElement%5Bid,name,code,formName,valueType,optionSetValue,optionSet%5Bid,name," + "options%5Bcode,name%5D%5D%5D%5D%5D" ) ) @@ -210,7 +211,7 @@ public void getPlanDefinitionByIdentifierWithoutAuthorization() public void getPlanDefinitionByIdentifierInvalidAuthorization() { userDhis2Server.expect( ExpectedCount.once(), method( HttpMethod.GET ) ).andExpect( header( "Authorization", "Basic Zmhpcl9jbGllbnQ6aW52YWxpZF8x" ) ) - .andExpect( requestTo( dhis2BaseUrl + "/api/" + dhis2ApiVersion + "/programs.json?paging=false&fields=id,name,code,description,selectIncidentDatesInFuture,selectEnrollmentDatesInFuture,displayIncidentDate,registration,withoutRegistration," + + .andExpect( requestTo( dhis2BaseUrl + "/api/" + dhis2ApiVersion + "/programs.json?paging=false&fields=id,name,code,description,lastUpdated,selectIncidentDatesInFuture,selectEnrollmentDatesInFuture,displayIncidentDate,registration,withoutRegistration," + "captureCoordinates,trackedEntityType%5Bid%5D,programTrackedEntityAttributes%5Bid,name,valueType,mandatory,allowFutureDate,trackedEntityAttribute%5Bid,name,code,valueType,generated%5D%5D,programStages%5Bid,name,description,repeatable," + "captureCoordinates,generatedByEnrollmentDate,minDaysFromStart,programStageDataElements%5Bid,compulsory,allowProvidedElsewhere,dataElement%5Bid,name,code,formName,valueType,optionSetValue,optionSet%5Bid,name,options%5Bcode," + "name%5D%5D%5D%5D%5D&filter=code:eq:OU_1234" ) ) @@ -238,7 +239,7 @@ private void getPlanDefinitionByIdentifier() throws Exception "fields=id,name,trackedEntityTypeAttributes%5Bid,name,valueType,mandatory,trackedEntityAttribute%5Bid,name,code,valueType,generated,optionSetValue,optionSet%5Bid,name,options%5Bcode,name%5D%5D%5D%5D" ) ) .andRespond( withSuccess( IOUtils.resourceToString( "/org/dhis2/fhir/adapter/dhis/test/single-tracked-entity-type.json", StandardCharsets.UTF_8 ), MediaType.APPLICATION_JSON ) ); userDhis2Server.expect( ExpectedCount.between( 1, 2 ), method( HttpMethod.GET ) ).andExpect( header( "Authorization", "Basic Zmhpcl9jbGllbnQ6Zmhpcl9jbGllbnRfMQ==" ) ) - .andExpect( requestTo( dhis2BaseUrl + "/api/" + dhis2ApiVersion + "/programs.json?paging=false&fields=id,name,code,description,selectIncidentDatesInFuture,selectEnrollmentDatesInFuture,displayIncidentDate,registration,withoutRegistration," + + .andExpect( requestTo( dhis2BaseUrl + "/api/" + dhis2ApiVersion + "/programs.json?paging=false&fields=id,name,code,description,lastUpdated,selectIncidentDatesInFuture,selectEnrollmentDatesInFuture,displayIncidentDate,registration,withoutRegistration," + "captureCoordinates,trackedEntityType%5Bid%5D,programTrackedEntityAttributes%5Bid,name,valueType,mandatory,allowFutureDate,trackedEntityAttribute%5Bid,name,code,valueType,generated%5D%5D,programStages%5Bid,name,description,repeatable," + "captureCoordinates,generatedByEnrollmentDate,minDaysFromStart,programStageDataElements%5Bid,compulsory,allowProvidedElsewhere,dataElement%5Bid,name,code,formName,valueType,optionSetValue,optionSet%5Bid,name,options%5Bcode," + "name%5D%5D%5D%5D%5D&filter=code:eq:PD_1234" ) ) @@ -267,7 +268,7 @@ public void searchPlanDefinitionWithoutAuthorization() public void searchPlanDefinitionInvalidAuthorization() { userDhis2Server.expect( ExpectedCount.once(), method( HttpMethod.GET ) ).andExpect( header( "Authorization", "Basic Zmhpcl9jbGllbnQ6aW52YWxpZF8x" ) ) - .andExpect( requestTo( dhis2BaseUrl + "/api/" + dhis2ApiVersion + "/programs.json?filter=name:$ilike:Test&paging=true&page=1&pageSize=10&order=id&fields=id,name,code,description,selectIncidentDatesInFuture,selectEnrollmentDatesInFuture," + + .andExpect( requestTo( dhis2BaseUrl + "/api/" + dhis2ApiVersion + "/programs.json?filter=name:$ilike:Test&paging=true&page=1&pageSize=10&order=id&fields=id,name,code,description,lastUpdated,selectIncidentDatesInFuture,selectEnrollmentDatesInFuture," + "displayIncidentDate,registration,withoutRegistration,captureCoordinates,trackedEntityType%5Bid%5D,programTrackedEntityAttributes%5Bid,name,valueType,mandatory,allowFutureDate,trackedEntityAttribute%5Bid,name,code,valueType,generated%5D%5D," + "programStages%5Bid,name,description,repeatable,captureCoordinates,generatedByEnrollmentDate,minDaysFromStart,programStageDataElements%5Bid,compulsory,allowProvidedElsewhere,dataElement%5Bid,name,code,formName,valueType,optionSetValue," + "optionSet%5Bid,name,options%5Bcode,name%5D%5D%5D%5D%5D" ) ) @@ -286,7 +287,7 @@ public void searchPlanDefinition() throws Exception "fields=id,name,trackedEntityTypeAttributes%5Bid,name,valueType,mandatory,trackedEntityAttribute%5Bid,name,code,valueType,generated,optionSetValue,optionSet%5Bid,name,options%5Bcode,name%5D%5D%5D%5D" ) ) .andRespond( withSuccess( IOUtils.resourceToString( "/org/dhis2/fhir/adapter/dhis/test/single-tracked-entity-type.json", StandardCharsets.UTF_8 ), MediaType.APPLICATION_JSON ) ); userDhis2Server.expect( ExpectedCount.once(), method( HttpMethod.GET ) ).andExpect( header( "Authorization", "Basic Zmhpcl9jbGllbnQ6Zmhpcl9jbGllbnRfMQ==" ) ) - .andExpect( requestTo( dhis2BaseUrl + "/api/" + dhis2ApiVersion + "/programs.json?filter=name:$ilike:Child%20Programme&paging=true&page=1&pageSize=10&order=id&fields=id,name,code,description,selectIncidentDatesInFuture," + + .andExpect( requestTo( dhis2BaseUrl + "/api/" + dhis2ApiVersion + "/programs.json?filter=name:$ilike:Child%20Programme&paging=true&page=1&pageSize=10&order=id&fields=id,name,code,description,lastUpdated,selectIncidentDatesInFuture," + "selectEnrollmentDatesInFuture,displayIncidentDate,registration,withoutRegistration,captureCoordinates,trackedEntityType%5Bid%5D,programTrackedEntityAttributes%5Bid,name,valueType,mandatory,allowFutureDate,trackedEntityAttribute%5Bid,name," + "code,valueType,generated%5D%5D,programStages%5Bid,name,description,repeatable,captureCoordinates,generatedByEnrollmentDate,minDaysFromStart,programStageDataElements%5Bid,compulsory,allowProvidedElsewhere,dataElement%5Bid,name,code,formName," + "valueType,optionSetValue,optionSet%5Bid,name,options%5Bcode,name%5D%5D%5D%5D%5D" ) ) diff --git a/app/src/test/java/org/dhis2/fhir/adapter/fhir/transform/fhir/impl/enrollment/AbstractEnrollmentTransformationAppTest.java b/app/src/test/java/org/dhis2/fhir/adapter/fhir/transform/fhir/impl/enrollment/AbstractEnrollmentTransformationAppTest.java index f96cac5b..7b0009fc 100644 --- a/app/src/test/java/org/dhis2/fhir/adapter/fhir/transform/fhir/impl/enrollment/AbstractEnrollmentTransformationAppTest.java +++ b/app/src/test/java/org/dhis2/fhir/adapter/fhir/transform/fhir/impl/enrollment/AbstractEnrollmentTransformationAppTest.java @@ -49,13 +49,14 @@ public abstract class AbstractEnrollmentTransformationAppTest extends AbstractAp protected void expectMetadataRequests() throws Exception { systemDhis2Server.expect( ExpectedCount.between( 0, 1 ), method( HttpMethod.GET ) ).andExpect( header( "Authorization", testConfiguration.getDhis2SystemAuthorization() ) ) - .andExpect( requestTo( dhis2BaseUrl + "/api/" + dhis2ApiVersion + "/programs/EPDyQuoRnXk.json?fields=id,name,code,description,selectIncidentDatesInFuture,selectEnrollmentDatesInFuture,displayIncidentDate,registration," + + .andExpect( requestTo( dhis2BaseUrl + "/api/" + dhis2ApiVersion + "/programs/EPDyQuoRnXk.json?fields=id,name,code,description,lastUpdated,selectIncidentDatesInFuture,selectEnrollmentDatesInFuture,displayIncidentDate,registration," + "withoutRegistration,captureCoordinates,trackedEntityType%5Bid%5D,programTrackedEntityAttributes%5Bid,name,valueType,mandatory,allowFutureDate,trackedEntityAttribute%5Bid,name,code,valueType,generated%5D%5D,programStages%5Bid,name," + "description,repeatable,captureCoordinates,generatedByEnrollmentDate,minDaysFromStart,programStageDataElements%5Bid,compulsory,allowProvidedElsewhere,dataElement%5Bid,name,code,formName,valueType,optionSetValue,optionSet%5Bid,name," + "options%5Bcode,name%5D%5D%5D%5D%5D" ) ) .andRespond( withSuccess( IOUtils.resourceToString( "/org/dhis2/fhir/adapter/dhis/test/single-program.json", StandardCharsets.UTF_8 ), MediaType.APPLICATION_JSON ) ); systemDhis2Server.expect( ExpectedCount.between( 0, 1 ), method( HttpMethod.GET ) ).andExpect( header( "Authorization", testConfiguration.getDhis2SystemAuthorization() ) ) - .andExpect( requestTo( dhis2BaseUrl + "/api/" + dhis2ApiVersion + "/programs.json?paging=false&fields=id,name,code,description,selectIncidentDatesInFuture,selectEnrollmentDatesInFuture,displayIncidentDate,registration,withoutRegistration," + + .andExpect( requestTo( dhis2BaseUrl + "/api/" + dhis2ApiVersion + "/programs.json?paging=false&fields=id,name,code,description,lastUpdated,selectIncidentDatesInFuture,selectEnrollmentDatesInFuture,displayIncidentDate,registration," + + "withoutRegistration," + "captureCoordinates," + "trackedEntityType%5Bid%5D,programTrackedEntityAttributes%5Bid,name,valueType,mandatory,allowFutureDate,trackedEntityAttribute%5Bid,name,code,valueType,generated%5D%5D,programStages%5Bid,name,description,repeatable,captureCoordinates," + "generatedByEnrollmentDate,minDaysFromStart,programStageDataElements%5Bid,compulsory,allowProvidedElsewhere,dataElement%5Bid,name,code,description,formName,valueType,optionSetValue,optionSet%5Bid,name,options%5Bcode," + @@ -74,7 +75,7 @@ protected void expectMetadataRequests() throws Exception .andRespond( withSuccess( IOUtils.resourceToString( "/org/dhis2/fhir/adapter/dhis/test/default-tracked-entity-type.json", StandardCharsets.UTF_8 ), MediaType.APPLICATION_JSON ) ); systemDhis2Server.expect( ExpectedCount.between( 0, 1 ), method( HttpMethod.GET ) ).andExpect( header( "Authorization", testConfiguration.getDhis2SystemAuthorization() ) ) .andExpect( requestTo( dhis2BaseUrl + "/api/" + dhis2ApiVersion + "/programs.json?paging=false&" + - "fields=id,name,code,description,selectIncidentDatesInFuture,selectEnrollmentDatesInFuture,displayIncidentDate,registration,withoutRegistration,captureCoordinates,trackedEntityType%5Bid%5D,programTrackedEntityAttributes%5Bid,name,valueType," + + "fields=id,name,code,description,lastUpdated,selectIncidentDatesInFuture,selectEnrollmentDatesInFuture,displayIncidentDate,registration,withoutRegistration,captureCoordinates,trackedEntityType%5Bid%5D,programTrackedEntityAttributes%5Bid,name,valueType," + "mandatory,allowFutureDate,trackedEntityAttribute%5Bid,name,code,valueType,generated%5D%5D,programStages%5Bid,name,description,repeatable,captureCoordinates,generatedByEnrollmentDate,minDaysFromStart,programStageDataElements%5Bid,compulsory," + "allowProvidedElsewhere,dataElement%5Bid,name,code,description,formName,valueType,optionSetValue,optionSet%5Bid,name,options%5Bcode,name%5D%5D%5D%5D%5D&filter=name:eq:Child%20Programme" ) ) .andRespond( withSuccess( IOUtils.resourceToString( "/org/dhis2/fhir/adapter/dhis/test/single-program.json", StandardCharsets.UTF_8 ), MediaType.APPLICATION_JSON ) ); diff --git a/app/src/test/java/org/dhis2/fhir/adapter/fhir/transform/fhir/impl/programstage/AbstractProgramStageTransformationAppTest.java b/app/src/test/java/org/dhis2/fhir/adapter/fhir/transform/fhir/impl/programstage/AbstractProgramStageTransformationAppTest.java index b104bc05..742bfca8 100644 --- a/app/src/test/java/org/dhis2/fhir/adapter/fhir/transform/fhir/impl/programstage/AbstractProgramStageTransformationAppTest.java +++ b/app/src/test/java/org/dhis2/fhir/adapter/fhir/transform/fhir/impl/programstage/AbstractProgramStageTransformationAppTest.java @@ -50,7 +50,8 @@ public abstract class AbstractProgramStageTransformationAppTest extends Abstract protected void expectMetadataRequests() throws Exception { systemDhis2Server.expect( ExpectedCount.between( 0, 1 ), method( HttpMethod.GET ) ).andExpect( header( "Authorization", testConfiguration.getDhis2SystemAuthorization() ) ) - .andExpect( requestTo( dhis2BaseUrl + "/api/" + dhis2ApiVersion + "/programs.json?paging=false&fields=id,name,code,description,selectIncidentDatesInFuture,selectEnrollmentDatesInFuture,displayIncidentDate,registration,withoutRegistration," + + .andExpect( requestTo( dhis2BaseUrl + "/api/" + dhis2ApiVersion + "/programs.json?paging=false&fields=id,name,code,description,lastUpdated,selectIncidentDatesInFuture,selectEnrollmentDatesInFuture,displayIncidentDate,registration," + + "withoutRegistration," + "captureCoordinates,trackedEntityType%5Bid%5D,programTrackedEntityAttributes%5Bid,name,valueType,mandatory,allowFutureDate,trackedEntityAttribute%5Bid,name,code,valueType,generated%5D%5D,programStages%5Bid,name,description,repeatable," + "captureCoordinates," + "generatedByEnrollmentDate,minDaysFromStart,programStageDataElements%5Bid,compulsory,allowProvidedElsewhere,dataElement%5Bid,name,code,formName,valueType,optionSetValue,optionSet%5Bid,name,options%5Bcode," + @@ -69,7 +70,7 @@ protected void expectMetadataRequests() throws Exception .andRespond( withSuccess( IOUtils.resourceToString( "/org/dhis2/fhir/adapter/dhis/test/default-tracked-entity-type.json", StandardCharsets.UTF_8 ), MediaType.APPLICATION_JSON ) ); systemDhis2Server.expect( ExpectedCount.between( 0, 1 ), method( HttpMethod.GET ) ).andExpect( header( "Authorization", testConfiguration.getDhis2SystemAuthorization() ) ) .andExpect( requestTo( dhis2BaseUrl + "/api/" + dhis2ApiVersion + "/programs.json?paging=false&" + - "fields=id,name,code,description,selectIncidentDatesInFuture,selectEnrollmentDatesInFuture,displayIncidentDate,registration,withoutRegistration,captureCoordinates,trackedEntityType%5Bid%5D,programTrackedEntityAttributes%5Bid,name,valueType," + + "fields=id,name,code,description,lastUpdated,selectIncidentDatesInFuture,selectEnrollmentDatesInFuture,displayIncidentDate,registration,withoutRegistration,captureCoordinates,trackedEntityType%5Bid%5D,programTrackedEntityAttributes%5Bid,name,valueType," + "mandatory,allowFutureDate,trackedEntityAttribute%5Bid,name,code,valueType,generated%5D%5D,programStages%5Bid,name,description,repeatable,captureCoordinates,generatedByEnrollmentDate,minDaysFromStart,programStageDataElements%5Bid,compulsory," + "allowProvidedElsewhere,dataElement%5Bid,name,code,description,formName,valueType,optionSetValue,optionSet%5Bid,name,options%5Bcode,name%5D%5D%5D%5D%5D&filter=name:eq:Child%20Programme" ) ) .andRespond( withSuccess( IOUtils.resourceToString( "/org/dhis2/fhir/adapter/dhis/test/single-program.json", StandardCharsets.UTF_8 ), MediaType.APPLICATION_JSON ) ); diff --git a/dhis/src/main/java/org/dhis2/fhir/adapter/dhis/model/DhisResourceType.java b/dhis/src/main/java/org/dhis2/fhir/adapter/dhis/model/DhisResourceType.java index 80d8238f..30e669e9 100644 --- a/dhis/src/main/java/org/dhis2/fhir/adapter/dhis/model/DhisResourceType.java +++ b/dhis/src/main/java/org/dhis2/fhir/adapter/dhis/model/DhisResourceType.java @@ -36,20 +36,16 @@ /** * Contains the different types of DHIS2 Resources that are can be created. + * The enum values should be listed in the order of the dependencies. * * @author volsch */ public enum DhisResourceType { /** - * Resource is a tracked entity instance. - */ - TRACKED_ENTITY( "trackedEntityInstances", "te", "TrackedEntityRule" ), - - /** - * Resource is a tracked entity type. + * Resource is a organisation unit. */ - TRACKED_ENTITY_TYPE( "trackedEntityTypes", "tt", "TrackedEntityTypeRule" ), + ORGANIZATION_UNIT( "organisationUnits", "ou", "OrganizationUnitRule" ), /** * The program metadata. @@ -61,20 +57,26 @@ public enum DhisResourceType */ PROGRAM_STAGE_METADATA( "programStages", "sm", "ProgramStageMetadataRule" ), + /** - * Resource is a program instance (aka enrollment). + * Resource is a tracked entity type. */ - ENROLLMENT( "enrollments", "en", "EnrollmentRule" ), + TRACKED_ENTITY_TYPE( "trackedEntityTypes", "tt", "TrackedEntityTypeRule" ), /** - * Resource is a program stage instance (aka event of a program instance). + * Resource is a tracked entity instance. */ - PROGRAM_STAGE_EVENT( "events", "ps", "ProgramStageRule" ), + TRACKED_ENTITY( "trackedEntityInstances", "te", "TrackedEntityRule" ), /** - * Resource is a organisation unit. + * Resource is a program instance (aka enrollment). + */ + ENROLLMENT( "enrollments", "en", "EnrollmentRule" ), + + /** + * Resource is a program stage instance (aka event of a program instance). */ - ORGANIZATION_UNIT( "organisationUnits", "ou", "OrganizationUnitRule" ); + PROGRAM_STAGE_EVENT( "events", "ps", "ProgramStageRule" ); private static final Map byTypeName = Arrays.stream( values() ).collect( Collectors.toMap( DhisResourceType::getTypeName, v -> v ) ); diff --git a/dhis/src/main/java/org/dhis2/fhir/adapter/dhis/orgunit/impl/OrganizationUnitServiceImpl.java b/dhis/src/main/java/org/dhis2/fhir/adapter/dhis/orgunit/impl/OrganizationUnitServiceImpl.java index c0839e5d..da9839d4 100644 --- a/dhis/src/main/java/org/dhis2/fhir/adapter/dhis/orgunit/impl/OrganizationUnitServiceImpl.java +++ b/dhis/src/main/java/org/dhis2/fhir/adapter/dhis/orgunit/impl/OrganizationUnitServiceImpl.java @@ -58,7 +58,7 @@ public OrganizationUnitServiceImpl( @Nonnull @Qualifier( "systemDhis2RestTemplat @Nonnull @Override - protected DhisResourceType getDhisResourceType() + public DhisResourceType getDhisResourceType() { return DhisResourceType.ORGANIZATION_UNIT; } diff --git a/dhis/src/main/java/org/dhis2/fhir/adapter/dhis/service/DhisMetadataService.java b/dhis/src/main/java/org/dhis2/fhir/adapter/dhis/service/DhisMetadataService.java index e36d5ce3..2e2e08ae 100644 --- a/dhis/src/main/java/org/dhis2/fhir/adapter/dhis/service/DhisMetadataService.java +++ b/dhis/src/main/java/org/dhis2/fhir/adapter/dhis/service/DhisMetadataService.java @@ -28,8 +28,6 @@ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -import org.dhis2.fhir.adapter.data.model.ProcessedItemInfo; -import org.dhis2.fhir.adapter.dhis.metadata.model.DhisSyncGroup; import org.dhis2.fhir.adapter.dhis.model.DhisMetadata; import org.dhis2.fhir.adapter.dhis.model.DhisResource; import org.dhis2.fhir.adapter.dhis.model.DhisResourceResult; @@ -37,11 +35,7 @@ import org.dhis2.fhir.adapter.dhis.model.UriFilterApplier; import javax.annotation.Nonnull; -import java.time.Instant; -import java.util.Collection; import java.util.Optional; -import java.util.Set; -import java.util.function.Consumer; /** * Service that provides access to DHIS2 metadata. @@ -49,7 +43,7 @@ * @param the concrete type of the metadata. * @author volsch */ -public interface DhisMetadataService +public interface DhisMetadataService extends DhisPolledService, DhisService { @Nonnull Optional findMetadataByReference( @Nonnull Reference reference ); @@ -62,8 +56,4 @@ public interface DhisMetadataService @Nonnull DhisResourceResult find( @Nonnull UriFilterApplier uriFilterApplier, int from, int max ); - - @Nonnull - Instant poll( @Nonnull DhisSyncGroup group, @Nonnull Instant lastUpdated, int toleranceMillis, - int maxSearchCount, @Nonnull Set excludedStoredBy, @Nonnull Consumer> consumer ); } diff --git a/dhis/src/main/java/org/dhis2/fhir/adapter/dhis/service/DhisPolledService.java b/dhis/src/main/java/org/dhis2/fhir/adapter/dhis/service/DhisPolledService.java new file mode 100644 index 00000000..87a7481f --- /dev/null +++ b/dhis/src/main/java/org/dhis2/fhir/adapter/dhis/service/DhisPolledService.java @@ -0,0 +1,52 @@ +package org.dhis2.fhir.adapter.dhis.service; + +/* + * Copyright (c) 2004-2019, University of Oslo + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * Neither the name of the HISP project nor the names of its contributors may + * be used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +import org.dhis2.fhir.adapter.data.model.ProcessedItemInfo; +import org.dhis2.fhir.adapter.dhis.metadata.model.DhisSyncGroup; +import org.dhis2.fhir.adapter.dhis.model.DhisResource; + +import javax.annotation.Nonnull; +import java.time.Instant; +import java.util.Collection; +import java.util.Set; +import java.util.function.Consumer; + +/** + * Service that provides polled access to DHIS2 data. + * + * @param the concrete type of the data. + * @author volsch + */ +public interface DhisPolledService extends DhisService +{ + @Nonnull + Instant poll( @Nonnull DhisSyncGroup group, @Nonnull Instant lastUpdated, int toleranceMillis, + int maxSearchCount, @Nonnull Set excludedStoredBy, @Nonnull Consumer> consumer ); +} diff --git a/dhis/src/main/java/org/dhis2/fhir/adapter/dhis/service/DhisService.java b/dhis/src/main/java/org/dhis2/fhir/adapter/dhis/service/DhisService.java new file mode 100644 index 00000000..9eaf55ba --- /dev/null +++ b/dhis/src/main/java/org/dhis2/fhir/adapter/dhis/service/DhisService.java @@ -0,0 +1,46 @@ +package org.dhis2.fhir.adapter.dhis.service; + +/* + * Copyright (c) 2004-2019, University of Oslo + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * Neither the name of the HISP project nor the names of its contributors may + * be used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +import org.dhis2.fhir.adapter.dhis.model.DhisResource; +import org.dhis2.fhir.adapter.dhis.model.DhisResourceType; + +import javax.annotation.Nonnull; + +/** + * Service that provides access to DHIS2 data. + * + * @param the concrete type of the DHIS2 resource. + * @author volsch + */ +public interface DhisService +{ + @Nonnull + DhisResourceType getDhisResourceType(); +} diff --git a/dhis/src/main/java/org/dhis2/fhir/adapter/dhis/service/impl/AbstractDhisMetadataServiceImpl.java b/dhis/src/main/java/org/dhis2/fhir/adapter/dhis/service/impl/AbstractDhisMetadataServiceImpl.java index 767102db..fc466c7b 100644 --- a/dhis/src/main/java/org/dhis2/fhir/adapter/dhis/service/impl/AbstractDhisMetadataServiceImpl.java +++ b/dhis/src/main/java/org/dhis2/fhir/adapter/dhis/service/impl/AbstractDhisMetadataServiceImpl.java @@ -91,7 +91,7 @@ protected AbstractDhisMetadataServiceImpl( @Nonnull @Qualifier( "systemDhis2Rest } @Nonnull - protected abstract DhisResourceType getDhisResourceType(); + public abstract DhisResourceType getDhisResourceType(); @Nonnull protected abstract Class getItemClass(); diff --git a/dhis/src/main/java/org/dhis2/fhir/adapter/dhis/sync/impl/DhisDataProcessorItemRetrieverImpl.java b/dhis/src/main/java/org/dhis2/fhir/adapter/dhis/sync/impl/DhisDataProcessorItemRetrieverImpl.java index 6a655afa..9f59da2c 100644 --- a/dhis/src/main/java/org/dhis2/fhir/adapter/dhis/sync/impl/DhisDataProcessorItemRetrieverImpl.java +++ b/dhis/src/main/java/org/dhis2/fhir/adapter/dhis/sync/impl/DhisDataProcessorItemRetrieverImpl.java @@ -36,20 +36,25 @@ import org.dhis2.fhir.adapter.data.processor.DataProcessorItemRetriever; import org.dhis2.fhir.adapter.dhis.config.DhisConfig; import org.dhis2.fhir.adapter.dhis.metadata.model.DhisSyncGroup; +import org.dhis2.fhir.adapter.dhis.model.DhisResource; import org.dhis2.fhir.adapter.dhis.model.DhisResourceType; -import org.dhis2.fhir.adapter.dhis.orgunit.OrganizationUnitService; +import org.dhis2.fhir.adapter.dhis.service.DhisPolledService; +import org.dhis2.fhir.adapter.dhis.service.DhisService; import org.dhis2.fhir.adapter.dhis.sync.SyncExcludedDhisUsernameRetriever; -import org.dhis2.fhir.adapter.dhis.tracker.program.EventService; -import org.dhis2.fhir.adapter.dhis.tracker.program.ProgramMetadataService; -import org.dhis2.fhir.adapter.dhis.tracker.trackedentity.TrackedEntityService; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.stereotype.Component; import javax.annotation.Nonnull; import java.time.Instant; import java.util.Collection; +import java.util.List; +import java.util.Map; +import java.util.Objects; import java.util.Set; +import java.util.concurrent.atomic.AtomicReference; import java.util.function.Consumer; +import java.util.stream.Collectors; +import java.util.stream.Stream; /** * The item retriever that polls DHIS2 resources. All relevant resources are @@ -66,13 +71,7 @@ public class DhisDataProcessorItemRetrieverImpl implements DataProcessorItemRetr private final Authorization systemDhis2Authorization; - private final OrganizationUnitService organizationUnitService; - - private final ProgramMetadataService programMetadataService; - - private final TrackedEntityService trackedEntityService; - - private final EventService eventService; + private final Map> polledServices; private final DhisSyncProcessorConfig processorConfig; @@ -80,20 +79,14 @@ public DhisDataProcessorItemRetrieverImpl( @Nonnull AuthorizationContext authorizationContext, @Nonnull @Qualifier( "systemDhis2Authorization" ) Authorization systemDhis2Authorization, @Nonnull SyncExcludedDhisUsernameRetriever excludedDhisUsernameRetriever, - @Nonnull OrganizationUnitService organizationUnitService, - @Nonnull ProgramMetadataService programMetadataService, - @Nonnull TrackedEntityService trackedEntityService, - @Nonnull EventService eventService, + @Nonnull List> polledServices, @Nonnull DhisSyncProcessorConfig processorConfig, @Nonnull DhisConfig config ) { this.authorizationContext = authorizationContext; this.systemDhis2Authorization = systemDhis2Authorization; this.excludedDhisUsernameRetriever = excludedDhisUsernameRetriever; - this.trackedEntityService = trackedEntityService; - this.eventService = eventService; - this.organizationUnitService = organizationUnitService; - this.programMetadataService = programMetadataService; + this.polledServices = polledServices.stream().collect( Collectors.toMap( DhisService::getDhisResourceType, ps -> ps ) ); this.processorConfig = processorConfig; } @@ -103,38 +96,21 @@ public Instant poll( @Nonnull DhisSyncGroup group, @Nonnull Instant lastUpdated, { final int toleranceMillis = processorConfig.getToleranceMillis(); final Set resourceTypes = processorConfig.getResourceTypes(); + authorizationContext.setAuthorization( systemDhis2Authorization ); try { final Set excludedDhisUsernames = excludedDhisUsernameRetriever.findAllDhisUsernames(); - Instant result = Instant.now(); + final AtomicReference result = new AtomicReference<>( Instant.now() ); - if ( resourceTypes.contains( DhisResourceType.ORGANIZATION_UNIT ) ) - { - final Instant currentResult = organizationUnitService.poll( group, lastUpdated, toleranceMillis, maxSearchCount, - excludedDhisUsernames, consumer ); - result = ObjectUtils.min( result, currentResult ); - } - if ( resourceTypes.contains( DhisResourceType.PROGRAM_METADATA ) ) - { - final Instant currentResult = programMetadataService.poll( group, lastUpdated, toleranceMillis, maxSearchCount, - excludedDhisUsernames, consumer ); - result = ObjectUtils.min( result, currentResult ); - } - if ( resourceTypes.contains( DhisResourceType.TRACKED_ENTITY ) ) - { - final Instant currentResult = trackedEntityService.poll( group, lastUpdated, toleranceMillis, maxSearchCount, - excludedDhisUsernames, consumer ); - result = ObjectUtils.min( result, currentResult ); - } - if ( resourceTypes.contains( DhisResourceType.PROGRAM_STAGE_EVENT ) ) - { - final Instant currentResult = eventService.poll( group, lastUpdated, toleranceMillis, maxSearchCount, - excludedDhisUsernames, consumer ); - result = ObjectUtils.min( result, currentResult ); - } + Stream.of( DhisResourceType.values() ).filter( resourceTypes::contains ).map( polledServices::get ).filter( Objects::nonNull ) + .forEach( polledService -> { + final Instant currentResult = polledService.poll( group, lastUpdated, toleranceMillis, maxSearchCount, + excludedDhisUsernames, consumer ); + result.set( ObjectUtils.min( result.get(), currentResult ) ); + } ); - return result; + return result.get(); } finally { diff --git a/dhis/src/main/java/org/dhis2/fhir/adapter/dhis/tracker/program/EnrollmentService.java b/dhis/src/main/java/org/dhis2/fhir/adapter/dhis/tracker/program/EnrollmentService.java index 4326ea09..fa3db68c 100644 --- a/dhis/src/main/java/org/dhis2/fhir/adapter/dhis/tracker/program/EnrollmentService.java +++ b/dhis/src/main/java/org/dhis2/fhir/adapter/dhis/tracker/program/EnrollmentService.java @@ -28,6 +28,8 @@ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ +import org.dhis2.fhir.adapter.dhis.service.DhisService; + import javax.annotation.Nonnull; import java.util.Optional; @@ -38,7 +40,7 @@ * @author volsch * @author Charles Chigoriwa (ITINORDIC) */ -public interface EnrollmentService +public interface EnrollmentService extends DhisService { @Nonnull Optional findLatestActiveRefreshed( @Nonnull String programId, @Nonnull String trackedEntityInstanceId, boolean localOnly ); diff --git a/dhis/src/main/java/org/dhis2/fhir/adapter/dhis/tracker/program/EventService.java b/dhis/src/main/java/org/dhis2/fhir/adapter/dhis/tracker/program/EventService.java index 99582bae..80215e74 100644 --- a/dhis/src/main/java/org/dhis2/fhir/adapter/dhis/tracker/program/EventService.java +++ b/dhis/src/main/java/org/dhis2/fhir/adapter/dhis/tracker/program/EventService.java @@ -28,17 +28,14 @@ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -import org.dhis2.fhir.adapter.data.model.ProcessedItemInfo; -import org.dhis2.fhir.adapter.dhis.metadata.model.DhisSyncGroup; import org.dhis2.fhir.adapter.dhis.model.DhisResourceResult; import org.dhis2.fhir.adapter.dhis.model.UriFilterApplier; +import org.dhis2.fhir.adapter.dhis.service.DhisPolledService; +import org.dhis2.fhir.adapter.dhis.service.DhisService; import javax.annotation.Nonnull; -import java.time.Instant; import java.util.Collection; import java.util.Optional; -import java.util.Set; -import java.util.function.Consumer; /** * Service to create, update and read DHIS2 Program Stage Instances (aka events) @@ -46,7 +43,7 @@ * * @author volsch */ -public interface EventService +public interface EventService extends DhisService, DhisPolledService { @Nonnull Collection findRefreshed( @Nonnull String programId, @Nonnull String programStageId, @@ -69,8 +66,4 @@ Collection find( @Nonnull String programId, @Nonnull String programStageI @Nonnull DhisResourceResult find( @Nonnull String programId, @Nonnull String programStageId, @Nonnull UriFilterApplier uriFilterApplier, int from, int max ); - - @Nonnull - Instant poll( @Nonnull DhisSyncGroup group, @Nonnull Instant lastUpdated, int toleranceMillis, - int maxSearchCount, @Nonnull Set excludedStoredBy, @Nonnull Consumer> consumer ); } diff --git a/dhis/src/main/java/org/dhis2/fhir/adapter/dhis/tracker/program/WritableProgram.java b/dhis/src/main/java/org/dhis2/fhir/adapter/dhis/tracker/program/WritableProgram.java index b6327539..457168ae 100644 --- a/dhis/src/main/java/org/dhis2/fhir/adapter/dhis/tracker/program/WritableProgram.java +++ b/dhis/src/main/java/org/dhis2/fhir/adapter/dhis/tracker/program/WritableProgram.java @@ -50,6 +50,8 @@ public class WritableProgram extends AbstractDhisMetadata implements Program, Se { private static final long serialVersionUID = -4906529875383953995L; + private ZonedDateTime lastUpdated; + @JsonInclude( JsonInclude.Include.NON_NULL ) private String id; @@ -294,7 +296,7 @@ public boolean isDeleted() @Override public ZonedDateTime getLastUpdated() { - return null; + return lastUpdated; } @Override diff --git a/dhis/src/main/java/org/dhis2/fhir/adapter/dhis/tracker/program/impl/EnrollmentServiceImpl.java b/dhis/src/main/java/org/dhis2/fhir/adapter/dhis/tracker/program/impl/EnrollmentServiceImpl.java index 1e33b3a5..b5067391 100644 --- a/dhis/src/main/java/org/dhis2/fhir/adapter/dhis/tracker/program/impl/EnrollmentServiceImpl.java +++ b/dhis/src/main/java/org/dhis2/fhir/adapter/dhis/tracker/program/impl/EnrollmentServiceImpl.java @@ -39,6 +39,7 @@ import org.dhis2.fhir.adapter.dhis.local.LocalDhisRepositoryPersistStatus; import org.dhis2.fhir.adapter.dhis.local.LocalDhisResourceRepositoryTemplate; import org.dhis2.fhir.adapter.dhis.model.DhisResourceComparator; +import org.dhis2.fhir.adapter.dhis.model.DhisResourceType; import org.dhis2.fhir.adapter.dhis.model.ImportStatus; import org.dhis2.fhir.adapter.dhis.model.ImportSummaries; import org.dhis2.fhir.adapter.dhis.model.ImportSummary; @@ -117,6 +118,13 @@ public EnrollmentServiceImpl( @Nonnull @Qualifier( "userDhis2RestTemplate" ) Res this.resourceRepositoryTemplate = new LocalDhisResourceRepositoryTemplate<>( Enrollment.class, requestCacheService, this ); } + @Nonnull + @Override + public DhisResourceType getDhisResourceType() + { + return DhisResourceType.ENROLLMENT; + } + @HystrixCommand( ignoreExceptions = UnauthorizedException.class ) @Nonnull @Override diff --git a/dhis/src/main/java/org/dhis2/fhir/adapter/dhis/tracker/program/impl/EventServiceImpl.java b/dhis/src/main/java/org/dhis2/fhir/adapter/dhis/tracker/program/impl/EventServiceImpl.java index fc9b8fc7..5799e5b4 100644 --- a/dhis/src/main/java/org/dhis2/fhir/adapter/dhis/tracker/program/impl/EventServiceImpl.java +++ b/dhis/src/main/java/org/dhis2/fhir/adapter/dhis/tracker/program/impl/EventServiceImpl.java @@ -45,6 +45,7 @@ import org.dhis2.fhir.adapter.dhis.model.DataValue; import org.dhis2.fhir.adapter.dhis.model.DhisResourceComparator; import org.dhis2.fhir.adapter.dhis.model.DhisResourceResult; +import org.dhis2.fhir.adapter.dhis.model.DhisResourceType; import org.dhis2.fhir.adapter.dhis.model.ImportStatus; import org.dhis2.fhir.adapter.dhis.model.ImportSummary; import org.dhis2.fhir.adapter.dhis.model.ImportSummaryWebMessage; @@ -136,6 +137,13 @@ public EventServiceImpl( @Nonnull @Qualifier( "userDhis2RestTemplate" ) RestTemp this.resourceRepositoryTemplate = new LocalDhisResourceRepositoryTemplate<>( Event.class, requestCacheService, this ); } + @Nonnull + @Override + public DhisResourceType getDhisResourceType() + { + return DhisResourceType.PROGRAM_STAGE_EVENT; + } + @HystrixCommand( ignoreExceptions = { DhisConflictException.class, UnauthorizedException.class } ) @Override public boolean delete( @Nonnull String eventId ) diff --git a/dhis/src/main/java/org/dhis2/fhir/adapter/dhis/tracker/program/impl/ProgramMetadataServiceImpl.java b/dhis/src/main/java/org/dhis2/fhir/adapter/dhis/tracker/program/impl/ProgramMetadataServiceImpl.java index 070f1d71..a17179c8 100644 --- a/dhis/src/main/java/org/dhis2/fhir/adapter/dhis/tracker/program/impl/ProgramMetadataServiceImpl.java +++ b/dhis/src/main/java/org/dhis2/fhir/adapter/dhis/tracker/program/impl/ProgramMetadataServiceImpl.java @@ -49,7 +49,7 @@ public class ProgramMetadataServiceImpl extends AbstractDhisMetadataServiceImpl implements ProgramMetadataService { protected static final String FIELDS = - "id,name,code,description,selectIncidentDatesInFuture,selectEnrollmentDatesInFuture,displayIncidentDate," + + "id,name,code,description,lastUpdated,selectIncidentDatesInFuture,selectEnrollmentDatesInFuture,displayIncidentDate," + "registration,withoutRegistration,captureCoordinates,trackedEntityType[id]," + "programTrackedEntityAttributes[id,name,valueType,mandatory,allowFutureDate," + "trackedEntityAttribute[id,name,code,valueType,generated]]," + @@ -64,7 +64,7 @@ public ProgramMetadataServiceImpl( @Nonnull @Qualifier( "systemDhis2RestTemplate @Nonnull @Override - protected DhisResourceType getDhisResourceType() + public DhisResourceType getDhisResourceType() { return DhisResourceType.PROGRAM_METADATA; } diff --git a/dhis/src/main/java/org/dhis2/fhir/adapter/dhis/tracker/trackedentity/TrackedEntityService.java b/dhis/src/main/java/org/dhis2/fhir/adapter/dhis/tracker/trackedentity/TrackedEntityService.java index 34688f99..c8348eac 100644 --- a/dhis/src/main/java/org/dhis2/fhir/adapter/dhis/tracker/trackedentity/TrackedEntityService.java +++ b/dhis/src/main/java/org/dhis2/fhir/adapter/dhis/tracker/trackedentity/TrackedEntityService.java @@ -28,25 +28,22 @@ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -import org.dhis2.fhir.adapter.data.model.ProcessedItemInfo; -import org.dhis2.fhir.adapter.dhis.metadata.model.DhisSyncGroup; import org.dhis2.fhir.adapter.dhis.model.DhisResourceResult; import org.dhis2.fhir.adapter.dhis.model.UriFilterApplier; +import org.dhis2.fhir.adapter.dhis.service.DhisPolledService; +import org.dhis2.fhir.adapter.dhis.service.DhisService; import javax.annotation.Nonnull; -import java.time.Instant; import java.util.Collection; import java.util.Map; import java.util.Optional; -import java.util.Set; -import java.util.function.Consumer; /** * Service that allows to create, read and update tracked entity instances. * * @author volsch */ -public interface TrackedEntityService +public interface TrackedEntityService extends DhisService, DhisPolledService { void updateGeneratedValues( @Nonnull TrackedEntityInstance trackedEntityInstance, @Nonnull TrackedEntityType type, @Nonnull Map requiredValues ); @@ -74,8 +71,4 @@ Collection findByAttrValue( @Nonnull String typeId, @Nonnull DhisResourceResult find( @Nonnull String trackedEntityTypeId, @Nonnull UriFilterApplier uriFilterApplier, int from, int max ); - - @Nonnull - Instant poll( @Nonnull DhisSyncGroup group, @Nonnull Instant lastUpdated, int toleranceMillis, - int maxSearchCount, @Nonnull Set excludedStoredBy, @Nonnull Consumer> consumer ); } diff --git a/dhis/src/main/java/org/dhis2/fhir/adapter/dhis/tracker/trackedentity/impl/TrackedEntityServiceImpl.java b/dhis/src/main/java/org/dhis2/fhir/adapter/dhis/tracker/trackedentity/impl/TrackedEntityServiceImpl.java index beda50e6..7c8102f0 100644 --- a/dhis/src/main/java/org/dhis2/fhir/adapter/dhis/tracker/trackedentity/impl/TrackedEntityServiceImpl.java +++ b/dhis/src/main/java/org/dhis2/fhir/adapter/dhis/tracker/trackedentity/impl/TrackedEntityServiceImpl.java @@ -154,6 +154,13 @@ public TrackedEntityServiceImpl( @Nonnull @Qualifier( "userDhis2RestTemplate" ) this.resourceRepositoryTemplate = new LocalDhisResourceRepositoryTemplate<>( TrackedEntityInstance.class, requestCacheService, this ); } + @Nonnull + @Override + public DhisResourceType getDhisResourceType() + { + return DhisResourceType.TRACKED_ENTITY; + } + @HystrixCommand( ignoreExceptions = { DhisConflictException.class, UnauthorizedException.class } ) @Override public void updateGeneratedValues( @Nonnull TrackedEntityInstance trackedEntityInstance, @Nonnull TrackedEntityType type, @Nonnull Map requiredValues ) diff --git a/dhis/src/test/java/org/dhis2/fhir/adapter/dhis/tracker/program/impl/ProgramMetadataServiceImplTest.java b/dhis/src/test/java/org/dhis2/fhir/adapter/dhis/tracker/program/impl/ProgramMetadataServiceImplTest.java index b1aabe2f..27653d09 100644 --- a/dhis/src/test/java/org/dhis2/fhir/adapter/dhis/tracker/program/impl/ProgramMetadataServiceImplTest.java +++ b/dhis/src/test/java/org/dhis2/fhir/adapter/dhis/tracker/program/impl/ProgramMetadataServiceImplTest.java @@ -81,7 +81,8 @@ public void setUp() @Test public void findMetadataByReferenceId() throws IOException { - mockServer.expect( requestTo( "http://localhost:8080/api/programs/93783.json?fields=id,name,code,description,selectIncidentDatesInFuture,selectEnrollmentDatesInFuture,displayIncidentDate,registration,withoutRegistration,captureCoordinates," + + mockServer.expect( requestTo( "http://localhost:8080/api/programs/93783.json?fields=id,name,code,description,lastUpdated,selectIncidentDatesInFuture,selectEnrollmentDatesInFuture,displayIncidentDate,registration,withoutRegistration," + + "captureCoordinates," + "trackedEntityType%5Bid%5D,programTrackedEntityAttributes%5Bid,name,valueType,mandatory,allowFutureDate,trackedEntityAttribute%5Bid,name,code,valueType,generated%5D%5D,programStages%5Bid,name,description,repeatable,captureCoordinates," + "generatedByEnrollmentDate,minDaysFromStart,programStageDataElements%5Bid,compulsory,allowProvidedElsewhere,dataElement%5Bid,name,code,formName,valueType,optionSetValue,optionSet%5Bid,name,options%5Bcode,name%5D%5D%5D%5D%5D" ) ) .andExpect( method( HttpMethod.GET ) ).andRespond( withSuccess( IOUtils.resourceToByteArray( "/org/dhis2/fhir/adapter/dhis/tracker/program/impl/program.json" ), MediaType.APPLICATION_JSON ) ); @@ -94,7 +95,7 @@ public void findMetadataByReferenceId() throws IOException @Test public void findMetadataByReferenceIdNotFound() { - mockServer.expect( requestTo( "http://localhost:8080/api/programs/93783.json?fields=id,name,code,description,selectIncidentDatesInFuture,selectEnrollmentDatesInFuture,displayIncidentDate,registration,withoutRegistration,captureCoordinates," + + mockServer.expect( requestTo( "http://localhost:8080/api/programs/93783.json?fields=id,name,code,description,lastUpdated,selectIncidentDatesInFuture,selectEnrollmentDatesInFuture,displayIncidentDate,registration,withoutRegistration,captureCoordinates," + "trackedEntityType%5Bid%5D,programTrackedEntityAttributes%5Bid,name,valueType,mandatory,allowFutureDate,trackedEntityAttribute%5Bid,name,code,valueType,generated%5D%5D,programStages%5Bid,name,description,repeatable,captureCoordinates," + "generatedByEnrollmentDate,minDaysFromStart,programStageDataElements%5Bid,compulsory,allowProvidedElsewhere,dataElement%5Bid,name,code,formName,valueType,optionSetValue,optionSet%5Bid,name,options%5Bcode,name%5D%5D%5D%5D%5D" ) ).andExpect( method( HttpMethod.GET ) ) .andRespond( MockRestResponseCreators.withStatus( HttpStatus.NOT_FOUND ).body( "{}" ).contentType( MediaType.APPLICATION_JSON ) ); @@ -106,7 +107,7 @@ public void findMetadataByReferenceIdNotFound() @Test( expected = HttpServerErrorException.class ) public void findMetadataByReferenceIdServerError() { - mockServer.expect( requestTo( "http://localhost:8080/api/programs/93783.json?fields=id,name,code,description,selectIncidentDatesInFuture,selectEnrollmentDatesInFuture,displayIncidentDate,registration,withoutRegistration,captureCoordinates," + + mockServer.expect( requestTo( "http://localhost:8080/api/programs/93783.json?fields=id,name,code,description,lastUpdated,selectIncidentDatesInFuture,selectEnrollmentDatesInFuture,displayIncidentDate,registration,withoutRegistration,captureCoordinates," + "trackedEntityType%5Bid%5D,programTrackedEntityAttributes%5Bid,name,valueType,mandatory,allowFutureDate,trackedEntityAttribute%5Bid,name,code,valueType,generated%5D%5D,programStages%5Bid,name,description,repeatable,captureCoordinates," + "generatedByEnrollmentDate,minDaysFromStart,programStageDataElements%5Bid,compulsory,allowProvidedElsewhere,dataElement%5Bid,name,code,formName,valueType,optionSetValue,optionSet%5Bid,name,options%5Bcode,name%5D%5D%5D%5D%5D" ) ).andExpect( method( HttpMethod.GET ) ) .andRespond( MockRestResponseCreators.withServerError() ); @@ -116,7 +117,7 @@ public void findMetadataByReferenceIdServerError() @Test public void findMetadataByReferenceCode() throws IOException { - mockServer.expect( requestTo( "http://localhost:8080/api/programs.json?paging=false&fields=id,name,code,description,selectIncidentDatesInFuture,selectEnrollmentDatesInFuture,displayIncidentDate,registration,withoutRegistration,captureCoordinates," + + mockServer.expect( requestTo( "http://localhost:8080/api/programs.json?paging=false&fields=id,name,code,description,lastUpdated,selectIncidentDatesInFuture,selectEnrollmentDatesInFuture,displayIncidentDate,registration,withoutRegistration,captureCoordinates," + "trackedEntityType%5Bid%5D,programTrackedEntityAttributes%5Bid,name,valueType,mandatory,allowFutureDate,trackedEntityAttribute%5Bid,name,code,valueType,generated%5D%5D,programStages%5Bid,name,description,repeatable,captureCoordinates," + "generatedByEnrollmentDate,minDaysFromStart,programStageDataElements%5Bid,compulsory,allowProvidedElsewhere,dataElement%5Bid,name,code,formName,valueType,optionSetValue,optionSet%5Bid,name,options%5Bcode," + "name%5D%5D%5D%5D%5D&filter=code:eq:OU_3783" ) ).andExpect( method( HttpMethod.GET ) ) @@ -130,7 +131,7 @@ public void findMetadataByReferenceCode() throws IOException @Test public void findMetadataByReferenceCodeNotFound() throws IOException { - mockServer.expect( requestTo( "http://localhost:8080/api/programs.json?paging=false&fields=id,name,code,description,selectIncidentDatesInFuture,selectEnrollmentDatesInFuture,displayIncidentDate,registration,withoutRegistration,captureCoordinates," + + mockServer.expect( requestTo( "http://localhost:8080/api/programs.json?paging=false&fields=id,name,code,description,lastUpdated,selectIncidentDatesInFuture,selectEnrollmentDatesInFuture,displayIncidentDate,registration,withoutRegistration,captureCoordinates," + "trackedEntityType%5Bid%5D,programTrackedEntityAttributes%5Bid,name,valueType,mandatory,allowFutureDate,trackedEntityAttribute%5Bid,name,code,valueType,generated%5D%5D,programStages%5Bid,name,description,repeatable,captureCoordinates," + "generatedByEnrollmentDate,minDaysFromStart,programStageDataElements%5Bid,compulsory,allowProvidedElsewhere,dataElement%5Bid,name,code,formName,valueType,optionSetValue,optionSet%5Bid,name,options%5Bcode," + "name%5D%5D%5D%5D%5D&filter=code:eq:OU_3783" ) ).andExpect( method( HttpMethod.GET ) ) @@ -143,7 +144,7 @@ public void findMetadataByReferenceCodeNotFound() throws IOException @Test public void findMetadataByReferenceName() throws IOException { - mockServer.expect( requestTo( "http://localhost:8080/api/programs.json?paging=false&fields=id,name,code,description,selectIncidentDatesInFuture,selectEnrollmentDatesInFuture,displayIncidentDate,registration,withoutRegistration,captureCoordinates," + + mockServer.expect( requestTo( "http://localhost:8080/api/programs.json?paging=false&fields=id,name,code,description,lastUpdated,selectIncidentDatesInFuture,selectEnrollmentDatesInFuture,displayIncidentDate,registration,withoutRegistration,captureCoordinates," + "trackedEntityType%5Bid%5D,programTrackedEntityAttributes%5Bid,name,valueType,mandatory,allowFutureDate,trackedEntityAttribute%5Bid,name,code,valueType,generated%5D%5D,programStages%5Bid,name,description,repeatable,captureCoordinates," + "generatedByEnrollmentDate,minDaysFromStart,programStageDataElements%5Bid,compulsory,allowProvidedElsewhere,dataElement%5Bid,name,code,formName,valueType,optionSetValue,optionSet%5Bid,name,options%5Bcode," + "name%5D%5D%5D%5D%5D&filter=name:eq:Freetown" ) ).andExpect( method( HttpMethod.GET ) ) @@ -157,7 +158,7 @@ public void findMetadataByReferenceName() throws IOException @Test public void findMetadataByReferenceNameNotFound() throws IOException { - mockServer.expect( requestTo( "http://localhost:8080/api/programs.json?paging=false&fields=id,name,code,description,selectIncidentDatesInFuture,selectEnrollmentDatesInFuture,displayIncidentDate,registration,withoutRegistration,captureCoordinates," + + mockServer.expect( requestTo( "http://localhost:8080/api/programs.json?paging=false&fields=id,name,code,description,lastUpdated,selectIncidentDatesInFuture,selectEnrollmentDatesInFuture,displayIncidentDate,registration,withoutRegistration,captureCoordinates," + "trackedEntityType%5Bid%5D,programTrackedEntityAttributes%5Bid,name,valueType,mandatory,allowFutureDate,trackedEntityAttribute%5Bid,name,code,valueType,generated%5D%5D,programStages%5Bid,name,description,repeatable,captureCoordinates," + "generatedByEnrollmentDate,minDaysFromStart,programStageDataElements%5Bid,compulsory,allowProvidedElsewhere,dataElement%5Bid,name,code,formName,valueType,optionSetValue,optionSet%5Bid,name,options%5Bcode," + "name%5D%5D%5D%5D%5D&filter=name:eq:Freetown" ) ).andExpect( method( HttpMethod.GET ) ) diff --git a/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/AbstractDhisToFhirTransformer.java b/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/AbstractDhisToFhirTransformer.java index ed19b435..91e0517c 100644 --- a/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/AbstractDhisToFhirTransformer.java +++ b/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/AbstractDhisToFhirTransformer.java @@ -250,7 +250,7 @@ protected Optional getResource( @Nonnull FhirClient fhi } @Nonnull - protected String createDhisFhirResourceId( @Nonnull RuleInfo ruleInfo, @Nonnull ScriptedDhisResource dhisResource ) + protected String createDhisFhirResourceId( @Nonnull RuleInfo ruleInfo, @Nonnull ScriptedDhisResource dhisResource ) { if ( ruleInfo.getRule().isSimpleFhirId() ) { @@ -532,10 +532,9 @@ protected boolean evaluateNotModified( @Nonnull DhisToFhirTransformerContext con } @Nonnull - protected String createAdapterIdentifierValue( @Nonnull RuleInfo rule, @Nonnull ScriptedDhisResource dhisResource ) + protected String createAdapterIdentifierValue( @Nonnull RuleInfo ruleInfo, @Nonnull ScriptedDhisResource dhisResource ) { - return DhisFhirResourceId.toString( rule.getRule().getDhisResourceType(), - Objects.requireNonNull( dhisResource.getId() ), rule.getRule().getId() ); + return createDhisFhirResourceId( ruleInfo, dhisResource ); } @Nullable From fbd04106d9807d48d982f725a0a8136f1c9766f6 Mon Sep 17 00:00:00 2001 From: Volker Schmidt Date: Tue, 30 Jul 2019 14:26:00 +0200 Subject: [PATCH 05/16] Adds support for DHIS2 program stage to FHIR Questionnaire. --- .../main/resources/default-application.yml | 1 + .../AbstractProgramStageFhirRestAppTest.java | 6 +- .../r4/R4ProgramMetadataFhirRestAppTest.java | 35 +-- ...R4ProgramStageMetadataFhirRestAppTest.java | 221 ++++++++++++++++++ ...stractEnrollmentTransformationAppTest.java | 22 +- ...ractProgramStageTransformationAppTest.java | 10 +- .../adapter/dhis/test/all-program-stages.json | 185 +++++++++++++++ .../dhis/test/default-program-stage.json | 101 ++++++++ .../dhis/test/single-program-stage.json | 99 ++++++++ .../sync/impl/DhisResourceRepositoryImpl.java | 10 +- .../program/ImmutableProgramStage.java | 7 + .../dhis/tracker/program/ProgramStage.java | 2 + .../program/ProgramStageMetadataService.java | 40 ++++ .../dhis/tracker/program/WritableProgram.java | 4 +- .../tracker/program/WritableProgramStage.java | 19 +- .../program/impl/DhisProgramStages.java | 62 +++++ .../impl/ProgramMetadataServiceImpl.java | 4 +- .../impl/ProgramStageMetadataServiceImpl.java | 86 +++++++ .../impl/ProgramMetadataServiceImplTest.java | 38 +-- .../ProgramStageMetadataServiceImplTest.java | 156 +++++++++++++ .../program/impl/emptyProgramStages.json | 3 + .../tracker/program/impl/programStage.json | 11 + .../tracker/program/impl/programStages.json | 24 ++ ...gramPlanDefinition.StructureDefinition.xml | 6 +- ...ogramQuestionnaire.StructureDefinition.xml | 16 +- .../R4ProgramMetadataToFhirDataProvider.java | 3 +- ...tadataToFhirPlanDefinitionTransformer.java | 3 +- ...rogramStageMetadataToFhirDataProvider.java | 70 ++++++ ...etadataToFhirQuestionnaireTransformer.java | 158 +++++++++++++ ...ProgramMetadataToFhirDataProviderTest.java | 2 +- ...taToFhirPlanDefinitionTransformerTest.java | 2 +- ...amStageMetadataToFhirDataProviderTest.java | 93 ++++++++ ...ataToFhirQuestionnaireTransformerTest.java | 170 ++++++++++++++ ...rogramStageMetadataToFhirDataProvider.java | 66 ++++++ ...etadataToFhirQuestionnaireTransformer.java | 93 ++++++++ ...ramStageMetadataToFhirRequestResolver.java | 76 ++++++ ...0.41_10_0__Plan_Definition_Sample_Data.sql | 2 + 37 files changed, 1843 insertions(+), 63 deletions(-) create mode 100644 app/src/test/java/org/dhis2/fhir/adapter/fhir/server/r4/R4ProgramStageMetadataFhirRestAppTest.java create mode 100644 app/src/test/resources/org/dhis2/fhir/adapter/dhis/test/all-program-stages.json create mode 100644 app/src/test/resources/org/dhis2/fhir/adapter/dhis/test/default-program-stage.json create mode 100644 app/src/test/resources/org/dhis2/fhir/adapter/dhis/test/single-program-stage.json create mode 100644 dhis/src/main/java/org/dhis2/fhir/adapter/dhis/tracker/program/ProgramStageMetadataService.java create mode 100644 dhis/src/main/java/org/dhis2/fhir/adapter/dhis/tracker/program/impl/DhisProgramStages.java create mode 100644 dhis/src/main/java/org/dhis2/fhir/adapter/dhis/tracker/program/impl/ProgramStageMetadataServiceImpl.java create mode 100644 dhis/src/test/java/org/dhis2/fhir/adapter/dhis/tracker/program/impl/ProgramStageMetadataServiceImplTest.java create mode 100644 dhis/src/test/resources/org/dhis2/fhir/adapter/dhis/tracker/program/impl/emptyProgramStages.json create mode 100644 dhis/src/test/resources/org/dhis2/fhir/adapter/dhis/tracker/program/impl/programStage.json create mode 100644 dhis/src/test/resources/org/dhis2/fhir/adapter/dhis/tracker/program/impl/programStages.json create mode 100644 fhir-r4/src/main/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/metadata/r4/R4ProgramStageMetadataToFhirDataProvider.java create mode 100644 fhir-r4/src/main/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/metadata/r4/R4ProgramStageMetadataToFhirQuestionnaireTransformer.java create mode 100644 fhir-r4/src/test/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/metadata/r4/R4ProgramStageMetadataToFhirDataProviderTest.java create mode 100644 fhir-r4/src/test/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/metadata/r4/R4ProgramStageMetadataToFhirQuestionnaireTransformerTest.java create mode 100644 fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/metadata/program/AbstractProgramStageMetadataToFhirDataProvider.java create mode 100644 fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/metadata/program/AbstractProgramStageMetadataToFhirQuestionnaireTransformer.java create mode 100644 fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/metadata/program/ProgramStageMetadataToFhirRequestResolver.java diff --git a/app/src/main/resources/default-application.yml b/app/src/main/resources/default-application.yml index 1feb5c15..2fb496db 100644 --- a/app/src/main/resources/default-application.yml +++ b/app/src/main/resources/default-application.yml @@ -357,6 +357,7 @@ dhis2.fhir-adapter: resource-types: - ORGANIZATION_UNIT - PROGRAM_METADATA + - PROGRAM_STAGE_METADATA - TRACKED_ENTITY - PROGRAM_STAGE_EVENT # The queue that is used to store distributed requests to synchronize the data from diff --git a/app/src/test/java/org/dhis2/fhir/adapter/fhir/server/AbstractProgramStageFhirRestAppTest.java b/app/src/test/java/org/dhis2/fhir/adapter/fhir/server/AbstractProgramStageFhirRestAppTest.java index b7ed5cd6..a5683b12 100644 --- a/app/src/test/java/org/dhis2/fhir/adapter/fhir/server/AbstractProgramStageFhirRestAppTest.java +++ b/app/src/test/java/org/dhis2/fhir/adapter/fhir/server/AbstractProgramStageFhirRestAppTest.java @@ -69,12 +69,14 @@ protected void expectProgramStageMetadataRequests() throws Exception .andExpect( requestTo( dhis2BaseUrl + "/api/" + dhis2ApiVersion + "/programs/EPDyQuoRnXk.json?" + "fields=id,name,code,description,lastUpdated,selectIncidentDatesInFuture,selectEnrollmentDatesInFuture,displayIncidentDate,registration,withoutRegistration,captureCoordinates,trackedEntityType%5Bid%5D,programTrackedEntityAttributes%5Bid," + "name,valueType," + - "mandatory,allowFutureDate,trackedEntityAttribute%5Bid,name,code,valueType,generated%5D%5D,programStages%5Bid,name,description,repeatable,captureCoordinates,generatedByEnrollmentDate,minDaysFromStart,programStageDataElements%5Bid,compulsory," + + "mandatory,allowFutureDate,trackedEntityAttribute%5Bid,name,code,valueType,generated%5D%5D,programStages%5Bid,program%5Bid%5D,lastUpdated,name,description,repeatable,captureCoordinates,generatedByEnrollmentDate,minDaysFromStart," + + "programStageDataElements%5Bid,compulsory," + "allowProvidedElsewhere,dataElement%5Bid,name,code,formName,valueType,optionSetValue,optionSet%5Bid,name,options%5Bcode,name%5D%5D%5D%5D%5D" ) ) .andRespond( withSuccess( IOUtils.resourceToString( "/org/dhis2/fhir/adapter/dhis/test/single-program.json", StandardCharsets.UTF_8 ), MediaType.APPLICATION_JSON ) ); systemDhis2Server.expect( ExpectedCount.between( 0, 1 ), method( HttpMethod.GET ) ).andExpect( header( "Authorization", testConfiguration.getDhis2SystemAuthorization() ) ) .andExpect( requestTo( dhis2BaseUrl + "/api/" + dhis2ApiVersion + "/programs.json?paging=false&fields=id,name,code,description,lastUpdated,selectIncidentDatesInFuture,selectEnrollmentDatesInFuture,displayIncidentDate,registration," + - "withoutRegistration,captureCoordinates,trackedEntityType%5Bid%5D,programTrackedEntityAttributes%5Bid,name,valueType,mandatory,allowFutureDate,trackedEntityAttribute%5Bid,name,code,valueType,generated%5D%5D,programStages%5Bid,name," + + "withoutRegistration,captureCoordinates,trackedEntityType%5Bid%5D,programTrackedEntityAttributes%5Bid,name,valueType,mandatory,allowFutureDate,trackedEntityAttribute%5Bid,name,code,valueType,generated%5D%5D,programStages%5Bid," + + "program%5Bid%5D,lastUpdated,name," + "description,repeatable,captureCoordinates,generatedByEnrollmentDate,minDaysFromStart,programStageDataElements%5Bid,compulsory,allowProvidedElsewhere,dataElement%5Bid,name,code,formName,valueType,optionSetValue,optionSet%5Bid,name," + "options%5Bcode,name%5D%5D%5D%5D%5D&filter=name:eq:Child%20Programme" ) ) .andRespond( withSuccess( IOUtils.resourceToString( "/org/dhis2/fhir/adapter/dhis/test/default-program.json", StandardCharsets.UTF_8 ), MediaType.APPLICATION_JSON ) ); diff --git a/app/src/test/java/org/dhis2/fhir/adapter/fhir/server/r4/R4ProgramMetadataFhirRestAppTest.java b/app/src/test/java/org/dhis2/fhir/adapter/fhir/server/r4/R4ProgramMetadataFhirRestAppTest.java index 695d91d2..e86e33b7 100644 --- a/app/src/test/java/org/dhis2/fhir/adapter/fhir/server/r4/R4ProgramMetadataFhirRestAppTest.java +++ b/app/src/test/java/org/dhis2/fhir/adapter/fhir/server/r4/R4ProgramMetadataFhirRestAppTest.java @@ -80,7 +80,8 @@ public void getPlanDefinitionWithInvalidAuthorization() .andExpect( requestTo( dhis2BaseUrl + "/api/" + dhis2ApiVersion + "/programs/EPDyQuoRnXk.json?" + "fields=id,name,code,description,lastUpdated,selectIncidentDatesInFuture,selectEnrollmentDatesInFuture,displayIncidentDate,registration,withoutRegistration,captureCoordinates,trackedEntityType%5Bid%5D,programTrackedEntityAttributes%5Bid," + "name,valueType," + - "mandatory,allowFutureDate,trackedEntityAttribute%5Bid,name,code,valueType,generated%5D%5D,programStages%5Bid,name,description,repeatable,captureCoordinates,generatedByEnrollmentDate,minDaysFromStart,programStageDataElements%5Bid,compulsory," + + "mandatory,allowFutureDate,trackedEntityAttribute%5Bid,name,code,valueType,generated%5D%5D,programStages%5Bid,program%5Bid%5D,lastUpdated,name,description,repeatable,captureCoordinates,generatedByEnrollmentDate,minDaysFromStart," + + "programStageDataElements%5Bid,compulsory," + "allowProvidedElsewhere,dataElement%5Bid,name,code,formName,valueType,optionSetValue,optionSet%5Bid,name,options%5Bcode,name%5D%5D%5D%5D%5D" ) ) .andRespond( withStatus( HttpStatus.UNAUTHORIZED ) ); @@ -107,13 +108,14 @@ private void getPlanDefinition() throws Exception .andRespond( withSuccess( IOUtils.resourceToString( "/org/dhis2/fhir/adapter/dhis/test/single-tracked-entity-type.json", StandardCharsets.UTF_8 ), MediaType.APPLICATION_JSON ) ); userDhis2Server.expect( ExpectedCount.once(), method( HttpMethod.GET ) ).andExpect( header( "Authorization", "Basic Zmhpcl9jbGllbnQ6Zmhpcl9jbGllbnRfMQ==" ) ) .andExpect( requestTo( dhis2BaseUrl + "/api/" + dhis2ApiVersion + "/programs/EPDyQuoRnXk.json?fields=id,name,code,description,lastUpdated,selectIncidentDatesInFuture,selectEnrollmentDatesInFuture,displayIncidentDate,registration," + - "withoutRegistration,captureCoordinates,trackedEntityType%5Bid%5D,programTrackedEntityAttributes%5Bid,name,valueType,mandatory,allowFutureDate,trackedEntityAttribute%5Bid,name,code,valueType,generated%5D%5D,programStages%5Bid,name," + + "withoutRegistration,captureCoordinates,trackedEntityType%5Bid%5D,programTrackedEntityAttributes%5Bid,name,valueType,mandatory,allowFutureDate,trackedEntityAttribute%5Bid,name,code,valueType,generated%5D%5D,programStages%5Bid," + + "program%5Bid%5D,lastUpdated,name," + "description,repeatable,captureCoordinates,generatedByEnrollmentDate,minDaysFromStart,programStageDataElements%5Bid,compulsory,allowProvidedElsewhere,dataElement%5Bid,name,code,formName,valueType,optionSetValue,optionSet%5Bid,name," + "options%5Bcode,name%5D%5D%5D%5D%5D" ) ) .andRespond( withSuccess( IOUtils.resourceToString( "/org/dhis2/fhir/adapter/dhis/test/single-program.json", StandardCharsets.UTF_8 ), MediaType.APPLICATION_JSON ) ); userDhis2Server.expect( ExpectedCount.between( 0, 1 ), method( HttpMethod.GET ) ).andExpect( header( "Authorization", "Basic Zmhpcl9jbGllbnQ6Zmhpcl9jbGllbnRfMQ==" ) ) .andExpect( requestTo( dhis2BaseUrl + "/api/" + dhis2ApiVersion + "/programs.json?paging=false&fields=id,name,code,description,lastUpdated,selectIncidentDatesInFuture,selectEnrollmentDatesInFuture,displayIncidentDate,registration," + - "withoutRegistration,captureCoordinates,trackedEntityType%5Bid%5D,programTrackedEntityAttributes%5Bid,name,valueType,mandatory,allowFutureDate,trackedEntityAttribute%5Bid,name,code,valueType,generated%5D%5D,programStages%5Bid,name," + + "withoutRegistration,captureCoordinates,trackedEntityType%5Bid%5D,programTrackedEntityAttributes%5Bid,name,valueType,mandatory,allowFutureDate,trackedEntityAttribute%5Bid,name,code,valueType,generated%5D%5D,programStages%5Bid,program%5Bid%5D,lastUpdated,name," + "description,repeatable,captureCoordinates,generatedByEnrollmentDate,minDaysFromStart,programStageDataElements%5Bid,compulsory,allowProvidedElsewhere,dataElement%5Bid,name,code,formName,valueType,optionSetValue,optionSet%5Bid,name," + "options%5Bcode,name%5D%5D%5D%5D%5D" ) ) .andRespond( withSuccess( IOUtils.resourceToString( "/org/dhis2/fhir/adapter/dhis/test/all-programs.json", StandardCharsets.UTF_8 ), MediaType.APPLICATION_JSON ) ); @@ -121,7 +123,7 @@ private void getPlanDefinition() throws Exception final IGenericClient client = createGenericClient(); client.registerInterceptor( new BasicAuthInterceptor( "fhir_client", "fhir_client_1" ) ); PlanDefinition planDefinition = client.read().resource( PlanDefinition.class ).withId( "EPDyQuoRnXk" ).execute(); - Assert.assertEquals( "Child Programme", planDefinition.getName() ); + Assert.assertEquals( "Child Programme", planDefinition.getTitle() ); systemDhis2Server.verify(); userDhis2Server.verify(); @@ -148,7 +150,7 @@ private void getPlanDefinitionNotExists() userDhis2Server.expect( ExpectedCount.once(), method( HttpMethod.GET ) ).andExpect( header( "Authorization", "Basic Zmhpcl9jbGllbnQ6Zmhpcl9jbGllbnRfMQ==" ) ) .andExpect( requestTo( dhis2BaseUrl + "/api/" + dhis2ApiVersion + "/programs/0dXIdLNUNEn.json?fields=id,name,code,description,lastUpdated,selectIncidentDatesInFuture,selectEnrollmentDatesInFuture,displayIncidentDate,registration," + - "withoutRegistration,captureCoordinates,trackedEntityType%5Bid%5D,programTrackedEntityAttributes%5Bid,name,valueType,mandatory,allowFutureDate,trackedEntityAttribute%5Bid,name,code,valueType,generated%5D%5D,programStages%5Bid,name," + + "withoutRegistration,captureCoordinates,trackedEntityType%5Bid%5D,programTrackedEntityAttributes%5Bid,name,valueType,mandatory,allowFutureDate,trackedEntityAttribute%5Bid,name,code,valueType,generated%5D%5D,programStages%5Bid,program%5Bid%5D,lastUpdated,name," + "description,repeatable,captureCoordinates,generatedByEnrollmentDate,minDaysFromStart,programStageDataElements%5Bid,compulsory,allowProvidedElsewhere,dataElement%5Bid,name,code,formName,valueType,optionSetValue,optionSet%5Bid,name," + "options%5Bcode,name%5D%5D%5D%5D%5D" ) ) .andRespond( withStatus( HttpStatus.NOT_FOUND ).contentType( MediaType.APPLICATION_JSON ).body( "{}" ) ); @@ -211,8 +213,9 @@ public void getPlanDefinitionByIdentifierWithoutAuthorization() public void getPlanDefinitionByIdentifierInvalidAuthorization() { userDhis2Server.expect( ExpectedCount.once(), method( HttpMethod.GET ) ).andExpect( header( "Authorization", "Basic Zmhpcl9jbGllbnQ6aW52YWxpZF8x" ) ) - .andExpect( requestTo( dhis2BaseUrl + "/api/" + dhis2ApiVersion + "/programs.json?paging=false&fields=id,name,code,description,lastUpdated,selectIncidentDatesInFuture,selectEnrollmentDatesInFuture,displayIncidentDate,registration,withoutRegistration," + - "captureCoordinates,trackedEntityType%5Bid%5D,programTrackedEntityAttributes%5Bid,name,valueType,mandatory,allowFutureDate,trackedEntityAttribute%5Bid,name,code,valueType,generated%5D%5D,programStages%5Bid,name,description,repeatable," + + .andExpect( requestTo( dhis2BaseUrl + "/api/" + dhis2ApiVersion + + "/programs.json?paging=false&fields=id,name,code,description,lastUpdated,selectIncidentDatesInFuture,selectEnrollmentDatesInFuture,displayIncidentDate,registration,withoutRegistration," + + "captureCoordinates,trackedEntityType%5Bid%5D,programTrackedEntityAttributes%5Bid,name,valueType,mandatory,allowFutureDate,trackedEntityAttribute%5Bid,name,code,valueType,generated%5D%5D,programStages%5Bid,program%5Bid%5D,lastUpdated,name,description,repeatable," + "captureCoordinates,generatedByEnrollmentDate,minDaysFromStart,programStageDataElements%5Bid,compulsory,allowProvidedElsewhere,dataElement%5Bid,name,code,formName,valueType,optionSetValue,optionSet%5Bid,name,options%5Bcode," + "name%5D%5D%5D%5D%5D&filter=code:eq:OU_1234" ) ) .andRespond( withStatus( HttpStatus.UNAUTHORIZED ) ); @@ -239,8 +242,9 @@ private void getPlanDefinitionByIdentifier() throws Exception "fields=id,name,trackedEntityTypeAttributes%5Bid,name,valueType,mandatory,trackedEntityAttribute%5Bid,name,code,valueType,generated,optionSetValue,optionSet%5Bid,name,options%5Bcode,name%5D%5D%5D%5D" ) ) .andRespond( withSuccess( IOUtils.resourceToString( "/org/dhis2/fhir/adapter/dhis/test/single-tracked-entity-type.json", StandardCharsets.UTF_8 ), MediaType.APPLICATION_JSON ) ); userDhis2Server.expect( ExpectedCount.between( 1, 2 ), method( HttpMethod.GET ) ).andExpect( header( "Authorization", "Basic Zmhpcl9jbGllbnQ6Zmhpcl9jbGllbnRfMQ==" ) ) - .andExpect( requestTo( dhis2BaseUrl + "/api/" + dhis2ApiVersion + "/programs.json?paging=false&fields=id,name,code,description,lastUpdated,selectIncidentDatesInFuture,selectEnrollmentDatesInFuture,displayIncidentDate,registration,withoutRegistration," + - "captureCoordinates,trackedEntityType%5Bid%5D,programTrackedEntityAttributes%5Bid,name,valueType,mandatory,allowFutureDate,trackedEntityAttribute%5Bid,name,code,valueType,generated%5D%5D,programStages%5Bid,name,description,repeatable," + + .andExpect( requestTo( dhis2BaseUrl + "/api/" + dhis2ApiVersion + "/programs.json?paging=false&fields=id,name,code,description,lastUpdated,selectIncidentDatesInFuture,selectEnrollmentDatesInFuture,displayIncidentDate,registration," + + "withoutRegistration," + + "captureCoordinates,trackedEntityType%5Bid%5D,programTrackedEntityAttributes%5Bid,name,valueType,mandatory,allowFutureDate,trackedEntityAttribute%5Bid,name,code,valueType,generated%5D%5D,programStages%5Bid,program%5Bid%5D,lastUpdated,name,description,repeatable," + "captureCoordinates,generatedByEnrollmentDate,minDaysFromStart,programStageDataElements%5Bid,compulsory,allowProvidedElsewhere,dataElement%5Bid,name,code,formName,valueType,optionSetValue,optionSet%5Bid,name,options%5Bcode," + "name%5D%5D%5D%5D%5D&filter=code:eq:PD_1234" ) ) .andRespond( withSuccess( IOUtils.resourceToString( "/org/dhis2/fhir/adapter/dhis/test/default-program.json", StandardCharsets.UTF_8 ), MediaType.APPLICATION_JSON ) ); @@ -251,7 +255,7 @@ private void getPlanDefinitionByIdentifier() throws Exception client.search().forResource( PlanDefinition.class ).where( PlanDefinition.IDENTIFIER.exactly().systemAndIdentifier( "http://www.dhis2.org/dhis2-fhir-adapter/systems/plan-definition-identifier", "PD_1234" ) ).returnBundle( Bundle.class ).execute(); Assert.assertEquals( 1, bundle.getEntry().size() ); PlanDefinition planDefinition = (PlanDefinition) bundle.getEntry().get( 0 ).getResource(); - Assert.assertEquals( "Child Programme", planDefinition.getName() ); + Assert.assertEquals( "Child Programme", planDefinition.getTitle() ); systemDhis2Server.verify(); userDhis2Server.verify(); @@ -270,13 +274,14 @@ public void searchPlanDefinitionInvalidAuthorization() userDhis2Server.expect( ExpectedCount.once(), method( HttpMethod.GET ) ).andExpect( header( "Authorization", "Basic Zmhpcl9jbGllbnQ6aW52YWxpZF8x" ) ) .andExpect( requestTo( dhis2BaseUrl + "/api/" + dhis2ApiVersion + "/programs.json?filter=name:$ilike:Test&paging=true&page=1&pageSize=10&order=id&fields=id,name,code,description,lastUpdated,selectIncidentDatesInFuture,selectEnrollmentDatesInFuture," + "displayIncidentDate,registration,withoutRegistration,captureCoordinates,trackedEntityType%5Bid%5D,programTrackedEntityAttributes%5Bid,name,valueType,mandatory,allowFutureDate,trackedEntityAttribute%5Bid,name,code,valueType,generated%5D%5D," + - "programStages%5Bid,name,description,repeatable,captureCoordinates,generatedByEnrollmentDate,minDaysFromStart,programStageDataElements%5Bid,compulsory,allowProvidedElsewhere,dataElement%5Bid,name,code,formName,valueType,optionSetValue," + + "programStages%5Bid,program%5Bid%5D,lastUpdated,name,description,repeatable,captureCoordinates,generatedByEnrollmentDate,minDaysFromStart,programStageDataElements%5Bid,compulsory,allowProvidedElsewhere,dataElement%5Bid,name,code,formName," + + "valueType,optionSetValue," + "optionSet%5Bid,name,options%5Bcode,name%5D%5D%5D%5D%5D" ) ) .andRespond( withStatus( HttpStatus.UNAUTHORIZED ) ); final IGenericClient client = createGenericClient(); client.registerInterceptor( new BasicAuthInterceptor( "fhir_client", "invalid_1" ) ); - client.search().forResource( PlanDefinition.class ).where( PlanDefinition.NAME.matches().value( "Test" ) ).returnBundle( Bundle.class ).execute(); + client.search().forResource( PlanDefinition.class ).where( PlanDefinition.TITLE.matches().value( "Test" ) ).returnBundle( Bundle.class ).execute(); } @Test @@ -289,15 +294,15 @@ public void searchPlanDefinition() throws Exception userDhis2Server.expect( ExpectedCount.once(), method( HttpMethod.GET ) ).andExpect( header( "Authorization", "Basic Zmhpcl9jbGllbnQ6Zmhpcl9jbGllbnRfMQ==" ) ) .andExpect( requestTo( dhis2BaseUrl + "/api/" + dhis2ApiVersion + "/programs.json?filter=name:$ilike:Child%20Programme&paging=true&page=1&pageSize=10&order=id&fields=id,name,code,description,lastUpdated,selectIncidentDatesInFuture," + "selectEnrollmentDatesInFuture,displayIncidentDate,registration,withoutRegistration,captureCoordinates,trackedEntityType%5Bid%5D,programTrackedEntityAttributes%5Bid,name,valueType,mandatory,allowFutureDate,trackedEntityAttribute%5Bid,name," + - "code,valueType,generated%5D%5D,programStages%5Bid,name,description,repeatable,captureCoordinates,generatedByEnrollmentDate,minDaysFromStart,programStageDataElements%5Bid,compulsory,allowProvidedElsewhere,dataElement%5Bid,name,code,formName," + + "code,valueType,generated%5D%5D,programStages%5Bid,program%5Bid%5D,lastUpdated,name,description,repeatable,captureCoordinates,generatedByEnrollmentDate,minDaysFromStart,programStageDataElements%5Bid,compulsory,allowProvidedElsewhere,dataElement%5Bid,name,code,formName," + "valueType,optionSetValue,optionSet%5Bid,name,options%5Bcode,name%5D%5D%5D%5D%5D" ) ) .andRespond( withSuccess( IOUtils.resourceToString( "/org/dhis2/fhir/adapter/dhis/test/default-program.json", StandardCharsets.UTF_8 ), MediaType.APPLICATION_JSON ) ); final IGenericClient client = createGenericClient(); client.registerInterceptor( new BasicAuthInterceptor( "fhir_client", "fhir_client_1" ) ); - Bundle bundle = client.search().forResource( PlanDefinition.class ).where( PlanDefinition.NAME.matches().value( "Child Programme" ) ).returnBundle( Bundle.class ).execute(); + Bundle bundle = client.search().forResource( PlanDefinition.class ).where( PlanDefinition.TITLE.matches().value( "Child Programme" ) ).returnBundle( Bundle.class ).execute(); Assert.assertEquals( 1, bundle.getEntry().size() ); PlanDefinition planDefinition = (PlanDefinition) bundle.getEntry().get( 0 ).getResource(); - Assert.assertEquals( "Child Programme", planDefinition.getName() ); + Assert.assertEquals( "Child Programme", planDefinition.getTitle() ); } } diff --git a/app/src/test/java/org/dhis2/fhir/adapter/fhir/server/r4/R4ProgramStageMetadataFhirRestAppTest.java b/app/src/test/java/org/dhis2/fhir/adapter/fhir/server/r4/R4ProgramStageMetadataFhirRestAppTest.java new file mode 100644 index 00000000..b86a8b22 --- /dev/null +++ b/app/src/test/java/org/dhis2/fhir/adapter/fhir/server/r4/R4ProgramStageMetadataFhirRestAppTest.java @@ -0,0 +1,221 @@ +package org.dhis2.fhir.adapter.fhir.server.r4; + +/* + * Copyright (c) 2004-2019, University of Oslo + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * Neither the name of the HISP project nor the names of its contributors may + * be used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +import ca.uhn.fhir.rest.client.api.IGenericClient; +import ca.uhn.fhir.rest.client.interceptor.BasicAuthInterceptor; +import ca.uhn.fhir.rest.server.exceptions.AuthenticationException; +import ca.uhn.fhir.rest.server.exceptions.ResourceNotFoundException; +import org.apache.commons.io.IOUtils; +import org.dhis2.fhir.adapter.AbstractAppTest; +import org.dhis2.fhir.adapter.fhir.model.FhirVersion; +import org.hl7.fhir.r4.model.Bundle; +import org.hl7.fhir.r4.model.Questionnaire; +import org.junit.Assert; +import org.junit.Test; +import org.springframework.http.HttpMethod; +import org.springframework.http.HttpStatus; +import org.springframework.http.MediaType; +import org.springframework.test.web.client.ExpectedCount; + +import javax.annotation.Nonnull; +import java.nio.charset.StandardCharsets; + +import static org.springframework.test.web.client.match.MockRestRequestMatchers.*; +import static org.springframework.test.web.client.response.MockRestResponseCreators.withStatus; +import static org.springframework.test.web.client.response.MockRestResponseCreators.withSuccess; + +/** + * R4 tests for rest interfaces that access + * DHIS2 program stage metadata. + * + * @author volsch + */ +public class R4ProgramStageMetadataFhirRestAppTest extends AbstractAppTest +{ + @Nonnull + @Override + protected FhirVersion getFhirVersion() + { + return FhirVersion.R4; + } + + @Test( expected = AuthenticationException.class ) + public void getQuestionnaireWithoutAuthorization() + { + final IGenericClient client = createGenericClient(); + client.read().resource( Questionnaire.class ).withId( "MsWxkiY6tMS" ).execute(); + } + + @Test( expected = AuthenticationException.class ) + public void getQuestionnaireWithInvalidAuthorization() + { + userDhis2Server.expect( ExpectedCount.once(), method( HttpMethod.GET ) ).andExpect( header( "Authorization", "Basic Zmhpcl9jbGllbnQ6aW52YWxpZF8x" ) ) + .andExpect( requestTo( dhis2BaseUrl + "/api/" + dhis2ApiVersion + "/programStages/MsWxkiY6tMS.json?fields=id,program%5Bid%5D,lastUpdated,name,description,repeatable,captureCoordinates,generatedByEnrollmentDate,minDaysFromStart," + + "programStageDataElements%5Bid,compulsory,allowProvidedElsewhere,dataElement%5Bid,name,code,formName,valueType,optionSetValue,optionSet%5Bid,name,options%5Bcode,name%5D%5D%5D%5D" ) ) + .andRespond( withStatus( HttpStatus.UNAUTHORIZED ) ); + + final IGenericClient client = createGenericClient(); + client.registerInterceptor( new BasicAuthInterceptor( "fhir_client", "invalid_1" ) ); + client.read().resource( Questionnaire.class ).withId( "MsWxkiY6tMS" ).execute(); + } + + @Test + public void getQuestionnaireRepeated() throws Exception + { + getQuestionnaire(); + getQuestionnaire(); + } + + private void getQuestionnaire() throws Exception + { + systemDhis2Server.reset(); + userDhis2Server.reset(); + + userDhis2Server.expect( ExpectedCount.once(), method( HttpMethod.GET ) ).andExpect( header( "Authorization", "Basic Zmhpcl9jbGllbnQ6Zmhpcl9jbGllbnRfMQ==" ) ) + .andExpect( requestTo( dhis2BaseUrl + "/api/" + dhis2ApiVersion + "/programStages/MsWxkiY6tMS.json?fields=id,program%5Bid%5D,lastUpdated,name,description,repeatable,captureCoordinates,generatedByEnrollmentDate,minDaysFromStart," + + "programStageDataElements%5Bid,compulsory,allowProvidedElsewhere,dataElement%5Bid,name,code,formName,valueType,optionSetValue,optionSet%5Bid,name,options%5Bcode,name%5D%5D%5D%5D" ) ) + .andRespond( withSuccess( IOUtils.resourceToString( "/org/dhis2/fhir/adapter/dhis/test/single-program-stage.json", StandardCharsets.UTF_8 ), MediaType.APPLICATION_JSON ) ); + + final IGenericClient client = createGenericClient(); + client.registerInterceptor( new BasicAuthInterceptor( "fhir_client", "fhir_client_1" ) ); + Questionnaire planDefinition = client.read().resource( Questionnaire.class ).withId( "MsWxkiY6tMS" ).execute(); + Assert.assertEquals( "Birth", planDefinition.getTitle() ); + + systemDhis2Server.verify(); + userDhis2Server.verify(); + } + + @Test( expected = ResourceNotFoundException.class ) + public void getQuestionnaireNotExistsRepeated() + { + try + { + getQuestionnaireNotExists(); + Assert.fail( "Exception expected also an first invocation." ); + } + catch ( ResourceNotFoundException e ) + { + getQuestionnaireNotExists(); + } + } + + private void getQuestionnaireNotExists() + { + systemDhis2Server.reset(); + userDhis2Server.reset(); + + userDhis2Server.expect( ExpectedCount.once(), method( HttpMethod.GET ) ).andExpect( header( "Authorization", "Basic Zmhpcl9jbGllbnQ6Zmhpcl9jbGllbnRfMQ==" ) ) + .andExpect( requestTo( dhis2BaseUrl + "/api/" + dhis2ApiVersion + "/programStages/0dXIdLNUNEn.json?fields=id,program%5Bid%5D,lastUpdated,name,description,repeatable,captureCoordinates,generatedByEnrollmentDate,minDaysFromStart," + + "programStageDataElements%5Bid,compulsory,allowProvidedElsewhere,dataElement%5Bid,name,code,formName,valueType,optionSetValue,optionSet%5Bid,name,options%5Bcode,name%5D%5D%5D%5D" ) ) + .andRespond( withStatus( HttpStatus.NOT_FOUND ).contentType( MediaType.APPLICATION_JSON ).body( "{}" ) ); + + try + { + final IGenericClient client = createGenericClient(); + client.registerInterceptor( new BasicAuthInterceptor( "fhir_client", "fhir_client_1" ) ); + client.read().resource( Questionnaire.class ).withId( "0dXIdLNUNEn" ).execute(); + } + catch ( ResourceNotFoundException e ) + { + systemDhis2Server.verify(); + userDhis2Server.verify(); + throw e; + } + } + + @Test( expected = ResourceNotFoundException.class ) + public void getQuestionnaireRuleNotFoundRepeated() + { + try + { + getQuestionnaireNotExists(); + Assert.fail( "Exception expected also an first invocation." ); + } + catch ( ResourceNotFoundException e ) + { + getQuestionnaireNotExists(); + } + } + + private void getQuestionnaireRuleNotFound() + { + systemDhis2Server.reset(); + userDhis2Server.reset(); + + try + { + final IGenericClient client = createGenericClient(); + client.registerInterceptor( new BasicAuthInterceptor( "fhir_client", "fhir_client_1" ) ); + client.read().resource( Questionnaire.class ).withId( "ldXIdLNUNEn" ).execute(); + } + catch ( ResourceNotFoundException e ) + { + systemDhis2Server.verify(); + userDhis2Server.verify(); + throw e; + } + } + + @Test( expected = AuthenticationException.class ) + public void searchQuestionnaireWithoutAuthorization() + { + final IGenericClient client = createGenericClient(); + client.search().forResource( Questionnaire.class ).where( Questionnaire.TITLE.matches().value( "Test" ) ).returnBundle( Bundle.class ).execute(); + } + + @Test( expected = AuthenticationException.class ) + public void searchQuestionnaireInvalidAuthorization() + { + userDhis2Server.expect( ExpectedCount.once(), method( HttpMethod.GET ) ).andExpect( header( "Authorization", "Basic Zmhpcl9jbGllbnQ6aW52YWxpZF8x" ) ) + .andExpect( requestTo( dhis2BaseUrl + "/api/" + dhis2ApiVersion + "/programStages.json?filter=name:$ilike:Test&paging=true&page=1&pageSize=10&order=id&fields=id,program%5Bid%5D,lastUpdated,name,description,repeatable,captureCoordinates," + + "generatedByEnrollmentDate,minDaysFromStart,programStageDataElements%5Bid,compulsory,allowProvidedElsewhere,dataElement%5Bid,name,code,formName,valueType,optionSetValue,optionSet%5Bid,name,options%5Bcode,name%5D%5D%5D%5D" ) ) + .andRespond( withStatus( HttpStatus.UNAUTHORIZED ) ); + + final IGenericClient client = createGenericClient(); + client.registerInterceptor( new BasicAuthInterceptor( "fhir_client", "invalid_1" ) ); + client.search().forResource( Questionnaire.class ).where( Questionnaire.TITLE.matches().value( "Test" ) ).returnBundle( Bundle.class ).execute(); + } + + @Test + public void searchQuestionnaire() throws Exception + { + userDhis2Server.expect( ExpectedCount.once(), method( HttpMethod.GET ) ).andExpect( header( "Authorization", "Basic Zmhpcl9jbGllbnQ6Zmhpcl9jbGllbnRfMQ==" ) ) + .andExpect( requestTo( dhis2BaseUrl + "/api/" + dhis2ApiVersion + "/programStages.json?filter=name:$ilike:Birth&paging=true&page=1&pageSize=10&order=id&fields=id,program%5Bid%5D,lastUpdated,name,description,repeatable,captureCoordinates," + + "generatedByEnrollmentDate,minDaysFromStart,programStageDataElements%5Bid,compulsory,allowProvidedElsewhere,dataElement%5Bid,name,code,formName,valueType,optionSetValue,optionSet%5Bid,name,options%5Bcode,name%5D%5D%5D%5D" ) ) + .andRespond( withSuccess( IOUtils.resourceToString( "/org/dhis2/fhir/adapter/dhis/test/default-program-stage.json", StandardCharsets.UTF_8 ), MediaType.APPLICATION_JSON ) ); + + final IGenericClient client = createGenericClient(); + client.registerInterceptor( new BasicAuthInterceptor( "fhir_client", "fhir_client_1" ) ); + Bundle bundle = client.search().forResource( Questionnaire.class ).where( Questionnaire.TITLE.matches().value( "Birth" ) ).returnBundle( Bundle.class ).execute(); + Assert.assertEquals( 1, bundle.getEntry().size() ); + Questionnaire planDefinition = (Questionnaire) bundle.getEntry().get( 0 ).getResource(); + Assert.assertEquals( "Birth", planDefinition.getTitle() ); + } +} diff --git a/app/src/test/java/org/dhis2/fhir/adapter/fhir/transform/fhir/impl/enrollment/AbstractEnrollmentTransformationAppTest.java b/app/src/test/java/org/dhis2/fhir/adapter/fhir/transform/fhir/impl/enrollment/AbstractEnrollmentTransformationAppTest.java index 7b0009fc..aec89b39 100644 --- a/app/src/test/java/org/dhis2/fhir/adapter/fhir/transform/fhir/impl/enrollment/AbstractEnrollmentTransformationAppTest.java +++ b/app/src/test/java/org/dhis2/fhir/adapter/fhir/transform/fhir/impl/enrollment/AbstractEnrollmentTransformationAppTest.java @@ -50,17 +50,16 @@ protected void expectMetadataRequests() throws Exception { systemDhis2Server.expect( ExpectedCount.between( 0, 1 ), method( HttpMethod.GET ) ).andExpect( header( "Authorization", testConfiguration.getDhis2SystemAuthorization() ) ) .andExpect( requestTo( dhis2BaseUrl + "/api/" + dhis2ApiVersion + "/programs/EPDyQuoRnXk.json?fields=id,name,code,description,lastUpdated,selectIncidentDatesInFuture,selectEnrollmentDatesInFuture,displayIncidentDate,registration," + - "withoutRegistration,captureCoordinates,trackedEntityType%5Bid%5D,programTrackedEntityAttributes%5Bid,name,valueType,mandatory,allowFutureDate,trackedEntityAttribute%5Bid,name,code,valueType,generated%5D%5D,programStages%5Bid,name," + - "description,repeatable,captureCoordinates,generatedByEnrollmentDate,minDaysFromStart,programStageDataElements%5Bid,compulsory,allowProvidedElsewhere,dataElement%5Bid,name,code,formName,valueType,optionSetValue,optionSet%5Bid,name," + - "options%5Bcode,name%5D%5D%5D%5D%5D" ) ) + "withoutRegistration,captureCoordinates,trackedEntityType%5Bid%5D,programTrackedEntityAttributes%5Bid,name,valueType,mandatory,allowFutureDate,trackedEntityAttribute%5Bid,name,code,valueType,generated%5D%5D,programStages%5Bid," + + "program%5Bid%5D,lastUpdated,name,description,repeatable,captureCoordinates,generatedByEnrollmentDate,minDaysFromStart,programStageDataElements%5Bid,compulsory,allowProvidedElsewhere,dataElement%5Bid,name,code,formName,valueType," + + "optionSetValue,optionSet%5Bid,name,options%5Bcode,name%5D%5D%5D%5D%5D" ) ) .andRespond( withSuccess( IOUtils.resourceToString( "/org/dhis2/fhir/adapter/dhis/test/single-program.json", StandardCharsets.UTF_8 ), MediaType.APPLICATION_JSON ) ); systemDhis2Server.expect( ExpectedCount.between( 0, 1 ), method( HttpMethod.GET ) ).andExpect( header( "Authorization", testConfiguration.getDhis2SystemAuthorization() ) ) .andExpect( requestTo( dhis2BaseUrl + "/api/" + dhis2ApiVersion + "/programs.json?paging=false&fields=id,name,code,description,lastUpdated,selectIncidentDatesInFuture,selectEnrollmentDatesInFuture,displayIncidentDate,registration," + - "withoutRegistration," + - "captureCoordinates," + - "trackedEntityType%5Bid%5D,programTrackedEntityAttributes%5Bid,name,valueType,mandatory,allowFutureDate,trackedEntityAttribute%5Bid,name,code,valueType,generated%5D%5D,programStages%5Bid,name,description,repeatable,captureCoordinates," + - "generatedByEnrollmentDate,minDaysFromStart,programStageDataElements%5Bid,compulsory,allowProvidedElsewhere,dataElement%5Bid,name,code,description,formName,valueType,optionSetValue,optionSet%5Bid,name,options%5Bcode," + - "name%5D%5D%5D%5D%5D&filter=name:eq:Child%20Programme" ) ) + "withoutRegistration,captureCoordinates,trackedEntityType%5Bid%5D,programTrackedEntityAttributes%5Bid,name,valueType,mandatory,allowFutureDate,trackedEntityAttribute%5Bid,name,code,valueType,generated%5D%5D,programStages%5Bid," + + "program%5Bid%5D," + + "lastUpdated,name,description,repeatable,captureCoordinates,generatedByEnrollmentDate,minDaysFromStart,programStageDataElements%5Bid,compulsory,allowProvidedElsewhere,dataElement%5Bid,name,code,description,formName,valueType,optionSetValue," + + "optionSet%5Bid,name,options%5Bcode,name%5D%5D%5D%5D%5D&filter=name:eq:Child%20Programme" ) ) .andRespond( withSuccess( IOUtils.resourceToString( "/org/dhis2/fhir/adapter/dhis/test/all-programs.json", StandardCharsets.UTF_8 ), MediaType.APPLICATION_JSON ) ); systemDhis2Server.expect( ExpectedCount.between( 0, 1 ), method( HttpMethod.GET ) ).andExpect( header( "Authorization", testConfiguration.getDhis2SystemAuthorization() ) ) .andExpect( requestTo( dhis2BaseUrl + "/api/" + dhis2ApiVersion + "/trackedEntityTypes/MCPQUTHX1Ze.json?fields=id,name,trackedEntityTypeAttributes%5Bid,name,valueType,mandatory,trackedEntityAttribute%5Bid,name,code,valueType,generated," + @@ -75,9 +74,10 @@ protected void expectMetadataRequests() throws Exception .andRespond( withSuccess( IOUtils.resourceToString( "/org/dhis2/fhir/adapter/dhis/test/default-tracked-entity-type.json", StandardCharsets.UTF_8 ), MediaType.APPLICATION_JSON ) ); systemDhis2Server.expect( ExpectedCount.between( 0, 1 ), method( HttpMethod.GET ) ).andExpect( header( "Authorization", testConfiguration.getDhis2SystemAuthorization() ) ) .andExpect( requestTo( dhis2BaseUrl + "/api/" + dhis2ApiVersion + "/programs.json?paging=false&" + - "fields=id,name,code,description,lastUpdated,selectIncidentDatesInFuture,selectEnrollmentDatesInFuture,displayIncidentDate,registration,withoutRegistration,captureCoordinates,trackedEntityType%5Bid%5D,programTrackedEntityAttributes%5Bid,name,valueType," + - "mandatory,allowFutureDate,trackedEntityAttribute%5Bid,name,code,valueType,generated%5D%5D,programStages%5Bid,name,description,repeatable,captureCoordinates,generatedByEnrollmentDate,minDaysFromStart,programStageDataElements%5Bid,compulsory," + - "allowProvidedElsewhere,dataElement%5Bid,name,code,description,formName,valueType,optionSetValue,optionSet%5Bid,name,options%5Bcode,name%5D%5D%5D%5D%5D&filter=name:eq:Child%20Programme" ) ) + "fields=id,name,code,description,lastUpdated,selectIncidentDatesInFuture,selectEnrollmentDatesInFuture,displayIncidentDate,registration,withoutRegistration,captureCoordinates,trackedEntityType%5Bid%5D,programTrackedEntityAttributes%5Bid," + + "name,valueType,mandatory,allowFutureDate,trackedEntityAttribute%5Bid,name,code,valueType,generated%5D%5D,programStages%5Bid,program%5Bid%5D,lastUpdated,name,description,repeatable,captureCoordinates,generatedByEnrollmentDate," + + "minDaysFromStart,programStageDataElements%5Bid,compulsory,allowProvidedElsewhere,dataElement%5Bid,name,code,description,formName,valueType,optionSetValue,optionSet%5Bid,name,options%5Bcode,name%5D%5D%5D%5D%5D" + + "&filter=name:eq:Child%20Programme" ) ) .andRespond( withSuccess( IOUtils.resourceToString( "/org/dhis2/fhir/adapter/dhis/test/single-program.json", StandardCharsets.UTF_8 ), MediaType.APPLICATION_JSON ) ); } } diff --git a/app/src/test/java/org/dhis2/fhir/adapter/fhir/transform/fhir/impl/programstage/AbstractProgramStageTransformationAppTest.java b/app/src/test/java/org/dhis2/fhir/adapter/fhir/transform/fhir/impl/programstage/AbstractProgramStageTransformationAppTest.java index 742bfca8..1cfadd70 100644 --- a/app/src/test/java/org/dhis2/fhir/adapter/fhir/transform/fhir/impl/programstage/AbstractProgramStageTransformationAppTest.java +++ b/app/src/test/java/org/dhis2/fhir/adapter/fhir/transform/fhir/impl/programstage/AbstractProgramStageTransformationAppTest.java @@ -52,7 +52,9 @@ protected void expectMetadataRequests() throws Exception systemDhis2Server.expect( ExpectedCount.between( 0, 1 ), method( HttpMethod.GET ) ).andExpect( header( "Authorization", testConfiguration.getDhis2SystemAuthorization() ) ) .andExpect( requestTo( dhis2BaseUrl + "/api/" + dhis2ApiVersion + "/programs.json?paging=false&fields=id,name,code,description,lastUpdated,selectIncidentDatesInFuture,selectEnrollmentDatesInFuture,displayIncidentDate,registration," + "withoutRegistration," + - "captureCoordinates,trackedEntityType%5Bid%5D,programTrackedEntityAttributes%5Bid,name,valueType,mandatory,allowFutureDate,trackedEntityAttribute%5Bid,name,code,valueType,generated%5D%5D,programStages%5Bid,name,description,repeatable," + + "captureCoordinates,trackedEntityType%5Bid%5D,programTrackedEntityAttributes%5Bid,name,valueType,mandatory,allowFutureDate,trackedEntityAttribute%5Bid,name,code,valueType,generated%5D%5D,programStages%5Bid,program%5Bid%5D,lastUpdated,name," + + "description," + + "repeatable," + "captureCoordinates," + "generatedByEnrollmentDate,minDaysFromStart,programStageDataElements%5Bid,compulsory,allowProvidedElsewhere,dataElement%5Bid,name,code,formName,valueType,optionSetValue,optionSet%5Bid,name,options%5Bcode," + "name%5D%5D%5D%5D%5D&filter=name:eq:Child%20Programme" ) ) @@ -70,8 +72,10 @@ protected void expectMetadataRequests() throws Exception .andRespond( withSuccess( IOUtils.resourceToString( "/org/dhis2/fhir/adapter/dhis/test/default-tracked-entity-type.json", StandardCharsets.UTF_8 ), MediaType.APPLICATION_JSON ) ); systemDhis2Server.expect( ExpectedCount.between( 0, 1 ), method( HttpMethod.GET ) ).andExpect( header( "Authorization", testConfiguration.getDhis2SystemAuthorization() ) ) .andExpect( requestTo( dhis2BaseUrl + "/api/" + dhis2ApiVersion + "/programs.json?paging=false&" + - "fields=id,name,code,description,lastUpdated,selectIncidentDatesInFuture,selectEnrollmentDatesInFuture,displayIncidentDate,registration,withoutRegistration,captureCoordinates,trackedEntityType%5Bid%5D,programTrackedEntityAttributes%5Bid,name,valueType," + - "mandatory,allowFutureDate,trackedEntityAttribute%5Bid,name,code,valueType,generated%5D%5D,programStages%5Bid,name,description,repeatable,captureCoordinates,generatedByEnrollmentDate,minDaysFromStart,programStageDataElements%5Bid,compulsory," + + "fields=id,name,code,description,lastUpdated,selectIncidentDatesInFuture,selectEnrollmentDatesInFuture,displayIncidentDate,registration,withoutRegistration,captureCoordinates,trackedEntityType%5Bid%5D,programTrackedEntityAttributes%5Bid," + + "name,valueType," + + "mandatory,allowFutureDate,trackedEntityAttribute%5Bid,name,code,valueType,generated%5D%5D,programStages%5Bid,program%5Bid%5D,lastUpdated,name,description,repeatable,captureCoordinates,generatedByEnrollmentDate,minDaysFromStart," + + "programStageDataElements%5Bid,compulsory," + "allowProvidedElsewhere,dataElement%5Bid,name,code,description,formName,valueType,optionSetValue,optionSet%5Bid,name,options%5Bcode,name%5D%5D%5D%5D%5D&filter=name:eq:Child%20Programme" ) ) .andRespond( withSuccess( IOUtils.resourceToString( "/org/dhis2/fhir/adapter/dhis/test/single-program.json", StandardCharsets.UTF_8 ), MediaType.APPLICATION_JSON ) ); } diff --git a/app/src/test/resources/org/dhis2/fhir/adapter/dhis/test/all-program-stages.json b/app/src/test/resources/org/dhis2/fhir/adapter/dhis/test/all-program-stages.json new file mode 100644 index 00000000..99a77892 --- /dev/null +++ b/app/src/test/resources/org/dhis2/fhir/adapter/dhis/test/all-program-stages.json @@ -0,0 +1,185 @@ +{ + "programStages": [ + { + "name": "Birth", + "id": "MsWxkiY6tMS", + "program": { + "id": "EPDyQuoRnXk" + }, + "generatedByEnrollmentDate": false, + "repeatable": false, + "minDaysFromStart": 0, + "programStageDataElements": [ + { + "id": "uRAfo19lLtY", + "compulsory": false, + "allowProvidedElsewhere": false, + "dataElement": { + "code": "DE_391382", + "name": "MCH Apgar comment", + "id": "wquzcS4X6He", + "formName": "Apgar comment", + "valueType": "TEXT", + "optionSetValue": false + } + }, + { + "id": "Ije8HSPh71d", + "compulsory": false, + "allowProvidedElsewhere": false, + "dataElement": { + "code": "DE_2006098", + "name": "MCH Apgar Score", + "id": "uzKeaNjgQz9", + "formName": "Apgar Score", + "valueType": "NUMBER", + "optionSetValue": false + } + }, + { + "id": "GLrVTw75zSK", + "compulsory": false, + "allowProvidedElsewhere": false, + "dataElement": { + "code": "DE_2005736", + "name": "MCH Weight (g)", + "id": "BnplxU2jGvX", + "formName": "Weight (g)", + "valueType": "NUMBER", + "optionSetValue": false + } + }, + { + "id": "aQd5KKFge1U", + "compulsory": false, + "allowProvidedElsewhere": false, + "dataElement": { + "code": "DE_2006101", + "name": "MCH BCG dose", + "id": "Lon4lYDF0SH", + "formName": "BCG dose", + "valueType": "BOOLEAN", + "optionSetValue": false + } + }, + { + "id": "lm3Cxju7CL0", + "compulsory": false, + "allowProvidedElsewhere": false, + "dataElement": { + "code": "DE_2006104", + "name": "MCH OPV dose", + "id": "ft7iD5ZzPxJ", + "formName": "OPV dose", + "valueType": "TEXT", + "optionSetValue": true, + "optionSet": { + "name": "MNCH Polio doses (0-3)", + "id": "kzgQRhOCadd", + "options": [ + { + "code": "0", + "name": "Dose 0" + }, + { + "code": "1", + "name": "Dose 1" + }, + { + "code": "2", + "name": "Dose 2" + }, + { + "code": "3", + "name": "Dose 3" + } + ] + } + } + } + ] + }, + { + "name": "Baby Postnatal", + "id": "qowTSevVSkd", + "program": { + "id": "EPDyQuoRnXk" + }, + "generatedByEnrollmentDate": false, + "repeatable": false, + "minDaysFromStart": 6, + "programStageDataElements": [ + { + "id": "s7bKmA7gfXq", + "compulsory": false, + "allowProvidedElsewhere": false, + "dataElement": { + "code": "DE_2006099", + "name": "MCH Infant Weight (g)", + "id": "UnN0Jdrfr4o", + "formName": "Infant Weight (g)", + "valueType": "NUMBER", + "optionSetValue": false + } + }, + { + "id": "g1eWTx5nsNx", + "compulsory": false, + "allowProvidedElsewhere": false, + "dataElement": { + "code": "DE_2006125", + "name": "MCH Measles dose", + "id": "KBLBjsTfNSS", + "formName": "Measles dose", + "valueType": "BOOLEAN", + "optionSetValue": false + } + }, + { + "id": "TKnlmmxUEss", + "compulsory": false, + "allowProvidedElsewhere": false, + "dataElement": { + "code": "DE_2006126", + "name": "MCH Yellow fever dose", + "id": "TNg7ABaHeP7", + "formName": "Yellow fever dose", + "valueType": "BOOLEAN", + "optionSetValue": false + } + }, + { + "id": "mx5k0I1SLDt", + "compulsory": false, + "allowProvidedElsewhere": false, + "dataElement": { + "code": "DE_2006105", + "name": "MCH DPT dose", + "id": "vNGGmCPNfky", + "formName": "DPT dose", + "valueType": "TEXT", + "optionSetValue": true, + "optionSet": { + "name": "MNCH DPT doses (1-3)", + "id": "udkr3ihaeD3", + "options": [ + { + "code": "1", + "name": "Dose 1" + }, + { + "code": "2", + "name": "Dose 2" + }, + { + "code": "3", + "name": "Dose 3" + } + ] + } + } + } + ] + } + ] +} diff --git a/app/src/test/resources/org/dhis2/fhir/adapter/dhis/test/default-program-stage.json b/app/src/test/resources/org/dhis2/fhir/adapter/dhis/test/default-program-stage.json new file mode 100644 index 00000000..15a57544 --- /dev/null +++ b/app/src/test/resources/org/dhis2/fhir/adapter/dhis/test/default-program-stage.json @@ -0,0 +1,101 @@ +{ + "pager": { "page": 1, "pageCount": 1, "total": 0, "pageSize": 1 }, + "programStages": [ + { + "name": "Birth", + "id": "MsWxkiY6tMS", + "generatedByEnrollmentDate": false, + "repeatable": false, + "minDaysFromStart": 0, + "programStageDataElements": [ + { + "id": "uRAfo19lLtY", + "compulsory": false, + "allowProvidedElsewhere": false, + "dataElement": { + "code": "DE_391382", + "name": "MCH Apgar comment", + "id": "wquzcS4X6He", + "formName": "Apgar comment", + "valueType": "TEXT", + "optionSetValue": false + } + }, + { + "id": "Ije8HSPh71d", + "compulsory": false, + "allowProvidedElsewhere": false, + "dataElement": { + "code": "DE_2006098", + "name": "MCH Apgar Score", + "id": "uzKeaNjgQz9", + "formName": "Apgar Score", + "valueType": "NUMBER", + "optionSetValue": false + } + }, + { + "id": "GLrVTw75zSK", + "compulsory": false, + "allowProvidedElsewhere": false, + "dataElement": { + "code": "DE_2005736", + "name": "MCH Weight (g)", + "id": "BnplxU2jGvX", + "formName": "Weight (g)", + "valueType": "NUMBER", + "optionSetValue": false + } + }, + { + "id": "aQd5KKFge1U", + "compulsory": false, + "allowProvidedElsewhere": false, + "dataElement": { + "code": "DE_2006101", + "name": "MCH BCG dose", + "id": "Lon4lYDF0SH", + "formName": "BCG dose", + "valueType": "BOOLEAN", + "optionSetValue": false + } + }, + { + "id": "lm3Cxju7CL0", + "compulsory": false, + "allowProvidedElsewhere": false, + "dataElement": { + "code": "DE_2006104", + "name": "MCH OPV dose", + "id": "ft7iD5ZzPxJ", + "formName": "OPV dose", + "valueType": "TEXT", + "optionSetValue": true, + "optionSet": { + "name": "MNCH Polio doses (0-3)", + "id": "kzgQRhOCadd", + "options": [ + { + "code": "0", + "name": "Dose 0" + }, + { + "code": "1", + "name": "Dose 1" + }, + { + "code": "2", + "name": "Dose 2" + }, + { + "code": "3", + "name": "Dose 3" + } + ] + } + } + } + ] + } + ] +} diff --git a/app/src/test/resources/org/dhis2/fhir/adapter/dhis/test/single-program-stage.json b/app/src/test/resources/org/dhis2/fhir/adapter/dhis/test/single-program-stage.json new file mode 100644 index 00000000..1bad997c --- /dev/null +++ b/app/src/test/resources/org/dhis2/fhir/adapter/dhis/test/single-program-stage.json @@ -0,0 +1,99 @@ +{ + "name": "Birth", + "id": "MsWxkiY6tMS", + "program": { + "id": "EPDyQuoRnXk" + }, + "generatedByEnrollmentDate": false, + "repeatable": false, + "minDaysFromStart": 0, + "programStageDataElements": [ + { + "id": "uRAfo19lLtY", + "compulsory": false, + "allowProvidedElsewhere": false, + "dataElement": { + "code": "DE_391382", + "name": "MCH Apgar comment", + "id": "wquzcS4X6He", + "formName": "Apgar comment", + "valueType": "TEXT", + "optionSetValue": false + } + }, + { + "id": "Ije8HSPh71d", + "compulsory": false, + "allowProvidedElsewhere": false, + "dataElement": { + "code": "DE_2006098", + "name": "MCH Apgar Score", + "id": "uzKeaNjgQz9", + "formName": "Apgar Score", + "valueType": "NUMBER", + "optionSetValue": false + } + }, + { + "id": "GLrVTw75zSK", + "compulsory": false, + "allowProvidedElsewhere": false, + "dataElement": { + "code": "DE_2005736", + "name": "MCH Weight (g)", + "id": "BnplxU2jGvX", + "formName": "Weight (g)", + "valueType": "NUMBER", + "optionSetValue": false + } + }, + { + "id": "aQd5KKFge1U", + "compulsory": false, + "allowProvidedElsewhere": false, + "dataElement": { + "code": "DE_2006101", + "name": "MCH BCG dose", + "id": "Lon4lYDF0SH", + "formName": "BCG dose", + "valueType": "BOOLEAN", + "optionSetValue": false + } + }, + { + "id": "lm3Cxju7CL0", + "compulsory": false, + "allowProvidedElsewhere": false, + "dataElement": { + "code": "DE_2006104", + "name": "MCH OPV dose", + "id": "ft7iD5ZzPxJ", + "formName": "OPV dose", + "valueType": "TEXT", + "optionSetValue": true, + "optionSet": { + "name": "MNCH Polio doses (0-3)", + "id": "kzgQRhOCadd", + "options": [ + { + "code": "0", + "name": "Dose 0" + }, + { + "code": "1", + "name": "Dose 1" + }, + { + "code": "2", + "name": "Dose 2" + }, + { + "code": "3", + "name": "Dose 3" + } + ] + } + } + } + ] +} diff --git a/dhis/src/main/java/org/dhis2/fhir/adapter/dhis/sync/impl/DhisResourceRepositoryImpl.java b/dhis/src/main/java/org/dhis2/fhir/adapter/dhis/sync/impl/DhisResourceRepositoryImpl.java index fef873ad..5d44677c 100644 --- a/dhis/src/main/java/org/dhis2/fhir/adapter/dhis/sync/impl/DhisResourceRepositoryImpl.java +++ b/dhis/src/main/java/org/dhis2/fhir/adapter/dhis/sync/impl/DhisResourceRepositoryImpl.java @@ -39,6 +39,7 @@ import org.dhis2.fhir.adapter.dhis.tracker.program.Event; import org.dhis2.fhir.adapter.dhis.tracker.program.EventService; import org.dhis2.fhir.adapter.dhis.tracker.program.ProgramMetadataService; +import org.dhis2.fhir.adapter.dhis.tracker.program.ProgramStageMetadataService; import org.dhis2.fhir.adapter.dhis.tracker.trackedentity.TrackedEntityInstance; import org.dhis2.fhir.adapter.dhis.tracker.trackedentity.TrackedEntityService; import org.slf4j.Logger; @@ -65,6 +66,8 @@ public class DhisResourceRepositoryImpl implements DhisResourceRepository private final ProgramMetadataService programMetadataService; + private final ProgramStageMetadataService programStageMetadataService; + private final TrackedEntityService trackedEntityService; private final EnrollmentService enrollmentService; @@ -72,10 +75,12 @@ public class DhisResourceRepositoryImpl implements DhisResourceRepository private final EventService eventService; public DhisResourceRepositoryImpl( @Nonnull OrganizationUnitService organizationUnitService, ProgramMetadataService programMetadataService, - @Nonnull TrackedEntityService trackedEntityService, @Nonnull EnrollmentService enrollmentService, @Nonnull EventService eventService ) + @Nonnull ProgramStageMetadataService programStageMetadataService, @Nonnull TrackedEntityService trackedEntityService, + @Nonnull EnrollmentService enrollmentService, @Nonnull EventService eventService ) { this.organizationUnitService = organizationUnitService; this.programMetadataService = programMetadataService; + this.programStageMetadataService = programStageMetadataService; this.trackedEntityService = trackedEntityService; this.enrollmentService = enrollmentService; this.eventService = eventService; @@ -91,6 +96,8 @@ public Optional findRefreshed( @Nonnull DhisResourceId d return organizationUnitService.findOneByReference( new Reference( dhisResourceId.getId(), ReferenceType.ID ) ); case PROGRAM_METADATA: return programMetadataService.findOneByReference( new Reference( dhisResourceId.getId(), ReferenceType.ID ) ); + case PROGRAM_STAGE_METADATA: + return programStageMetadataService.findOneByReference( new Reference( dhisResourceId.getId(), ReferenceType.ID ) ); case TRACKED_ENTITY: return trackedEntityService.findOneByIdRefreshed( dhisResourceId.getId() ); case PROGRAM_STAGE_EVENT: @@ -112,6 +119,7 @@ public Optional findRefreshedDeleted( @Nonnull DhisResou return eventService.findOneDeletedById( dhisResourceId.getId() ); case ORGANIZATION_UNIT: case PROGRAM_METADATA: + case PROGRAM_STAGE_METADATA: case TRACKED_ENTITY: case ENROLLMENT: throw new UnsupportedOperationException( "Retrieving deleted " + dhisResourceId.getType() + " DHIS resource items is not supported." ); diff --git a/dhis/src/main/java/org/dhis2/fhir/adapter/dhis/tracker/program/ImmutableProgramStage.java b/dhis/src/main/java/org/dhis2/fhir/adapter/dhis/tracker/program/ImmutableProgramStage.java index 405d180e..bb461091 100644 --- a/dhis/src/main/java/org/dhis2/fhir/adapter/dhis/tracker/program/ImmutableProgramStage.java +++ b/dhis/src/main/java/org/dhis2/fhir/adapter/dhis/tracker/program/ImmutableProgramStage.java @@ -66,6 +66,13 @@ public Set getAllReferences() return delegate.getAllReferences(); } + @JsonIgnore + @Override + public String getProgramId() + { + return delegate.getProgramId(); + } + @JsonIgnore @Override public String getId() diff --git a/dhis/src/main/java/org/dhis2/fhir/adapter/dhis/tracker/program/ProgramStage.java b/dhis/src/main/java/org/dhis2/fhir/adapter/dhis/tracker/program/ProgramStage.java index 435f596b..0535b110 100644 --- a/dhis/src/main/java/org/dhis2/fhir/adapter/dhis/tracker/program/ProgramStage.java +++ b/dhis/src/main/java/org/dhis2/fhir/adapter/dhis/tracker/program/ProgramStage.java @@ -48,6 +48,8 @@ @Scriptable public interface ProgramStage extends DhisResource, DhisMetadata { + String getProgramId(); + boolean isRepeatable(); boolean isCaptureCoordinates(); diff --git a/dhis/src/main/java/org/dhis2/fhir/adapter/dhis/tracker/program/ProgramStageMetadataService.java b/dhis/src/main/java/org/dhis2/fhir/adapter/dhis/tracker/program/ProgramStageMetadataService.java new file mode 100644 index 00000000..ac5093e7 --- /dev/null +++ b/dhis/src/main/java/org/dhis2/fhir/adapter/dhis/tracker/program/ProgramStageMetadataService.java @@ -0,0 +1,40 @@ +package org.dhis2.fhir.adapter.dhis.tracker.program; + +/* + * Copyright (c) 2004-2019, University of Oslo + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * Neither the name of the HISP project nor the names of its contributors may + * be used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +import org.dhis2.fhir.adapter.dhis.service.DhisMetadataService; + +/** + * Metadata service for DHIS2 program stages. + * + * @author volsch + */ +public interface ProgramStageMetadataService extends DhisMetadataService +{ +} diff --git a/dhis/src/main/java/org/dhis2/fhir/adapter/dhis/tracker/program/WritableProgram.java b/dhis/src/main/java/org/dhis2/fhir/adapter/dhis/tracker/program/WritableProgram.java index 457168ae..ceecf229 100644 --- a/dhis/src/main/java/org/dhis2/fhir/adapter/dhis/tracker/program/WritableProgram.java +++ b/dhis/src/main/java/org/dhis2/fhir/adapter/dhis/tracker/program/WritableProgram.java @@ -139,12 +139,12 @@ public void setDescription( String description ) @Override public String getTrackedEntityTypeId() { - return (trackedEntityType == null) ? null : trackedEntityType.getId(); + return trackedEntityType == null ? null : trackedEntityType.getId(); } public void setTrackedEntityTypeId( String trackedEntityTypeId ) { - this.trackedEntityType = (trackedEntityTypeId == null) ? null : new Id( trackedEntityTypeId ); + this.trackedEntityType = trackedEntityTypeId == null ? null : new Id( trackedEntityTypeId ); } @Override diff --git a/dhis/src/main/java/org/dhis2/fhir/adapter/dhis/tracker/program/WritableProgramStage.java b/dhis/src/main/java/org/dhis2/fhir/adapter/dhis/tracker/program/WritableProgramStage.java index 588b057c..d0474ce4 100644 --- a/dhis/src/main/java/org/dhis2/fhir/adapter/dhis/tracker/program/WritableProgramStage.java +++ b/dhis/src/main/java/org/dhis2/fhir/adapter/dhis/tracker/program/WritableProgramStage.java @@ -34,6 +34,7 @@ import org.dhis2.fhir.adapter.dhis.model.DhisResourceId; import org.dhis2.fhir.adapter.dhis.model.DhisResourceType; import org.dhis2.fhir.adapter.dhis.model.Reference; +import org.dhis2.fhir.adapter.model.Id; import javax.annotation.Nonnull; import javax.annotation.Nullable; @@ -48,6 +49,11 @@ public class WritableProgramStage extends AbstractDhisMetadata implements Progra { private static final long serialVersionUID = -7544648580734783374L; + private ZonedDateTime lastUpdated; + + @JsonProperty + private Id program; + private String id; private String name; @@ -74,6 +80,17 @@ public class WritableProgramStage extends AbstractDhisMetadata implements Progra @JsonIgnore private transient volatile Map dataElementsByCode; + @JsonIgnore + public String getProgramId() + { + return program == null ? null : program.getId(); + } + + public void setProgramId( String programId ) + { + program = programId == null ? null : new Id( programId ); + } + @Override public String getId() { @@ -262,7 +279,7 @@ public boolean isDeleted() @Override public ZonedDateTime getLastUpdated() { - return null; + return lastUpdated; } @Override diff --git a/dhis/src/main/java/org/dhis2/fhir/adapter/dhis/tracker/program/impl/DhisProgramStages.java b/dhis/src/main/java/org/dhis2/fhir/adapter/dhis/tracker/program/impl/DhisProgramStages.java new file mode 100644 index 00000000..8757310e --- /dev/null +++ b/dhis/src/main/java/org/dhis2/fhir/adapter/dhis/tracker/program/impl/DhisProgramStages.java @@ -0,0 +1,62 @@ +package org.dhis2.fhir.adapter.dhis.tracker.program.impl; + +/* + * Copyright (c) 2004-2019, University of Oslo + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * Neither the name of the HISP project nor the names of its contributors may + * be used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +import com.fasterxml.jackson.annotation.JsonProperty; +import org.dhis2.fhir.adapter.dhis.service.impl.DhisMetadataItems; +import org.dhis2.fhir.adapter.dhis.tracker.program.WritableProgramStage; + +import java.io.Serializable; +import java.util.List; + +/** + * Container object used for DHIS2 program stages. + * + * @author volsch + */ +public class DhisProgramStages extends DhisMetadataItems implements Serializable +{ + private static final long serialVersionUID = 1084527285362478422L; + + @JsonProperty( "programStages" ) + public List getProgramStages() + { + return getItems(); + } + + public void setProgramStages( List programStages ) + { + setItems( programStages ); + } + + public List toModel() + { + return getItems(); + } +} diff --git a/dhis/src/main/java/org/dhis2/fhir/adapter/dhis/tracker/program/impl/ProgramMetadataServiceImpl.java b/dhis/src/main/java/org/dhis2/fhir/adapter/dhis/tracker/program/impl/ProgramMetadataServiceImpl.java index a17179c8..eaa0e56d 100644 --- a/dhis/src/main/java/org/dhis2/fhir/adapter/dhis/tracker/program/impl/ProgramMetadataServiceImpl.java +++ b/dhis/src/main/java/org/dhis2/fhir/adapter/dhis/tracker/program/impl/ProgramMetadataServiceImpl.java @@ -53,9 +53,7 @@ public class ProgramMetadataServiceImpl extends AbstractDhisMetadataServiceImpl< "registration,withoutRegistration,captureCoordinates,trackedEntityType[id]," + "programTrackedEntityAttributes[id,name,valueType,mandatory,allowFutureDate," + "trackedEntityAttribute[id,name,code,valueType,generated]]," + - "programStages[id,name,description,repeatable,captureCoordinates,generatedByEnrollmentDate,minDaysFromStart," + - "programStageDataElements[id,compulsory,allowProvidedElsewhere," + - "dataElement[id,name,code,formName,valueType,optionSetValue,optionSet[id,name,options[code,name]]]]]"; + "programStages[" + ProgramStageMetadataServiceImpl.FIELDS + "]"; public ProgramMetadataServiceImpl( @Nonnull @Qualifier( "systemDhis2RestTemplate" ) RestTemplate systemRestTemplate, @Nonnull @Qualifier( "userDhis2RestTemplate" ) RestTemplate userRestTemplate ) { diff --git a/dhis/src/main/java/org/dhis2/fhir/adapter/dhis/tracker/program/impl/ProgramStageMetadataServiceImpl.java b/dhis/src/main/java/org/dhis2/fhir/adapter/dhis/tracker/program/impl/ProgramStageMetadataServiceImpl.java new file mode 100644 index 00000000..554f04bd --- /dev/null +++ b/dhis/src/main/java/org/dhis2/fhir/adapter/dhis/tracker/program/impl/ProgramStageMetadataServiceImpl.java @@ -0,0 +1,86 @@ +package org.dhis2.fhir.adapter.dhis.tracker.program.impl; + +/* + * Copyright (c) 2004-2019, University of Oslo + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * Neither the name of the HISP project nor the names of its contributors may + * be used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +import org.dhis2.fhir.adapter.dhis.model.DhisResourceType; +import org.dhis2.fhir.adapter.dhis.service.impl.AbstractDhisMetadataServiceImpl; +import org.dhis2.fhir.adapter.dhis.service.impl.DhisMetadataItems; +import org.dhis2.fhir.adapter.dhis.tracker.program.ProgramStage; +import org.dhis2.fhir.adapter.dhis.tracker.program.ProgramStageMetadataService; +import org.dhis2.fhir.adapter.dhis.tracker.program.WritableProgramStage; +import org.springframework.beans.factory.annotation.Qualifier; +import org.springframework.stereotype.Service; +import org.springframework.web.client.RestTemplate; + +import javax.annotation.Nonnull; + +/** + * Implementation of a {@link ProgramStageMetadataService}. + * + * @author volsch + */ +@Service +public class ProgramStageMetadataServiceImpl extends AbstractDhisMetadataServiceImpl implements ProgramStageMetadataService +{ + protected static final String FIELDS = "id,program[id],lastUpdated,name,description,repeatable,captureCoordinates,generatedByEnrollmentDate,minDaysFromStart," + + "programStageDataElements[id,compulsory,allowProvidedElsewhere,dataElement[id,name,code,formName,valueType,optionSetValue,optionSet[id,name,options[code,name]]]]"; + + public ProgramStageMetadataServiceImpl( @Nonnull @Qualifier( "systemDhis2RestTemplate" ) RestTemplate systemRestTemplate, @Nonnull @Qualifier( "userDhis2RestTemplate" ) RestTemplate userRestTemplate ) + { + super( systemRestTemplate, userRestTemplate ); + } + + @Nonnull + @Override + public DhisResourceType getDhisResourceType() + { + return DhisResourceType.PROGRAM_STAGE_METADATA; + } + + @Nonnull + @Override + protected Class getItemClass() + { + return WritableProgramStage.class; + } + + @Nonnull + @Override + protected Class> getItemsClass() + { + return DhisProgramStages.class; + } + + @Nonnull + @Override + protected String getFieldNames() + { + return FIELDS; + } +} diff --git a/dhis/src/test/java/org/dhis2/fhir/adapter/dhis/tracker/program/impl/ProgramMetadataServiceImplTest.java b/dhis/src/test/java/org/dhis2/fhir/adapter/dhis/tracker/program/impl/ProgramMetadataServiceImplTest.java index 27653d09..c12402da 100644 --- a/dhis/src/test/java/org/dhis2/fhir/adapter/dhis/tracker/program/impl/ProgramMetadataServiceImplTest.java +++ b/dhis/src/test/java/org/dhis2/fhir/adapter/dhis/tracker/program/impl/ProgramMetadataServiceImplTest.java @@ -82,9 +82,10 @@ public void setUp() public void findMetadataByReferenceId() throws IOException { mockServer.expect( requestTo( "http://localhost:8080/api/programs/93783.json?fields=id,name,code,description,lastUpdated,selectIncidentDatesInFuture,selectEnrollmentDatesInFuture,displayIncidentDate,registration,withoutRegistration," + - "captureCoordinates," + - "trackedEntityType%5Bid%5D,programTrackedEntityAttributes%5Bid,name,valueType,mandatory,allowFutureDate,trackedEntityAttribute%5Bid,name,code,valueType,generated%5D%5D,programStages%5Bid,name,description,repeatable,captureCoordinates," + - "generatedByEnrollmentDate,minDaysFromStart,programStageDataElements%5Bid,compulsory,allowProvidedElsewhere,dataElement%5Bid,name,code,formName,valueType,optionSetValue,optionSet%5Bid,name,options%5Bcode,name%5D%5D%5D%5D%5D" ) ) + "captureCoordinates,trackedEntityType%5Bid%5D,programTrackedEntityAttributes%5Bid,name,valueType,mandatory,allowFutureDate,trackedEntityAttribute%5Bid,name,code,valueType,generated%5D%5D,programStages%5Bid,program%5Bid%5D,lastUpdated,name," + + "description," + + "repeatable,captureCoordinates,generatedByEnrollmentDate,minDaysFromStart,programStageDataElements%5Bid,compulsory,allowProvidedElsewhere,dataElement%5Bid,name,code,formName,valueType,optionSetValue,optionSet%5Bid,name," + + "options%5Bcode,name%5D%5D%5D%5D%5D" ) ) .andExpect( method( HttpMethod.GET ) ).andRespond( withSuccess( IOUtils.resourceToByteArray( "/org/dhis2/fhir/adapter/dhis/tracker/program/impl/program.json" ), MediaType.APPLICATION_JSON ) ); Optional ou = service.findMetadataByReference( new Reference( "93783", ReferenceType.ID ) ); @@ -95,8 +96,10 @@ public void findMetadataByReferenceId() throws IOException @Test public void findMetadataByReferenceIdNotFound() { - mockServer.expect( requestTo( "http://localhost:8080/api/programs/93783.json?fields=id,name,code,description,lastUpdated,selectIncidentDatesInFuture,selectEnrollmentDatesInFuture,displayIncidentDate,registration,withoutRegistration,captureCoordinates," + - "trackedEntityType%5Bid%5D,programTrackedEntityAttributes%5Bid,name,valueType,mandatory,allowFutureDate,trackedEntityAttribute%5Bid,name,code,valueType,generated%5D%5D,programStages%5Bid,name,description,repeatable,captureCoordinates," + + mockServer.expect( requestTo( + "http://localhost:8080/api/programs/93783.json?fields=id,name,code,description,lastUpdated,selectIncidentDatesInFuture,selectEnrollmentDatesInFuture,displayIncidentDate,registration,withoutRegistration,captureCoordinates," + + "trackedEntityType%5Bid%5D,programTrackedEntityAttributes%5Bid,name,valueType,mandatory,allowFutureDate,trackedEntityAttribute%5Bid,name,code,valueType,generated%5D%5D,programStages%5Bid,program%5Bid%5D,lastUpdated,name,description,repeatable," + + "captureCoordinates," + "generatedByEnrollmentDate,minDaysFromStart,programStageDataElements%5Bid,compulsory,allowProvidedElsewhere,dataElement%5Bid,name,code,formName,valueType,optionSetValue,optionSet%5Bid,name,options%5Bcode,name%5D%5D%5D%5D%5D" ) ).andExpect( method( HttpMethod.GET ) ) .andRespond( MockRestResponseCreators.withStatus( HttpStatus.NOT_FOUND ).body( "{}" ).contentType( MediaType.APPLICATION_JSON ) ); @@ -107,8 +110,9 @@ public void findMetadataByReferenceIdNotFound() @Test( expected = HttpServerErrorException.class ) public void findMetadataByReferenceIdServerError() { - mockServer.expect( requestTo( "http://localhost:8080/api/programs/93783.json?fields=id,name,code,description,lastUpdated,selectIncidentDatesInFuture,selectEnrollmentDatesInFuture,displayIncidentDate,registration,withoutRegistration,captureCoordinates," + - "trackedEntityType%5Bid%5D,programTrackedEntityAttributes%5Bid,name,valueType,mandatory,allowFutureDate,trackedEntityAttribute%5Bid,name,code,valueType,generated%5D%5D,programStages%5Bid,name,description,repeatable,captureCoordinates," + + mockServer.expect( requestTo( + "http://localhost:8080/api/programs/93783.json?fields=id,name,code,description,lastUpdated,selectIncidentDatesInFuture,selectEnrollmentDatesInFuture,displayIncidentDate,registration,withoutRegistration,captureCoordinates," + + "trackedEntityType%5Bid%5D,programTrackedEntityAttributes%5Bid,name,valueType,mandatory,allowFutureDate,trackedEntityAttribute%5Bid,name,code,valueType,generated%5D%5D,programStages%5Bid,program%5Bid%5D,lastUpdated,name,description,repeatable,captureCoordinates," + "generatedByEnrollmentDate,minDaysFromStart,programStageDataElements%5Bid,compulsory,allowProvidedElsewhere,dataElement%5Bid,name,code,formName,valueType,optionSetValue,optionSet%5Bid,name,options%5Bcode,name%5D%5D%5D%5D%5D" ) ).andExpect( method( HttpMethod.GET ) ) .andRespond( MockRestResponseCreators.withServerError() ); service.findMetadataByReference( new Reference( "93783", ReferenceType.ID ) ); @@ -117,8 +121,9 @@ public void findMetadataByReferenceIdServerError() @Test public void findMetadataByReferenceCode() throws IOException { - mockServer.expect( requestTo( "http://localhost:8080/api/programs.json?paging=false&fields=id,name,code,description,lastUpdated,selectIncidentDatesInFuture,selectEnrollmentDatesInFuture,displayIncidentDate,registration,withoutRegistration,captureCoordinates," + - "trackedEntityType%5Bid%5D,programTrackedEntityAttributes%5Bid,name,valueType,mandatory,allowFutureDate,trackedEntityAttribute%5Bid,name,code,valueType,generated%5D%5D,programStages%5Bid,name,description,repeatable,captureCoordinates," + + mockServer.expect( requestTo( "http://localhost:8080/api/programs.json?paging=false&fields=id,name,code,description,lastUpdated,selectIncidentDatesInFuture,selectEnrollmentDatesInFuture,displayIncidentDate,registration,withoutRegistration," + + "captureCoordinates," + + "trackedEntityType%5Bid%5D,programTrackedEntityAttributes%5Bid,name,valueType,mandatory,allowFutureDate,trackedEntityAttribute%5Bid,name,code,valueType,generated%5D%5D,programStages%5Bid,program%5Bid%5D,lastUpdated,name,description,repeatable,captureCoordinates," + "generatedByEnrollmentDate,minDaysFromStart,programStageDataElements%5Bid,compulsory,allowProvidedElsewhere,dataElement%5Bid,name,code,formName,valueType,optionSetValue,optionSet%5Bid,name,options%5Bcode," + "name%5D%5D%5D%5D%5D&filter=code:eq:OU_3783" ) ).andExpect( method( HttpMethod.GET ) ) .andRespond( withSuccess( IOUtils.resourceToByteArray( "/org/dhis2/fhir/adapter/dhis/tracker/program/impl/programs.json" ), MediaType.APPLICATION_JSON ) ); @@ -131,8 +136,9 @@ public void findMetadataByReferenceCode() throws IOException @Test public void findMetadataByReferenceCodeNotFound() throws IOException { - mockServer.expect( requestTo( "http://localhost:8080/api/programs.json?paging=false&fields=id,name,code,description,lastUpdated,selectIncidentDatesInFuture,selectEnrollmentDatesInFuture,displayIncidentDate,registration,withoutRegistration,captureCoordinates," + - "trackedEntityType%5Bid%5D,programTrackedEntityAttributes%5Bid,name,valueType,mandatory,allowFutureDate,trackedEntityAttribute%5Bid,name,code,valueType,generated%5D%5D,programStages%5Bid,name,description,repeatable,captureCoordinates," + + mockServer.expect( requestTo( "http://localhost:8080/api/programs.json?paging=false&fields=id,name,code,description,lastUpdated,selectIncidentDatesInFuture,selectEnrollmentDatesInFuture,displayIncidentDate,registration,withoutRegistration," + + "captureCoordinates," + + "trackedEntityType%5Bid%5D,programTrackedEntityAttributes%5Bid,name,valueType,mandatory,allowFutureDate,trackedEntityAttribute%5Bid,name,code,valueType,generated%5D%5D,programStages%5Bid,program%5Bid%5D,lastUpdated,name,description,repeatable,captureCoordinates," + "generatedByEnrollmentDate,minDaysFromStart,programStageDataElements%5Bid,compulsory,allowProvidedElsewhere,dataElement%5Bid,name,code,formName,valueType,optionSetValue,optionSet%5Bid,name,options%5Bcode," + "name%5D%5D%5D%5D%5D&filter=code:eq:OU_3783" ) ).andExpect( method( HttpMethod.GET ) ) .andRespond( withSuccess( IOUtils.resourceToByteArray( "/org/dhis2/fhir/adapter/dhis/tracker/program/impl/emptyPrograms.json" ), MediaType.APPLICATION_JSON ) ); @@ -144,8 +150,9 @@ public void findMetadataByReferenceCodeNotFound() throws IOException @Test public void findMetadataByReferenceName() throws IOException { - mockServer.expect( requestTo( "http://localhost:8080/api/programs.json?paging=false&fields=id,name,code,description,lastUpdated,selectIncidentDatesInFuture,selectEnrollmentDatesInFuture,displayIncidentDate,registration,withoutRegistration,captureCoordinates," + - "trackedEntityType%5Bid%5D,programTrackedEntityAttributes%5Bid,name,valueType,mandatory,allowFutureDate,trackedEntityAttribute%5Bid,name,code,valueType,generated%5D%5D,programStages%5Bid,name,description,repeatable,captureCoordinates," + + mockServer.expect( requestTo( "http://localhost:8080/api/programs.json?paging=false&fields=id,name,code,description,lastUpdated,selectIncidentDatesInFuture,selectEnrollmentDatesInFuture,displayIncidentDate,registration,withoutRegistration," + + "captureCoordinates," + + "trackedEntityType%5Bid%5D,programTrackedEntityAttributes%5Bid,name,valueType,mandatory,allowFutureDate,trackedEntityAttribute%5Bid,name,code,valueType,generated%5D%5D,programStages%5Bid,program%5Bid%5D,lastUpdated,name,description,repeatable,captureCoordinates," + "generatedByEnrollmentDate,minDaysFromStart,programStageDataElements%5Bid,compulsory,allowProvidedElsewhere,dataElement%5Bid,name,code,formName,valueType,optionSetValue,optionSet%5Bid,name,options%5Bcode," + "name%5D%5D%5D%5D%5D&filter=name:eq:Freetown" ) ).andExpect( method( HttpMethod.GET ) ) .andRespond( withSuccess( IOUtils.resourceToByteArray( "/org/dhis2/fhir/adapter/dhis/tracker/program/impl/programs.json" ), MediaType.APPLICATION_JSON ) ); @@ -158,8 +165,9 @@ public void findMetadataByReferenceName() throws IOException @Test public void findMetadataByReferenceNameNotFound() throws IOException { - mockServer.expect( requestTo( "http://localhost:8080/api/programs.json?paging=false&fields=id,name,code,description,lastUpdated,selectIncidentDatesInFuture,selectEnrollmentDatesInFuture,displayIncidentDate,registration,withoutRegistration,captureCoordinates," + - "trackedEntityType%5Bid%5D,programTrackedEntityAttributes%5Bid,name,valueType,mandatory,allowFutureDate,trackedEntityAttribute%5Bid,name,code,valueType,generated%5D%5D,programStages%5Bid,name,description,repeatable,captureCoordinates," + + mockServer.expect( requestTo( "http://localhost:8080/api/programs.json?paging=false&fields=id,name,code,description,lastUpdated,selectIncidentDatesInFuture,selectEnrollmentDatesInFuture,displayIncidentDate,registration,withoutRegistration," + + "captureCoordinates," + + "trackedEntityType%5Bid%5D,programTrackedEntityAttributes%5Bid,name,valueType,mandatory,allowFutureDate,trackedEntityAttribute%5Bid,name,code,valueType,generated%5D%5D,programStages%5Bid,program%5Bid%5D,lastUpdated,name,description,repeatable,captureCoordinates," + "generatedByEnrollmentDate,minDaysFromStart,programStageDataElements%5Bid,compulsory,allowProvidedElsewhere,dataElement%5Bid,name,code,formName,valueType,optionSetValue,optionSet%5Bid,name,options%5Bcode," + "name%5D%5D%5D%5D%5D&filter=name:eq:Freetown" ) ).andExpect( method( HttpMethod.GET ) ) .andRespond( withSuccess( IOUtils.resourceToByteArray( "/org/dhis2/fhir/adapter/dhis/tracker/program/impl/emptyPrograms.json" ), MediaType.APPLICATION_JSON ) ); diff --git a/dhis/src/test/java/org/dhis2/fhir/adapter/dhis/tracker/program/impl/ProgramStageMetadataServiceImplTest.java b/dhis/src/test/java/org/dhis2/fhir/adapter/dhis/tracker/program/impl/ProgramStageMetadataServiceImplTest.java new file mode 100644 index 00000000..eba3e0b4 --- /dev/null +++ b/dhis/src/test/java/org/dhis2/fhir/adapter/dhis/tracker/program/impl/ProgramStageMetadataServiceImplTest.java @@ -0,0 +1,156 @@ +package org.dhis2.fhir.adapter.dhis.tracker.program.impl; + +/* + * Copyright (c) 2004-2019, University of Oslo + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * Neither the name of the HISP project nor the names of its contributors may + * be used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +import org.apache.commons.io.IOUtils; +import org.dhis2.fhir.adapter.dhis.model.Reference; +import org.dhis2.fhir.adapter.dhis.model.ReferenceType; +import org.dhis2.fhir.adapter.dhis.tracker.program.ProgramStage; +import org.dhis2.fhir.adapter.dhis.tracker.program.ProgramStageMetadataService; +import org.dhis2.fhir.adapter.model.ValueType; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; +import org.springframework.boot.web.client.RestTemplateBuilder; +import org.springframework.http.HttpMethod; +import org.springframework.http.HttpStatus; +import org.springframework.http.MediaType; +import org.springframework.test.web.client.MockRestServiceServer; +import org.springframework.test.web.client.response.MockRestResponseCreators; +import org.springframework.web.client.HttpServerErrorException; +import org.springframework.web.client.RestTemplate; + +import java.io.IOException; +import java.util.Optional; + +import static org.junit.Assert.*; +import static org.springframework.test.web.client.match.MockRestRequestMatchers.method; +import static org.springframework.test.web.client.match.MockRestRequestMatchers.requestTo; +import static org.springframework.test.web.client.response.MockRestResponseCreators.withSuccess; + +/** + * Unit tests for {@link ProgramStageMetadataServiceImpl}. + * + * @author volsch + */ +public class ProgramStageMetadataServiceImplTest +{ + private RestTemplate systemRestTemplate; + + private RestTemplate userRestTemplate; + + private MockRestServiceServer mockServer; + + private ProgramStageMetadataService service; + + @Before + public void setUp() + { + systemRestTemplate = new RestTemplateBuilder().rootUri( "http://localhost:8080/api" ).build(); + userRestTemplate = new RestTemplateBuilder().rootUri( "http://localhost:8080/api" ).build(); + mockServer = MockRestServiceServer.createServer( systemRestTemplate ); + service = new ProgramStageMetadataServiceImpl( systemRestTemplate, userRestTemplate ); + } + + @Test + public void findMetadataByReferenceId() throws IOException + { + mockServer.expect( requestTo( "http://localhost:8080/api/programStages/93783.json?fields=id,program%5Bid%5D,lastUpdated,name,description,repeatable,captureCoordinates,generatedByEnrollmentDate,minDaysFromStart,programStageDataElements%5Bid," + + "compulsory," + + "allowProvidedElsewhere,dataElement%5Bid,name,code,formName,valueType,optionSetValue,optionSet%5Bid,name,options%5Bcode,name%5D%5D%5D%5D" ) ) + .andExpect( method( HttpMethod.GET ) ).andRespond( withSuccess( IOUtils.resourceToByteArray( "/org/dhis2/fhir/adapter/dhis/tracker/program/impl/programStage.json" ), MediaType.APPLICATION_JSON ) ); + + Optional ou = service.findMetadataByReference( new Reference( "93783", ReferenceType.ID ) ); + Assert.assertTrue( ou.isPresent() ); + assertProgramStage( ou.get() ); + } + + @Test + public void findMetadataByReferenceIdNotFound() + { + mockServer.expect( requestTo( "http://localhost:8080/api/programStages/93783.json?fields=id,program%5Bid%5D,lastUpdated,name,description,repeatable,captureCoordinates,generatedByEnrollmentDate,minDaysFromStart,programStageDataElements%5Bid,compulsory," + + "allowProvidedElsewhere,dataElement%5Bid,name,code,formName,valueType,optionSetValue,optionSet%5Bid,name,options%5Bcode,name%5D%5D%5D%5D" ) ).andExpect( method( HttpMethod.GET ) ) + .andRespond( MockRestResponseCreators.withStatus( HttpStatus.NOT_FOUND ).body( "{}" ).contentType( MediaType.APPLICATION_JSON ) ); + + Optional ou = service.findMetadataByReference( new Reference( "93783", ReferenceType.ID ) ); + Assert.assertFalse( ou.isPresent() ); + } + + @Test( expected = HttpServerErrorException.class ) + public void findMetadataByReferenceIdServerError() + { + mockServer.expect( requestTo( "http://localhost:8080/api/programStages/93783.json?fields=id,program%5Bid%5D,lastUpdated,name,description,repeatable,captureCoordinates,generatedByEnrollmentDate,minDaysFromStart,programStageDataElements%5Bid,compulsory," + + "allowProvidedElsewhere,dataElement%5Bid,name,code,formName,valueType,optionSetValue,optionSet%5Bid,name,options%5Bcode,name%5D%5D%5D%5D" ) ).andExpect( method( HttpMethod.GET ) ) + .andRespond( MockRestResponseCreators.withServerError() ); + service.findMetadataByReference( new Reference( "93783", ReferenceType.ID ) ); + } + + @Test + public void findMetadataByReferenceName() throws IOException + { + mockServer.expect( requestTo( "http://localhost:8080/api/programStages.json?paging=false&fields=id,program%5Bid%5D,lastUpdated,name,description,repeatable,captureCoordinates,generatedByEnrollmentDate,minDaysFromStart,programStageDataElements%5Bid,compulsory," + + "allowProvidedElsewhere,dataElement%5Bid,name,code,formName,valueType,optionSetValue,optionSet%5Bid,name,options%5Bcode,name%5D%5D%5D%5D&filter=name:eq:Birth" ) ).andExpect( method( HttpMethod.GET ) ) + .andRespond( withSuccess( IOUtils.resourceToByteArray( "/org/dhis2/fhir/adapter/dhis/tracker/program/impl/programStages.json" ), MediaType.APPLICATION_JSON ) ); + + Optional ou = service.findMetadataByReference( new Reference( "Birth", ReferenceType.NAME ) ); + Assert.assertTrue( ou.isPresent() ); + assertProgramStage( ou.get() ); + } + + @Test + public void findMetadataByReferenceNameNotFound() throws IOException + { + mockServer.expect( requestTo( "http://localhost:8080/api/programStages.json?paging=false&fields=id,program%5Bid%5D,lastUpdated,name,description,repeatable,captureCoordinates,generatedByEnrollmentDate,minDaysFromStart,programStageDataElements%5Bid,compulsory," + + "allowProvidedElsewhere,dataElement%5Bid,name,code,formName,valueType,optionSetValue,optionSet%5Bid,name,options%5Bcode,name%5D%5D%5D%5D&filter=name:eq:xBirth" ) ).andExpect( method( HttpMethod.GET ) ) + .andRespond( withSuccess( IOUtils.resourceToByteArray( "/org/dhis2/fhir/adapter/dhis/tracker/program/impl/emptyPrograms.json" ), MediaType.APPLICATION_JSON ) ); + + Optional ou = service.findMetadataByReference( new Reference( "xBirth", ReferenceType.NAME ) ); + Assert.assertFalse( ou.isPresent() ); + } + + private void assertProgramStage( ProgramStage programStage ) + { + assertNotNull( programStage ); + assertEquals( "A03MvHHogjR", programStage.getId() ); + assertEquals( "IpHINAT79UW", programStage.getProgramId() ); + assertEquals( "Birth", programStage.getName() ); + assertEquals( 2, programStage.getMinDaysFromStart() ); + assertNotNull( programStage.getDataElements() ); + assertEquals( "a3kGcGDCuk6", programStage.getDataElements().get( 0 ).getElement().getId() ); + assertEquals( "MCH Apgar Score", programStage.getDataElements().get( 0 ).getElement().getName() ); + assertEquals( "DE_2006098", programStage.getDataElements().get( 0 ).getElement().getCode() ); + assertEquals( ValueType.NUMBER, programStage.getDataElements().get( 0 ).getElement().getValueType() ); + assertTrue( programStage.getDataElements().get( 0 ).isAllowProvidedElsewhere() ); + assertTrue( programStage.getDataElements().get( 0 ).isCompulsory() ); + assertNull( programStage.getDataElements().get( 0 ).getElement().getOptionSet() ); + assertTrue( programStage.isGeneratedByEnrollmentDate() ); + assertTrue( programStage.isCaptureCoordinates() ); + assertTrue( programStage.isRepeatable() ); + } +} \ No newline at end of file diff --git a/dhis/src/test/resources/org/dhis2/fhir/adapter/dhis/tracker/program/impl/emptyProgramStages.json b/dhis/src/test/resources/org/dhis2/fhir/adapter/dhis/tracker/program/impl/emptyProgramStages.json new file mode 100644 index 00000000..6c232bc6 --- /dev/null +++ b/dhis/src/test/resources/org/dhis2/fhir/adapter/dhis/tracker/program/impl/emptyProgramStages.json @@ -0,0 +1,3 @@ +{ + "programStages": [ ] +} \ No newline at end of file diff --git a/dhis/src/test/resources/org/dhis2/fhir/adapter/dhis/tracker/program/impl/programStage.json b/dhis/src/test/resources/org/dhis2/fhir/adapter/dhis/tracker/program/impl/programStage.json new file mode 100644 index 00000000..93b9829f --- /dev/null +++ b/dhis/src/test/resources/org/dhis2/fhir/adapter/dhis/tracker/program/impl/programStage.json @@ -0,0 +1,11 @@ +{ + "name": "Birth", "id": "A03MvHHogjR", "program": { "id": "IpHINAT79UW" }, "generatedByEnrollmentDate": true, "repeatable": true, "captureCoordinates": true, "minDaysFromStart": 2, "programStageDataElements": [ + { "id": "LBNxoXdMnkv", "compulsory": true, "allowProvidedElsewhere": true, "dataElement": { "code": "DE_2006098", "name": "MCH Apgar Score", "id": "a3kGcGDCuk6", "valueType": "NUMBER", "optionSetValue": false } }, + { + "id": "O4dwFWakvGO", "compulsory": false, "allowProvidedElsewhere": false, "dataElement": { + "code": "DE_2006104", "name": "MCH OPV dose", "id": "ebaJjqltK5N", "valueType": "TEXT", "optionSetValue": true, + "optionSet": { "name": "MNCH Polio doses (0-3)", "id": "kzgQRhOCadd", "options": [ { "code": "0", "name": "Dose 0" }, { "code": "1", "name": "Dose 1" } ] } + } + } +] +} diff --git a/dhis/src/test/resources/org/dhis2/fhir/adapter/dhis/tracker/program/impl/programStages.json b/dhis/src/test/resources/org/dhis2/fhir/adapter/dhis/tracker/program/impl/programStages.json new file mode 100644 index 00000000..8be57dd1 --- /dev/null +++ b/dhis/src/test/resources/org/dhis2/fhir/adapter/dhis/tracker/program/impl/programStages.json @@ -0,0 +1,24 @@ +{ + "programStages": [ + { + "name": "Birth", "id": "A03MvHHogjR", "program": { "id": "IpHINAT79UW" }, "generatedByEnrollmentDate": true, "repeatable": true, "captureCoordinates": true, "minDaysFromStart": 2, "programStageDataElements": [ + { "id": "LBNxoXdMnkv", "compulsory": true, "allowProvidedElsewhere": true, "dataElement": { "code": "DE_2006098", "name": "MCH Apgar Score", "id": "a3kGcGDCuk6", "valueType": "NUMBER", "optionSetValue": false } }, + { + "id": "O4dwFWakvGO", "compulsory": false, "allowProvidedElsewhere": false, "dataElement": { + "code": "DE_2006104", "name": "MCH OPV dose", "id": "ebaJjqltK5N", "valueType": "TEXT", "optionSetValue": true, + "optionSet": { "name": "MNCH Polio doses (0-3)", "id": "kzgQRhOCadd", "options": [ { "code": "0", "name": "Dose 0" }, { "code": "1", "name": "Dose 1" } ] } + } + } + ] + }, { + "name": "Baby Postnatal", "id": "ZzYYXq4fJie", "generatedByEnrollmentDate": false, "repeatable": false, "captureCoordinates": false, "minDaysFromStart": 6, "programStageDataElements": [ + { "id": "ztoQtbuXzsI", "compulsory": false, "allowProvidedElsewhere": true, "dataElement": { "code": "DE_2006099", "name": "MCH Infant Weight (g)", "id": "GQY2lXrypjO", "valueType": "NUMBER", "optionSetValue": false } } + ] + } + ], "programTrackedEntityAttributes": [ + { + "name": "Child Programme First name", "id": "l2T72XzBCLd", "program": { "id": "IpHINAT79UW" }, "valueType": "TEXT", "allowFutureDate": true, "mandatory": true, + "trackedEntityAttribute": { "code": "MMD_PER_NAM", "name": "First name", "id": "w75KJ2mc4zz", "generated": true, "valueType": "TEXT" } + }, { "name": "Child Programme Last name", "id": "XmRd2ZDjWhF", "valueType": "TEXT", "allowFutureDate": false, "mandatory": false, "trackedEntityAttribute": { "name": "Last name", "id": "zDhUuAYrxNC", "generated": false, "valueType": "TEXT" } } +] +} diff --git a/docs/fhir/profiles/TrackerProgramPlanDefinition.StructureDefinition.xml b/docs/fhir/profiles/TrackerProgramPlanDefinition.StructureDefinition.xml index 38b47048..bf33d0bb 100644 --- a/docs/fhir/profiles/TrackerProgramPlanDefinition.StructureDefinition.xml +++ b/docs/fhir/profiles/TrackerProgramPlanDefinition.StructureDefinition.xml @@ -96,13 +96,9 @@ - - - - - + diff --git a/docs/fhir/profiles/TrackerProgramQuestionnaire.StructureDefinition.xml b/docs/fhir/profiles/TrackerProgramQuestionnaire.StructureDefinition.xml index 0702ebad..eeef7fc0 100644 --- a/docs/fhir/profiles/TrackerProgramQuestionnaire.StructureDefinition.xml +++ b/docs/fhir/profiles/TrackerProgramQuestionnaire.StructureDefinition.xml @@ -96,9 +96,13 @@ + + + + - + @@ -186,6 +190,10 @@ + + + + @@ -200,6 +208,12 @@ + + + + + + diff --git a/fhir-r4/src/main/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/metadata/r4/R4ProgramMetadataToFhirDataProvider.java b/fhir-r4/src/main/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/metadata/r4/R4ProgramMetadataToFhirDataProvider.java index 180a4560..da5594af 100644 --- a/fhir-r4/src/main/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/metadata/r4/R4ProgramMetadataToFhirDataProvider.java +++ b/fhir-r4/src/main/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/metadata/r4/R4ProgramMetadataToFhirDataProvider.java @@ -65,6 +65,7 @@ public Set getFhirVersions() @Override protected void initSearchFilter( @Nonnull FhirVersion fhirVersion, @Nonnull RuleInfo ruleInfo, @Nonnull SearchFilter searchFilter ) { - searchFilter.add( PlanDefinition.SP_NAME, SearchParamType.STRING, "name" ); + searchFilter.add( PlanDefinition.SP_NAME, SearchParamType.STRING, "code" ); + searchFilter.add( PlanDefinition.SP_TITLE, SearchParamType.STRING, "name" ); } } diff --git a/fhir-r4/src/main/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/metadata/r4/R4ProgramMetadataToFhirPlanDefinitionTransformer.java b/fhir-r4/src/main/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/metadata/r4/R4ProgramMetadataToFhirPlanDefinitionTransformer.java index 6002c267..44333c79 100644 --- a/fhir-r4/src/main/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/metadata/r4/R4ProgramMetadataToFhirPlanDefinitionTransformer.java +++ b/fhir-r4/src/main/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/metadata/r4/R4ProgramMetadataToFhirPlanDefinitionTransformer.java @@ -92,7 +92,8 @@ protected boolean transformInternal( @Nonnull FhirClient fhirClient, @Nonnull Dh } fhirPlanDefinition.setUrl( dhisProgram.getId() ); - fhirPlanDefinition.setName( dhisProgram.getName() ); + fhirPlanDefinition.setName( dhisProgram.getCode() ); + fhirPlanDefinition.setTitle( dhisProgram.getName() ); fhirPlanDefinition.setStatus( Enumerations.PublicationStatus.ACTIVE ); fhirPlanDefinition.setDescription( dhisProgram.getDescription() ); fhirPlanDefinition.setAction( null ); diff --git a/fhir-r4/src/main/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/metadata/r4/R4ProgramStageMetadataToFhirDataProvider.java b/fhir-r4/src/main/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/metadata/r4/R4ProgramStageMetadataToFhirDataProvider.java new file mode 100644 index 00000000..5801cc6b --- /dev/null +++ b/fhir-r4/src/main/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/metadata/r4/R4ProgramStageMetadataToFhirDataProvider.java @@ -0,0 +1,70 @@ +package org.dhis2.fhir.adapter.fhir.transform.dhis.impl.metadata.r4; + +/* + * Copyright (c) 2004-2019, University of Oslo + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * Neither the name of the HISP project nor the names of its contributors may + * be used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +import org.dhis2.fhir.adapter.dhis.tracker.program.ProgramStageMetadataService; +import org.dhis2.fhir.adapter.fhir.metadata.model.ProgramStageMetadataRule; +import org.dhis2.fhir.adapter.fhir.metadata.model.RuleInfo; +import org.dhis2.fhir.adapter.fhir.model.FhirVersion; +import org.dhis2.fhir.adapter.fhir.script.ScriptExecutor; +import org.dhis2.fhir.adapter.fhir.transform.dhis.impl.metadata.program.AbstractProgramStageMetadataToFhirDataProvider; +import org.dhis2.fhir.adapter.fhir.transform.dhis.search.SearchFilter; +import org.dhis2.fhir.adapter.fhir.transform.dhis.search.SearchParamType; +import org.hl7.fhir.r4.model.Questionnaire; +import org.springframework.stereotype.Component; + +import javax.annotation.Nonnull; +import java.util.Set; + +/** + * R4 specific version of DHIS2 Program Stage Metadata data provider. + * + * @author volsch + */ +@Component +public class R4ProgramStageMetadataToFhirDataProvider extends AbstractProgramStageMetadataToFhirDataProvider +{ + public R4ProgramStageMetadataToFhirDataProvider( @Nonnull ScriptExecutor scriptExecutor, @Nonnull ProgramStageMetadataService programStageMetadataService ) + { + super( scriptExecutor, programStageMetadataService ); + } + + @Nonnull + @Override + public Set getFhirVersions() + { + return FhirVersion.R4_ONLY; + } + + @Override + protected void initSearchFilter( @Nonnull FhirVersion fhirVersion, @Nonnull RuleInfo ruleInfo, @Nonnull SearchFilter searchFilter ) + { + searchFilter.add( Questionnaire.SP_TITLE, SearchParamType.STRING, "name" ); + } +} diff --git a/fhir-r4/src/main/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/metadata/r4/R4ProgramStageMetadataToFhirQuestionnaireTransformer.java b/fhir-r4/src/main/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/metadata/r4/R4ProgramStageMetadataToFhirQuestionnaireTransformer.java new file mode 100644 index 00000000..2575b8ee --- /dev/null +++ b/fhir-r4/src/main/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/metadata/r4/R4ProgramStageMetadataToFhirQuestionnaireTransformer.java @@ -0,0 +1,158 @@ +package org.dhis2.fhir.adapter.fhir.transform.dhis.impl.metadata.r4; + +/* + * Copyright (c) 2004-2019, University of Oslo + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * Neither the name of the HISP project nor the names of its contributors may + * be used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +import org.dhis2.fhir.adapter.dhis.orgunit.OrganizationUnitService; +import org.dhis2.fhir.adapter.dhis.tracker.program.ProgramStage; +import org.dhis2.fhir.adapter.fhir.data.repository.FhirDhisAssignmentRepository; +import org.dhis2.fhir.adapter.fhir.metadata.model.FhirClient; +import org.dhis2.fhir.adapter.fhir.metadata.model.ProgramStageMetadataRule; +import org.dhis2.fhir.adapter.fhir.metadata.model.RuleInfo; +import org.dhis2.fhir.adapter.fhir.metadata.repository.SystemRepository; +import org.dhis2.fhir.adapter.fhir.model.FhirVersion; +import org.dhis2.fhir.adapter.fhir.repository.FhirResourceRepository; +import org.dhis2.fhir.adapter.fhir.script.ScriptExecutor; +import org.dhis2.fhir.adapter.fhir.transform.dhis.DhisToFhirTransformerContext; +import org.dhis2.fhir.adapter.fhir.transform.dhis.impl.metadata.program.AbstractProgramStageMetadataToFhirQuestionnaireTransformer; +import org.dhis2.fhir.adapter.fhir.transform.scripted.AccessibleScriptedDhisMetadata; +import org.dhis2.fhir.adapter.lock.LockManager; +import org.dhis2.fhir.adapter.model.ValueType; +import org.hl7.fhir.instance.model.api.IBaseResource; +import org.hl7.fhir.r4.model.Coding; +import org.hl7.fhir.r4.model.Enumerations; +import org.hl7.fhir.r4.model.Questionnaire; +import org.hl7.fhir.r4.model.Questionnaire.QuestionnaireItemComponent; +import org.hl7.fhir.r4.model.Questionnaire.QuestionnaireItemType; +import org.springframework.stereotype.Component; + +import javax.annotation.Nonnull; +import javax.annotation.Nullable; +import java.util.Map; +import java.util.Set; + +import static org.hl7.fhir.r4.model.Questionnaire.QuestionnaireItemType.*; + +/** + * R4 specific version of DHIS2 Program Stage Metadata to FHIR Questionnaire transformer. + * + * @author volsch + */ +@Component +public class R4ProgramStageMetadataToFhirQuestionnaireTransformer extends AbstractProgramStageMetadataToFhirQuestionnaireTransformer +{ + public R4ProgramStageMetadataToFhirQuestionnaireTransformer( @Nonnull ScriptExecutor scriptExecutor, @Nonnull LockManager lockManager, @Nonnull SystemRepository systemRepository, @Nonnull FhirResourceRepository fhirResourceRepository, + @Nonnull FhirDhisAssignmentRepository fhirDhisAssignmentRepository, @Nonnull OrganizationUnitService organizationUnitService ) + { + super( scriptExecutor, lockManager, systemRepository, fhirResourceRepository, fhirDhisAssignmentRepository, organizationUnitService ); + } + + @Nonnull + @Override + public Set getFhirVersions() + { + return FhirVersion.R4_ONLY; + } + + @Override + protected boolean transformInternal( @Nonnull FhirClient fhirClient, @Nonnull DhisToFhirTransformerContext context, @Nonnull RuleInfo ruleInfo, @Nonnull Map scriptVariables, + @Nonnull AccessibleScriptedDhisMetadata input, @Nonnull IBaseResource output ) + { + final ProgramStage dhisProgramStage = (ProgramStage) input.getDhisResource(); + final Questionnaire fhirQuestionnaire = (Questionnaire) output; + + fhirQuestionnaire.setUrl( dhisProgramStage.getId() ); + fhirQuestionnaire.setTitle( dhisProgramStage.getName() ); + fhirQuestionnaire.setStatus( Enumerations.PublicationStatus.ACTIVE ); + fhirQuestionnaire.setDescription( dhisProgramStage.getDescription() ); + fhirQuestionnaire.setItem( null ); + + dhisProgramStage.getDataElements().forEach( dataElement -> { + final QuestionnaireItemType type = convertValueType( dataElement.getElement().getValueType() ); + + if ( type != null ) + { + final QuestionnaireItemComponent itemComponent = fhirQuestionnaire.addItem(); + + itemComponent.setLinkId( dataElement.getElementId() ); + itemComponent.setText( dataElement.getElement().getName() ); + itemComponent.setRequired( dataElement.isCompulsory() ); + itemComponent.setType( type ); + + if ( dataElement.getElement().isOptionSetValue() ) + { + dataElement.getElement().getOptionSet().getOptions().forEach( option -> itemComponent.addAnswerOption().setValue( + new Coding().setCode( option.getCode() ).setDisplay( option.getName() ) ) ); + } + } + } ); + + return true; + } + + @Nullable + protected QuestionnaireItemType convertValueType( @Nonnull ValueType valueType ) + { + switch ( valueType ) + { + case TEXT: + case EMAIL: + case LETTER: + case ORGANISATION_UNIT: + case PHONE_NUMBER: + case TRACKER_ASSOCIATE: + case URL: + case USERNAME: + return STRING; + case LONG_TEXT: + return TEXT; + case INTEGER: + case INTEGER_POSITIVE: + case INTEGER_NEGATIVE: + case INTEGER_ZERO_OR_POSITIVE: + return INTEGER; + case NUMBER: + case PERCENTAGE: + case UNIT_INTERVAL: + return DECIMAL; + case DATETIME: + case AGE: + return DATETIME; + case DATE: + return DATE; + case TIME: + return TIME; + case BOOLEAN: + case TRUE_ONLY: + return BOOLEAN; + } + + // unhandled data type + return null; + } +} diff --git a/fhir-r4/src/test/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/metadata/r4/R4ProgramMetadataToFhirDataProviderTest.java b/fhir-r4/src/test/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/metadata/r4/R4ProgramMetadataToFhirDataProviderTest.java index 4798edd1..6ed3a090 100644 --- a/fhir-r4/src/test/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/metadata/r4/R4ProgramMetadataToFhirDataProviderTest.java +++ b/fhir-r4/src/test/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/metadata/r4/R4ProgramMetadataToFhirDataProviderTest.java @@ -79,7 +79,7 @@ public void getFhirVersions() @Test public void searchFilterName() throws URISyntaxException { - final SearchFilterCollector searchFilterCollector = new SearchFilterCollector( Collections.singletonMap( "name", Collections.singletonList( "Child Programme" ) ) ); + final SearchFilterCollector searchFilterCollector = new SearchFilterCollector( Collections.singletonMap( "title", Collections.singletonList( "Child Programme" ) ) ); final SearchFilter searchFilter = new SearchFilter( searchFilterCollector, false ); provider.initSearchFilter( FhirVersion.R4, new RuleInfo<>( new ProgramMetadataRule(), Collections.emptyList() ), searchFilter ); diff --git a/fhir-r4/src/test/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/metadata/r4/R4ProgramMetadataToFhirPlanDefinitionTransformerTest.java b/fhir-r4/src/test/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/metadata/r4/R4ProgramMetadataToFhirPlanDefinitionTransformerTest.java index 3d14b07b..adccd703 100644 --- a/fhir-r4/src/test/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/metadata/r4/R4ProgramMetadataToFhirPlanDefinitionTransformerTest.java +++ b/fhir-r4/src/test/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/metadata/r4/R4ProgramMetadataToFhirPlanDefinitionTransformerTest.java @@ -152,7 +152,7 @@ public void transformInternal() transformer.transformInternal( fhirClient, context, new RuleInfo<>( new ProgramMetadataRule(), Collections.emptyList() ), new HashMap<>(), new WritableScriptedDhisMetadata( program, scriptExecutionContext ), fhirPlanDefinition ); - Assert.assertEquals( "Test Program", fhirPlanDefinition.getName() ); + Assert.assertEquals( "Test Program", fhirPlanDefinition.getTitle() ); Assert.assertEquals( "Test Description", fhirPlanDefinition.getDescription() ); Assert.assertEquals( 2, fhirPlanDefinition.getAction().size() ); diff --git a/fhir-r4/src/test/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/metadata/r4/R4ProgramStageMetadataToFhirDataProviderTest.java b/fhir-r4/src/test/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/metadata/r4/R4ProgramStageMetadataToFhirDataProviderTest.java new file mode 100644 index 00000000..8b2c37f7 --- /dev/null +++ b/fhir-r4/src/test/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/metadata/r4/R4ProgramStageMetadataToFhirDataProviderTest.java @@ -0,0 +1,93 @@ +package org.dhis2.fhir.adapter.fhir.transform.dhis.impl.metadata.r4; + +/* + * Copyright (c) 2004-2019, University of Oslo + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * Neither the name of the HISP project nor the names of its contributors may + * be used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +import org.dhis2.fhir.adapter.dhis.tracker.program.ProgramStageMetadataService; +import org.dhis2.fhir.adapter.fhir.metadata.model.ProgramStageMetadataRule; +import org.dhis2.fhir.adapter.fhir.metadata.model.RuleInfo; +import org.dhis2.fhir.adapter.fhir.model.FhirVersion; +import org.dhis2.fhir.adapter.fhir.script.ScriptExecutor; +import org.dhis2.fhir.adapter.fhir.transform.dhis.search.SearchFilter; +import org.dhis2.fhir.adapter.fhir.transform.dhis.search.SearchFilterCollector; +import org.junit.Assert; +import org.junit.Rule; +import org.junit.Test; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.junit.MockitoJUnit; +import org.mockito.junit.MockitoRule; +import org.springframework.web.util.DefaultUriBuilderFactory; +import org.springframework.web.util.UriBuilder; + +import java.net.URI; +import java.net.URISyntaxException; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +/** + * Unit tests for {@link R4ProgramStageMetadataToFhirDataProvider}. + * + * @author volsch + */ +public class R4ProgramStageMetadataToFhirDataProviderTest +{ + @Mock + private ScriptExecutor scriptExecutor; + + @Mock + private ProgramStageMetadataService programStageMetadataService; + + @InjectMocks + private R4ProgramStageMetadataToFhirDataProvider provider; + + @Rule + public MockitoRule rule = MockitoJUnit.rule(); + + @Test + public void getFhirVersions() + { + Assert.assertEquals( FhirVersion.R4_ONLY, provider.getFhirVersions() ); + } + + @Test + public void searchFilterName() throws URISyntaxException + { + final SearchFilterCollector searchFilterCollector = new SearchFilterCollector( Collections.singletonMap( "title", Collections.singletonList( "Birth" ) ) ); + final SearchFilter searchFilter = new SearchFilter( searchFilterCollector, false ); + + provider.initSearchFilter( FhirVersion.R4, new RuleInfo<>( new ProgramStageMetadataRule(), Collections.emptyList() ), searchFilter ); + + final List variables = new ArrayList<>(); + UriBuilder uriBuilder = new DefaultUriBuilderFactory().builder(); + uriBuilder = searchFilterCollector.add( uriBuilder, variables ); + + Assert.assertEquals( new URI( "?filter=%5Bname:$ilike:Birth%5D" ), uriBuilder.build( variables ) ); + } +} diff --git a/fhir-r4/src/test/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/metadata/r4/R4ProgramStageMetadataToFhirQuestionnaireTransformerTest.java b/fhir-r4/src/test/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/metadata/r4/R4ProgramStageMetadataToFhirQuestionnaireTransformerTest.java new file mode 100644 index 00000000..3cf0b25c --- /dev/null +++ b/fhir-r4/src/test/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/metadata/r4/R4ProgramStageMetadataToFhirQuestionnaireTransformerTest.java @@ -0,0 +1,170 @@ +package org.dhis2.fhir.adapter.fhir.transform.dhis.impl.metadata.r4; + +/* + * Copyright (c) 2004-2019, University of Oslo + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * Neither the name of the HISP project nor the names of its contributors may + * be used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +import org.dhis2.fhir.adapter.dhis.model.WritableDataElement; +import org.dhis2.fhir.adapter.dhis.model.WritableOption; +import org.dhis2.fhir.adapter.dhis.model.WritableOptionSet; +import org.dhis2.fhir.adapter.dhis.orgunit.OrganizationUnitService; +import org.dhis2.fhir.adapter.dhis.tracker.program.WritableProgramStage; +import org.dhis2.fhir.adapter.dhis.tracker.program.WritableProgramStageDataElement; +import org.dhis2.fhir.adapter.fhir.data.repository.FhirDhisAssignmentRepository; +import org.dhis2.fhir.adapter.fhir.metadata.model.FhirClient; +import org.dhis2.fhir.adapter.fhir.metadata.model.ProgramStageMetadataRule; +import org.dhis2.fhir.adapter.fhir.metadata.model.RuleInfo; +import org.dhis2.fhir.adapter.fhir.metadata.repository.SystemRepository; +import org.dhis2.fhir.adapter.fhir.model.FhirVersion; +import org.dhis2.fhir.adapter.fhir.repository.FhirResourceRepository; +import org.dhis2.fhir.adapter.fhir.script.ScriptExecutionContext; +import org.dhis2.fhir.adapter.fhir.script.ScriptExecutor; +import org.dhis2.fhir.adapter.fhir.transform.dhis.DhisToFhirTransformerContext; +import org.dhis2.fhir.adapter.fhir.transform.scripted.WritableScriptedDhisMetadata; +import org.dhis2.fhir.adapter.lock.LockManager; +import org.dhis2.fhir.adapter.model.ValueType; +import org.hl7.fhir.r4.model.Coding; +import org.hl7.fhir.r4.model.Questionnaire; +import org.junit.Assert; +import org.junit.Rule; +import org.junit.Test; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.junit.MockitoJUnit; +import org.mockito.junit.MockitoRule; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; + +/** + * Unit tests for {@link R4ProgramStageMetadataToFhirQuestionnaireTransformer}. + * + * @author volsch + */ +public class R4ProgramStageMetadataToFhirQuestionnaireTransformerTest +{ + @Mock + private ScriptExecutor scriptExecutor; + + @Mock + private LockManager lockManager; + + @Mock + private SystemRepository systemRepository; + + @Mock + private FhirResourceRepository fhirResourceRepository; + + @Mock + private FhirDhisAssignmentRepository fhirDhisAssignmentRepository; + + @Mock + private OrganizationUnitService organizationUnitService; + + @Mock + private DhisToFhirTransformerContext context; + + @Mock + private FhirClient fhirClient; + + @Mock + private ScriptExecutionContext scriptExecutionContext; + + @InjectMocks + private R4ProgramStageMetadataToFhirQuestionnaireTransformer transformer; + + @Rule + public MockitoRule rule = MockitoJUnit.rule(); + + @Test + public void getFhirVersions() + { + Assert.assertEquals( FhirVersion.R4_ONLY, transformer.getFhirVersions() ); + } + + @Test + public void transformInternal() + { + final WritableProgramStage programStage = new WritableProgramStage(); + programStage.setId( "b1234567890" ); + programStage.setName( "Test Stage 1" ); + programStage.setDescription( "Test Description 1" ); + programStage.setRepeatable( false ); + programStage.setDataElements( new ArrayList<>() ); + + WritableProgramStageDataElement programStageDataElement = new WritableProgramStageDataElement(); + WritableDataElement dataElement = new WritableDataElement(); + dataElement.setId( "d0123456789" ); + dataElement.setName( "Value 1" ); + dataElement.setValueType( ValueType.TEXT ); + programStageDataElement.setCompulsory( false ); + programStageDataElement.setElement( dataElement ); + programStage.getDataElements().add( programStageDataElement ); + + WritableOptionSet optionSet = new WritableOptionSet(); + optionSet.setOptions( new ArrayList<>() ); + optionSet.getOptions().add( new WritableOption( "5", "Test Value 1" ) ); + optionSet.getOptions().add( new WritableOption( "7", "Test Value 2" ) ); + + programStageDataElement = new WritableProgramStageDataElement(); + dataElement = new WritableDataElement(); + dataElement.setId( "d1123456789" ); + dataElement.setName( "Value 2" ); + dataElement.setValueType( ValueType.INTEGER ); + dataElement.setOptionSetValue( true ); + dataElement.setOptionSet( optionSet ); + programStageDataElement.setCompulsory( true ); + programStageDataElement.setElement( dataElement ); + programStage.getDataElements().add( programStageDataElement ); + + final Questionnaire fhirQuestionnaire = new Questionnaire(); + + transformer.transformInternal( fhirClient, context, new RuleInfo<>( new ProgramStageMetadataRule(), Collections.emptyList() ), new HashMap<>(), + new WritableScriptedDhisMetadata( programStage, scriptExecutionContext ), fhirQuestionnaire ); + + Assert.assertEquals( "b1234567890", fhirQuestionnaire.getUrl() ); + Assert.assertEquals( "Test Stage 1", fhirQuestionnaire.getTitle() ); + Assert.assertEquals( "Test Description 1", fhirQuestionnaire.getDescription() ); + Assert.assertEquals( 2, fhirQuestionnaire.getItem().size() ); + + Assert.assertEquals( "d0123456789", fhirQuestionnaire.getItem().get( 0 ).getLinkId() ); + Assert.assertEquals( "Value 1", fhirQuestionnaire.getItem().get( 0 ).getText() ); + Assert.assertEquals( 0, fhirQuestionnaire.getItem().get( 0 ).getAnswerOption().size() ); + Assert.assertFalse( fhirQuestionnaire.getItem().get( 0 ).getRequired() ); + + Assert.assertEquals( "d1123456789", fhirQuestionnaire.getItem().get( 1 ).getLinkId() ); + Assert.assertEquals( "Value 2", fhirQuestionnaire.getItem().get( 1 ).getText() ); + Assert.assertEquals( 2, fhirQuestionnaire.getItem().get( 1 ).getAnswerOption().size() ); + Assert.assertTrue( fhirQuestionnaire.getItem().get( 1 ).getAnswerOption().get( 0 ).getValue() instanceof Coding ); + Assert.assertEquals( "5", ( (Coding) fhirQuestionnaire.getItem().get( 1 ).getAnswerOption().get( 0 ).getValue() ).getCode() ); + Assert.assertEquals( "Test Value 1", ( (Coding) fhirQuestionnaire.getItem().get( 1 ).getAnswerOption().get( 0 ).getValue() ).getDisplay() ); + Assert.assertEquals( "7", ( (Coding) fhirQuestionnaire.getItem().get( 1 ).getAnswerOption().get( 1 ).getValue() ).getCode() ); + Assert.assertEquals( "Test Value 2", ( (Coding) fhirQuestionnaire.getItem().get( 1 ).getAnswerOption().get( 1 ).getValue() ).getDisplay() ); + Assert.assertTrue( fhirQuestionnaire.getItem().get( 1 ).getRequired() ); + } +} diff --git a/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/metadata/program/AbstractProgramStageMetadataToFhirDataProvider.java b/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/metadata/program/AbstractProgramStageMetadataToFhirDataProvider.java new file mode 100644 index 00000000..05062a36 --- /dev/null +++ b/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/metadata/program/AbstractProgramStageMetadataToFhirDataProvider.java @@ -0,0 +1,66 @@ +package org.dhis2.fhir.adapter.fhir.transform.dhis.impl.metadata.program; + +/* + * Copyright (c) 2004-2019, University of Oslo + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * Neither the name of the HISP project nor the names of its contributors may + * be used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +import org.dhis2.fhir.adapter.dhis.model.DhisResourceType; +import org.dhis2.fhir.adapter.dhis.tracker.program.ProgramStage; +import org.dhis2.fhir.adapter.dhis.tracker.program.ProgramStageMetadataService; +import org.dhis2.fhir.adapter.fhir.metadata.model.ProgramStageMetadataRule; +import org.dhis2.fhir.adapter.fhir.script.ScriptExecutor; +import org.dhis2.fhir.adapter.fhir.transform.dhis.DhisToFhirDataProvider; +import org.dhis2.fhir.adapter.fhir.transform.dhis.impl.metadata.AbstractDhisMetadataToFhirDataProvider; + +import javax.annotation.Nonnull; + +/** + * Abstract implementation of {@link DhisToFhirDataProvider} for DHIS2 Program Stage Metadata. + * + * @author volsch + */ +public abstract class AbstractProgramStageMetadataToFhirDataProvider extends AbstractDhisMetadataToFhirDataProvider +{ + public AbstractProgramStageMetadataToFhirDataProvider( @Nonnull ScriptExecutor scriptExecutor, @Nonnull ProgramStageMetadataService programStageMetadataService ) + { + super( scriptExecutor, programStageMetadataService ); + } + + @Nonnull + @Override + public DhisResourceType getDhisResourceType() + { + return DhisResourceType.PROGRAM_STAGE_METADATA; + } + + @Nonnull + @Override + protected Class getRuleClass() + { + return ProgramStageMetadataRule.class; + } +} diff --git a/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/metadata/program/AbstractProgramStageMetadataToFhirQuestionnaireTransformer.java b/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/metadata/program/AbstractProgramStageMetadataToFhirQuestionnaireTransformer.java new file mode 100644 index 00000000..0bc8ba45 --- /dev/null +++ b/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/metadata/program/AbstractProgramStageMetadataToFhirQuestionnaireTransformer.java @@ -0,0 +1,93 @@ +package org.dhis2.fhir.adapter.fhir.transform.dhis.impl.metadata.program; + +/* + * Copyright (c) 2004-2019, University of Oslo + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * Neither the name of the HISP project nor the names of its contributors may + * be used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +import org.dhis2.fhir.adapter.dhis.model.DhisResourceType; +import org.dhis2.fhir.adapter.dhis.orgunit.OrganizationUnitService; +import org.dhis2.fhir.adapter.fhir.data.repository.FhirDhisAssignmentRepository; +import org.dhis2.fhir.adapter.fhir.metadata.model.FhirResourceType; +import org.dhis2.fhir.adapter.fhir.metadata.model.ProgramStageMetadataRule; +import org.dhis2.fhir.adapter.fhir.metadata.repository.SystemRepository; +import org.dhis2.fhir.adapter.fhir.repository.FhirResourceRepository; +import org.dhis2.fhir.adapter.fhir.script.ScriptExecutor; +import org.dhis2.fhir.adapter.fhir.transform.dhis.impl.DhisToFhirTransformer; +import org.dhis2.fhir.adapter.fhir.transform.dhis.impl.metadata.AbstractReadOnlyDhisMetadataToTypedFhirTransformer; +import org.dhis2.fhir.adapter.fhir.transform.scripted.AccessibleScriptedDhisMetadata; +import org.dhis2.fhir.adapter.lock.LockManager; +import org.hl7.fhir.instance.model.api.IBaseResource; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import javax.annotation.Nonnull; + +/** + * Implementation of {@link DhisToFhirTransformer} for transforming DHIS2 + * program stage metadata to FHIR Questionnaire. + * + * @param the concrete type of the FHIR resource into which the DHIS2 resource should be transformed. + * @author volsch + */ +public abstract class AbstractProgramStageMetadataToFhirQuestionnaireTransformer extends AbstractReadOnlyDhisMetadataToTypedFhirTransformer +{ + private final Logger logger = LoggerFactory.getLogger( getClass() ); + + public AbstractProgramStageMetadataToFhirQuestionnaireTransformer( @Nonnull ScriptExecutor scriptExecutor, @Nonnull LockManager lockManager, @Nonnull SystemRepository systemRepository, @Nonnull FhirResourceRepository fhirResourceRepository, + @Nonnull FhirDhisAssignmentRepository fhirDhisAssignmentRepository, @Nonnull OrganizationUnitService organizationUnitService ) + { + super( scriptExecutor, lockManager, systemRepository, fhirResourceRepository, fhirDhisAssignmentRepository ); + } + + @Nonnull + @Override + public DhisResourceType getDhisResourceType() + { + return DhisResourceType.PROGRAM_STAGE_METADATA; + } + + @Nonnull + @Override + protected FhirResourceType getFhirResourceType() + { + return FhirResourceType.QUESTIONNAIRE; + } + + @Nonnull + @Override + public Class getDhisResourceClass() + { + return AccessibleScriptedDhisMetadata.class; + } + + @Nonnull + @Override + public Class getRuleClass() + { + return ProgramStageMetadataRule.class; + } +} diff --git a/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/metadata/program/ProgramStageMetadataToFhirRequestResolver.java b/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/metadata/program/ProgramStageMetadataToFhirRequestResolver.java new file mode 100644 index 00000000..4186ebae --- /dev/null +++ b/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/metadata/program/ProgramStageMetadataToFhirRequestResolver.java @@ -0,0 +1,76 @@ +package org.dhis2.fhir.adapter.fhir.transform.dhis.impl.metadata.program; + +/* + * Copyright (c) 2004-2019, University of Oslo + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * Neither the name of the HISP project nor the names of its contributors may + * be used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +import org.dhis2.fhir.adapter.dhis.model.DhisMetadata; +import org.dhis2.fhir.adapter.dhis.model.DhisResource; +import org.dhis2.fhir.adapter.dhis.model.DhisResourceType; +import org.dhis2.fhir.adapter.fhir.metadata.repository.FhirClientRepository; +import org.dhis2.fhir.adapter.fhir.metadata.repository.RuleRepository; +import org.dhis2.fhir.adapter.fhir.script.ScriptExecutionContext; +import org.dhis2.fhir.adapter.fhir.transform.dhis.impl.DhisToFhirRequestResolver; +import org.dhis2.fhir.adapter.fhir.transform.dhis.impl.metadata.AbstractDhisMetadataToFhirRequestResolver; +import org.dhis2.fhir.adapter.fhir.transform.dhis.model.DhisRequest; +import org.dhis2.fhir.adapter.fhir.transform.scripted.ImmutableScriptedDhisMetadata; +import org.dhis2.fhir.adapter.fhir.transform.scripted.ScriptedDhisResource; +import org.dhis2.fhir.adapter.fhir.transform.scripted.WritableScriptedDhisMetadata; +import org.springframework.stereotype.Component; + +import javax.annotation.Nonnull; + +/** + * Implementation of {@link DhisToFhirRequestResolver} for DHIS2 program stage metadata. + * + * @author volsch + */ +@Component +public class ProgramStageMetadataToFhirRequestResolver extends AbstractDhisMetadataToFhirRequestResolver +{ + private final ScriptExecutionContext scriptExecutionContext; + + public ProgramStageMetadataToFhirRequestResolver( @Nonnull FhirClientRepository fhirClientRepository, @Nonnull RuleRepository ruleRepository, @Nonnull ScriptExecutionContext scriptExecutionContext ) + { + super( fhirClientRepository, ruleRepository ); + this.scriptExecutionContext = scriptExecutionContext; + } + + @Nonnull + @Override + public DhisResourceType getDhisResourceType() + { + return DhisResourceType.PROGRAM_STAGE_METADATA; + } + + @Nonnull + @Override + public ScriptedDhisResource convert( @Nonnull DhisResource dhisResource, @Nonnull DhisRequest dhisRequest ) + { + return new ImmutableScriptedDhisMetadata( new WritableScriptedDhisMetadata( (DhisMetadata) dhisResource, scriptExecutionContext ) ); + } +} diff --git a/fhir/src/main/resources/db/migration/sample/V1.1.0.41_10_0__Plan_Definition_Sample_Data.sql b/fhir/src/main/resources/db/migration/sample/V1.1.0.41_10_0__Plan_Definition_Sample_Data.sql index e21ec8fd..e3918522 100644 --- a/fhir/src/main/resources/db/migration/sample/V1.1.0.41_10_0__Plan_Definition_Sample_Data.sql +++ b/fhir/src/main/resources/db/migration/sample/V1.1.0.41_10_0__Plan_Definition_Sample_Data.sql @@ -34,5 +34,7 @@ WHERE id='73cd99c5-0ca8-42ad-a53b-1891fccce08f'; -- virtual subscription for FHIR organizations INSERT INTO fhir_client_resource (id, version, fhir_client_id, fhir_resource_type, exp_only, fhir_criteria_parameters, description) VALUES ('2520acc5-86b4-4716-ae0f-ea0531eb885a', 0, '73cd99c5-0ca8-42ad-a53b-1891fccce08f', 'PLAN_DEFINITION', TRUE, NULL, 'Virtual subscription for all Plan Definitions.'); +INSERT INTO fhir_client_resource (id, version, fhir_client_id, fhir_resource_type, exp_only, fhir_criteria_parameters, description) +VALUES ('0918c4f3-995c-4130-b607-b28023a1a3a0', 0, '73cd99c5-0ca8-42ad-a53b-1891fccce08f', 'QUESTIONNAIRE', TRUE, NULL, 'Virtual subscription for all Questionnaires.'); INSERT INTO fhir_client_resource_update(id) VALUES ('2520acc5-86b4-4716-ae0f-ea0531eb885a'); From c8dec4877db4765b5e3c8ea21c201d93ee0d7906 Mon Sep 17 00:00:00 2001 From: Volker Schmidt Date: Wed, 31 Jul 2019 12:06:35 +0200 Subject: [PATCH 06/16] Fixes issue with generated FHIR server resource IDs. --- .../adapter/dhis/model/DhisResourceType.java | 9 +- .../fhir/metadata/model/FhirResourceType.java | 46 ++-- .../repository/FhirResourceRepository.java | 2 +- .../repository/impl/DhisRepositoryImpl.java | 5 +- .../impl/FhirResourceRepositoryImpl.java | 33 ++- .../impl/FhirResourceRepositoryImplTest.java | 207 ++++++++++++++++++ 6 files changed, 273 insertions(+), 29 deletions(-) create mode 100644 fhir/src/test/java/org/dhis2/fhir/adapter/fhir/repository/impl/FhirResourceRepositoryImplTest.java diff --git a/dhis/src/main/java/org/dhis2/fhir/adapter/dhis/model/DhisResourceType.java b/dhis/src/main/java/org/dhis2/fhir/adapter/dhis/model/DhisResourceType.java index 30e669e9..8b6ca64a 100644 --- a/dhis/src/main/java/org/dhis2/fhir/adapter/dhis/model/DhisResourceType.java +++ b/dhis/src/main/java/org/dhis2/fhir/adapter/dhis/model/DhisResourceType.java @@ -47,16 +47,15 @@ public enum DhisResourceType */ ORGANIZATION_UNIT( "organisationUnits", "ou", "OrganizationUnitRule" ), - /** - * The program metadata. - */ - PROGRAM_METADATA( "programs", "pm", "ProgramMetadataRule" ), - /** * The program stage metadata. */ PROGRAM_STAGE_METADATA( "programStages", "sm", "ProgramStageMetadataRule" ), + /** + * The program metadata. + */ + PROGRAM_METADATA( "programs", "pm", "ProgramMetadataRule" ), /** * Resource is a tracked entity type. diff --git a/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/metadata/model/FhirResourceType.java b/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/metadata/model/FhirResourceType.java index 6bf41c17..ed0b04c6 100644 --- a/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/metadata/model/FhirResourceType.java +++ b/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/metadata/model/FhirResourceType.java @@ -58,22 +58,22 @@ */ public enum FhirResourceType { - CONDITION( FhirVersion.ALL, "Condition", 19, Collections.emptySet(), Collections.singleton( "Condition" ) ), - DIAGNOSTIC_REPORT( FhirVersion.ALL, "DiagnosticReport", 30, Collections.emptySet(), Collections.singleton( "DiagnosticReport" ) ), - ENCOUNTER( FhirVersion.ALL, "Encounter", 4, Collections.emptySet(), Collections.singleton( "Encounter" ) ), - IMMUNIZATION( FhirVersion.ALL, "Immunization", 22, Collections.emptySet(), Collections.singleton( "Immunization" ) ), - LOCATION( FhirVersion.ALL, "Location", 2, Collections.emptySet(), Collections.singleton( "Location" ) ), - MEDICATION_REQUEST( FhirVersion.ALL, "MedicationRequest", 21, Collections.emptySet(), Collections.singleton( "MedicationRequest" ) ), - OBSERVATION( FhirVersion.ALL, "Observation", 20, Collections.emptySet(), Collections.singleton( "Observation" ) ), - ORGANIZATION( FhirVersion.ALL, "Organization", 1, Collections.emptySet(), Collections.singleton( "Organization" ) ), - PATIENT( FhirVersion.ALL, "Patient", 10, Collections.emptySet(), Collections.singleton( "Patient" ) ), - RELATED_PERSON( FhirVersion.ALL, "RelatedPerson", 11, Collections.emptySet(), Collections.singleton( "RelatedPerson" ) ), - PRACTITIONER( FhirVersion.ALL, "Practitioner", 9, Collections.emptySet(), Collections.singleton( "Practitioner" ) ), - MEASURE_REPORT( FhirVersion.ALL, "MeasureReport", 30, Collections.singleton( "MeasureReport" ), Collections.singleton( "MeasureReport" ) ), - PLAN_DEFINITION( FhirVersion.R4_ONLY, "PlanDefinition", 30, Collections.emptySet(), Collections.singleton( "PlanDefinition" ) ), - QUESTIONNAIRE( FhirVersion.R4_ONLY, "Questionnaire", 31, Collections.emptySet(), Collections.singleton( "Questionnaire" ) ), - CARE_PLAN( FhirVersion.R4_ONLY, "CarePlan", 35, Collections.emptySet(), Collections.singleton( "CarePlan" ) ), - QUESTIONNAIRE_RESPONSE( FhirVersion.R4_ONLY, "QuestionnaireResponse", 40, Collections.emptySet(), Collections.singleton( "QuestionnaireResponse" ) ); + CONDITION( FhirVersion.ALL, "Condition", false, 19, Collections.emptySet(), Collections.singleton( "Condition" ) ), + DIAGNOSTIC_REPORT( FhirVersion.ALL, "DiagnosticReport", false, 30, Collections.emptySet(), Collections.singleton( "DiagnosticReport" ) ), + ENCOUNTER( FhirVersion.ALL, "Encounter", false, 4, Collections.emptySet(), Collections.singleton( "Encounter" ) ), + IMMUNIZATION( FhirVersion.ALL, "Immunization", false, 22, Collections.emptySet(), Collections.singleton( "Immunization" ) ), + LOCATION( FhirVersion.ALL, "Location", false, 2, Collections.emptySet(), Collections.singleton( "Location" ) ), + MEDICATION_REQUEST( FhirVersion.ALL, "MedicationRequest", false, 21, Collections.emptySet(), Collections.singleton( "MedicationRequest" ) ), + OBSERVATION( FhirVersion.ALL, "Observation", false, 20, Collections.emptySet(), Collections.singleton( "Observation" ) ), + ORGANIZATION( FhirVersion.ALL, "Organization", false, 1, Collections.emptySet(), Collections.singleton( "Organization" ) ), + PATIENT( FhirVersion.ALL, "Patient", false, 10, Collections.emptySet(), Collections.singleton( "Patient" ) ), + RELATED_PERSON( FhirVersion.ALL, "RelatedPerson", false, 11, Collections.emptySet(), Collections.singleton( "RelatedPerson" ) ), + PRACTITIONER( FhirVersion.ALL, "Practitioner", false, 9, Collections.emptySet(), Collections.singleton( "Practitioner" ) ), + MEASURE_REPORT( FhirVersion.ALL, "MeasureReport", false, 30, Collections.singleton( "MeasureReport" ), Collections.singleton( "MeasureReport" ) ), + PLAN_DEFINITION( FhirVersion.R4_ONLY, "PlanDefinition", true, 30, Collections.emptySet(), Collections.singleton( "PlanDefinition" ) ), + QUESTIONNAIRE( FhirVersion.R4_ONLY, "Questionnaire", true, 31, Collections.emptySet(), Collections.singleton( "Questionnaire" ) ), + CARE_PLAN( FhirVersion.R4_ONLY, "CarePlan", false, 35, Collections.emptySet(), Collections.singleton( "CarePlan" ) ), + QUESTIONNAIRE_RESPONSE( FhirVersion.R4_ONLY, "QuestionnaireResponse", false, 40, Collections.emptySet(), Collections.singleton( "QuestionnaireResponse" ) ); private static final Map resourcesBySimpleClassName = Arrays.stream( values() ).flatMap( v -> v.getSimpleClassNames().stream().map( scn -> new SimpleEntry<>( scn, v ) ) ) .collect( Collectors.toMap( SimpleEntry::getKey, SimpleEntry::getValue ) ); @@ -85,17 +85,21 @@ public static FhirResourceType getByResource( @Nullable IBaseResource resource ) { return null; } + FhirResourceType frt; Class c = resource.getClass(); + do { frt = resourcesBySimpleClassName.get( c.getSimpleName() ); + if ( frt == null ) { c = c.getSuperclass(); } } while ( (frt == null) && (c != null) && (c != Object.class) ); + return frt; } @@ -115,6 +119,8 @@ public static FhirResourceType getByResourceTypeName( @Nullable String resourceT private final String resourceTypeName; + private final boolean syncDhisId; + private final int order; private final Set transactionalWith; @@ -123,10 +129,11 @@ public static FhirResourceType getByResourceTypeName( @Nullable String resourceT private volatile Set transactionalWithTypes; - FhirResourceType( Set fhirVersions, String resourceTypeName, int order, Collection transactionalWith, Collection simpleClassNames ) + FhirResourceType( Set fhirVersions, String resourceTypeName, boolean syncDhisId, int order, Collection transactionalWith, Collection simpleClassNames ) { this.fhirVersions = fhirVersions; this.resourceTypeName = resourceTypeName; + this.syncDhisId = syncDhisId; this.order = order; this.transactionalWith = new HashSet<>( transactionalWith ); this.simpleClassNames = Collections.unmodifiableSet( new HashSet<>( simpleClassNames ) ); @@ -144,6 +151,11 @@ public String getResourceTypeName() return resourceTypeName; } + public boolean isSyncDhisId() + { + return syncDhisId; + } + public int getOrder() { return order; diff --git a/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/repository/FhirResourceRepository.java b/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/repository/FhirResourceRepository.java index 48cfc16d..8ca0374a 100644 --- a/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/repository/FhirResourceRepository.java +++ b/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/repository/FhirResourceRepository.java @@ -75,7 +75,7 @@ public interface FhirResourceRepository IBaseResource transform( @Nonnull UUID fhirClientId, @Nonnull FhirVersion fhirVersion, @Nullable IBaseResource resource ); @Nonnull - IBaseResource save( @Nonnull FhirClient fhirClient, @Nonnull IBaseResource resource ); + IBaseResource save( @Nonnull FhirClient fhirClient, @Nonnull IBaseResource resource, @Nullable String dhisResourceId ); boolean delete( @Nonnull FhirClient fhirClient, @Nonnull IBaseResource resource ); } diff --git a/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/repository/impl/DhisRepositoryImpl.java b/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/repository/impl/DhisRepositoryImpl.java index a613d7a2..b2c17a31 100644 --- a/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/repository/impl/DhisRepositoryImpl.java +++ b/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/repository/impl/DhisRepositoryImpl.java @@ -451,7 +451,8 @@ protected boolean saveInternally( @Nonnull DhisResource resource ) } else { - final IBaseResource resultingResource = fhirResourceRepository.save( transformerRequest.getFhirClient(), outcome.getResource() ); + final IBaseResource resultingResource = fhirResourceRepository + .save( transformerRequest.getFhirClient(), outcome.getResource(), resource.getId() ); // resource may have been set as attribute in transformer context (e.g. shared encounter) outcome.getResource().setId( resultingResource.getIdElement() ); fhirDhisAssignmentRepository.saveFhirResourceId( outcome.getRule(), transformerRequest.getFhirClient(), @@ -482,6 +483,7 @@ public Optional read( @Nonnull FhirClient fhirClient, @Nonnull Fh if ( dhisResource == null ) { logger.debug( "DHIS resource could not be found." ); + return Optional.empty(); } @@ -493,6 +495,7 @@ public Optional read( @Nonnull FhirClient fhirClient, @Nonnull Fh if ( transformerRequest == null ) { logger.debug( "No matching rule has been found." ); + return Optional.empty(); } diff --git a/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/repository/impl/FhirResourceRepositoryImpl.java b/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/repository/impl/FhirResourceRepositoryImpl.java index 086f7b9b..fbbb7f7c 100644 --- a/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/repository/impl/FhirResourceRepositoryImpl.java +++ b/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/repository/impl/FhirResourceRepositoryImpl.java @@ -276,7 +276,7 @@ public boolean delete( @Nonnull FhirClient fhirClient, @Nonnull IBaseResource re "T(org.dhis2.fhir.adapter.fhir.metadata.model.FhirResourceType).getByResource(#resource).getResourceTypeName(), #resource.getIdElement().getIdPart(), true}" ) @Nonnull @Override - public IBaseResource save( @Nonnull FhirClient fhirClient, @Nonnull IBaseResource resource ) + public IBaseResource save( @Nonnull FhirClient fhirClient, @Nonnull IBaseResource resource, @Nullable String dhisResourceId ) { if ( !fhirClient.getFhirEndpoint().isUseRemote() ) { @@ -287,25 +287,28 @@ public IBaseResource save( @Nonnull FhirClient fhirClient, @Nonnull IBaseResourc final FhirContext fhirContext = fhirContexts.get( fhirClient.getFhirVersion() ); final IGenericClient client = FhirClientUtils.createClient( fhirContext, fhirClient.getFhirEndpoint() ); + final IBaseResource preparedResource = prepareResource( resource, dhisResourceId ); final MethodOutcome methodOutcome; + if ( resource.getIdElement().hasIdPart() ) { try { - methodOutcome = client.update().resource( resource ).prefer( PreferReturnEnum.REPRESENTATION ).execute(); + methodOutcome = client.update().resource( preparedResource ).prefer( PreferReturnEnum.REPRESENTATION ).execute(); } catch ( PreconditionFailedException e ) { throw new OptimisticFhirResourceLockException( "Could not update FHIR resource " + - resource.getIdElement() + " because of an optimistic locking failure.", e ); + preparedResource.getIdElement() + " because of an optimistic locking failure.", e ); } } else { - methodOutcome = client.create().resource( resource ).prefer( PreferReturnEnum.REPRESENTATION ).execute(); + methodOutcome = client.create().resource( preparedResource ).prefer( PreferReturnEnum.REPRESENTATION ).execute(); } ProcessedItemInfo processedItemInfo = null; + if ( (methodOutcome.getResource() != null) && (methodOutcome.getResource().getMeta() != null) ) { // resource itself may contain old version ID (even if it should not) @@ -313,7 +316,7 @@ public IBaseResource save( @Nonnull FhirClient fhirClient, @Nonnull IBaseResourc } else if ( (methodOutcome.getId() != null) && methodOutcome.getId().hasVersionIdPart() ) { - processedItemInfo = ProcessedFhirItemInfoUtils.create( resource, methodOutcome.getId() ); + processedItemInfo = ProcessedFhirItemInfoUtils.create( preparedResource, methodOutcome.getId() ); } if ( processedItemInfo == null ) @@ -327,9 +330,11 @@ else if ( (methodOutcome.getId() != null) && methodOutcome.getId().hasVersionIdP } final IBaseResource result; + if ( methodOutcome.getResource() == null ) { result = resource; + if ( methodOutcome.getId() != null ) { result.setId( methodOutcome.getId() ); @@ -343,6 +348,24 @@ else if ( (methodOutcome.getId() != null) && methodOutcome.getId().hasVersionIdP return result; } + @Nonnull + protected T prepareResource( @Nonnull T resource, @Nullable String dhisResourceId ) + { + final FhirResourceType fhirResourceType = FhirResourceType.getByResource( resource ); + + if ( fhirResourceType == null ) + { + throw new FhirResourceTransformationException( "Could not determine FHIR resource type for " + resource.getClass().getSimpleName() ); + } + + if ( fhirResourceType.isSyncDhisId() && dhisResourceId != null && !resource.getIdElement().hasIdPart() ) + { + resource.setId( dhisResourceId ); + } + + return resource; + } + @TransactionalEventListener( phase = TransactionPhase.BEFORE_COMMIT, classes = AutoCreatedFhirClientResourceEvent.class ) public void autoCreatedSubscriptionResource( @Nonnull AutoCreatedFhirClientResourceEvent event ) { diff --git a/fhir/src/test/java/org/dhis2/fhir/adapter/fhir/repository/impl/FhirResourceRepositoryImplTest.java b/fhir/src/test/java/org/dhis2/fhir/adapter/fhir/repository/impl/FhirResourceRepositoryImplTest.java new file mode 100644 index 00000000..6fcd4ef9 --- /dev/null +++ b/fhir/src/test/java/org/dhis2/fhir/adapter/fhir/repository/impl/FhirResourceRepositoryImplTest.java @@ -0,0 +1,207 @@ +package org.dhis2.fhir.adapter.fhir.repository.impl; + +/* + * Copyright (c) 2004-2019, University of Oslo + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * Neither the name of the HISP project nor the names of its contributors may + * be used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +import ca.uhn.fhir.context.FhirContext; +import ca.uhn.fhir.context.FhirVersionEnum; +import ca.uhn.fhir.model.api.IFhirVersion; +import ca.uhn.fhir.model.primitive.IdDt; +import org.dhis2.fhir.adapter.fhir.client.StoredFhirResourceService; +import org.dhis2.fhir.adapter.fhir.metadata.repository.FhirClientResourceRepository; +import org.dhis2.fhir.adapter.fhir.script.ScriptExecutor; +import org.dhis2.fhir.adapter.spring.StaticObjectProvider; +import org.hl7.fhir.instance.model.api.IBaseMetaType; +import org.hl7.fhir.instance.model.api.IBaseResource; +import org.hl7.fhir.instance.model.api.IIdType; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.mockito.Mock; +import org.mockito.Mockito; +import org.mockito.junit.MockitoJUnit; +import org.mockito.junit.MockitoRule; + +import java.util.Arrays; +import java.util.Collections; +import java.util.List; + +/** + * Implementation of {@link FhirResourceRepositoryImpl}. + * + * @author volsch + */ +public class FhirResourceRepositoryImplTest +{ + @Mock + private ScriptExecutor scriptExecutor; + + @Mock + private StoredFhirResourceService storedItemService; + + @Mock + private FhirClientResourceRepository fhirClientResourceRepository; + + @Mock + private FhirContext fhirContextDstu3; + + @Mock + private IFhirVersion fhirVersionDstu3; + + @Mock + private FhirContext fhirContextR4; + + @Mock + private IFhirVersion fhirVersionR4; + + private FhirResourceRepositoryImpl repository; + + @Rule + public MockitoRule mockitoRule = MockitoJUnit.rule(); + + @Before + public void setUp() + { + Mockito.when( fhirContextDstu3.getVersion() ).thenReturn( fhirVersionDstu3 ); + Mockito.when( fhirVersionDstu3.getVersion() ).thenReturn( FhirVersionEnum.DSTU3 ); + Mockito.when( fhirContextR4.getVersion() ).thenReturn( fhirVersionR4 ); + Mockito.when( fhirVersionR4.getVersion() ).thenReturn( FhirVersionEnum.R4 ); + + repository = new FhirResourceRepositoryImpl( scriptExecutor, storedItemService, fhirClientResourceRepository, + new StaticObjectProvider<>( Arrays.asList( fhirContextDstu3, fhirContextR4 ) ), new StaticObjectProvider<>( Collections.emptyList() ) ); + } + + @Test + public void prepareResourceWithoutAnyId() + { + Patient patient = new Patient(); + patient = repository.prepareResource( patient, null ); + Assert.assertTrue( patient.getIdElement().isEmpty() ); + } + + @Test + public void prepareResourceNonMatchingWithDhisId() + { + Patient patient = new Patient(); + patient = repository.prepareResource( patient, "a0123456789" ); + Assert.assertTrue( patient.getIdElement().isEmpty() ); + } + + @Test + public void prepareResourceWithDhisId() + { + PlanDefinition planDefinition = new PlanDefinition(); + planDefinition = repository.prepareResource( planDefinition, "a0123456789" ); + Assert.assertEquals( "a0123456789", planDefinition.getIdElement().getIdPart() ); + } + + @Test + public void prepareResourceWithAllIds() + { + PlanDefinition planDefinition = new PlanDefinition(); + planDefinition.setId( "d0123456789" ); + planDefinition = repository.prepareResource( planDefinition, "a0123456789" ); + Assert.assertEquals( "d0123456789", planDefinition.getIdElement().getIdPart() ); + } + + public static class Patient extends AbstractBaseResource + { + private static final long serialVersionUID = -1428885428508171576L; + } + + public static class PlanDefinition extends AbstractBaseResource + { + private static final long serialVersionUID = -1428885428508171576L; + } + + public static abstract class AbstractBaseResource implements IBaseResource + { + private static final long serialVersionUID = 7566894473303706042L; + + private IIdType id = new IdDt(); + + @Override + public IBaseMetaType getMeta() + { + return null; + } + + @Override + public IIdType getIdElement() + { + return id; + } + + @Override + public IBaseResource setId( String theId ) + { + id = new IdDt( theId ); + + return this; + } + + @Override + public IBaseResource setId( IIdType theId ) + { + this.id = theId; + + return this; + } + + @Override + public FhirVersionEnum getStructureFhirVersionEnum() + { + return FhirVersionEnum.R4; + } + + @Override + public boolean isEmpty() + { + return false; + } + + @Override + public boolean hasFormatComment() + { + return false; + } + + @Override + public List getFormatCommentsPre() + { + return null; + } + + @Override + public List getFormatCommentsPost() + { + return null; + } + } +} \ No newline at end of file From f0e7261439d10caf6a1925213d23eec6beed0261 Mon Sep 17 00:00:00 2001 From: Volker Schmidt Date: Wed, 31 Jul 2019 14:31:05 +0200 Subject: [PATCH 07/16] Adds support for tracked entity resource type in Plan Definitions. --- .../r4/R4ProgramMetadataFhirRestAppTest.java | 3 + .../LocationExtension.StructureDefinition.xml | 17 +-- ...ourceTypeExtension.StructureDefinition.xml | 57 ++++++++++ ...gramPlanDefinition.StructureDefinition.xml | 21 ++++ ...tadataToFhirPlanDefinitionTransformer.java | 12 +- ...taToFhirPlanDefinitionTransformerTest.java | 14 ++- .../extension/ResourceTypeExtensionUtils.java | 68 +++++++++++ ...tadataToFhirPlanDefinitionTransformer.java | 33 +++++- .../ResourceTypeExtensionUtilsTest.java | 107 ++++++++++++++++++ ...taToFhirPlanDefinitionTransformerTest.java | 88 ++++++++++---- 10 files changed, 372 insertions(+), 48 deletions(-) create mode 100644 docs/fhir/profiles/ResourceTypeExtension.StructureDefinition.xml create mode 100644 fhir/src/main/java/org/dhis2/fhir/adapter/fhir/extension/ResourceTypeExtensionUtils.java create mode 100644 fhir/src/test/java/org/dhis2/fhir/adapter/fhir/extension/ResourceTypeExtensionUtilsTest.java diff --git a/app/src/test/java/org/dhis2/fhir/adapter/fhir/server/r4/R4ProgramMetadataFhirRestAppTest.java b/app/src/test/java/org/dhis2/fhir/adapter/fhir/server/r4/R4ProgramMetadataFhirRestAppTest.java index e86e33b7..e828ae5b 100644 --- a/app/src/test/java/org/dhis2/fhir/adapter/fhir/server/r4/R4ProgramMetadataFhirRestAppTest.java +++ b/app/src/test/java/org/dhis2/fhir/adapter/fhir/server/r4/R4ProgramMetadataFhirRestAppTest.java @@ -34,6 +34,7 @@ import ca.uhn.fhir.rest.server.exceptions.ResourceNotFoundException; import org.apache.commons.io.IOUtils; import org.dhis2.fhir.adapter.AbstractAppTest; +import org.dhis2.fhir.adapter.fhir.extension.ResourceTypeExtensionUtils; import org.dhis2.fhir.adapter.fhir.model.FhirVersion; import org.hl7.fhir.r4.model.Bundle; import org.hl7.fhir.r4.model.PlanDefinition; @@ -124,6 +125,8 @@ private void getPlanDefinition() throws Exception client.registerInterceptor( new BasicAuthInterceptor( "fhir_client", "fhir_client_1" ) ); PlanDefinition planDefinition = client.read().resource( PlanDefinition.class ).withId( "EPDyQuoRnXk" ).execute(); Assert.assertEquals( "Child Programme", planDefinition.getTitle() ); + Assert.assertNotNull( planDefinition.getExtensionByUrl( ResourceTypeExtensionUtils.URL ) ); + Assert.assertEquals( "Patient", planDefinition.getExtensionByUrl( ResourceTypeExtensionUtils.URL ).getValue().primitiveValue() ); systemDhis2Server.verify(); userDhis2Server.verify(); diff --git a/docs/fhir/profiles/LocationExtension.StructureDefinition.xml b/docs/fhir/profiles/LocationExtension.StructureDefinition.xml index 4211ca46..51891d76 100644 --- a/docs/fhir/profiles/LocationExtension.StructureDefinition.xml +++ b/docs/fhir/profiles/LocationExtension.StructureDefinition.xml @@ -28,7 +28,7 @@ --> - + @@ -47,21 +47,6 @@ - - - - - - - - - - - - - - - diff --git a/docs/fhir/profiles/ResourceTypeExtension.StructureDefinition.xml b/docs/fhir/profiles/ResourceTypeExtension.StructureDefinition.xml new file mode 100644 index 00000000..76f90869 --- /dev/null +++ b/docs/fhir/profiles/ResourceTypeExtension.StructureDefinition.xml @@ -0,0 +1,57 @@ + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/docs/fhir/profiles/TrackerProgramPlanDefinition.StructureDefinition.xml b/docs/fhir/profiles/TrackerProgramPlanDefinition.StructureDefinition.xml index bf33d0bb..25e9ae28 100644 --- a/docs/fhir/profiles/TrackerProgramPlanDefinition.StructureDefinition.xml +++ b/docs/fhir/profiles/TrackerProgramPlanDefinition.StructureDefinition.xml @@ -50,6 +50,27 @@ + + + + + + + + + + + + + + + + + + + + + diff --git a/fhir-r4/src/main/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/metadata/r4/R4ProgramMetadataToFhirPlanDefinitionTransformer.java b/fhir-r4/src/main/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/metadata/r4/R4ProgramMetadataToFhirPlanDefinitionTransformer.java index 44333c79..90f5a217 100644 --- a/fhir-r4/src/main/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/metadata/r4/R4ProgramMetadataToFhirPlanDefinitionTransformer.java +++ b/fhir-r4/src/main/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/metadata/r4/R4ProgramMetadataToFhirPlanDefinitionTransformer.java @@ -28,6 +28,7 @@ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ +import ca.uhn.fhir.model.api.IElement; import org.dhis2.fhir.adapter.dhis.orgunit.OrganizationUnitService; import org.dhis2.fhir.adapter.dhis.tracker.program.Program; import org.dhis2.fhir.adapter.dhis.tracker.trackedentity.TrackedEntityMetadataService; @@ -50,11 +51,13 @@ import org.hl7.fhir.r4.model.Enumerations; import org.hl7.fhir.r4.model.PlanDefinition; import org.hl7.fhir.r4.model.PlanDefinition.PlanDefinitionActionComponent; +import org.hl7.fhir.r4.model.ResourceFactory; import org.springframework.stereotype.Component; import javax.annotation.Nonnull; import java.util.Map; import java.util.Set; +import java.util.function.Function; /** * R4 specific version of DHIS2 Program Metadata to FHIR Plan Definition transformer. @@ -86,7 +89,7 @@ protected boolean transformInternal( @Nonnull FhirClient fhirClient, @Nonnull Dh final Program dhisProgram = (Program) input.getDhisResource(); final PlanDefinition fhirPlanDefinition = (PlanDefinition) output; - if ( !isApplicableProgram( dhisProgram ) ) + if ( !addSubjectResourceType( dhisProgram, fhirPlanDefinition ) ) { return false; } @@ -111,4 +114,11 @@ protected boolean transformInternal( @Nonnull FhirClient fhirClient, @Nonnull Dh return true; } + + @Nonnull + @Override + protected Function getTypeFactory() + { + return ResourceFactory::createType; + } } diff --git a/fhir-r4/src/test/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/metadata/r4/R4ProgramMetadataToFhirPlanDefinitionTransformerTest.java b/fhir-r4/src/test/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/metadata/r4/R4ProgramMetadataToFhirPlanDefinitionTransformerTest.java index adccd703..a795ad71 100644 --- a/fhir-r4/src/test/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/metadata/r4/R4ProgramMetadataToFhirPlanDefinitionTransformerTest.java +++ b/fhir-r4/src/test/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/metadata/r4/R4ProgramMetadataToFhirPlanDefinitionTransformerTest.java @@ -36,9 +36,12 @@ import org.dhis2.fhir.adapter.dhis.tracker.trackedentity.TrackedEntityMetadataService; import org.dhis2.fhir.adapter.dhis.tracker.trackedentity.WritableTrackedEntityType; import org.dhis2.fhir.adapter.fhir.data.repository.FhirDhisAssignmentRepository; +import org.dhis2.fhir.adapter.fhir.extension.ResourceTypeExtensionUtils; import org.dhis2.fhir.adapter.fhir.metadata.model.FhirClient; +import org.dhis2.fhir.adapter.fhir.metadata.model.FhirResourceType; import org.dhis2.fhir.adapter.fhir.metadata.model.ProgramMetadataRule; import org.dhis2.fhir.adapter.fhir.metadata.model.RuleInfo; +import org.dhis2.fhir.adapter.fhir.metadata.model.TrackedEntityRule; import org.dhis2.fhir.adapter.fhir.metadata.repository.SystemRepository; import org.dhis2.fhir.adapter.fhir.metadata.repository.TrackedEntityRuleRepository; import org.dhis2.fhir.adapter.fhir.model.FhirVersion; @@ -120,10 +123,13 @@ public void getFhirVersions() @Test public void transformInternal() { + final TrackedEntityRule rule2 = new TrackedEntityRule(); + rule2.setEvaluationOrder( 100 ); + rule2.setFhirResourceType( FhirResourceType.PATIENT ); + Mockito.doReturn( Optional.of( new WritableTrackedEntityType( "a1234567890", "Test", Collections.emptyList() ) ) ) .when( trackedEntityMetadataService ).findTypeByReference( Mockito.eq( new Reference( "a1234567890", ReferenceType.ID ) ) ); - Mockito.doReturn( Collections.singletonList( new RuleInfo<>( new ProgramMetadataRule(), Collections.emptyList() ) ) ) - .when( trackedEntityRuleRepository ).findByTypeRefs( Mockito.eq( new HashSet<>( + Mockito.doReturn( Collections.singletonList( rule2 ) ).when( trackedEntityRuleRepository ).findByTypeRefs( Mockito.eq( new HashSet<>( Arrays.asList( new Reference( "a1234567890", ReferenceType.ID ), new Reference( "Test", ReferenceType.NAME ) ) ) ) ); final WritableProgram program = new WritableProgram(); @@ -156,6 +162,10 @@ public void transformInternal() Assert.assertEquals( "Test Description", fhirPlanDefinition.getDescription() ); Assert.assertEquals( 2, fhirPlanDefinition.getAction().size() ); + Assert.assertEquals( 1, fhirPlanDefinition.getExtension().size() ); + Assert.assertEquals( ResourceTypeExtensionUtils.URL, fhirPlanDefinition.getExtension().get( 0 ).getUrl() ); + Assert.assertEquals( "Patient", fhirPlanDefinition.getExtension().get( 0 ).getValue().toString() ); + Assert.assertEquals( "b1234567890", fhirPlanDefinition.getAction().get( 0 ).getId() ); Assert.assertEquals( "Test Stage 1", fhirPlanDefinition.getAction().get( 0 ).getTitle() ); Assert.assertEquals( "Test Description 1", fhirPlanDefinition.getAction().get( 0 ).getDescription() ); diff --git a/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/extension/ResourceTypeExtensionUtils.java b/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/extension/ResourceTypeExtensionUtils.java new file mode 100644 index 00000000..595cc28f --- /dev/null +++ b/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/extension/ResourceTypeExtensionUtils.java @@ -0,0 +1,68 @@ +package org.dhis2.fhir.adapter.fhir.extension; + +/* + * Copyright (c) 2004-2019, University of Oslo + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * Neither the name of the HISP project nor the names of its contributors may + * be used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +import ca.uhn.fhir.model.api.IElement; +import org.dhis2.fhir.adapter.fhir.metadata.model.FhirResourceType; +import org.hl7.fhir.instance.model.api.IBaseExtension; +import org.hl7.fhir.instance.model.api.IBaseHasExtensions; +import org.hl7.fhir.instance.model.api.IPrimitiveType; + +import javax.annotation.Nonnull; +import javax.annotation.Nullable; +import java.util.function.Function; + +/** + * Utility class to process FHIR resource type extension. + * + * @author volsch + */ +public abstract class ResourceTypeExtensionUtils +{ + public static final String URL = "http://www.dhis2.org/dhis2-fhir-adapter/fhir/extensions/resource-type"; + + @SuppressWarnings( "unchecked" ) + public static void setValue( @Nonnull IBaseHasExtensions resource, @Nullable FhirResourceType fhirResourceType, @Nonnull Function typeFactory ) + { + resource.getExtension().removeIf( e -> URL.equals( e.getUrl() ) ); + + if ( fhirResourceType != null ) + { + final IBaseExtension extension = resource.addExtension(); + + extension.setUrl( URL ); + extension.setValue( ( (IPrimitiveType) typeFactory.apply( "string" ) ).setValue( fhirResourceType.getResourceTypeName() ) ); + } + } + + private ResourceTypeExtensionUtils() + { + super(); + } +} diff --git a/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/metadata/program/AbstractProgramMetadataToFhirPlanDefinitionTransformer.java b/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/metadata/program/AbstractProgramMetadataToFhirPlanDefinitionTransformer.java index bb7aaefe..a2b48af7 100644 --- a/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/metadata/program/AbstractProgramMetadataToFhirPlanDefinitionTransformer.java +++ b/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/metadata/program/AbstractProgramMetadataToFhirPlanDefinitionTransformer.java @@ -28,6 +28,7 @@ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ +import ca.uhn.fhir.model.api.IElement; import org.dhis2.fhir.adapter.dhis.model.DhisResourceType; import org.dhis2.fhir.adapter.dhis.model.Reference; import org.dhis2.fhir.adapter.dhis.model.ReferenceType; @@ -36,6 +37,8 @@ import org.dhis2.fhir.adapter.dhis.tracker.trackedentity.TrackedEntityMetadataService; import org.dhis2.fhir.adapter.dhis.tracker.trackedentity.TrackedEntityType; import org.dhis2.fhir.adapter.fhir.data.repository.FhirDhisAssignmentRepository; +import org.dhis2.fhir.adapter.fhir.extension.ResourceTypeExtensionUtils; +import org.dhis2.fhir.adapter.fhir.metadata.model.AbstractRule; import org.dhis2.fhir.adapter.fhir.metadata.model.FhirResourceType; import org.dhis2.fhir.adapter.fhir.metadata.model.ProgramMetadataRule; import org.dhis2.fhir.adapter.fhir.metadata.repository.SystemRepository; @@ -46,11 +49,13 @@ import org.dhis2.fhir.adapter.fhir.transform.dhis.impl.metadata.AbstractReadOnlyDhisMetadataToTypedFhirTransformer; import org.dhis2.fhir.adapter.fhir.transform.scripted.AccessibleScriptedDhisMetadata; import org.dhis2.fhir.adapter.lock.LockManager; +import org.hl7.fhir.instance.model.api.IBaseHasExtensions; import org.hl7.fhir.instance.model.api.IBaseResource; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import javax.annotation.Nonnull; +import java.util.function.Function; /** * Implementation of {@link DhisToFhirTransformer} for transforming DHIS2 @@ -63,6 +68,8 @@ public abstract class AbstractProgramMetadataToFhirPlanDefinitionTransformer getRuleClass() return ProgramMetadataRule.class; } - protected boolean isApplicableProgram( @Nonnull Program program ) + protected boolean addSubjectResourceType( @Nonnull Program program, @Nonnull IBaseHasExtensions resource ) { - if ( program.isWithoutRegistration() || program.getTrackedEntityTypeId() == null ) + if ( program.getTrackedEntityTypeId() == null ) { - return false; + ResourceTypeExtensionUtils.setValue( resource, null, getTypeFactory() ); + + return true; } - final TrackedEntityType trackedEntityType = trackedEntityMetadataService.findTypeByReference( new Reference( program.getTrackedEntityTypeId(), ReferenceType.ID ) ).orElse( null ); + final TrackedEntityType trackedEntityType = trackedEntityMetadataService.findTypeByReference( + new Reference( program.getTrackedEntityTypeId(), ReferenceType.ID ) ).orElse( null ); if ( trackedEntityType == null ) { return false; } - return !trackedEntityRuleRepository.findByTypeRefs( trackedEntityType.getAllReferences() ).isEmpty(); + final FhirResourceType fhirResourceType = trackedEntityRuleRepository.findByTypeRefs( trackedEntityType.getAllReferences() ).stream() + .sorted( ( o1, o2 ) -> o2.getEvaluationOrder() - o1.getEvaluationOrder() ).map( AbstractRule::getFhirResourceType ).findFirst().orElse( null ); + + if ( fhirResourceType == null ) + { + return false; + } + + ResourceTypeExtensionUtils.setValue( resource, fhirResourceType, getTypeFactory() ); + + return true; } + + @Nonnull + protected abstract Function getTypeFactory(); } diff --git a/fhir/src/test/java/org/dhis2/fhir/adapter/fhir/extension/ResourceTypeExtensionUtilsTest.java b/fhir/src/test/java/org/dhis2/fhir/adapter/fhir/extension/ResourceTypeExtensionUtilsTest.java new file mode 100644 index 00000000..d245ec1d --- /dev/null +++ b/fhir/src/test/java/org/dhis2/fhir/adapter/fhir/extension/ResourceTypeExtensionUtilsTest.java @@ -0,0 +1,107 @@ +package org.dhis2.fhir.adapter.fhir.extension; + +/* + * Copyright (c) 2004-2019, University of Oslo + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * Neither the name of the HISP project nor the names of its contributors may + * be used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +import ca.uhn.fhir.model.api.ExtensionDt; +import ca.uhn.fhir.model.api.IElement; +import ca.uhn.fhir.model.primitive.StringDt; +import org.dhis2.fhir.adapter.fhir.metadata.model.FhirResourceType; +import org.hl7.fhir.instance.model.api.IBaseExtension; +import org.hl7.fhir.instance.model.api.IBaseHasExtensions; +import org.junit.Assert; +import org.junit.Test; + +import java.util.ArrayList; +import java.util.List; + +/** + * Unit tests for {@link ResourceTypeExtensionUtils}. + * + * @author volsch + */ +public class ResourceTypeExtensionUtilsTest +{ + @Test + public void resetValue() + { + PlanDefinition planDefinition = new PlanDefinition(); + + ResourceTypeExtensionUtils.setValue( planDefinition, FhirResourceType.PATIENT, TypeFactory::createType ); + ResourceTypeExtensionUtils.setValue( planDefinition, null, TypeFactory::createType ); + + Assert.assertTrue( planDefinition.getExtension().isEmpty() ); + } + + @Test + public void setValue() + { + PlanDefinition planDefinition = new PlanDefinition(); + + ResourceTypeExtensionUtils.setValue( planDefinition, FhirResourceType.PATIENT, TypeFactory::createType ); + + Assert.assertEquals( 1, planDefinition.getExtension().size() ); + Assert.assertEquals( ResourceTypeExtensionUtils.URL, planDefinition.getExtension().get( 0 ).getUrl() ); + Assert.assertEquals( "Patient", planDefinition.getExtension().get( 0 ).getValue().toString() ); + } + + public static class PlanDefinition implements IBaseHasExtensions + { + private final List> extensions = new ArrayList<>(); + + @Override + public IBaseExtension addExtension() + { + extensions.add( new ExtensionDt() ); + + return extensions.get( extensions.size() - 1 ); + } + + @Override + public List> getExtension() + { + return extensions; + } + + @Override + public boolean hasExtension() + { + return !extensions.isEmpty(); + } + } + + public static class TypeFactory + { + public static IElement createType( String name ) + { + Assert.assertEquals( "string", name ); + + return new StringDt(); + } + } +} \ No newline at end of file diff --git a/fhir/src/test/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/metadata/program/AbstractProgramMetadataToFhirPlanDefinitionTransformerTest.java b/fhir/src/test/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/metadata/program/AbstractProgramMetadataToFhirPlanDefinitionTransformerTest.java index 8ee2c709..5c473888 100644 --- a/fhir/src/test/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/metadata/program/AbstractProgramMetadataToFhirPlanDefinitionTransformerTest.java +++ b/fhir/src/test/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/metadata/program/AbstractProgramMetadataToFhirPlanDefinitionTransformerTest.java @@ -28,6 +28,8 @@ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ +import ca.uhn.fhir.model.api.ExtensionDt; +import ca.uhn.fhir.model.primitive.StringDt; import org.dhis2.fhir.adapter.dhis.model.DhisResourceType; import org.dhis2.fhir.adapter.dhis.model.Reference; import org.dhis2.fhir.adapter.dhis.model.ReferenceType; @@ -36,15 +38,17 @@ import org.dhis2.fhir.adapter.dhis.tracker.trackedentity.TrackedEntityMetadataService; import org.dhis2.fhir.adapter.dhis.tracker.trackedentity.WritableTrackedEntityType; import org.dhis2.fhir.adapter.fhir.data.repository.FhirDhisAssignmentRepository; +import org.dhis2.fhir.adapter.fhir.extension.ResourceTypeExtensionUtils; import org.dhis2.fhir.adapter.fhir.metadata.model.FhirResourceType; -import org.dhis2.fhir.adapter.fhir.metadata.model.ProgramMetadataRule; -import org.dhis2.fhir.adapter.fhir.metadata.model.RuleInfo; +import org.dhis2.fhir.adapter.fhir.metadata.model.TrackedEntityRule; import org.dhis2.fhir.adapter.fhir.metadata.repository.SystemRepository; import org.dhis2.fhir.adapter.fhir.metadata.repository.TrackedEntityRuleRepository; import org.dhis2.fhir.adapter.fhir.repository.FhirResourceRepository; import org.dhis2.fhir.adapter.fhir.script.ScriptExecutor; import org.dhis2.fhir.adapter.fhir.transform.scripted.AccessibleScriptedDhisMetadata; import org.dhis2.fhir.adapter.lock.LockManager; +import org.hl7.fhir.instance.model.api.IBaseExtension; +import org.hl7.fhir.instance.model.api.IBaseHasExtensions; import org.junit.Assert; import org.junit.Before; import org.junit.Rule; @@ -58,6 +62,7 @@ import java.util.Arrays; import java.util.Collections; import java.util.HashSet; +import java.util.List; import java.util.Optional; import static org.mockito.Mockito.CALLS_REAL_METHODS; @@ -97,7 +102,7 @@ public class AbstractProgramMetadataToFhirPlanDefinitionTransformerTest private AbstractProgramMetadataToFhirPlanDefinitionTransformer transformer; @Rule - public MockitoRule rule = MockitoJUnit.rule(); + public MockitoRule rule = MockitoJUnit.rule().silent(); @Before public void setUp() @@ -105,6 +110,8 @@ public void setUp() transformer = Mockito.mock( AbstractProgramMetadataToFhirPlanDefinitionTransformer.class, withSettings().useConstructor( scriptExecutor, lockManager, systemRepository, fhirResourceRepository, fhirDhisAssignmentRepository, organizationUnitService, trackedEntityMetadataService, trackedEntityRuleRepository ).defaultAnswer( CALLS_REAL_METHODS ) ); + + Mockito.when( transformer.getTypeFactory() ).thenReturn( name -> new StringDt() ); } @Test @@ -126,12 +133,19 @@ public void getDhisResourceClass() } @Test - public void isApplicableProgram() + public void addSubjectResourceType() { + final TrackedEntityRule rule1 = new TrackedEntityRule(); + rule1.setEvaluationOrder( 1 ); + rule1.setFhirResourceType( FhirResourceType.RELATED_PERSON ); + + final TrackedEntityRule rule2 = new TrackedEntityRule(); + rule2.setEvaluationOrder( 100 ); + rule2.setFhirResourceType( FhirResourceType.PATIENT ); + Mockito.doReturn( Optional.of( new WritableTrackedEntityType( "a1234567890", "Test", Collections.emptyList() ) ) ) .when( trackedEntityMetadataService ).findTypeByReference( Mockito.eq( new Reference( "a1234567890", ReferenceType.ID ) ) ); - Mockito.doReturn( Collections.singletonList( new RuleInfo<>( new ProgramMetadataRule(), Collections.emptyList() ) ) ) - .when( trackedEntityRuleRepository ).findByTypeRefs( Mockito.eq( new HashSet<>( + Mockito.doReturn( Arrays.asList( rule1, rule2 ) ).when( trackedEntityRuleRepository ).findByTypeRefs( Mockito.eq( new HashSet<>( Arrays.asList( new Reference( "a1234567890", ReferenceType.ID ), new Reference( "Test", ReferenceType.NAME ) ) ) ) ); final WritableProgram program = new WritableProgram(); @@ -141,24 +155,17 @@ public void isApplicableProgram() program.setTrackedEntityTypeId( "a1234567890" ); program.setStages( new ArrayList<>() ); - Assert.assertTrue( transformer.isApplicableProgram( program ) ); - } + final PlanDefinition planDefinition = new PlanDefinition(); - @Test - public void isApplicableProgramWithoutRegistration() - { - final WritableProgram program = new WritableProgram(); - program.setName( "Test Program" ); - program.setDescription( "Test Description" ); - program.setWithoutRegistration( true ); - program.setTrackedEntityTypeId( "a1234567890" ); - program.setStages( new ArrayList<>() ); + Assert.assertTrue( transformer.addSubjectResourceType( program, planDefinition ) ); - Assert.assertFalse( transformer.isApplicableProgram( program ) ); + Assert.assertEquals( 1, planDefinition.getExtension().size() ); + Assert.assertEquals( ResourceTypeExtensionUtils.URL, planDefinition.getExtension().get( 0 ).getUrl() ); + Assert.assertEquals( "Patient", planDefinition.getExtension().get( 0 ).getValue().toString() ); } @Test - public void isApplicableProgramWithoutTrackedEntityType() + public void addSubjectResourceTypeWithoutTrackedEntityType() { final WritableProgram program = new WritableProgram(); program.setName( "Test Program" ); @@ -166,11 +173,15 @@ public void isApplicableProgramWithoutTrackedEntityType() program.setWithoutRegistration( false ); program.setStages( new ArrayList<>() ); - Assert.assertFalse( transformer.isApplicableProgram( program ) ); + final PlanDefinition planDefinition = new PlanDefinition(); + + Assert.assertTrue( transformer.addSubjectResourceType( program, planDefinition ) ); + + Assert.assertEquals( 0, planDefinition.getExtension().size() ); } @Test - public void isApplicableProgramWithoutTrackedEntityMetadata() + public void addSubjectResourceTypeWithoutTrackedEntityMetadata() { Mockito.doReturn( Optional.empty() ) .when( trackedEntityMetadataService ).findTypeByReference( Mockito.eq( new Reference( "a1234567890", ReferenceType.ID ) ) ); @@ -182,11 +193,13 @@ public void isApplicableProgramWithoutTrackedEntityMetadata() program.setTrackedEntityTypeId( "a1234567890" ); program.setStages( new ArrayList<>() ); - Assert.assertFalse( transformer.isApplicableProgram( program ) ); + final PlanDefinition planDefinition = new PlanDefinition(); + + Assert.assertFalse( transformer.addSubjectResourceType( program, planDefinition ) ); } @Test - public void isApplicableProgramWithoutRules() + public void addSubjectResourceTypeWithoutRules() { Mockito.doReturn( Optional.of( new WritableTrackedEntityType( "a1234567890", "Test", Collections.emptyList() ) ) ) .when( trackedEntityMetadataService ).findTypeByReference( Mockito.eq( new Reference( "a1234567890", ReferenceType.ID ) ) ); @@ -201,6 +214,33 @@ public void isApplicableProgramWithoutRules() program.setTrackedEntityTypeId( "a1234567890" ); program.setStages( new ArrayList<>() ); - Assert.assertFalse( transformer.isApplicableProgram( program ) ); + final PlanDefinition planDefinition = new PlanDefinition(); + + Assert.assertFalse( transformer.addSubjectResourceType( program, planDefinition ) ); + } + + public static class PlanDefinition implements IBaseHasExtensions + { + private final List> extensions = new ArrayList<>(); + + @Override + public IBaseExtension addExtension() + { + extensions.add( new ExtensionDt() ); + + return extensions.get( extensions.size() - 1 ); + } + + @Override + public List> getExtension() + { + return extensions; + } + + @Override + public boolean hasExtension() + { + return !extensions.isEmpty(); + } } } \ No newline at end of file From 5930c6377e8526ed22c88a54c9e9d7b76746dc1c Mon Sep 17 00:00:00 2001 From: Volker Schmidt Date: Wed, 31 Jul 2019 17:54:47 +0200 Subject: [PATCH 08/16] Adds value type extension for Questionnaire items. --- ...ourceTypeExtension.StructureDefinition.xml | 2 +- ...ogramQuestionnaire.StructureDefinition.xml | 20 ++++++ ...ValueTypeExtension.StructureDefinition.xml | 57 ++++++++++++++++ ...etadataToFhirQuestionnaireTransformer.java | 13 ++++ ...ataToFhirQuestionnaireTransformerTest.java | 12 ++++ .../fhir/extension/BaseExtensionUtils.java | 65 ++++++++++++++++++ .../extension/ResourceTypeExtensionUtils.java | 13 +--- .../extension/ValueTypeExtensionUtils.java | 56 ++++++++++++++++ ...etadataToFhirQuestionnaireTransformer.java | 5 ++ .../extension/BaseExtensionUtilsTest.java | 64 ++++++++++++++++++ .../ResourceTypeExtensionUtilsTest.java | 47 +------------ .../fhir/extension/TestPlanDefinition.java | 66 +++++++++++++++++++ .../adapter/fhir/extension/TypeFactory.java | 48 ++++++++++++++ .../ValueTypeExtensionUtilsTest.java | 53 +++++++++++++++ 14 files changed, 463 insertions(+), 58 deletions(-) create mode 100644 docs/fhir/profiles/ValueTypeExtension.StructureDefinition.xml create mode 100644 fhir/src/main/java/org/dhis2/fhir/adapter/fhir/extension/BaseExtensionUtils.java create mode 100644 fhir/src/main/java/org/dhis2/fhir/adapter/fhir/extension/ValueTypeExtensionUtils.java create mode 100644 fhir/src/test/java/org/dhis2/fhir/adapter/fhir/extension/BaseExtensionUtilsTest.java create mode 100644 fhir/src/test/java/org/dhis2/fhir/adapter/fhir/extension/TestPlanDefinition.java create mode 100644 fhir/src/test/java/org/dhis2/fhir/adapter/fhir/extension/TypeFactory.java create mode 100644 fhir/src/test/java/org/dhis2/fhir/adapter/fhir/extension/ValueTypeExtensionUtilsTest.java diff --git a/docs/fhir/profiles/ResourceTypeExtension.StructureDefinition.xml b/docs/fhir/profiles/ResourceTypeExtension.StructureDefinition.xml index 76f90869..fc42e483 100644 --- a/docs/fhir/profiles/ResourceTypeExtension.StructureDefinition.xml +++ b/docs/fhir/profiles/ResourceTypeExtension.StructureDefinition.xml @@ -36,7 +36,7 @@ - + diff --git a/docs/fhir/profiles/TrackerProgramQuestionnaire.StructureDefinition.xml b/docs/fhir/profiles/TrackerProgramQuestionnaire.StructureDefinition.xml index eeef7fc0..f7fa6224 100644 --- a/docs/fhir/profiles/TrackerProgramQuestionnaire.StructureDefinition.xml +++ b/docs/fhir/profiles/TrackerProgramQuestionnaire.StructureDefinition.xml @@ -164,6 +164,26 @@ + + + + + + + + + + + + + + + + + + + + diff --git a/docs/fhir/profiles/ValueTypeExtension.StructureDefinition.xml b/docs/fhir/profiles/ValueTypeExtension.StructureDefinition.xml new file mode 100644 index 00000000..5fae7e76 --- /dev/null +++ b/docs/fhir/profiles/ValueTypeExtension.StructureDefinition.xml @@ -0,0 +1,57 @@ + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/fhir-r4/src/main/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/metadata/r4/R4ProgramStageMetadataToFhirQuestionnaireTransformer.java b/fhir-r4/src/main/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/metadata/r4/R4ProgramStageMetadataToFhirQuestionnaireTransformer.java index 2575b8ee..8b113b97 100644 --- a/fhir-r4/src/main/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/metadata/r4/R4ProgramStageMetadataToFhirQuestionnaireTransformer.java +++ b/fhir-r4/src/main/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/metadata/r4/R4ProgramStageMetadataToFhirQuestionnaireTransformer.java @@ -28,9 +28,11 @@ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ +import ca.uhn.fhir.model.api.IElement; import org.dhis2.fhir.adapter.dhis.orgunit.OrganizationUnitService; import org.dhis2.fhir.adapter.dhis.tracker.program.ProgramStage; import org.dhis2.fhir.adapter.fhir.data.repository.FhirDhisAssignmentRepository; +import org.dhis2.fhir.adapter.fhir.extension.ValueTypeExtensionUtils; import org.dhis2.fhir.adapter.fhir.metadata.model.FhirClient; import org.dhis2.fhir.adapter.fhir.metadata.model.ProgramStageMetadataRule; import org.dhis2.fhir.adapter.fhir.metadata.model.RuleInfo; @@ -49,12 +51,14 @@ import org.hl7.fhir.r4.model.Questionnaire; import org.hl7.fhir.r4.model.Questionnaire.QuestionnaireItemComponent; import org.hl7.fhir.r4.model.Questionnaire.QuestionnaireItemType; +import org.hl7.fhir.r4.model.ResourceFactory; import org.springframework.stereotype.Component; import javax.annotation.Nonnull; import javax.annotation.Nullable; import java.util.Map; import java.util.Set; +import java.util.function.Function; import static org.hl7.fhir.r4.model.Questionnaire.QuestionnaireItemType.*; @@ -104,6 +108,8 @@ protected boolean transformInternal( @Nonnull FhirClient fhirClient, @Nonnull Dh itemComponent.setRequired( dataElement.isCompulsory() ); itemComponent.setType( type ); + ValueTypeExtensionUtils.setValue( itemComponent, dataElement.getElement().getValueType(), getTypeFactory() ); + if ( dataElement.getElement().isOptionSetValue() ) { dataElement.getElement().getOptionSet().getOptions().forEach( option -> itemComponent.addAnswerOption().setValue( @@ -155,4 +161,11 @@ protected QuestionnaireItemType convertValueType( @Nonnull ValueType valueType ) // unhandled data type return null; } + + @Nonnull + @Override + protected Function getTypeFactory() + { + return ResourceFactory::createType; + } } diff --git a/fhir-r4/src/test/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/metadata/r4/R4ProgramStageMetadataToFhirQuestionnaireTransformerTest.java b/fhir-r4/src/test/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/metadata/r4/R4ProgramStageMetadataToFhirQuestionnaireTransformerTest.java index 3cf0b25c..c27859e3 100644 --- a/fhir-r4/src/test/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/metadata/r4/R4ProgramStageMetadataToFhirQuestionnaireTransformerTest.java +++ b/fhir-r4/src/test/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/metadata/r4/R4ProgramStageMetadataToFhirQuestionnaireTransformerTest.java @@ -35,6 +35,7 @@ import org.dhis2.fhir.adapter.dhis.tracker.program.WritableProgramStage; import org.dhis2.fhir.adapter.dhis.tracker.program.WritableProgramStageDataElement; import org.dhis2.fhir.adapter.fhir.data.repository.FhirDhisAssignmentRepository; +import org.dhis2.fhir.adapter.fhir.extension.ValueTypeExtensionUtils; import org.dhis2.fhir.adapter.fhir.metadata.model.FhirClient; import org.dhis2.fhir.adapter.fhir.metadata.model.ProgramStageMetadataRule; import org.dhis2.fhir.adapter.fhir.metadata.model.RuleInfo; @@ -49,6 +50,7 @@ import org.dhis2.fhir.adapter.model.ValueType; import org.hl7.fhir.r4.model.Coding; import org.hl7.fhir.r4.model.Questionnaire; +import org.hl7.fhir.r4.model.StringType; import org.junit.Assert; import org.junit.Rule; import org.junit.Test; @@ -157,6 +159,11 @@ public void transformInternal() Assert.assertEquals( 0, fhirQuestionnaire.getItem().get( 0 ).getAnswerOption().size() ); Assert.assertFalse( fhirQuestionnaire.getItem().get( 0 ).getRequired() ); + Assert.assertEquals( 1, fhirQuestionnaire.getItem().get( 0 ).getExtension().size() ); + Assert.assertEquals( ValueTypeExtensionUtils.URL, fhirQuestionnaire.getItem().get( 0 ).getExtension().get( 0 ).getUrl() ); + Assert.assertTrue( fhirQuestionnaire.getItem().get( 0 ).getExtension().get( 0 ).getValue() instanceof StringType ); + Assert.assertEquals( "TEXT", ( (StringType) fhirQuestionnaire.getItem().get( 0 ).getExtension().get( 0 ).getValue() ).getValue() ); + Assert.assertEquals( "d1123456789", fhirQuestionnaire.getItem().get( 1 ).getLinkId() ); Assert.assertEquals( "Value 2", fhirQuestionnaire.getItem().get( 1 ).getText() ); Assert.assertEquals( 2, fhirQuestionnaire.getItem().get( 1 ).getAnswerOption().size() ); @@ -166,5 +173,10 @@ public void transformInternal() Assert.assertEquals( "7", ( (Coding) fhirQuestionnaire.getItem().get( 1 ).getAnswerOption().get( 1 ).getValue() ).getCode() ); Assert.assertEquals( "Test Value 2", ( (Coding) fhirQuestionnaire.getItem().get( 1 ).getAnswerOption().get( 1 ).getValue() ).getDisplay() ); Assert.assertTrue( fhirQuestionnaire.getItem().get( 1 ).getRequired() ); + + Assert.assertEquals( 1, fhirQuestionnaire.getItem().get( 1 ).getExtension().size() ); + Assert.assertEquals( ValueTypeExtensionUtils.URL, fhirQuestionnaire.getItem().get( 1 ).getExtension().get( 0 ).getUrl() ); + Assert.assertTrue( fhirQuestionnaire.getItem().get( 1 ).getExtension().get( 0 ).getValue() instanceof StringType ); + Assert.assertEquals( "INTEGER", ( (StringType) fhirQuestionnaire.getItem().get( 1 ).getExtension().get( 0 ).getValue() ).getValue() ); } } diff --git a/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/extension/BaseExtensionUtils.java b/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/extension/BaseExtensionUtils.java new file mode 100644 index 00000000..6811ec79 --- /dev/null +++ b/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/extension/BaseExtensionUtils.java @@ -0,0 +1,65 @@ +package org.dhis2.fhir.adapter.fhir.extension; + +/* + * Copyright (c) 2004-2019, University of Oslo + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * Neither the name of the HISP project nor the names of its contributors may + * be used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +import ca.uhn.fhir.model.api.IElement; +import org.hl7.fhir.instance.model.api.IBaseExtension; +import org.hl7.fhir.instance.model.api.IBaseHasExtensions; +import org.hl7.fhir.instance.model.api.IPrimitiveType; + +import javax.annotation.Nonnull; +import javax.annotation.Nullable; +import java.util.function.Function; + +/** + * Utility class to process FHIR resource extensions. + * + * @author volsch + */ +abstract class BaseExtensionUtils +{ + @SuppressWarnings( "unchecked" ) + protected static void setStringValue( @Nonnull String url, @Nonnull IBaseHasExtensions resource, @Nullable String value, @Nonnull Function typeFactory ) + { + resource.getExtension().removeIf( e -> url.equals( e.getUrl() ) ); + + if ( value != null ) + { + final IBaseExtension extension = resource.addExtension(); + + extension.setUrl( url ); + extension.setValue( ( (IPrimitiveType) typeFactory.apply( "string" ) ).setValue( value ) ); + } + } + + private BaseExtensionUtils() + { + super(); + } +} diff --git a/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/extension/ResourceTypeExtensionUtils.java b/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/extension/ResourceTypeExtensionUtils.java index 595cc28f..6a316e7d 100644 --- a/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/extension/ResourceTypeExtensionUtils.java +++ b/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/extension/ResourceTypeExtensionUtils.java @@ -30,9 +30,7 @@ import ca.uhn.fhir.model.api.IElement; import org.dhis2.fhir.adapter.fhir.metadata.model.FhirResourceType; -import org.hl7.fhir.instance.model.api.IBaseExtension; import org.hl7.fhir.instance.model.api.IBaseHasExtensions; -import org.hl7.fhir.instance.model.api.IPrimitiveType; import javax.annotation.Nonnull; import javax.annotation.Nullable; @@ -47,18 +45,9 @@ public abstract class ResourceTypeExtensionUtils { public static final String URL = "http://www.dhis2.org/dhis2-fhir-adapter/fhir/extensions/resource-type"; - @SuppressWarnings( "unchecked" ) public static void setValue( @Nonnull IBaseHasExtensions resource, @Nullable FhirResourceType fhirResourceType, @Nonnull Function typeFactory ) { - resource.getExtension().removeIf( e -> URL.equals( e.getUrl() ) ); - - if ( fhirResourceType != null ) - { - final IBaseExtension extension = resource.addExtension(); - - extension.setUrl( URL ); - extension.setValue( ( (IPrimitiveType) typeFactory.apply( "string" ) ).setValue( fhirResourceType.getResourceTypeName() ) ); - } + BaseExtensionUtils.setStringValue( URL, resource, fhirResourceType == null ? null : fhirResourceType.getResourceTypeName(), typeFactory ); } private ResourceTypeExtensionUtils() diff --git a/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/extension/ValueTypeExtensionUtils.java b/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/extension/ValueTypeExtensionUtils.java new file mode 100644 index 00000000..127b9c7c --- /dev/null +++ b/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/extension/ValueTypeExtensionUtils.java @@ -0,0 +1,56 @@ +package org.dhis2.fhir.adapter.fhir.extension; + +/* + * Copyright (c) 2004-2019, University of Oslo + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * Neither the name of the HISP project nor the names of its contributors may + * be used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +import ca.uhn.fhir.model.api.IElement; +import org.dhis2.fhir.adapter.model.ValueType; +import org.hl7.fhir.instance.model.api.IBaseHasExtensions; + +import javax.annotation.Nonnull; +import java.util.function.Function; + +/** + * Utility class to process FHIR value type extension. + * + * @author volsch + */ +public abstract class ValueTypeExtensionUtils +{ + public static final String URL = "http://www.dhis2.org/dhis2-fhir-adapter/fhir/extensions/value-type"; + + public static void setValue( @Nonnull IBaseHasExtensions resource, @Nonnull ValueType valueType, @Nonnull Function typeFactory ) + { + BaseExtensionUtils.setStringValue( URL, resource, valueType.name(), typeFactory ); + } + + private ValueTypeExtensionUtils() + { + super(); + } +} diff --git a/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/metadata/program/AbstractProgramStageMetadataToFhirQuestionnaireTransformer.java b/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/metadata/program/AbstractProgramStageMetadataToFhirQuestionnaireTransformer.java index 0bc8ba45..2252165a 100644 --- a/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/metadata/program/AbstractProgramStageMetadataToFhirQuestionnaireTransformer.java +++ b/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/metadata/program/AbstractProgramStageMetadataToFhirQuestionnaireTransformer.java @@ -28,6 +28,7 @@ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ +import ca.uhn.fhir.model.api.IElement; import org.dhis2.fhir.adapter.dhis.model.DhisResourceType; import org.dhis2.fhir.adapter.dhis.orgunit.OrganizationUnitService; import org.dhis2.fhir.adapter.fhir.data.repository.FhirDhisAssignmentRepository; @@ -45,6 +46,7 @@ import org.slf4j.LoggerFactory; import javax.annotation.Nonnull; +import java.util.function.Function; /** * Implementation of {@link DhisToFhirTransformer} for transforming DHIS2 @@ -90,4 +92,7 @@ public Class getRuleClass() { return ProgramStageMetadataRule.class; } + + @Nonnull + protected abstract Function getTypeFactory(); } diff --git a/fhir/src/test/java/org/dhis2/fhir/adapter/fhir/extension/BaseExtensionUtilsTest.java b/fhir/src/test/java/org/dhis2/fhir/adapter/fhir/extension/BaseExtensionUtilsTest.java new file mode 100644 index 00000000..b6c8758a --- /dev/null +++ b/fhir/src/test/java/org/dhis2/fhir/adapter/fhir/extension/BaseExtensionUtilsTest.java @@ -0,0 +1,64 @@ +package org.dhis2.fhir.adapter.fhir.extension; + +/* + * Copyright (c) 2004-2019, University of Oslo + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * Neither the name of the HISP project nor the names of its contributors may + * be used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +import org.junit.Assert; +import org.junit.Test; + +/** + * Unit tests for {@link BaseExtensionUtils}. + * + * @author volsch + */ +public class BaseExtensionUtilsTest +{ + @Test + public void resetValue() + { + TestPlanDefinition planDefinition = new TestPlanDefinition(); + + BaseExtensionUtils.setStringValue( "testUrl", planDefinition, "testValue", TypeFactory::createType ); + BaseExtensionUtils.setStringValue( "testUrl", planDefinition, null, TypeFactory::createType ); + + Assert.assertTrue( planDefinition.getExtension().isEmpty() ); + } + + @Test + public void setValue() + { + TestPlanDefinition planDefinition = new TestPlanDefinition(); + + BaseExtensionUtils.setStringValue( "testUrl", planDefinition, "testValue", TypeFactory::createType ); + + Assert.assertEquals( 1, planDefinition.getExtension().size() ); + Assert.assertEquals( "testUrl", planDefinition.getExtension().get( 0 ).getUrl() ); + Assert.assertEquals( "testValue", planDefinition.getExtension().get( 0 ).getValue().toString() ); + } + +} \ No newline at end of file diff --git a/fhir/src/test/java/org/dhis2/fhir/adapter/fhir/extension/ResourceTypeExtensionUtilsTest.java b/fhir/src/test/java/org/dhis2/fhir/adapter/fhir/extension/ResourceTypeExtensionUtilsTest.java index d245ec1d..b45787a9 100644 --- a/fhir/src/test/java/org/dhis2/fhir/adapter/fhir/extension/ResourceTypeExtensionUtilsTest.java +++ b/fhir/src/test/java/org/dhis2/fhir/adapter/fhir/extension/ResourceTypeExtensionUtilsTest.java @@ -28,18 +28,10 @@ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -import ca.uhn.fhir.model.api.ExtensionDt; -import ca.uhn.fhir.model.api.IElement; -import ca.uhn.fhir.model.primitive.StringDt; import org.dhis2.fhir.adapter.fhir.metadata.model.FhirResourceType; -import org.hl7.fhir.instance.model.api.IBaseExtension; -import org.hl7.fhir.instance.model.api.IBaseHasExtensions; import org.junit.Assert; import org.junit.Test; -import java.util.ArrayList; -import java.util.List; - /** * Unit tests for {@link ResourceTypeExtensionUtils}. * @@ -50,7 +42,7 @@ public class ResourceTypeExtensionUtilsTest @Test public void resetValue() { - PlanDefinition planDefinition = new PlanDefinition(); + TestPlanDefinition planDefinition = new TestPlanDefinition(); ResourceTypeExtensionUtils.setValue( planDefinition, FhirResourceType.PATIENT, TypeFactory::createType ); ResourceTypeExtensionUtils.setValue( planDefinition, null, TypeFactory::createType ); @@ -61,7 +53,7 @@ public void resetValue() @Test public void setValue() { - PlanDefinition planDefinition = new PlanDefinition(); + TestPlanDefinition planDefinition = new TestPlanDefinition(); ResourceTypeExtensionUtils.setValue( planDefinition, FhirResourceType.PATIENT, TypeFactory::createType ); @@ -69,39 +61,4 @@ public void setValue() Assert.assertEquals( ResourceTypeExtensionUtils.URL, planDefinition.getExtension().get( 0 ).getUrl() ); Assert.assertEquals( "Patient", planDefinition.getExtension().get( 0 ).getValue().toString() ); } - - public static class PlanDefinition implements IBaseHasExtensions - { - private final List> extensions = new ArrayList<>(); - - @Override - public IBaseExtension addExtension() - { - extensions.add( new ExtensionDt() ); - - return extensions.get( extensions.size() - 1 ); - } - - @Override - public List> getExtension() - { - return extensions; - } - - @Override - public boolean hasExtension() - { - return !extensions.isEmpty(); - } - } - - public static class TypeFactory - { - public static IElement createType( String name ) - { - Assert.assertEquals( "string", name ); - - return new StringDt(); - } - } } \ No newline at end of file diff --git a/fhir/src/test/java/org/dhis2/fhir/adapter/fhir/extension/TestPlanDefinition.java b/fhir/src/test/java/org/dhis2/fhir/adapter/fhir/extension/TestPlanDefinition.java new file mode 100644 index 00000000..b2f01a17 --- /dev/null +++ b/fhir/src/test/java/org/dhis2/fhir/adapter/fhir/extension/TestPlanDefinition.java @@ -0,0 +1,66 @@ +package org.dhis2.fhir.adapter.fhir.extension; + +/* + * Copyright (c) 2004-2019, University of Oslo + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * Neither the name of the HISP project nor the names of its contributors may + * be used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +import ca.uhn.fhir.model.api.ExtensionDt; +import org.hl7.fhir.instance.model.api.IBaseExtension; +import org.hl7.fhir.instance.model.api.IBaseHasExtensions; + +import java.util.ArrayList; +import java.util.List; + +/** + * Test plan definition FHIR resource. + * + * @author volsch + */ +class TestPlanDefinition implements IBaseHasExtensions +{ + private final List> extensions = new ArrayList<>(); + + @Override + public IBaseExtension addExtension() + { + extensions.add( new ExtensionDt() ); + + return extensions.get( extensions.size() - 1 ); + } + + @Override + public List> getExtension() + { + return extensions; + } + + @Override + public boolean hasExtension() + { + return !extensions.isEmpty(); + } +} diff --git a/fhir/src/test/java/org/dhis2/fhir/adapter/fhir/extension/TypeFactory.java b/fhir/src/test/java/org/dhis2/fhir/adapter/fhir/extension/TypeFactory.java new file mode 100644 index 00000000..32329ee4 --- /dev/null +++ b/fhir/src/test/java/org/dhis2/fhir/adapter/fhir/extension/TypeFactory.java @@ -0,0 +1,48 @@ +package org.dhis2.fhir.adapter.fhir.extension; + +/* + * Copyright (c) 2004-2019, University of Oslo + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * Neither the name of the HISP project nor the names of its contributors may + * be used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +import ca.uhn.fhir.model.api.IElement; +import ca.uhn.fhir.model.primitive.StringDt; +import org.junit.Assert; + +/** + * Test type factory. + * + * @author volsch + */ +class TypeFactory +{ + public static IElement createType( String name ) + { + Assert.assertEquals( "string", name ); + + return new StringDt(); + } +} diff --git a/fhir/src/test/java/org/dhis2/fhir/adapter/fhir/extension/ValueTypeExtensionUtilsTest.java b/fhir/src/test/java/org/dhis2/fhir/adapter/fhir/extension/ValueTypeExtensionUtilsTest.java new file mode 100644 index 00000000..6593491a --- /dev/null +++ b/fhir/src/test/java/org/dhis2/fhir/adapter/fhir/extension/ValueTypeExtensionUtilsTest.java @@ -0,0 +1,53 @@ +package org.dhis2.fhir.adapter.fhir.extension; + +/* + * Copyright (c) 2004-2019, University of Oslo + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * Neither the name of the HISP project nor the names of its contributors may + * be used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +import org.dhis2.fhir.adapter.model.ValueType; +import org.junit.Assert; +import org.junit.Test; + +/** + * Unit tests for {@link ValueTypeExtensionUtils}. + * + * @author volsch + */ +public class ValueTypeExtensionUtilsTest +{ + @Test + public void setValue() + { + TestPlanDefinition planDefinition = new TestPlanDefinition(); + + ValueTypeExtensionUtils.setValue( planDefinition, ValueType.INTEGER_POSITIVE, TypeFactory::createType ); + + Assert.assertEquals( 1, planDefinition.getExtension().size() ); + Assert.assertEquals( ValueTypeExtensionUtils.URL, planDefinition.getExtension().get( 0 ).getUrl() ); + Assert.assertEquals( "INTEGER_POSITIVE", planDefinition.getExtension().get( 0 ).getValue().toString() ); + } +} \ No newline at end of file From 08c5732f186e3b549c7c76812b9f57e4f56ffb38 Mon Sep 17 00:00:00 2001 From: Volker Schmidt Date: Thu, 25 Jul 2019 12:45:04 +0200 Subject: [PATCH 09/16] Adds tests for transformation of DHIS2 Program to FHIR Plan Definition. --- .../AbstractProgramStageFhirRestAppTest.java | 5 +- .../r4/R4ProgramMetadataFhirRestAppTest.java | 335 ++++++++++++++++++ .../adapter/dhis/model/DhisResourceType.java | 25 +- ...adataToFhirPlanDefinitionTransformer.java} | 6 +- .../fhir/metadata/model/AbstractRule.java | 5 +- .../repository/CustomRuleRepository.java | 2 +- .../impl/CustomRuleRepositoryImpl.java | 7 +- ...ractDhisMetadataToFhirRequestResolver.java | 2 +- ...adataToFhirPlanDefinitionTransformer.java} | 4 +- 9 files changed, 366 insertions(+), 25 deletions(-) create mode 100644 app/src/test/java/org/dhis2/fhir/adapter/fhir/server/r4/R4ProgramMetadataFhirRestAppTest.java rename fhir-r4/src/main/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/metadata/r4/{R4ProgramMetadataToFhirCarePlanTransformer.java => R4ProgramMetadataToFhirPlanDefinitionTransformer.java} (92%) rename fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/metadata/program/{AbstractProgramMetadataToFhirCarePlanTransformer.java => AbstractProgramMetadataToFhirPlanDefinitionTransformer.java} (92%) diff --git a/app/src/test/java/org/dhis2/fhir/adapter/fhir/server/AbstractProgramStageFhirRestAppTest.java b/app/src/test/java/org/dhis2/fhir/adapter/fhir/server/AbstractProgramStageFhirRestAppTest.java index fba6ac0b..8962a7e8 100644 --- a/app/src/test/java/org/dhis2/fhir/adapter/fhir/server/AbstractProgramStageFhirRestAppTest.java +++ b/app/src/test/java/org/dhis2/fhir/adapter/fhir/server/AbstractProgramStageFhirRestAppTest.java @@ -74,9 +74,8 @@ protected void expectProgramStageMetadataRequests() throws Exception systemDhis2Server.expect( ExpectedCount.between( 0, 1 ), method( HttpMethod.GET ) ).andExpect( header( "Authorization", testConfiguration.getDhis2SystemAuthorization() ) ) .andExpect( requestTo( dhis2BaseUrl + "/api/" + dhis2ApiVersion + "/programs.json?paging=false&fields=id,name,code,description,selectIncidentDatesInFuture,selectEnrollmentDatesInFuture,displayIncidentDate,registration," + "withoutRegistration,captureCoordinates,trackedEntityType%5Bid%5D,programTrackedEntityAttributes%5Bid,name,valueType,mandatory,allowFutureDate,trackedEntityAttribute%5Bid,name,code,valueType,generated%5D%5D,programStages%5Bid,name," + - "description," + - "repeatable,captureCoordinates,generatedByEnrollmentDate,minDaysFromStart,programStageDataElements%5Bid,compulsory,allowProvidedElsewhere,dataElement%5Bid,name,code,formName,valueType,optionSetValue,optionSet%5Bid,name,options%5Bcode," + - "name%5D%5D%5D%5D%5D&filter=name:eq:Child%20Programme" ) ) + "description,repeatable,captureCoordinates,generatedByEnrollmentDate,minDaysFromStart,programStageDataElements%5Bid,compulsory,allowProvidedElsewhere,dataElement%5Bid,name,code,formName,valueType,optionSetValue,optionSet%5Bid,name," + + "options%5Bcode,name%5D%5D%5D%5D%5D&filter=name:eq:Child%20Programme" ) ) .andRespond( withSuccess( IOUtils.resourceToString( "/org/dhis2/fhir/adapter/dhis/test/default-program.json", StandardCharsets.UTF_8 ), MediaType.APPLICATION_JSON ) ); } } diff --git a/app/src/test/java/org/dhis2/fhir/adapter/fhir/server/r4/R4ProgramMetadataFhirRestAppTest.java b/app/src/test/java/org/dhis2/fhir/adapter/fhir/server/r4/R4ProgramMetadataFhirRestAppTest.java new file mode 100644 index 00000000..e7d1c616 --- /dev/null +++ b/app/src/test/java/org/dhis2/fhir/adapter/fhir/server/r4/R4ProgramMetadataFhirRestAppTest.java @@ -0,0 +1,335 @@ +package org.dhis2.fhir.adapter.fhir.server.r4; + +/* + * Copyright (c) 2004-2019, University of Oslo + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * Neither the name of the HISP project nor the names of its contributors may + * be used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +import ca.uhn.fhir.rest.client.api.IGenericClient; +import ca.uhn.fhir.rest.client.interceptor.BasicAuthInterceptor; +import ca.uhn.fhir.rest.server.exceptions.AuthenticationException; +import ca.uhn.fhir.rest.server.exceptions.ResourceNotFoundException; +import org.apache.commons.io.IOUtils; +import org.dhis2.fhir.adapter.AbstractAppTest; +import org.dhis2.fhir.adapter.fhir.model.FhirVersion; +import org.hl7.fhir.r4.model.Bundle; +import org.hl7.fhir.r4.model.PlanDefinition; +import org.junit.Assert; +import org.junit.Ignore; +import org.junit.Test; +import org.springframework.http.HttpMethod; +import org.springframework.http.HttpStatus; +import org.springframework.http.MediaType; +import org.springframework.test.web.client.ExpectedCount; + +import javax.annotation.Nonnull; +import java.nio.charset.StandardCharsets; + +import static org.springframework.test.web.client.match.MockRestRequestMatchers.*; +import static org.springframework.test.web.client.response.MockRestResponseCreators.withStatus; +import static org.springframework.test.web.client.response.MockRestResponseCreators.withSuccess; + +/** + * R4 tests for rest interfaces that access + * DHIS2 program metadata. + * + * @author volsch + */ +@Ignore +public class R4ProgramMetadataFhirRestAppTest extends AbstractAppTest +{ + @Nonnull + @Override + protected FhirVersion getFhirVersion() + { + return FhirVersion.R4; + } + + @Test( expected = AuthenticationException.class ) + public void getPlanDefinitionWithoutAuthorization() + { + final IGenericClient client = createGenericClient(); + client.read().resource( PlanDefinition.class ).withId( "ldXIdLNUNEn" ).execute(); + } + + @Test( expected = AuthenticationException.class ) + public void getPlanDefinitionWithInvalidAuthorization() + { + userDhis2Server.expect( ExpectedCount.once(), method( HttpMethod.GET ) ).andExpect( header( "Authorization", "Basic Zmhpcl9jbGllbnQ6aW52YWxpZF8x" ) ) + .andExpect( requestTo( dhis2BaseUrl + "/api/" + dhis2ApiVersion + "/programs/EPDyQuoRnXk.json?" + + "fields=id,name,code,description,selectIncidentDatesInFuture,selectEnrollmentDatesInFuture,displayIncidentDate,registration,withoutRegistration,captureCoordinates,trackedEntityType%5Bid%5D,programTrackedEntityAttributes%5Bid,name,valueType," + + "mandatory,allowFutureDate,trackedEntityAttribute%5Bid,name,code,valueType,generated%5D%5D,programStages%5Bid,name,description,repeatable,captureCoordinates,generatedByEnrollmentDate,minDaysFromStart,programStageDataElements%5Bid,compulsory," + + "allowProvidedElsewhere,dataElement%5Bid,name,code,formName,valueType,optionSetValue,optionSet%5Bid,name,options%5Bcode,name%5D%5D%5D%5D%5D" ) ) + .andRespond( withStatus( HttpStatus.UNAUTHORIZED ) ); + + final IGenericClient client = createGenericClient(); + client.registerInterceptor( new BasicAuthInterceptor( "fhir_client", "invalid_1" ) ); + client.read().resource( PlanDefinition.class ).withId( "EPDyQuoRnXk" ).execute(); + } + + @Test + public void getPlanDefinitionRepeated() throws Exception + { + getPlanDefinition(); + getPlanDefinition(); + } + + private void getPlanDefinition() throws Exception + { + systemDhis2Server.reset(); + userDhis2Server.reset(); + + systemDhis2Server.expect( ExpectedCount.between( 0, 1 ), method( HttpMethod.GET ) ).andExpect( header( "Authorization", testConfiguration.getDhis2SystemAuthorization() ) ) + .andExpect( requestTo( dhis2BaseUrl + "/api/" + dhis2ApiVersion + "/trackedEntityTypes/MCPQUTHX1Ze.json?" + + "fields=id,name,trackedEntityTypeAttributes%5Bid,name,valueType,mandatory,trackedEntityAttribute%5Bid,name,code,valueType,generated,optionSetValue,optionSet%5Bid,name,options%5Bcode,name%5D%5D%5D%5D" ) ) + .andRespond( withSuccess( IOUtils.resourceToString( "/org/dhis2/fhir/adapter/dhis/test/single-tracked-entity-type.json", StandardCharsets.UTF_8 ), MediaType.APPLICATION_JSON ) ); + userDhis2Server.expect( ExpectedCount.once(), method( HttpMethod.GET ) ).andExpect( header( "Authorization", "Basic Zmhpcl9jbGllbnQ6Zmhpcl9jbGllbnRfMQ==" ) ) + .andExpect( requestTo( dhis2BaseUrl + "/api/" + dhis2ApiVersion + "/programs/EPDyQuoRnXk.json?fields=id,name,code,description,selectIncidentDatesInFuture,selectEnrollmentDatesInFuture,displayIncidentDate,registration," + + "withoutRegistration,captureCoordinates,trackedEntityType%5Bid%5D,programTrackedEntityAttributes%5Bid,name,valueType,mandatory,allowFutureDate,trackedEntityAttribute%5Bid,name,code,valueType,generated%5D%5D,programStages%5Bid,name," + + "description,repeatable,captureCoordinates,generatedByEnrollmentDate,minDaysFromStart,programStageDataElements%5Bid,compulsory,allowProvidedElsewhere,dataElement%5Bid,name,code,formName,valueType,optionSetValue,optionSet%5Bid,name," + + "options%5Bcode,name%5D%5D%5D%5D%5D" ) ) + .andRespond( withSuccess( IOUtils.resourceToString( "/org/dhis2/fhir/adapter/dhis/test/single-program.json", StandardCharsets.UTF_8 ), MediaType.APPLICATION_JSON ) ); + userDhis2Server.expect( ExpectedCount.between( 0, 1 ), method( HttpMethod.GET ) ).andExpect( header( "Authorization", "Basic Zmhpcl9jbGllbnQ6Zmhpcl9jbGllbnRfMQ==" ) ) + .andExpect( requestTo( dhis2BaseUrl + "/api/" + dhis2ApiVersion + "/programs.json?paging=false&fields=id,name,code,description,selectIncidentDatesInFuture,selectEnrollmentDatesInFuture,displayIncidentDate,registration," + + "withoutRegistration,captureCoordinates,trackedEntityType%5Bid%5D,programTrackedEntityAttributes%5Bid,name,valueType,mandatory,allowFutureDate,trackedEntityAttribute%5Bid,name,code,valueType,generated%5D%5D,programStages%5Bid,name," + + "description,repeatable,captureCoordinates,generatedByEnrollmentDate,minDaysFromStart,programStageDataElements%5Bid,compulsory,allowProvidedElsewhere,dataElement%5Bid,name,code,formName,valueType,optionSetValue,optionSet%5Bid,name," + + "options%5Bcode,name%5D%5D%5D%5D%5D" ) ) + .andRespond( withSuccess( IOUtils.resourceToString( "/org/dhis2/fhir/adapter/dhis/test/all-programs.json", StandardCharsets.UTF_8 ), MediaType.APPLICATION_JSON ) ); + + final IGenericClient client = createGenericClient(); + client.registerInterceptor( new BasicAuthInterceptor( "fhir_client", "fhir_client_1" ) ); + PlanDefinition planDefinition = client.read().resource( PlanDefinition.class ).withId( "EPDyQuoRnXk" ).execute(); + Assert.assertEquals( "Child Programme", planDefinition.getName() ); + + systemDhis2Server.verify(); + userDhis2Server.verify(); + } + + @Test( expected = ResourceNotFoundException.class ) + public void getPlanDefinitionNotExistsRepeated() + { + try + { + getPlanDefinitionNotExists(); + Assert.fail( "Exception expected also an first invocation." ); + } + catch ( ResourceNotFoundException e ) + { + getPlanDefinitionNotExists(); + } + } + + private void getPlanDefinitionNotExists() + { + systemDhis2Server.reset(); + userDhis2Server.reset(); + + userDhis2Server.expect( ExpectedCount.once(), method( HttpMethod.GET ) ).andExpect( header( "Authorization", "Basic Zmhpcl9jbGllbnQ6Zmhpcl9jbGllbnRfMQ==" ) ) + .andExpect( requestTo( dhis2BaseUrl + "/api/" + dhis2ApiVersion + "/programs/0dXIdLNUNEn.json?fields=id,name,code,description,selectIncidentDatesInFuture,selectEnrollmentDatesInFuture,displayIncidentDate,registration," + + "withoutRegistration,captureCoordinates,trackedEntityType%5Bid%5D,programTrackedEntityAttributes%5Bid,name,valueType,mandatory,allowFutureDate,trackedEntityAttribute%5Bid,name,code,valueType,generated%5D%5D,programStages%5Bid,name," + + "description,repeatable,captureCoordinates,generatedByEnrollmentDate,minDaysFromStart,programStageDataElements%5Bid,compulsory,allowProvidedElsewhere,dataElement%5Bid,name,code,formName,valueType,optionSetValue,optionSet%5Bid,name," + + "options%5Bcode,name%5D%5D%5D%5D%5D" ) ) + .andRespond( withStatus( HttpStatus.NOT_FOUND ).contentType( MediaType.APPLICATION_JSON ).body( "{}" ) ); + + try + { + final IGenericClient client = createGenericClient(); + client.registerInterceptor( new BasicAuthInterceptor( "fhir_client", "fhir_client_1" ) ); + client.read().resource( PlanDefinition.class ).withId( "0dXIdLNUNEn" ).execute(); + } + catch ( ResourceNotFoundException e ) + { + systemDhis2Server.verify(); + userDhis2Server.verify(); + throw e; + } + } + + @Test( expected = ResourceNotFoundException.class ) + public void getPlanDefinitionRuleNotFoundRepeated() + { + try + { + getPlanDefinitionNotExists(); + Assert.fail( "Exception expected also an first invocation." ); + } + catch ( ResourceNotFoundException e ) + { + getPlanDefinitionNotExists(); + } + } + + private void getPlanDefinitionRuleNotFound() + { + systemDhis2Server.reset(); + userDhis2Server.reset(); + + try + { + final IGenericClient client = createGenericClient(); + client.registerInterceptor( new BasicAuthInterceptor( "fhir_client", "fhir_client_1" ) ); + client.read().resource( PlanDefinition.class ).withId( "ldXIdLNUNEn" ).execute(); + } + catch ( ResourceNotFoundException e ) + { + systemDhis2Server.verify(); + userDhis2Server.verify(); + throw e; + } + } + + @Test( expected = AuthenticationException.class ) + public void getPlanDefinitionByIdentifierWithoutAuthorization() + { + final IGenericClient client = createGenericClient(); + client.search().forResource( PlanDefinition.class ).where( PlanDefinition.IDENTIFIER.exactly().systemAndIdentifier( "http://www.dhis2.org/dhis2fhiradapter/systems/plan-definition-identifier", "XX_1234" ) ).returnBundle( Bundle.class ).execute(); + } + + @Test( expected = AuthenticationException.class ) + public void getPlanDefinitionByIdentifierInvalidAuthorization() + { + userDhis2Server.expect( ExpectedCount.once(), method( HttpMethod.GET ) ).andExpect( header( "Authorization", "Basic Zmhpcl9jbGllbnQ6aW52YWxpZF8x" ) ) + .andExpect( requestTo( dhis2BaseUrl + "/api/" + dhis2ApiVersion + "/organisationUnits.json?paging=false&fields=lastUpdated,id,code,name,shortName,displayName,level,openingDate,closedDate,coordinates,leaf,parent%5Bid%5D&filter=code:eq:OU_1234" ) ) + .andRespond( withStatus( HttpStatus.UNAUTHORIZED ) ); + + final IGenericClient client = createGenericClient(); + client.registerInterceptor( new BasicAuthInterceptor( "fhir_client", "invalid_1" ) ); + client.search().forResource( PlanDefinition.class ).where( PlanDefinition.IDENTIFIER.exactly().systemAndIdentifier( "http://www.dhis2.org/dhis2fhiradapter/systems/planDefinition-identifier", "OU_1234" ) ).returnBundle( Bundle.class ).execute(); + } + + @Test + public void getPlanDefinitionByIdentifierRepeated() throws Exception + { + getPlanDefinitionByIdentifier(); + getPlanDefinitionByIdentifier(); + } + + private void getPlanDefinitionByIdentifier() throws Exception + { + systemDhis2Server.reset(); + userDhis2Server.reset(); + + userDhis2Server.expect( ExpectedCount.between( 1, 2 ), method( HttpMethod.GET ) ).andExpect( header( "Authorization", "Basic Zmhpcl9jbGllbnQ6Zmhpcl9jbGllbnRfMQ==" ) ) + .andExpect( requestTo( dhis2BaseUrl + "/api/" + dhis2ApiVersion + "/organisationUnits.json?paging=false&fields=lastUpdated,id,code,name,shortName,displayName,level,openingDate,closedDate,coordinates,leaf,parent%5Bid%5D&filter=code:eq:OU_1234" ) ) + .andRespond( withSuccess( IOUtils.resourceToString( "/org/dhis2/fhir/adapter/dhis/test/default-org-unit-OU_1234.json", StandardCharsets.UTF_8 ), MediaType.APPLICATION_JSON ) ); + + final IGenericClient client = createGenericClient(); + client.registerInterceptor( new BasicAuthInterceptor( "fhir_client", "fhir_client_1" ) ); + Bundle bundle = + client.search().forResource( PlanDefinition.class ).where( PlanDefinition.IDENTIFIER.exactly().systemAndIdentifier( "http://www.dhis2.org/dhis2fhiradapter/systems/planDefinition-identifier", "OU_1234" ) ).returnBundle( Bundle.class ).execute(); + Assert.assertEquals( 1, bundle.getEntry().size() ); + PlanDefinition planDefinition = (PlanDefinition) bundle.getEntry().get( 0 ).getResource(); + Assert.assertEquals( "Test Hospital", planDefinition.getName() ); + Assert.assertEquals( 1, planDefinition.getIdentifier().size() ); + Assert.assertEquals( "http://www.dhis2.org/dhis2fhiradapter/systems/planDefinition-identifier", planDefinition.getIdentifier().get( 0 ).getSystem() ); + Assert.assertEquals( "OU_1234", planDefinition.getIdentifier().get( 0 ).getValue() ); + + systemDhis2Server.verify(); + userDhis2Server.verify(); + } + + @Test( expected = AuthenticationException.class ) + public void searchPlanDefinitionWithoutAuthorization() + { + final IGenericClient client = createGenericClient(); + client.search().forResource( PlanDefinition.class ).where( PlanDefinition.NAME.matches().value( "Test" ) ).returnBundle( Bundle.class ).execute(); + } + + @Test( expected = AuthenticationException.class ) + public void searchPlanDefinitionInvalidAuthorization() + { + userDhis2Server.expect( ExpectedCount.once(), method( HttpMethod.GET ) ).andExpect( header( "Authorization", "Basic Zmhpcl9jbGllbnQ6aW52YWxpZF8x" ) ) + .andExpect( requestTo( dhis2BaseUrl + "/api/" + dhis2ApiVersion + "/programs.json?filter=name:$ilike:Test&paging=true&page=1&pageSize=10&order=id&fields=id,name,code,description,selectIncidentDatesInFuture,selectEnrollmentDatesInFuture," + + "displayIncidentDate,registration,withoutRegistration,captureCoordinates,trackedEntityType%5Bid%5D,programTrackedEntityAttributes%5Bid,name,valueType,mandatory,allowFutureDate,trackedEntityAttribute%5Bid,name,code,valueType,generated%5D%5D," + + "programStages%5Bid,name,description,repeatable,captureCoordinates,generatedByEnrollmentDate,minDaysFromStart,programStageDataElements%5Bid,compulsory,allowProvidedElsewhere,dataElement%5Bid,name,code,formName,valueType,optionSetValue," + + "optionSet%5Bid,name,options%5Bcode,name%5D%5D%5D%5D%5D" ) ) + .andRespond( withStatus( HttpStatus.UNAUTHORIZED ) ); + + final IGenericClient client = createGenericClient(); + client.registerInterceptor( new BasicAuthInterceptor( "fhir_client", "invalid_1" ) ); + client.search().forResource( PlanDefinition.class ).where( PlanDefinition.NAME.matches().value( "Test" ) ).returnBundle( Bundle.class ).execute(); + } + + @Test + public void searchPlanDefinition() throws Exception + { + userDhis2Server.expect( ExpectedCount.once(), method( HttpMethod.GET ) ).andExpect( header( "Authorization", "Basic Zmhpcl9jbGllbnQ6Zmhpcl9jbGllbnRfMQ==" ) ) + .andExpect( requestTo( dhis2BaseUrl + "/api/" + dhis2ApiVersion + "/organisationUnits.json?filter=name:$ilike:Test&paging=true&page=1&pageSize=10&order=id" + + "&fields=lastUpdated,id,code,name,shortName,displayName,level,openingDate,closedDate,coordinates,leaf,parent%5Bid%5D" ) ) + .andRespond( withSuccess( IOUtils.resourceToString( "/org/dhis2/fhir/adapter/dhis/test/multi-org-unit.json", StandardCharsets.UTF_8 ), MediaType.APPLICATION_JSON ) ); + userDhis2Server.expect( ExpectedCount.between( 0, 1 ), method( HttpMethod.GET ) ).andExpect( header( "Authorization", "Basic Zmhpcl9jbGllbnQ6Zmhpcl9jbGllbnRfMQ==" ) ) + .andExpect( requestTo( dhis2BaseUrl + "/api/" + dhis2ApiVersion + "/organisationUnits.json?paging=false&fields=lastUpdated,id,code,name,shortName,displayName,level,openingDate,closedDate,coordinates,leaf,parent%5Bid%5D&filter=code:eq:OU_1234" ) ) + .andRespond( withSuccess( IOUtils.resourceToString( "/org/dhis2/fhir/adapter/dhis/test/default-org-unit-OU_1234.json", StandardCharsets.UTF_8 ), MediaType.APPLICATION_JSON ) ); + userDhis2Server.expect( ExpectedCount.between( 0, 1 ), method( HttpMethod.GET ) ).andExpect( header( "Authorization", "Basic Zmhpcl9jbGllbnQ6Zmhpcl9jbGllbnRfMQ==" ) ) + .andExpect( requestTo( dhis2BaseUrl + "/api/" + dhis2ApiVersion + "/organisationUnits.json?paging=false&fields=lastUpdated,id,code,name,shortName,displayName,level,openingDate,closedDate,coordinates,leaf,parent%5Bid%5D&filter=code:eq:OU_4567" ) ) + .andRespond( withSuccess( IOUtils.resourceToString( "/org/dhis2/fhir/adapter/dhis/test/default-org-unit-OU_4567.json", StandardCharsets.UTF_8 ), MediaType.APPLICATION_JSON ) ); + + final IGenericClient client = createGenericClient(); + client.registerInterceptor( new BasicAuthInterceptor( "fhir_client", "fhir_client_1" ) ); + Bundle bundle = client.search().forResource( PlanDefinition.class ).where( PlanDefinition.NAME.matches().value( "Test" ) ).returnBundle( Bundle.class ).execute(); + Assert.assertEquals( 2, bundle.getEntry().size() ); + PlanDefinition planDefinition = (PlanDefinition) bundle.getEntry().get( 0 ).getResource(); + Assert.assertEquals( "Test Hospital", planDefinition.getName() ); + Assert.assertEquals( 1, planDefinition.getIdentifier().size() ); + Assert.assertEquals( "http://www.dhis2.org/dhis2fhiradapter/systems/planDefinition-identifier", planDefinition.getIdentifier().get( 0 ).getSystem() ); + Assert.assertEquals( "OU_1234", planDefinition.getIdentifier().get( 0 ).getValue() ); + planDefinition = (PlanDefinition) bundle.getEntry().get( 1 ).getResource(); + Assert.assertEquals( "Test Hospital 2", planDefinition.getName() ); + Assert.assertEquals( 1, planDefinition.getIdentifier().size() ); + Assert.assertEquals( "http://www.dhis2.org/dhis2fhiradapter/systems/planDefinition-identifier", planDefinition.getIdentifier().get( 0 ).getSystem() ); + Assert.assertEquals( "OU_4567", planDefinition.getIdentifier().get( 0 ).getValue() ); + } + + @Test + public void searchParentPlanDefinition() throws Exception + { + userDhis2Server.expect( ExpectedCount.once(), method( HttpMethod.GET ) ).andExpect( header( "Authorization", "Basic Zmhpcl9jbGllbnQ6Zmhpcl9jbGllbnRfMQ==" ) ) + .andExpect( requestTo( dhis2BaseUrl + "/api/" + dhis2ApiVersion + "/organisationUnits.json?filter=parent.id:eq:bdXIaLNUNEp&paging=true&page=1&pageSize=10&order=id" + + "&fields=lastUpdated,id,code,name,shortName,displayName,level,openingDate,closedDate,coordinates,leaf,parent%5Bid%5D" ) ) + .andRespond( withSuccess( IOUtils.resourceToString( "/org/dhis2/fhir/adapter/dhis/test/multi-org-unit.json", StandardCharsets.UTF_8 ), MediaType.APPLICATION_JSON ) ); + userDhis2Server.expect( ExpectedCount.between( 0, 1 ), method( HttpMethod.GET ) ).andExpect( header( "Authorization", "Basic Zmhpcl9jbGllbnQ6Zmhpcl9jbGllbnRfMQ==" ) ) + .andExpect( requestTo( dhis2BaseUrl + "/api/" + dhis2ApiVersion + "/organisationUnits.json?paging=false&fields=lastUpdated,id,code,name,shortName,displayName,level,openingDate,closedDate,coordinates,leaf,parent%5Bid%5D&filter=code:eq:OU_1234" ) ) + .andRespond( withSuccess( IOUtils.resourceToString( "/org/dhis2/fhir/adapter/dhis/test/default-org-unit-OU_1234.json", StandardCharsets.UTF_8 ), MediaType.APPLICATION_JSON ) ); + userDhis2Server.expect( ExpectedCount.between( 0, 1 ), method( HttpMethod.GET ) ).andExpect( header( "Authorization", "Basic Zmhpcl9jbGllbnQ6Zmhpcl9jbGllbnRfMQ==" ) ) + .andExpect( requestTo( dhis2BaseUrl + "/api/" + dhis2ApiVersion + "/organisationUnits.json?paging=false&fields=lastUpdated,id,code,name,shortName,displayName,level,openingDate,closedDate,coordinates,leaf,parent%5Bid%5D&filter=code:eq:OU_4567" ) ) + .andRespond( withSuccess( IOUtils.resourceToString( "/org/dhis2/fhir/adapter/dhis/test/default-org-unit-OU_4567.json", StandardCharsets.UTF_8 ), MediaType.APPLICATION_JSON ) ); + + final IGenericClient client = createGenericClient(); + client.registerInterceptor( new BasicAuthInterceptor( "fhir_client", "fhir_client_1" ) ); + Bundle bundle = client.search().forResource( PlanDefinition.class ).where( PlanDefinition.NAME.matchesExactly().value( "ChildProgram" ) ).returnBundle( Bundle.class ).execute(); + Assert.assertEquals( 2, bundle.getEntry().size() ); + PlanDefinition planDefinition = (PlanDefinition) bundle.getEntry().get( 0 ).getResource(); + Assert.assertEquals( "Test Hospital", planDefinition.getName() ); + Assert.assertEquals( 1, planDefinition.getIdentifier().size() ); + Assert.assertEquals( "http://www.dhis2.org/dhis2fhiradapter/systems/planDefinition-identifier", planDefinition.getIdentifier().get( 0 ).getSystem() ); + Assert.assertEquals( "OU_1234", planDefinition.getIdentifier().get( 0 ).getValue() ); + planDefinition = (PlanDefinition) bundle.getEntry().get( 1 ).getResource(); + Assert.assertEquals( "Test Hospital 2", planDefinition.getName() ); + Assert.assertEquals( 1, planDefinition.getIdentifier().size() ); + Assert.assertEquals( "http://www.dhis2.org/dhis2fhiradapter/systems/planDefinition-identifier", planDefinition.getIdentifier().get( 0 ).getSystem() ); + Assert.assertEquals( "OU_4567", planDefinition.getIdentifier().get( 0 ).getValue() ); + } +} diff --git a/dhis/src/main/java/org/dhis2/fhir/adapter/dhis/model/DhisResourceType.java b/dhis/src/main/java/org/dhis2/fhir/adapter/dhis/model/DhisResourceType.java index 654008ad..80d8238f 100644 --- a/dhis/src/main/java/org/dhis2/fhir/adapter/dhis/model/DhisResourceType.java +++ b/dhis/src/main/java/org/dhis2/fhir/adapter/dhis/model/DhisResourceType.java @@ -44,37 +44,37 @@ public enum DhisResourceType /** * Resource is a tracked entity instance. */ - TRACKED_ENTITY( "trackedEntityInstances", "te" ), + TRACKED_ENTITY( "trackedEntityInstances", "te", "TrackedEntityRule" ), /** * Resource is a tracked entity type. */ - TRACKED_ENTITY_TYPE( "trackedEntityTypes", "tt" ), + TRACKED_ENTITY_TYPE( "trackedEntityTypes", "tt", "TrackedEntityTypeRule" ), /** * The program metadata. */ - PROGRAM_METADATA( "programs", "pm" ), + PROGRAM_METADATA( "programs", "pm", "ProgramMetadataRule" ), /** * The program stage metadata. */ - PROGRAM_STAGE_METADATA( "programStages", "sm" ), + PROGRAM_STAGE_METADATA( "programStages", "sm", "ProgramStageMetadataRule" ), /** * Resource is a program instance (aka enrollment). */ - ENROLLMENT( "enrollments", "en" ), + ENROLLMENT( "enrollments", "en", "EnrollmentRule" ), /** * Resource is a program stage instance (aka event of a program instance). */ - PROGRAM_STAGE_EVENT( "events", "ps" ), + PROGRAM_STAGE_EVENT( "events", "ps", "ProgramStageRule" ), /** * Resource is a organisation unit. */ - ORGANIZATION_UNIT( "organisationUnits", "ou" ); + ORGANIZATION_UNIT( "organisationUnits", "ou", "OrganizationUnitRule" ); private static final Map byTypeName = Arrays.stream( values() ).collect( Collectors.toMap( DhisResourceType::getTypeName, v -> v ) ); @@ -96,10 +96,13 @@ public static DhisResourceType getByAbbreviation( @Nullable String abbreviation private final String abbreviation; - DhisResourceType( @Nonnull String typeName, @Nonnull String abbreviation ) + private final String ruleType; + + DhisResourceType( @Nonnull String typeName, @Nonnull String abbreviation, @Nonnull String ruleType ) { this.typeName = typeName; this.abbreviation = abbreviation; + this.ruleType = ruleType; } @Nonnull @@ -113,4 +116,10 @@ public String getAbbreviation() { return abbreviation; } + + @Nonnull + public String getRuleType() + { + return ruleType; + } } diff --git a/fhir-r4/src/main/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/metadata/r4/R4ProgramMetadataToFhirCarePlanTransformer.java b/fhir-r4/src/main/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/metadata/r4/R4ProgramMetadataToFhirPlanDefinitionTransformer.java similarity index 92% rename from fhir-r4/src/main/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/metadata/r4/R4ProgramMetadataToFhirCarePlanTransformer.java rename to fhir-r4/src/main/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/metadata/r4/R4ProgramMetadataToFhirPlanDefinitionTransformer.java index 4fbd6e20..6002c267 100644 --- a/fhir-r4/src/main/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/metadata/r4/R4ProgramMetadataToFhirCarePlanTransformer.java +++ b/fhir-r4/src/main/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/metadata/r4/R4ProgramMetadataToFhirPlanDefinitionTransformer.java @@ -42,7 +42,7 @@ import org.dhis2.fhir.adapter.fhir.repository.FhirResourceRepository; import org.dhis2.fhir.adapter.fhir.script.ScriptExecutor; import org.dhis2.fhir.adapter.fhir.transform.dhis.DhisToFhirTransformerContext; -import org.dhis2.fhir.adapter.fhir.transform.dhis.impl.metadata.program.AbstractProgramMetadataToFhirCarePlanTransformer; +import org.dhis2.fhir.adapter.fhir.transform.dhis.impl.metadata.program.AbstractProgramMetadataToFhirPlanDefinitionTransformer; import org.dhis2.fhir.adapter.fhir.transform.scripted.AccessibleScriptedDhisMetadata; import org.dhis2.fhir.adapter.lock.LockManager; import org.hl7.fhir.instance.model.api.IBaseResource; @@ -62,9 +62,9 @@ * @author volsch */ @Component -public class R4ProgramMetadataToFhirCarePlanTransformer extends AbstractProgramMetadataToFhirCarePlanTransformer +public class R4ProgramMetadataToFhirPlanDefinitionTransformer extends AbstractProgramMetadataToFhirPlanDefinitionTransformer { - public R4ProgramMetadataToFhirCarePlanTransformer( @Nonnull ScriptExecutor scriptExecutor, @Nonnull LockManager lockManager, @Nonnull SystemRepository systemRepository, @Nonnull FhirResourceRepository fhirResourceRepository, + public R4ProgramMetadataToFhirPlanDefinitionTransformer( @Nonnull ScriptExecutor scriptExecutor, @Nonnull LockManager lockManager, @Nonnull SystemRepository systemRepository, @Nonnull FhirResourceRepository fhirResourceRepository, @Nonnull FhirDhisAssignmentRepository fhirDhisAssignmentRepository, @Nonnull OrganizationUnitService organizationUnitService, @Nonnull TrackedEntityMetadataService trackedEntityMetadataService, @Nonnull TrackedEntityRuleRepository trackedEntityRuleRepository ) { diff --git a/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/metadata/model/AbstractRule.java b/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/metadata/model/AbstractRule.java index b5bf83f7..14e64cc8 100644 --- a/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/metadata/model/AbstractRule.java +++ b/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/metadata/model/AbstractRule.java @@ -98,8 +98,7 @@ "SELECT r FROM AbstractRule r JOIN r.applicableCodeSet acs WHERE r.fhirResourceType=:fhirResourceType AND r.enabled=true " + "AND r.expEnabled=true AND acs.code IN (:codeSetCodes)" ), @NamedQuery( name = AbstractRule.FIND_IMP_RULE_BY_ID_NAMED_QUERY, query = - "SELECT r FROM AbstractRule r WHERE r.fhirResourceType=:fhirResourceType AND TYPE(r)=:dhisResourceType AND r.id=:ruleId AND r.enabled=true AND r.impEnabled=true" ), - @NamedQuery( name = AbstractRule.FIND_ALL_EXP_NAMED_QUERY, query = "SELECT our FROM OrganizationUnitRule our WHERE our.enabled=true AND our.expEnabled=true AND (our.fhirCreateEnabled=true OR our.fhirUpdateEnabled=true)" ) + "SELECT r FROM AbstractRule r WHERE r.fhirResourceType=:fhirResourceType AND TYPE(r)=:dhisResourceType AND r.id=:ruleId AND r.enabled=true AND r.impEnabled=true" ) } ) @JsonTypeInfo( use = JsonTypeInfo.Id.NAME, property = "dhisResourceType", include = JsonTypeInfo.As.EXISTING_PROPERTY ) @JsonSubTypes( { @@ -129,8 +128,6 @@ public abstract class AbstractRule extends VersionedBaseMetadata implements Seri public static final String FIND_IMP_RULE_BY_ID_NAMED_QUERY = "AbstractRule.findImpById"; - public static final String FIND_ALL_EXP_NAMED_QUERY = "AbstractRule.findAllExp"; - public static final int MAX_NAME_LENGTH = 230; @NotBlank diff --git a/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/metadata/repository/CustomRuleRepository.java b/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/metadata/repository/CustomRuleRepository.java index fcd443a4..f1b83c57 100644 --- a/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/metadata/repository/CustomRuleRepository.java +++ b/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/metadata/repository/CustomRuleRepository.java @@ -67,6 +67,6 @@ public interface CustomRuleRepository @RestResource( exported = false ) @Nonnull - Collection> findAllExp(); + Collection> findAllExp( @Nonnull DhisResourceType dhisResourceType ); } diff --git a/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/metadata/repository/impl/CustomRuleRepositoryImpl.java b/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/metadata/repository/impl/CustomRuleRepositoryImpl.java index 5305b436..5f070628 100644 --- a/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/metadata/repository/impl/CustomRuleRepositoryImpl.java +++ b/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/metadata/repository/impl/CustomRuleRepositoryImpl.java @@ -111,11 +111,12 @@ public List> findAllExpByInputData( @Nonnull Fh @RestResource( exported = false ) @Nonnull - @Cacheable( key = "{#root.methodName}", cacheManager = "metadataCacheManager", cacheNames = "rule" ) + @Cacheable( key = "{#root.methodName, #a0}", cacheManager = "metadataCacheManager", cacheNames = "rule" ) @Transactional( readOnly = true ) - public Collection> findAllExp() + public Collection> findAllExp( @Nonnull DhisResourceType dhisResourceType ) { - final List rules = entityManager.createNamedQuery( AbstractRule.FIND_ALL_EXP_NAMED_QUERY, AbstractRule.class ).getResultList(); + final List rules = entityManager.createQuery( "SELECT r FROM " + dhisResourceType.getRuleType() + + " r WHERE r.enabled=true AND r.expEnabled=true AND (r.fhirCreateEnabled=true OR r.fhirUpdateEnabled=true)", AbstractRule.class ).getResultList(); return rules.stream().map( r -> { Hibernate.initialize( r.getDhisDataReferences() ); diff --git a/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/metadata/AbstractDhisMetadataToFhirRequestResolver.java b/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/metadata/AbstractDhisMetadataToFhirRequestResolver.java index 97782994..574e8695 100644 --- a/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/metadata/AbstractDhisMetadataToFhirRequestResolver.java +++ b/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/metadata/AbstractDhisMetadataToFhirRequestResolver.java @@ -61,7 +61,7 @@ protected AbstractDhisMetadataToFhirRequestResolver( @Nonnull FhirClientReposito @Override public List> resolveRules( @Nonnull ScriptedDhisResource dhisResource ) { - return ruleRepository.findAllExp().stream().sorted().collect( Collectors.toList() ); + return ruleRepository.findAllExp( dhisResource.getResourceType() ).stream().sorted().collect( Collectors.toList() ); } @Nonnull diff --git a/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/metadata/program/AbstractProgramMetadataToFhirCarePlanTransformer.java b/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/metadata/program/AbstractProgramMetadataToFhirPlanDefinitionTransformer.java similarity index 92% rename from fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/metadata/program/AbstractProgramMetadataToFhirCarePlanTransformer.java rename to fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/metadata/program/AbstractProgramMetadataToFhirPlanDefinitionTransformer.java index 8707f71d..bb7aaefe 100644 --- a/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/metadata/program/AbstractProgramMetadataToFhirCarePlanTransformer.java +++ b/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/metadata/program/AbstractProgramMetadataToFhirPlanDefinitionTransformer.java @@ -59,7 +59,7 @@ * @param the concrete type of the FHIR resource into which the DHIS2 resource should be transformed. * @author volsch */ -public abstract class AbstractProgramMetadataToFhirCarePlanTransformer extends AbstractReadOnlyDhisMetadataToTypedFhirTransformer +public abstract class AbstractProgramMetadataToFhirPlanDefinitionTransformer extends AbstractReadOnlyDhisMetadataToTypedFhirTransformer { private final Logger logger = LoggerFactory.getLogger( getClass() ); @@ -67,7 +67,7 @@ public abstract class AbstractProgramMetadataToFhirCarePlanTransformer Date: Mon, 29 Jul 2019 16:30:30 +0200 Subject: [PATCH 10/16] Adds tests for program to plan definition transformation. --- .../main/resources/default-application.yml | 1 + .../r4/R4ProgramMetadataFhirRestAppTest.java | 91 +++----- .../adapter/dhis/test/default-program.json | 1 + ...ProgramMetadataToFhirDataProviderTest.java | 93 ++++++++ ...taToFhirPlanDefinitionTransformerTest.java | 171 +++++++++++++++ .../impl/AbstractDhisToFhirDataProvider.java | 4 +- .../AbstractDhisToFhirRequestResolver.java | 4 +- .../impl/AbstractDhisToFhirTransformer.java | 33 +++ .../dhis/impl/DhisToFhirRuleComparator.java | 6 +- .../DhisToFhirTransformerContextImpl.java | 3 + .../DhisToFhirTransformerRequestImpl.java | 7 +- .../DhisToFhirTransformerServiceImpl.java | 17 ++ .../dhis/impl/TransformerRequestKey.java | 16 +- .../dhis/search/SearchFilterCollector.java | 6 +- ...0.41_10_0__Plan_Definition_Sample_Data.sql | 38 ++++ ...taToFhirPlanDefinitionTransformerTest.java | 206 ++++++++++++++++++ 16 files changed, 624 insertions(+), 73 deletions(-) create mode 100644 fhir-r4/src/test/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/metadata/r4/R4ProgramMetadataToFhirDataProviderTest.java create mode 100644 fhir-r4/src/test/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/metadata/r4/R4ProgramMetadataToFhirPlanDefinitionTransformerTest.java create mode 100644 fhir/src/main/resources/db/migration/sample/V1.1.0.41_10_0__Plan_Definition_Sample_Data.sql create mode 100644 fhir/src/test/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/metadata/program/AbstractProgramMetadataToFhirPlanDefinitionTransformerTest.java diff --git a/app/src/main/resources/default-application.yml b/app/src/main/resources/default-application.yml index e4fc83f5..1feb5c15 100644 --- a/app/src/main/resources/default-application.yml +++ b/app/src/main/resources/default-application.yml @@ -356,6 +356,7 @@ dhis2.fhir-adapter: # DHIS 2 resource types that will be synchronized. resource-types: - ORGANIZATION_UNIT + - PROGRAM_METADATA - TRACKED_ENTITY - PROGRAM_STAGE_EVENT # The queue that is used to store distributed requests to synchronize the data from diff --git a/app/src/test/java/org/dhis2/fhir/adapter/fhir/server/r4/R4ProgramMetadataFhirRestAppTest.java b/app/src/test/java/org/dhis2/fhir/adapter/fhir/server/r4/R4ProgramMetadataFhirRestAppTest.java index e7d1c616..1402d045 100644 --- a/app/src/test/java/org/dhis2/fhir/adapter/fhir/server/r4/R4ProgramMetadataFhirRestAppTest.java +++ b/app/src/test/java/org/dhis2/fhir/adapter/fhir/server/r4/R4ProgramMetadataFhirRestAppTest.java @@ -38,7 +38,6 @@ import org.hl7.fhir.r4.model.Bundle; import org.hl7.fhir.r4.model.PlanDefinition; import org.junit.Assert; -import org.junit.Ignore; import org.junit.Test; import org.springframework.http.HttpMethod; import org.springframework.http.HttpStatus; @@ -58,7 +57,6 @@ * * @author volsch */ -@Ignore public class R4ProgramMetadataFhirRestAppTest extends AbstractAppTest { @Nonnull @@ -205,19 +203,22 @@ private void getPlanDefinitionRuleNotFound() public void getPlanDefinitionByIdentifierWithoutAuthorization() { final IGenericClient client = createGenericClient(); - client.search().forResource( PlanDefinition.class ).where( PlanDefinition.IDENTIFIER.exactly().systemAndIdentifier( "http://www.dhis2.org/dhis2fhiradapter/systems/plan-definition-identifier", "XX_1234" ) ).returnBundle( Bundle.class ).execute(); + client.search().forResource( PlanDefinition.class ).where( PlanDefinition.IDENTIFIER.exactly().systemAndIdentifier( "http://www.dhis2.org/dhis2-fhir-adapter/systems/plan-definition-identifier", "XX_1234" ) ).returnBundle( Bundle.class ).execute(); } @Test( expected = AuthenticationException.class ) public void getPlanDefinitionByIdentifierInvalidAuthorization() { userDhis2Server.expect( ExpectedCount.once(), method( HttpMethod.GET ) ).andExpect( header( "Authorization", "Basic Zmhpcl9jbGllbnQ6aW52YWxpZF8x" ) ) - .andExpect( requestTo( dhis2BaseUrl + "/api/" + dhis2ApiVersion + "/organisationUnits.json?paging=false&fields=lastUpdated,id,code,name,shortName,displayName,level,openingDate,closedDate,coordinates,leaf,parent%5Bid%5D&filter=code:eq:OU_1234" ) ) + .andExpect( requestTo( dhis2BaseUrl + "/api/" + dhis2ApiVersion + "/programs.json?paging=false&fields=id,name,code,description,selectIncidentDatesInFuture,selectEnrollmentDatesInFuture,displayIncidentDate,registration,withoutRegistration," + + "captureCoordinates,trackedEntityType%5Bid%5D,programTrackedEntityAttributes%5Bid,name,valueType,mandatory,allowFutureDate,trackedEntityAttribute%5Bid,name,code,valueType,generated%5D%5D,programStages%5Bid,name,description,repeatable," + + "captureCoordinates,generatedByEnrollmentDate,minDaysFromStart,programStageDataElements%5Bid,compulsory,allowProvidedElsewhere,dataElement%5Bid,name,code,formName,valueType,optionSetValue,optionSet%5Bid,name,options%5Bcode," + + "name%5D%5D%5D%5D%5D&filter=code:eq:OU_1234" ) ) .andRespond( withStatus( HttpStatus.UNAUTHORIZED ) ); final IGenericClient client = createGenericClient(); client.registerInterceptor( new BasicAuthInterceptor( "fhir_client", "invalid_1" ) ); - client.search().forResource( PlanDefinition.class ).where( PlanDefinition.IDENTIFIER.exactly().systemAndIdentifier( "http://www.dhis2.org/dhis2fhiradapter/systems/planDefinition-identifier", "OU_1234" ) ).returnBundle( Bundle.class ).execute(); + client.search().forResource( PlanDefinition.class ).where( PlanDefinition.IDENTIFIER.exactly().systemAndIdentifier( "http://www.dhis2.org/dhis2-fhir-adapter/systems/plan-definition-identifier", "OU_1234" ) ).returnBundle( Bundle.class ).execute(); } @Test @@ -232,20 +233,24 @@ private void getPlanDefinitionByIdentifier() throws Exception systemDhis2Server.reset(); userDhis2Server.reset(); + systemDhis2Server.expect( ExpectedCount.between( 0, 1 ), method( HttpMethod.GET ) ).andExpect( header( "Authorization", testConfiguration.getDhis2SystemAuthorization() ) ) + .andExpect( requestTo( dhis2BaseUrl + "/api/" + dhis2ApiVersion + "/trackedEntityTypes/MCPQUTHX1Ze.json?" + + "fields=id,name,trackedEntityTypeAttributes%5Bid,name,valueType,mandatory,trackedEntityAttribute%5Bid,name,code,valueType,generated,optionSetValue,optionSet%5Bid,name,options%5Bcode,name%5D%5D%5D%5D" ) ) + .andRespond( withSuccess( IOUtils.resourceToString( "/org/dhis2/fhir/adapter/dhis/test/single-tracked-entity-type.json", StandardCharsets.UTF_8 ), MediaType.APPLICATION_JSON ) ); userDhis2Server.expect( ExpectedCount.between( 1, 2 ), method( HttpMethod.GET ) ).andExpect( header( "Authorization", "Basic Zmhpcl9jbGllbnQ6Zmhpcl9jbGllbnRfMQ==" ) ) - .andExpect( requestTo( dhis2BaseUrl + "/api/" + dhis2ApiVersion + "/organisationUnits.json?paging=false&fields=lastUpdated,id,code,name,shortName,displayName,level,openingDate,closedDate,coordinates,leaf,parent%5Bid%5D&filter=code:eq:OU_1234" ) ) - .andRespond( withSuccess( IOUtils.resourceToString( "/org/dhis2/fhir/adapter/dhis/test/default-org-unit-OU_1234.json", StandardCharsets.UTF_8 ), MediaType.APPLICATION_JSON ) ); + .andExpect( requestTo( dhis2BaseUrl + "/api/" + dhis2ApiVersion + "/programs.json?paging=false&fields=id,name,code,description,selectIncidentDatesInFuture,selectEnrollmentDatesInFuture,displayIncidentDate,registration,withoutRegistration," + + "captureCoordinates,trackedEntityType%5Bid%5D,programTrackedEntityAttributes%5Bid,name,valueType,mandatory,allowFutureDate,trackedEntityAttribute%5Bid,name,code,valueType,generated%5D%5D,programStages%5Bid,name,description,repeatable," + + "captureCoordinates,generatedByEnrollmentDate,minDaysFromStart,programStageDataElements%5Bid,compulsory,allowProvidedElsewhere,dataElement%5Bid,name,code,formName,valueType,optionSetValue,optionSet%5Bid,name,options%5Bcode," + + "name%5D%5D%5D%5D%5D&filter=code:eq:PD_1234" ) ) + .andRespond( withSuccess( IOUtils.resourceToString( "/org/dhis2/fhir/adapter/dhis/test/default-program.json", StandardCharsets.UTF_8 ), MediaType.APPLICATION_JSON ) ); final IGenericClient client = createGenericClient(); client.registerInterceptor( new BasicAuthInterceptor( "fhir_client", "fhir_client_1" ) ); Bundle bundle = - client.search().forResource( PlanDefinition.class ).where( PlanDefinition.IDENTIFIER.exactly().systemAndIdentifier( "http://www.dhis2.org/dhis2fhiradapter/systems/planDefinition-identifier", "OU_1234" ) ).returnBundle( Bundle.class ).execute(); + client.search().forResource( PlanDefinition.class ).where( PlanDefinition.IDENTIFIER.exactly().systemAndIdentifier( "http://www.dhis2.org/dhis2-fhir-adapter/systems/plan-definition-identifier", "PD_1234" ) ).returnBundle( Bundle.class ).execute(); Assert.assertEquals( 1, bundle.getEntry().size() ); PlanDefinition planDefinition = (PlanDefinition) bundle.getEntry().get( 0 ).getResource(); - Assert.assertEquals( "Test Hospital", planDefinition.getName() ); - Assert.assertEquals( 1, planDefinition.getIdentifier().size() ); - Assert.assertEquals( "http://www.dhis2.org/dhis2fhiradapter/systems/planDefinition-identifier", planDefinition.getIdentifier().get( 0 ).getSystem() ); - Assert.assertEquals( "OU_1234", planDefinition.getIdentifier().get( 0 ).getValue() ); + Assert.assertEquals( "Child Programme", planDefinition.getName() ); systemDhis2Server.verify(); userDhis2Server.verify(); @@ -276,60 +281,22 @@ public void searchPlanDefinitionInvalidAuthorization() @Test public void searchPlanDefinition() throws Exception { + systemDhis2Server.expect( ExpectedCount.between( 0, 1 ), method( HttpMethod.GET ) ).andExpect( header( "Authorization", testConfiguration.getDhis2SystemAuthorization() ) ) + .andExpect( requestTo( dhis2BaseUrl + "/api/" + dhis2ApiVersion + "/trackedEntityTypes/MCPQUTHX1Ze.json?" + + "fields=id,name,trackedEntityTypeAttributes%5Bid,name,valueType,mandatory,trackedEntityAttribute%5Bid,name,code,valueType,generated,optionSetValue,optionSet%5Bid,name,options%5Bcode,name%5D%5D%5D%5D" ) ) + .andRespond( withSuccess( IOUtils.resourceToString( "/org/dhis2/fhir/adapter/dhis/test/single-tracked-entity-type.json", StandardCharsets.UTF_8 ), MediaType.APPLICATION_JSON ) ); userDhis2Server.expect( ExpectedCount.once(), method( HttpMethod.GET ) ).andExpect( header( "Authorization", "Basic Zmhpcl9jbGllbnQ6Zmhpcl9jbGllbnRfMQ==" ) ) - .andExpect( requestTo( dhis2BaseUrl + "/api/" + dhis2ApiVersion + "/organisationUnits.json?filter=name:$ilike:Test&paging=true&page=1&pageSize=10&order=id" + - "&fields=lastUpdated,id,code,name,shortName,displayName,level,openingDate,closedDate,coordinates,leaf,parent%5Bid%5D" ) ) - .andRespond( withSuccess( IOUtils.resourceToString( "/org/dhis2/fhir/adapter/dhis/test/multi-org-unit.json", StandardCharsets.UTF_8 ), MediaType.APPLICATION_JSON ) ); - userDhis2Server.expect( ExpectedCount.between( 0, 1 ), method( HttpMethod.GET ) ).andExpect( header( "Authorization", "Basic Zmhpcl9jbGllbnQ6Zmhpcl9jbGllbnRfMQ==" ) ) - .andExpect( requestTo( dhis2BaseUrl + "/api/" + dhis2ApiVersion + "/organisationUnits.json?paging=false&fields=lastUpdated,id,code,name,shortName,displayName,level,openingDate,closedDate,coordinates,leaf,parent%5Bid%5D&filter=code:eq:OU_1234" ) ) - .andRespond( withSuccess( IOUtils.resourceToString( "/org/dhis2/fhir/adapter/dhis/test/default-org-unit-OU_1234.json", StandardCharsets.UTF_8 ), MediaType.APPLICATION_JSON ) ); - userDhis2Server.expect( ExpectedCount.between( 0, 1 ), method( HttpMethod.GET ) ).andExpect( header( "Authorization", "Basic Zmhpcl9jbGllbnQ6Zmhpcl9jbGllbnRfMQ==" ) ) - .andExpect( requestTo( dhis2BaseUrl + "/api/" + dhis2ApiVersion + "/organisationUnits.json?paging=false&fields=lastUpdated,id,code,name,shortName,displayName,level,openingDate,closedDate,coordinates,leaf,parent%5Bid%5D&filter=code:eq:OU_4567" ) ) - .andRespond( withSuccess( IOUtils.resourceToString( "/org/dhis2/fhir/adapter/dhis/test/default-org-unit-OU_4567.json", StandardCharsets.UTF_8 ), MediaType.APPLICATION_JSON ) ); - - final IGenericClient client = createGenericClient(); - client.registerInterceptor( new BasicAuthInterceptor( "fhir_client", "fhir_client_1" ) ); - Bundle bundle = client.search().forResource( PlanDefinition.class ).where( PlanDefinition.NAME.matches().value( "Test" ) ).returnBundle( Bundle.class ).execute(); - Assert.assertEquals( 2, bundle.getEntry().size() ); - PlanDefinition planDefinition = (PlanDefinition) bundle.getEntry().get( 0 ).getResource(); - Assert.assertEquals( "Test Hospital", planDefinition.getName() ); - Assert.assertEquals( 1, planDefinition.getIdentifier().size() ); - Assert.assertEquals( "http://www.dhis2.org/dhis2fhiradapter/systems/planDefinition-identifier", planDefinition.getIdentifier().get( 0 ).getSystem() ); - Assert.assertEquals( "OU_1234", planDefinition.getIdentifier().get( 0 ).getValue() ); - planDefinition = (PlanDefinition) bundle.getEntry().get( 1 ).getResource(); - Assert.assertEquals( "Test Hospital 2", planDefinition.getName() ); - Assert.assertEquals( 1, planDefinition.getIdentifier().size() ); - Assert.assertEquals( "http://www.dhis2.org/dhis2fhiradapter/systems/planDefinition-identifier", planDefinition.getIdentifier().get( 0 ).getSystem() ); - Assert.assertEquals( "OU_4567", planDefinition.getIdentifier().get( 0 ).getValue() ); - } - - @Test - public void searchParentPlanDefinition() throws Exception - { - userDhis2Server.expect( ExpectedCount.once(), method( HttpMethod.GET ) ).andExpect( header( "Authorization", "Basic Zmhpcl9jbGllbnQ6Zmhpcl9jbGllbnRfMQ==" ) ) - .andExpect( requestTo( dhis2BaseUrl + "/api/" + dhis2ApiVersion + "/organisationUnits.json?filter=parent.id:eq:bdXIaLNUNEp&paging=true&page=1&pageSize=10&order=id" + - "&fields=lastUpdated,id,code,name,shortName,displayName,level,openingDate,closedDate,coordinates,leaf,parent%5Bid%5D" ) ) - .andRespond( withSuccess( IOUtils.resourceToString( "/org/dhis2/fhir/adapter/dhis/test/multi-org-unit.json", StandardCharsets.UTF_8 ), MediaType.APPLICATION_JSON ) ); - userDhis2Server.expect( ExpectedCount.between( 0, 1 ), method( HttpMethod.GET ) ).andExpect( header( "Authorization", "Basic Zmhpcl9jbGllbnQ6Zmhpcl9jbGllbnRfMQ==" ) ) - .andExpect( requestTo( dhis2BaseUrl + "/api/" + dhis2ApiVersion + "/organisationUnits.json?paging=false&fields=lastUpdated,id,code,name,shortName,displayName,level,openingDate,closedDate,coordinates,leaf,parent%5Bid%5D&filter=code:eq:OU_1234" ) ) - .andRespond( withSuccess( IOUtils.resourceToString( "/org/dhis2/fhir/adapter/dhis/test/default-org-unit-OU_1234.json", StandardCharsets.UTF_8 ), MediaType.APPLICATION_JSON ) ); - userDhis2Server.expect( ExpectedCount.between( 0, 1 ), method( HttpMethod.GET ) ).andExpect( header( "Authorization", "Basic Zmhpcl9jbGllbnQ6Zmhpcl9jbGllbnRfMQ==" ) ) - .andExpect( requestTo( dhis2BaseUrl + "/api/" + dhis2ApiVersion + "/organisationUnits.json?paging=false&fields=lastUpdated,id,code,name,shortName,displayName,level,openingDate,closedDate,coordinates,leaf,parent%5Bid%5D&filter=code:eq:OU_4567" ) ) - .andRespond( withSuccess( IOUtils.resourceToString( "/org/dhis2/fhir/adapter/dhis/test/default-org-unit-OU_4567.json", StandardCharsets.UTF_8 ), MediaType.APPLICATION_JSON ) ); + .andExpect( requestTo( dhis2BaseUrl + "/api/" + dhis2ApiVersion + "/programs.json?filter=name:$ilike:Child%20Programme&paging=true&page=1&pageSize=10&order=id&fields=id,name,code,description,selectIncidentDatesInFuture," + + "selectEnrollmentDatesInFuture,displayIncidentDate,registration,withoutRegistration,captureCoordinates,trackedEntityType%5Bid%5D,programTrackedEntityAttributes%5Bid,name,valueType,mandatory,allowFutureDate,trackedEntityAttribute%5Bid,name," + + "code,valueType,generated%5D%5D,programStages%5Bid,name,description,repeatable,captureCoordinates,generatedByEnrollmentDate,minDaysFromStart,programStageDataElements%5Bid,compulsory,allowProvidedElsewhere,dataElement%5Bid,name,code,formName," + + "valueType,optionSetValue,optionSet%5Bid,name,options%5Bcode,name%5D%5D%5D%5D%5D" ) ) + .andRespond( withSuccess( IOUtils.resourceToString( "/org/dhis2/fhir/adapter/dhis/test/default-program.json", StandardCharsets.UTF_8 ), MediaType.APPLICATION_JSON ) ); final IGenericClient client = createGenericClient(); client.registerInterceptor( new BasicAuthInterceptor( "fhir_client", "fhir_client_1" ) ); - Bundle bundle = client.search().forResource( PlanDefinition.class ).where( PlanDefinition.NAME.matchesExactly().value( "ChildProgram" ) ).returnBundle( Bundle.class ).execute(); - Assert.assertEquals( 2, bundle.getEntry().size() ); + Bundle bundle = client.search().forResource( PlanDefinition.class ).where( PlanDefinition.NAME.matches().value( "Child Programme" ) ).returnBundle( Bundle.class ).execute(); + Assert.assertEquals( 1, bundle.getEntry().size() ); PlanDefinition planDefinition = (PlanDefinition) bundle.getEntry().get( 0 ).getResource(); - Assert.assertEquals( "Test Hospital", planDefinition.getName() ); - Assert.assertEquals( 1, planDefinition.getIdentifier().size() ); - Assert.assertEquals( "http://www.dhis2.org/dhis2fhiradapter/systems/planDefinition-identifier", planDefinition.getIdentifier().get( 0 ).getSystem() ); - Assert.assertEquals( "OU_1234", planDefinition.getIdentifier().get( 0 ).getValue() ); - planDefinition = (PlanDefinition) bundle.getEntry().get( 1 ).getResource(); - Assert.assertEquals( "Test Hospital 2", planDefinition.getName() ); - Assert.assertEquals( 1, planDefinition.getIdentifier().size() ); - Assert.assertEquals( "http://www.dhis2.org/dhis2fhiradapter/systems/planDefinition-identifier", planDefinition.getIdentifier().get( 0 ).getSystem() ); - Assert.assertEquals( "OU_4567", planDefinition.getIdentifier().get( 0 ).getValue() ); + Assert.assertEquals( "Child Programme", planDefinition.getName() ); } } diff --git a/app/src/test/resources/org/dhis2/fhir/adapter/dhis/test/default-program.json b/app/src/test/resources/org/dhis2/fhir/adapter/dhis/test/default-program.json index b1fe9749..1d348852 100644 --- a/app/src/test/resources/org/dhis2/fhir/adapter/dhis/test/default-program.json +++ b/app/src/test/resources/org/dhis2/fhir/adapter/dhis/test/default-program.json @@ -1,4 +1,5 @@ { + "pager": { "page": 1, "pageCount": 1, "total": 0, "pageSize": 1 }, "programs": [ { "name": "Child Programme", diff --git a/fhir-r4/src/test/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/metadata/r4/R4ProgramMetadataToFhirDataProviderTest.java b/fhir-r4/src/test/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/metadata/r4/R4ProgramMetadataToFhirDataProviderTest.java new file mode 100644 index 00000000..4798edd1 --- /dev/null +++ b/fhir-r4/src/test/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/metadata/r4/R4ProgramMetadataToFhirDataProviderTest.java @@ -0,0 +1,93 @@ +package org.dhis2.fhir.adapter.fhir.transform.dhis.impl.metadata.r4; + +/* + * Copyright (c) 2004-2019, University of Oslo + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * Neither the name of the HISP project nor the names of its contributors may + * be used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +import org.dhis2.fhir.adapter.dhis.tracker.program.ProgramMetadataService; +import org.dhis2.fhir.adapter.fhir.metadata.model.ProgramMetadataRule; +import org.dhis2.fhir.adapter.fhir.metadata.model.RuleInfo; +import org.dhis2.fhir.adapter.fhir.model.FhirVersion; +import org.dhis2.fhir.adapter.fhir.script.ScriptExecutor; +import org.dhis2.fhir.adapter.fhir.transform.dhis.search.SearchFilter; +import org.dhis2.fhir.adapter.fhir.transform.dhis.search.SearchFilterCollector; +import org.junit.Assert; +import org.junit.Rule; +import org.junit.Test; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.junit.MockitoJUnit; +import org.mockito.junit.MockitoRule; +import org.springframework.web.util.DefaultUriBuilderFactory; +import org.springframework.web.util.UriBuilder; + +import java.net.URI; +import java.net.URISyntaxException; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +/** + * Unit tests for {@link R4ProgramMetadataToFhirDataProvider}. + * + * @author volsch + */ +public class R4ProgramMetadataToFhirDataProviderTest +{ + @Mock + private ScriptExecutor scriptExecutor; + + @Mock + private ProgramMetadataService programMetadataService; + + @InjectMocks + private R4ProgramMetadataToFhirDataProvider provider; + + @Rule + public MockitoRule rule = MockitoJUnit.rule(); + + @Test + public void getFhirVersions() + { + Assert.assertEquals( FhirVersion.R4_ONLY, provider.getFhirVersions() ); + } + + @Test + public void searchFilterName() throws URISyntaxException + { + final SearchFilterCollector searchFilterCollector = new SearchFilterCollector( Collections.singletonMap( "name", Collections.singletonList( "Child Programme" ) ) ); + final SearchFilter searchFilter = new SearchFilter( searchFilterCollector, false ); + + provider.initSearchFilter( FhirVersion.R4, new RuleInfo<>( new ProgramMetadataRule(), Collections.emptyList() ), searchFilter ); + + final List variables = new ArrayList<>(); + UriBuilder uriBuilder = new DefaultUriBuilderFactory().builder(); + uriBuilder = searchFilterCollector.add( uriBuilder, variables ); + + Assert.assertEquals( new URI( "?filter=%5Bname:$ilike:Child%20Programme%5D" ), uriBuilder.build( variables ) ); + } +} diff --git a/fhir-r4/src/test/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/metadata/r4/R4ProgramMetadataToFhirPlanDefinitionTransformerTest.java b/fhir-r4/src/test/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/metadata/r4/R4ProgramMetadataToFhirPlanDefinitionTransformerTest.java new file mode 100644 index 00000000..3d14b07b --- /dev/null +++ b/fhir-r4/src/test/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/metadata/r4/R4ProgramMetadataToFhirPlanDefinitionTransformerTest.java @@ -0,0 +1,171 @@ +package org.dhis2.fhir.adapter.fhir.transform.dhis.impl.metadata.r4; + +/* + * Copyright (c) 2004-2019, University of Oslo + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * Neither the name of the HISP project nor the names of its contributors may + * be used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +import org.dhis2.fhir.adapter.dhis.model.Reference; +import org.dhis2.fhir.adapter.dhis.model.ReferenceType; +import org.dhis2.fhir.adapter.dhis.orgunit.OrganizationUnitService; +import org.dhis2.fhir.adapter.dhis.tracker.program.WritableProgram; +import org.dhis2.fhir.adapter.dhis.tracker.program.WritableProgramStage; +import org.dhis2.fhir.adapter.dhis.tracker.trackedentity.TrackedEntityMetadataService; +import org.dhis2.fhir.adapter.dhis.tracker.trackedentity.WritableTrackedEntityType; +import org.dhis2.fhir.adapter.fhir.data.repository.FhirDhisAssignmentRepository; +import org.dhis2.fhir.adapter.fhir.metadata.model.FhirClient; +import org.dhis2.fhir.adapter.fhir.metadata.model.ProgramMetadataRule; +import org.dhis2.fhir.adapter.fhir.metadata.model.RuleInfo; +import org.dhis2.fhir.adapter.fhir.metadata.repository.SystemRepository; +import org.dhis2.fhir.adapter.fhir.metadata.repository.TrackedEntityRuleRepository; +import org.dhis2.fhir.adapter.fhir.model.FhirVersion; +import org.dhis2.fhir.adapter.fhir.repository.FhirResourceRepository; +import org.dhis2.fhir.adapter.fhir.script.ScriptExecutionContext; +import org.dhis2.fhir.adapter.fhir.script.ScriptExecutor; +import org.dhis2.fhir.adapter.fhir.transform.dhis.DhisToFhirTransformerContext; +import org.dhis2.fhir.adapter.fhir.transform.scripted.WritableScriptedDhisMetadata; +import org.dhis2.fhir.adapter.lock.LockManager; +import org.hl7.fhir.r4.model.PlanDefinition; +import org.junit.Assert; +import org.junit.Rule; +import org.junit.Test; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.Mockito; +import org.mockito.junit.MockitoJUnit; +import org.mockito.junit.MockitoRule; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Optional; + +/** + * Unit tests for {@link R4ProgramMetadataToFhirPlanDefinitionTransformer}. + * + * @author volsch + */ +public class R4ProgramMetadataToFhirPlanDefinitionTransformerTest +{ + @Mock + private ScriptExecutor scriptExecutor; + + @Mock + private LockManager lockManager; + + @Mock + private SystemRepository systemRepository; + + @Mock + private FhirResourceRepository fhirResourceRepository; + + @Mock + private FhirDhisAssignmentRepository fhirDhisAssignmentRepository; + + @Mock + private OrganizationUnitService organizationUnitService; + + @Mock + private TrackedEntityMetadataService trackedEntityMetadataService; + + @Mock + private TrackedEntityRuleRepository trackedEntityRuleRepository; + + @Mock + private DhisToFhirTransformerContext context; + + @Mock + private FhirClient fhirClient; + + @Mock + private ScriptExecutionContext scriptExecutionContext; + + @InjectMocks + private R4ProgramMetadataToFhirPlanDefinitionTransformer transformer; + + @Rule + public MockitoRule rule = MockitoJUnit.rule(); + + @Test + public void getFhirVersions() + { + Assert.assertEquals( FhirVersion.R4_ONLY, transformer.getFhirVersions() ); + } + + @Test + public void transformInternal() + { + Mockito.doReturn( Optional.of( new WritableTrackedEntityType( "a1234567890", "Test", Collections.emptyList() ) ) ) + .when( trackedEntityMetadataService ).findTypeByReference( Mockito.eq( new Reference( "a1234567890", ReferenceType.ID ) ) ); + Mockito.doReturn( Collections.singletonList( new RuleInfo<>( new ProgramMetadataRule(), Collections.emptyList() ) ) ) + .when( trackedEntityRuleRepository ).findByTypeRefs( Mockito.eq( new HashSet<>( + Arrays.asList( new Reference( "a1234567890", ReferenceType.ID ), new Reference( "Test", ReferenceType.NAME ) ) ) ) ); + + final WritableProgram program = new WritableProgram(); + program.setName( "Test Program" ); + program.setDescription( "Test Description" ); + program.setWithoutRegistration( false ); + program.setTrackedEntityTypeId( "a1234567890" ); + program.setStages( new ArrayList<>() ); + + WritableProgramStage programStage = new WritableProgramStage(); + programStage.setId( "b1234567890" ); + programStage.setName( "Test Stage 1" ); + programStage.setDescription( "Test Description 1" ); + programStage.setRepeatable( false ); + program.getStages().add( programStage ); + + programStage = new WritableProgramStage(); + programStage.setId( "b1234567891" ); + programStage.setName( "Test Stage 2" ); + programStage.setDescription( "Test Description 2" ); + programStage.setRepeatable( true ); + program.getStages().add( programStage ); + + final PlanDefinition fhirPlanDefinition = new PlanDefinition(); + + transformer.transformInternal( fhirClient, context, new RuleInfo<>( new ProgramMetadataRule(), Collections.emptyList() ), new HashMap<>(), + new WritableScriptedDhisMetadata( program, scriptExecutionContext ), fhirPlanDefinition ); + + Assert.assertEquals( "Test Program", fhirPlanDefinition.getName() ); + Assert.assertEquals( "Test Description", fhirPlanDefinition.getDescription() ); + Assert.assertEquals( 2, fhirPlanDefinition.getAction().size() ); + + Assert.assertEquals( "b1234567890", fhirPlanDefinition.getAction().get( 0 ).getId() ); + Assert.assertEquals( "Test Stage 1", fhirPlanDefinition.getAction().get( 0 ).getTitle() ); + Assert.assertEquals( "Test Description 1", fhirPlanDefinition.getAction().get( 0 ).getDescription() ); + Assert.assertEquals( PlanDefinition.ActionCardinalityBehavior.SINGLE, fhirPlanDefinition.getAction().get( 0 ).getCardinalityBehavior() ); + Assert.assertEquals( "Questionnaire/b1234567890", fhirPlanDefinition.getAction().get( 0 ).getDefinition().primitiveValue() ); + + Assert.assertEquals( "b1234567891", fhirPlanDefinition.getAction().get( 1 ).getId() ); + Assert.assertEquals( "Test Stage 2", fhirPlanDefinition.getAction().get( 1 ).getTitle() ); + Assert.assertEquals( "Test Description 2", fhirPlanDefinition.getAction().get( 1 ).getDescription() ); + Assert.assertEquals( PlanDefinition.ActionCardinalityBehavior.MULTIPLE, fhirPlanDefinition.getAction().get( 1 ).getCardinalityBehavior() ); + Assert.assertEquals( "Questionnaire/b1234567891", fhirPlanDefinition.getAction().get( 1 ).getDefinition().primitiveValue() ); + } +} diff --git a/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/AbstractDhisToFhirDataProvider.java b/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/AbstractDhisToFhirDataProvider.java index fb0448d5..ca186c88 100644 --- a/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/AbstractDhisToFhirDataProvider.java +++ b/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/AbstractDhisToFhirDataProvider.java @@ -110,8 +110,8 @@ public DhisResource findByDhisFhirIdentifierCasted( @Nonnull FhirClient fhirClie public PreparedDhisToFhirSearch prepareSearchCasted( @Nonnull FhirVersion fhirVersion, @Nonnull List> ruleInfos, @Nullable Map> filter, @Nullable DateRangeParam lastUpdatedDateRange, int count ) throws DhisToFhirDataProviderException { final Class ruleClass = getRuleClass(); - final List> castedRuleInfos = - ruleInfos.stream().map( r -> new RuleInfo<>( getRuleClass().cast( r.getRule() ), r.getDhisDataReferences() ) ).collect( Collectors.toList() ); + final List> castedRuleInfos = ruleInfos.stream().map( r -> new RuleInfo<>( getRuleClass().cast( r.getRule() ), r.getDhisDataReferences() ) ).collect( Collectors.toList() ); + return prepareSearch( fhirVersion, castedRuleInfos, filter, lastUpdatedDateRange, count ); } } diff --git a/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/AbstractDhisToFhirRequestResolver.java b/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/AbstractDhisToFhirRequestResolver.java index 7a9d2b0f..74ef3c83 100644 --- a/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/AbstractDhisToFhirRequestResolver.java +++ b/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/AbstractDhisToFhirRequestResolver.java @@ -58,11 +58,13 @@ public AbstractDhisToFhirRequestResolver( @Nonnull FhirClientRepository fhirClie @Nonnull public Optional resolveFhirClient( @Nonnull ScriptedDhisResource scriptedDhisResource ) { - Optional fhirClient = fhirClientRepository.findExpEnabledOnly(); + final Optional fhirClient = fhirClientRepository.findExpEnabledOnly(); + if ( fhirClient.isPresent() && fhirClient.get().isEnabled() && fhirClient.get().isExpEnabled() ) { return fhirClient; } + return Optional.empty(); } } diff --git a/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/AbstractDhisToFhirTransformer.java b/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/AbstractDhisToFhirTransformer.java index 222cdbeb..ed19b435 100644 --- a/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/AbstractDhisToFhirTransformer.java +++ b/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/AbstractDhisToFhirTransformer.java @@ -144,47 +144,59 @@ protected Optional getExistingResource( @Nonnull FhirCl if ( context.isUseAdapterIdentifier() ) { final IBaseResource resource = getResourceByAdapterIdentifier( fhirClient, context, ruleInfo, scriptVariables ).orElse( null ); + if ( resource != null ) { if ( !ruleInfo.getRule().isEffectiveFhirUpdateEnable() ) { logger.info( "Existing FHIR resource could be found by adapter identifier, but rule {} does not allow updating FHIR resources.", ruleInfo ); + return Optional.empty(); } + return Optional.of( resource ); } } IBaseResource resource = getResourceBySystemIdentifier( fhirClient, context, ruleInfo, scriptVariables ).orElse( null ); + if ( resource != null ) { if ( !ruleInfo.getRule().isEffectiveFhirUpdateEnable() ) { logger.info( "Existing FHIR resource could be found by system identifier, but rule {} does not allow updating FHIR resources.", ruleInfo ); + return Optional.empty(); } + return Optional.of( resource ); } resource = getResourceByAssignment( fhirClient, context, ruleInfo, scriptVariables ).orElse( null ); + if ( resource != null ) { if ( !ruleInfo.getRule().isEffectiveFhirUpdateEnable() ) { logger.info( "Existing FHIR resource could be found by assigned identifier, but rule {} does not allow updating FHIR resources.", ruleInfo ); + return Optional.empty(); } + return Optional.of( resource ); } resource = getActiveResource( fhirClient, context, ruleInfo, scriptVariables ).orElse( null ); + if ( resource != null ) { if ( !ruleInfo.getRule().isEffectiveFhirUpdateEnable() ) { logger.info( "Existing active FHIR resource could be found, but rule {} does not allow updating FHIR resources.", ruleInfo ); + return Optional.empty(); } + return Optional.of( resource ); } @@ -217,6 +229,7 @@ protected Optional getResource( @Nonnull FhirClient fhi } IBaseResource resource = getExistingResource( fhirClient, context, ruleInfo, scriptVariables ).orElse( null ); + if ( resource != null ) { return Optional.of( resource ); @@ -225,6 +238,7 @@ protected Optional getResource( @Nonnull FhirClient fhi if ( ruleInfo.getRule().isEffectiveFhirCreateEnable() ) { resource = createResource( fhirClient, context, ruleInfo, scriptVariables, sync ); + if ( (resource != null) && !sync ) { lockResource( fhirClient, context, ruleInfo, scriptVariables ); @@ -255,6 +269,7 @@ protected Optional getResourceByAdapterIdentifier( { final R scriptedDhisResource = getDhisResourceClass().cast( TransformerUtils.getScriptVariable( scriptVariables, ScriptVariable.INPUT, ScriptedDhisResource.class ) ); + return getResourceByAdapterIdentifierInternal( fhirClient, context, ruleInfo, scriptedDhisResource, scriptVariables ); } @@ -269,6 +284,7 @@ private Optional clone( context, r ) ); } @@ -279,6 +295,7 @@ protected Optional getResourceBySystemIdentifier( @Nonn { final R scriptedDhisResource = getDhisResourceClass().cast( TransformerUtils.getScriptVariable( scriptVariables, ScriptVariable.INPUT, ScriptedDhisResource.class ) ); + return getResourceBySystemIdentifierInternal( fhirClient, context, ruleInfo, scriptedDhisResource, scriptVariables, new DefaultIdentifierValueProvider() ); } @@ -291,10 +308,12 @@ protected Optional getResourceByAssignment( @Nonnull Fh TransformerUtils.getScriptVariable( scriptVariables, ScriptVariable.INPUT, ScriptedDhisResource.class ) ); final String fhirResourceId = fhirDhisAssignmentRepository.findFirstFhirResourceId( ruleInfo.getRule(), fhirClient, Objects.requireNonNull( scriptedDhisResource.getResourceId() ) ); + if ( fhirResourceId == null ) { return Optional.empty(); } + return getFhirResourceRepository().findRefreshed( fhirClient.getId(), fhirClient.getFhirVersion(), fhirClient.getFhirEndpoint(), ruleInfo.getRule().getFhirResourceType().getResourceTypeName(), fhirResourceId ) .map( r -> clone( context, r ) ); } @@ -305,17 +324,20 @@ private Optional scriptVariables, @Nonnull IdentifierValueProvider identifierValueProvider ) throws TransformerException { final ResourceSystem resourceSystem = context.getResourceSystem( ruleInfo.getRule().getFhirResourceType() ); + if ( resourceSystem == null ) { return Optional.empty(); } final String identifierValue = identifierValueProvider.getIdentifierValue( context, ruleInfo, null, scriptedDhisResource, scriptVariables ); + if ( identifierValue == null ) { logger.info( "FHIR resource type {} defines resource system, but resource does not include an identifier.", ruleInfo.getRule().getFhirResourceType() ); return Optional.empty(); } + if ( StringUtils.isNotBlank( resourceSystem.getCodePrefix() ) && (!identifierValue.startsWith( resourceSystem.getCodePrefix() ) || identifierValue.equals( resourceSystem.getCodePrefix() )) ) { logger.info( "Resource identifier \"{}\" does not start with required prefix \"{}\" for resource type {}.", @@ -324,6 +346,7 @@ private Optional clone( context, r ) ); } @@ -359,6 +382,7 @@ protected boolean updateIdentifiers( @Nonnull DhisToFhirTransformerContext conte if ( resourceSystem != null ) { final String identifierValue = getIdentifierValue( context, ruleInfo, null, scriptedDhisResource, scriptVariables ); + if ( identifierValue == null ) { logger.debug( "FHIR resource type {} defines resource system, but tracked entity does not include an identifier.", ruleInfo.getRule().getFhirResourceType() ); @@ -369,6 +393,7 @@ protected boolean updateIdentifiers( @Nonnull DhisToFhirTransformerContext conte { logger.info( "Tracked entity identifier \"{}\" does not start with required prefix \"{}\" for resource type {}.", identifierValue, resourceSystem.getCodePrefix(), ruleInfo.getRule().getFhirResourceType() ); + return false; } @@ -420,9 +445,11 @@ protected Optional getTrackedEntityFhirResource( @Nonnu @Nonnull ScriptedTrackedEntityInstance scriptedTrackedEntityInstance, @Nonnull Map scriptVariables ) throws TransformerException { IBaseResource resource; + if ( context.getDhisRequest().isDhisFhirId() ) { final IBaseReference reference = context.getDhisFhirResourceReference( scriptedTrackedEntityInstance, trackedEntityResourceType ); + if ( reference == null ) { return Optional.empty(); @@ -436,6 +463,7 @@ protected Optional getTrackedEntityFhirResource( @Nonnu else { resource = getResourceBySystemIdentifierInternal( fhirClient, context, ruleInfo, scriptedTrackedEntityInstance, scriptVariables, new TrackedEntityIdentifierValueProvider() ).orElse( null ); + if ( resource != null ) { return Optional.of( resource ); @@ -445,6 +473,7 @@ protected Optional getTrackedEntityFhirResource( @Nonnu resource = getResourceByAdapterIdentifierInternal( fhirClient, context, ruleInfo, scriptedTrackedEntityInstance, scriptVariables ).orElse( null ); } } + return Optional.ofNullable( resource ); } @@ -471,10 +500,12 @@ protected T cloneToModified( @Nonnull DhisToFhirTransf } final T modifiedResource = clone( context, resource ); + if ( !updateIdentifiers( context, ruleInfo, modifiedResource, scriptVariables, false ) ) { return null; } + return modifiedResource; } @@ -496,6 +527,7 @@ protected boolean evaluateNotModified( @Nonnull DhisToFhirTransformerContext con final AbstractFhirResourceDhisToFhirTransformerUtils fhirResourceUtils = TransformerUtils.getScriptVariable( scriptVariables, ScriptVariable.FHIR_RESOURCE_UTILS, AbstractFhirResourceDhisToFhirTransformerUtils.class ); + return fhirResourceUtils.equalsDeep( base1, base2 ); } @@ -563,6 +595,7 @@ public String getIdentifierValue( @Nonnull DhisToFhirTransformerContext context, { throw new FatalTransformerException( "Identifier value of a tracked entity instance cannot be looked up with an alternative lookup script." ); } + return AbstractDhisToFhirTransformer.this.getTrackedEntityIdentifierValue( context, ruleInfo, scriptedDhisResource, scriptVariables ); } } diff --git a/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/DhisToFhirRuleComparator.java b/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/DhisToFhirRuleComparator.java index 9168e4fd..2fb3fc35 100644 --- a/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/DhisToFhirRuleComparator.java +++ b/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/DhisToFhirRuleComparator.java @@ -1,7 +1,7 @@ package org.dhis2.fhir.adapter.fhir.transform.dhis.impl; /* - * Copyright (c) 2004-2018, University of Oslo + * Copyright (c) 2004-2019, University of Oslo * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -51,11 +51,13 @@ protected DhisToFhirRuleComparator() @Override public int compare( RuleInfo o1, RuleInfo o2 ) { - int value = o1.getRule().getFhirResourceType().getOrder() - o2.getRule().getFhirResourceType().getOrder(); + final int value = o1.getRule().getFhirResourceType().getOrder() - o2.getRule().getFhirResourceType().getOrder(); + if ( value != 0 ) { return value; } + return o1.getRule().compareTo( o2.getRule() ); } } diff --git a/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/DhisToFhirTransformerContextImpl.java b/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/DhisToFhirTransformerContextImpl.java index cc3b73d6..ec19a786 100644 --- a/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/DhisToFhirTransformerContextImpl.java +++ b/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/DhisToFhirTransformerContextImpl.java @@ -208,10 +208,12 @@ public Object getAttribute( @Nonnull String name ) public IBaseReference getDhisFhirResourceReference( @Nullable ScriptedDhisResource dhisResource, Object... fhirResourceTypes ) { final List references = getDhisFhirResourceReferencesLimited( dhisResource, 1, fhirResourceTypes ); + if ( references.isEmpty() ) { return null; } + return references.get( 0 ); } @@ -229,6 +231,7 @@ protected List getDhisFhirResourceReferencesLimited( @Nullable S { return Collections.emptyList(); } + return fhirReferenceResolver.resolveFhirReferences( fhirClient, dhisResource, Stream.of( fhirResourceTypes ).map( frt -> { try { diff --git a/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/DhisToFhirTransformerRequestImpl.java b/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/DhisToFhirTransformerRequestImpl.java index 1248979f..1ca9d5ce 100644 --- a/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/DhisToFhirTransformerRequestImpl.java +++ b/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/DhisToFhirTransformerRequestImpl.java @@ -93,6 +93,7 @@ public ScriptedDhisResource getInput() { throw new FatalTransformerException( "Transformer input has not yet been set." ); } + return input; } @@ -128,6 +129,7 @@ public UUID getRuleId() { throw new FatalTransformerException( "Rule ID is not available." ); } + return rules.get( ruleIndex ).getRule().getId(); } @@ -159,11 +161,14 @@ public RuleInfo nextRule() { return null; } + final RuleInfo ruleInfo = rules.get( ruleIndex++ ); - if ( (input != null) && (ruleInfo.getRule().getDhisResourceType() != input.getResourceType()) ) + + if ( input != null && ruleInfo.getRule().getDhisResourceType() != input.getResourceType() ) { input = null; } + return ruleInfo; } } diff --git a/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/DhisToFhirTransformerServiceImpl.java b/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/DhisToFhirTransformerServiceImpl.java index 64e391ac..5b90e841 100644 --- a/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/DhisToFhirTransformerServiceImpl.java +++ b/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/DhisToFhirTransformerServiceImpl.java @@ -246,6 +246,7 @@ public List resolveFhirReferences( @Nonnull FhirClient fhirClien do { final DhisToFhirTransformOutcome outcome = transform( transformerRequest ); + if ( outcome == null ) { transformerRequest = null; @@ -301,6 +302,7 @@ public DhisToFhirTransformerRequest createTransformerRequest( @Nonnull FhirClien final DhisToFhirRequestResolver requestResolver = getRequestResolver( dhisRequest.getResourceType() ); final ScriptedDhisResource scriptedResource = requestResolver.convert( Objects.requireNonNull( DhisBeanTransformerUtils.clone( resource ) ), dhisRequest ); + return createTransformerRequest( false, fhirClient, dhisRequest, scriptedResource, Collections.singletonList( ruleInfo ) ); } @@ -309,6 +311,7 @@ public DhisToFhirTransformerRequest createTransformerRequest( @Nonnull FhirClien public DhisToFhirTransformerRequest createTransformerRequest( @Nonnull FhirClient fhirClient, @Nonnull DhisRequest dhisRequest, @Nonnull DhisResource resource, @Nonnull List> rules ) { final DhisToFhirRequestResolver requestResolver = getRequestResolver( dhisRequest.getResourceType() ); + return createTransformerRequest( dhisRequest, rr -> rr.convert( resource, dhisRequest ), requestResolver, ( rr, scriptedResource ) -> rr.resolveRules( scriptedResource, rules ), ri -> true, ( si, rr ) -> fhirClient ); } @@ -326,6 +329,7 @@ protected DhisToFhirTransformerRequest createTransformerRequest( @Nonnull DhisRe @Nonnull Predicate> ruleInfoPredicate, @Nonnull BiFunction fhirClientFunction ) { final DhisToFhirRequestResolver requestResolver = getRequestResolver( dhisRequest.getResourceType() ); + return createTransformerRequest( dhisRequest, scriptedDhisResourceFunction, requestResolver, DhisToFhirRequestResolver::resolveRules, ruleInfoPredicate, fhirClientFunction ); } @@ -337,6 +341,7 @@ protected DhisToFhirTransformerRequest createTransformerRequest( @Nonnull DhisRe final ScriptedDhisResource scriptedResource = scriptedDhisResourceFunction.apply( requestResolver ); final List> ruleInfos = ruleResolverFunction.apply( requestResolver, scriptedResource ) .stream().filter( ruleInfoPredicate ).collect( Collectors.toList() ); + if ( ruleInfos.isEmpty() ) { logger.info( "Could not find any rule to process DHIS resource." ); @@ -344,6 +349,7 @@ protected DhisToFhirTransformerRequest createTransformerRequest( @Nonnull DhisRe } final FhirClient fhirClient = fhirClientFunction.apply( scriptedResource, requestResolver ); + if ( fhirClient == null ) { logger.info( "Could not determine FHIR client to process DHIS resource." ); @@ -363,15 +369,19 @@ public DhisToFhirTransformerRequest createTransformerRequest( @Nonnull FhirClien final List> ruleInfos = requestResolver.resolveRules( scriptedDhisResource ); final RuleInfo matchingRuleInfo = ruleInfos.stream().filter( ri -> ri.getRule().getId().equals( ruleId ) ).findFirst().orElse( null ); + if ( (matchingRuleInfo == null) || !fhirResourceType.equals( matchingRuleInfo.getRule().getFhirResourceType() ) ) { logger.info( "Could not find any matching rule to process DHIS resource." ); + return null; } + final List> groupingRuleInfos = ruleInfos.stream() .filter( ri -> ri.getRule().isGrouping() ).collect( Collectors.toList() ); final List> resultingRuleInfos; + if ( groupingRuleInfos.isEmpty() ) { resultingRuleInfos = Collections.singletonList( matchingRuleInfo ); @@ -385,6 +395,7 @@ else if ( matchingRuleInfo.getRule().isGrouping() ) resultingRuleInfos = ruleInfos.stream() .filter( ri -> ri.getRule().isGrouping() || ri.getRule().getId().equals( ruleId ) ).collect( Collectors.toList() ); } + return createTransformerRequest( matchingRuleInfo.getRule().isGrouping(), fhirClient, dhisRequest, scriptedDhisResource, resultingRuleInfos ); } @@ -400,6 +411,7 @@ protected DhisToFhirTransformerRequest createTransformerRequest( boolean groupin .collect( Collectors.toMap( ResourceSystem::getFhirResourceType, rs -> rs ) ); final Map transformerUtils = this.transformerUtils.get( fhirClient.getFhirVersion() ); + if ( transformerUtils == null ) { throw new TransformerMappingException( "No transformer utils can be found for FHIR version " + fhirClient.getFhirVersion() ); @@ -420,6 +432,7 @@ public DhisToFhirTransformerRequest updateTransformerRequest( @Nonnull DhisReque final DhisToFhirTransformerRequestImpl transformerRequestImpl = (DhisToFhirTransformerRequestImpl) transformerRequest; transformerRequestImpl.setInput( scriptedInput ); + return transformerRequest; } @@ -428,6 +441,7 @@ private List> filterAvailableResourceRules( @No { final Map availableResourcesByType = availableResources.stream() .collect( Collectors.toMap( AvailableFhirClientResource::getResourceType, a -> a ) ); + // not all resources may be available on FHIR client return ruleInfos.stream().filter( r -> { final AvailableFhirClientResource availableResource = availableResourcesByType.get( r.getRule().getFhirResourceType() ); @@ -443,10 +457,12 @@ private List> filterAvailableResourceRules( @No private DhisToFhirRequestResolver getRequestResolver( @Nonnull DhisResourceType resourceType ) { final DhisToFhirRequestResolver requestResolver = requestResolvers.get( resourceType ); + if ( requestResolver == null ) { throw new TransformerMappingException( "No request resolver can be found for DHIS resource type " + resourceType ); } + return requestResolver; } @@ -458,6 +474,7 @@ public DhisToFhirTransformOutcome transform( @Nonnull D final boolean firstRule = transformerRequestImpl.isFirstRule(); RuleInfo ruleInfo; + while ( (ruleInfo = transformerRequestImpl.nextRule()) != null ) { Cache transformerRequestCache = null; diff --git a/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/TransformerRequestKey.java b/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/TransformerRequestKey.java index 59f08884..e7fc60e2 100644 --- a/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/TransformerRequestKey.java +++ b/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/TransformerRequestKey.java @@ -57,11 +57,19 @@ public TransformerRequestKey( @Nonnull UUID ruleId, @Nonnull DhisResourceId dhis @Override public boolean equals( Object o ) { - if ( this == o ) return true; - if ( o == null || getClass() != o.getClass() ) return false; + if ( this == o ) + { + return true; + } + + if ( o == null || getClass() != o.getClass() ) + { + return false; + } + TransformerRequestKey that = (TransformerRequestKey) o; - return ruleId.equals( that.ruleId ) && - dhisResourceId.equals( that.dhisResourceId ); + + return ruleId.equals( that.ruleId ) && dhisResourceId.equals( that.dhisResourceId ); } @Override diff --git a/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/dhis/search/SearchFilterCollector.java b/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/dhis/search/SearchFilterCollector.java index a818504b..9a57a55a 100644 --- a/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/dhis/search/SearchFilterCollector.java +++ b/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/dhis/search/SearchFilterCollector.java @@ -79,6 +79,7 @@ public SearchFilterCollector( @Nullable ItemContainerType itemContainerType, @Nu public SearchFilterCollector( @Nullable Function dhisPropertyRefResolver, @Nullable Map> filter ) { this.dhisPropertyRefResolver = dhisPropertyRefResolver; + if ( filter != null ) { filter.forEach( ( key, value ) -> { @@ -142,10 +143,11 @@ public void addQueryParam( @Nonnull String propertyName, @Nullable String value public void addFilterExpression( @Nonnull String propertyName, @Nonnull String operator, @Nullable String value ) { - if ( ( value != null ) && ( value.indexOf( ':' ) >= 0 ) ) + if ( value != null && value.indexOf( ':' ) >= 0 ) { throw new DhisToFhirDataProviderException( "Colon characters in search parameter values are not supported." ); } + filterExpressions.add( propertyName + ':' + operator + ':' + StringUtils.defaultString( value ) ); } @@ -162,11 +164,13 @@ public U add( @Nonnull U uriBuilder, @Nonnull List uriBuilder.queryParam( key, values.toArray() ) ); filterExpressions.forEach( fe -> { uriBuilder.queryParam( "filter", "{sfc" + variables.size() + "}" ); variables.add( fe ); } ); + return uriBuilder; } } diff --git a/fhir/src/main/resources/db/migration/sample/V1.1.0.41_10_0__Plan_Definition_Sample_Data.sql b/fhir/src/main/resources/db/migration/sample/V1.1.0.41_10_0__Plan_Definition_Sample_Data.sql new file mode 100644 index 00000000..e21ec8fd --- /dev/null +++ b/fhir/src/main/resources/db/migration/sample/V1.1.0.41_10_0__Plan_Definition_Sample_Data.sql @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2004-2018, University of Oslo + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * Neither the name of the HISP project nor the names of its contributors may + * be used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO PROGRAM_STAGE_EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +-- @formatter:off + +UPDATE fhir_client SET fhir_version='R4', remote_base_url='http://localhost:8082/hapi-fhir-jpaserver/fhir' +WHERE id='73cd99c5-0ca8-42ad-a53b-1891fccce08f'; + +-- virtual subscription for FHIR organizations +INSERT INTO fhir_client_resource (id, version, fhir_client_id, fhir_resource_type, exp_only, fhir_criteria_parameters, description) +VALUES ('2520acc5-86b4-4716-ae0f-ea0531eb885a', 0, '73cd99c5-0ca8-42ad-a53b-1891fccce08f', 'PLAN_DEFINITION', TRUE, NULL, 'Virtual subscription for all Plan Definitions.'); +INSERT INTO fhir_client_resource_update(id) +VALUES ('2520acc5-86b4-4716-ae0f-ea0531eb885a'); diff --git a/fhir/src/test/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/metadata/program/AbstractProgramMetadataToFhirPlanDefinitionTransformerTest.java b/fhir/src/test/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/metadata/program/AbstractProgramMetadataToFhirPlanDefinitionTransformerTest.java new file mode 100644 index 00000000..8ee2c709 --- /dev/null +++ b/fhir/src/test/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/metadata/program/AbstractProgramMetadataToFhirPlanDefinitionTransformerTest.java @@ -0,0 +1,206 @@ +package org.dhis2.fhir.adapter.fhir.transform.dhis.impl.metadata.program; + +/* + * Copyright (c) 2004-2019, University of Oslo + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * Neither the name of the HISP project nor the names of its contributors may + * be used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +import org.dhis2.fhir.adapter.dhis.model.DhisResourceType; +import org.dhis2.fhir.adapter.dhis.model.Reference; +import org.dhis2.fhir.adapter.dhis.model.ReferenceType; +import org.dhis2.fhir.adapter.dhis.orgunit.OrganizationUnitService; +import org.dhis2.fhir.adapter.dhis.tracker.program.WritableProgram; +import org.dhis2.fhir.adapter.dhis.tracker.trackedentity.TrackedEntityMetadataService; +import org.dhis2.fhir.adapter.dhis.tracker.trackedentity.WritableTrackedEntityType; +import org.dhis2.fhir.adapter.fhir.data.repository.FhirDhisAssignmentRepository; +import org.dhis2.fhir.adapter.fhir.metadata.model.FhirResourceType; +import org.dhis2.fhir.adapter.fhir.metadata.model.ProgramMetadataRule; +import org.dhis2.fhir.adapter.fhir.metadata.model.RuleInfo; +import org.dhis2.fhir.adapter.fhir.metadata.repository.SystemRepository; +import org.dhis2.fhir.adapter.fhir.metadata.repository.TrackedEntityRuleRepository; +import org.dhis2.fhir.adapter.fhir.repository.FhirResourceRepository; +import org.dhis2.fhir.adapter.fhir.script.ScriptExecutor; +import org.dhis2.fhir.adapter.fhir.transform.scripted.AccessibleScriptedDhisMetadata; +import org.dhis2.fhir.adapter.lock.LockManager; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.mockito.Mock; +import org.mockito.Mockito; +import org.mockito.junit.MockitoJUnit; +import org.mockito.junit.MockitoRule; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.HashSet; +import java.util.Optional; + +import static org.mockito.Mockito.CALLS_REAL_METHODS; +import static org.mockito.Mockito.withSettings; + +/** + * Unit tests for {@link AbstractProgramMetadataToFhirPlanDefinitionTransformer}. + * + * @author volsch + */ +public class AbstractProgramMetadataToFhirPlanDefinitionTransformerTest +{ + @Mock + private ScriptExecutor scriptExecutor; + + @Mock + private LockManager lockManager; + + @Mock + private SystemRepository systemRepository; + + @Mock + private FhirResourceRepository fhirResourceRepository; + + @Mock + private FhirDhisAssignmentRepository fhirDhisAssignmentRepository; + + @Mock + private OrganizationUnitService organizationUnitService; + + @Mock + private TrackedEntityMetadataService trackedEntityMetadataService; + + @Mock + private TrackedEntityRuleRepository trackedEntityRuleRepository; + + private AbstractProgramMetadataToFhirPlanDefinitionTransformer transformer; + + @Rule + public MockitoRule rule = MockitoJUnit.rule(); + + @Before + public void setUp() + { + transformer = Mockito.mock( AbstractProgramMetadataToFhirPlanDefinitionTransformer.class, + withSettings().useConstructor( scriptExecutor, lockManager, systemRepository, fhirResourceRepository, fhirDhisAssignmentRepository, organizationUnitService, + trackedEntityMetadataService, trackedEntityRuleRepository ).defaultAnswer( CALLS_REAL_METHODS ) ); + } + + @Test + public void getDhisResourceType() + { + Assert.assertEquals( DhisResourceType.PROGRAM_METADATA, transformer.getDhisResourceType() ); + } + + @Test + public void getFhirResourceType() + { + Assert.assertEquals( FhirResourceType.PLAN_DEFINITION, transformer.getFhirResourceType() ); + } + + @Test + public void getDhisResourceClass() + { + Assert.assertEquals( AccessibleScriptedDhisMetadata.class, transformer.getDhisResourceClass() ); + } + + @Test + public void isApplicableProgram() + { + Mockito.doReturn( Optional.of( new WritableTrackedEntityType( "a1234567890", "Test", Collections.emptyList() ) ) ) + .when( trackedEntityMetadataService ).findTypeByReference( Mockito.eq( new Reference( "a1234567890", ReferenceType.ID ) ) ); + Mockito.doReturn( Collections.singletonList( new RuleInfo<>( new ProgramMetadataRule(), Collections.emptyList() ) ) ) + .when( trackedEntityRuleRepository ).findByTypeRefs( Mockito.eq( new HashSet<>( + Arrays.asList( new Reference( "a1234567890", ReferenceType.ID ), new Reference( "Test", ReferenceType.NAME ) ) ) ) ); + + final WritableProgram program = new WritableProgram(); + program.setName( "Test Program" ); + program.setDescription( "Test Description" ); + program.setWithoutRegistration( false ); + program.setTrackedEntityTypeId( "a1234567890" ); + program.setStages( new ArrayList<>() ); + + Assert.assertTrue( transformer.isApplicableProgram( program ) ); + } + + @Test + public void isApplicableProgramWithoutRegistration() + { + final WritableProgram program = new WritableProgram(); + program.setName( "Test Program" ); + program.setDescription( "Test Description" ); + program.setWithoutRegistration( true ); + program.setTrackedEntityTypeId( "a1234567890" ); + program.setStages( new ArrayList<>() ); + + Assert.assertFalse( transformer.isApplicableProgram( program ) ); + } + + @Test + public void isApplicableProgramWithoutTrackedEntityType() + { + final WritableProgram program = new WritableProgram(); + program.setName( "Test Program" ); + program.setDescription( "Test Description" ); + program.setWithoutRegistration( false ); + program.setStages( new ArrayList<>() ); + + Assert.assertFalse( transformer.isApplicableProgram( program ) ); + } + + @Test + public void isApplicableProgramWithoutTrackedEntityMetadata() + { + Mockito.doReturn( Optional.empty() ) + .when( trackedEntityMetadataService ).findTypeByReference( Mockito.eq( new Reference( "a1234567890", ReferenceType.ID ) ) ); + + final WritableProgram program = new WritableProgram(); + program.setName( "Test Program" ); + program.setDescription( "Test Description" ); + program.setWithoutRegistration( false ); + program.setTrackedEntityTypeId( "a1234567890" ); + program.setStages( new ArrayList<>() ); + + Assert.assertFalse( transformer.isApplicableProgram( program ) ); + } + + @Test + public void isApplicableProgramWithoutRules() + { + Mockito.doReturn( Optional.of( new WritableTrackedEntityType( "a1234567890", "Test", Collections.emptyList() ) ) ) + .when( trackedEntityMetadataService ).findTypeByReference( Mockito.eq( new Reference( "a1234567890", ReferenceType.ID ) ) ); + Mockito.doReturn( Collections.emptyList() ) + .when( trackedEntityRuleRepository ).findByTypeRefs( Mockito.eq( new HashSet<>( + Arrays.asList( new Reference( "a1234567890", ReferenceType.ID ), new Reference( "Test", ReferenceType.NAME ) ) ) ) ); + + final WritableProgram program = new WritableProgram(); + program.setName( "Test Program" ); + program.setDescription( "Test Description" ); + program.setWithoutRegistration( false ); + program.setTrackedEntityTypeId( "a1234567890" ); + program.setStages( new ArrayList<>() ); + + Assert.assertFalse( transformer.isApplicableProgram( program ) ); + } +} \ No newline at end of file From 3dda89913f05a14d68c854c832dd1e89af8dcb99 Mon Sep 17 00:00:00 2001 From: Volker Schmidt Date: Mon, 29 Jul 2019 19:36:34 +0200 Subject: [PATCH 11/16] Enables export of program metadata. --- .../impl/DhisDataProcessorItemRetrieverImpl.java | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/dhis/src/main/java/org/dhis2/fhir/adapter/dhis/sync/impl/DhisDataProcessorItemRetrieverImpl.java b/dhis/src/main/java/org/dhis2/fhir/adapter/dhis/sync/impl/DhisDataProcessorItemRetrieverImpl.java index cdf131af..6a655afa 100644 --- a/dhis/src/main/java/org/dhis2/fhir/adapter/dhis/sync/impl/DhisDataProcessorItemRetrieverImpl.java +++ b/dhis/src/main/java/org/dhis2/fhir/adapter/dhis/sync/impl/DhisDataProcessorItemRetrieverImpl.java @@ -1,7 +1,7 @@ package org.dhis2.fhir.adapter.dhis.sync.impl; /* - * Copyright (c) 2004-2018, University of Oslo + * Copyright (c) 2004-2019, University of Oslo * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -40,6 +40,7 @@ import org.dhis2.fhir.adapter.dhis.orgunit.OrganizationUnitService; import org.dhis2.fhir.adapter.dhis.sync.SyncExcludedDhisUsernameRetriever; import org.dhis2.fhir.adapter.dhis.tracker.program.EventService; +import org.dhis2.fhir.adapter.dhis.tracker.program.ProgramMetadataService; import org.dhis2.fhir.adapter.dhis.tracker.trackedentity.TrackedEntityService; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.stereotype.Component; @@ -67,6 +68,8 @@ public class DhisDataProcessorItemRetrieverImpl implements DataProcessorItemRetr private final OrganizationUnitService organizationUnitService; + private final ProgramMetadataService programMetadataService; + private final TrackedEntityService trackedEntityService; private final EventService eventService; @@ -78,6 +81,7 @@ public DhisDataProcessorItemRetrieverImpl( @Nonnull @Qualifier( "systemDhis2Authorization" ) Authorization systemDhis2Authorization, @Nonnull SyncExcludedDhisUsernameRetriever excludedDhisUsernameRetriever, @Nonnull OrganizationUnitService organizationUnitService, + @Nonnull ProgramMetadataService programMetadataService, @Nonnull TrackedEntityService trackedEntityService, @Nonnull EventService eventService, @Nonnull DhisSyncProcessorConfig processorConfig, @@ -89,6 +93,7 @@ public DhisDataProcessorItemRetrieverImpl( this.trackedEntityService = trackedEntityService; this.eventService = eventService; this.organizationUnitService = organizationUnitService; + this.programMetadataService = programMetadataService; this.processorConfig = processorConfig; } @@ -110,6 +115,12 @@ public Instant poll( @Nonnull DhisSyncGroup group, @Nonnull Instant lastUpdated, excludedDhisUsernames, consumer ); result = ObjectUtils.min( result, currentResult ); } + if ( resourceTypes.contains( DhisResourceType.PROGRAM_METADATA ) ) + { + final Instant currentResult = programMetadataService.poll( group, lastUpdated, toleranceMillis, maxSearchCount, + excludedDhisUsernames, consumer ); + result = ObjectUtils.min( result, currentResult ); + } if ( resourceTypes.contains( DhisResourceType.TRACKED_ENTITY ) ) { final Instant currentResult = trackedEntityService.poll( group, lastUpdated, toleranceMillis, maxSearchCount, From ccad79e8644d65e057a6ef73e4c1c6c07c4e2c31 Mon Sep 17 00:00:00 2001 From: Volker Schmidt Date: Mon, 29 Jul 2019 22:45:12 +0200 Subject: [PATCH 12/16] Provides polling support for plan definitions. --- .../AbstractProgramStageFhirRestAppTest.java | 5 +- .../r4/R4ProgramMetadataFhirRestAppTest.java | 17 ++--- ...stractEnrollmentTransformationAppTest.java | 7 +- ...ractProgramStageTransformationAppTest.java | 5 +- .../adapter/dhis/model/DhisResourceType.java | 28 ++++---- .../impl/OrganizationUnitServiceImpl.java | 2 +- .../dhis/service/DhisMetadataService.java | 12 +--- .../dhis/service/DhisPolledService.java | 52 +++++++++++++++ .../adapter/dhis/service/DhisService.java | 46 +++++++++++++ .../impl/AbstractDhisMetadataServiceImpl.java | 2 +- .../DhisDataProcessorItemRetrieverImpl.java | 66 ++++++------------- .../tracker/program/EnrollmentService.java | 4 +- .../dhis/tracker/program/EventService.java | 13 +--- .../dhis/tracker/program/WritableProgram.java | 4 +- .../program/impl/EnrollmentServiceImpl.java | 8 +++ .../program/impl/EventServiceImpl.java | 8 +++ .../impl/ProgramMetadataServiceImpl.java | 4 +- .../trackedentity/TrackedEntityService.java | 13 +--- .../impl/TrackedEntityServiceImpl.java | 7 ++ .../impl/ProgramMetadataServiceImplTest.java | 15 +++-- .../impl/AbstractDhisToFhirTransformer.java | 7 +- 21 files changed, 204 insertions(+), 121 deletions(-) create mode 100644 dhis/src/main/java/org/dhis2/fhir/adapter/dhis/service/DhisPolledService.java create mode 100644 dhis/src/main/java/org/dhis2/fhir/adapter/dhis/service/DhisService.java diff --git a/app/src/test/java/org/dhis2/fhir/adapter/fhir/server/AbstractProgramStageFhirRestAppTest.java b/app/src/test/java/org/dhis2/fhir/adapter/fhir/server/AbstractProgramStageFhirRestAppTest.java index 8962a7e8..b7ed5cd6 100644 --- a/app/src/test/java/org/dhis2/fhir/adapter/fhir/server/AbstractProgramStageFhirRestAppTest.java +++ b/app/src/test/java/org/dhis2/fhir/adapter/fhir/server/AbstractProgramStageFhirRestAppTest.java @@ -67,12 +67,13 @@ protected void expectProgramStageMetadataRequests() throws Exception .andRespond( withSuccess( IOUtils.resourceToString( "/org/dhis2/fhir/adapter/dhis/test/single-org-unit-OU_1234.json", StandardCharsets.UTF_8 ), MediaType.APPLICATION_JSON ) ); systemDhis2Server.expect( ExpectedCount.between( 0, 1 ), method( HttpMethod.GET ) ).andExpect( header( "Authorization", testConfiguration.getDhis2SystemAuthorization() ) ) .andExpect( requestTo( dhis2BaseUrl + "/api/" + dhis2ApiVersion + "/programs/EPDyQuoRnXk.json?" + - "fields=id,name,code,description,selectIncidentDatesInFuture,selectEnrollmentDatesInFuture,displayIncidentDate,registration,withoutRegistration,captureCoordinates,trackedEntityType%5Bid%5D,programTrackedEntityAttributes%5Bid,name,valueType," + + "fields=id,name,code,description,lastUpdated,selectIncidentDatesInFuture,selectEnrollmentDatesInFuture,displayIncidentDate,registration,withoutRegistration,captureCoordinates,trackedEntityType%5Bid%5D,programTrackedEntityAttributes%5Bid," + + "name,valueType," + "mandatory,allowFutureDate,trackedEntityAttribute%5Bid,name,code,valueType,generated%5D%5D,programStages%5Bid,name,description,repeatable,captureCoordinates,generatedByEnrollmentDate,minDaysFromStart,programStageDataElements%5Bid,compulsory," + "allowProvidedElsewhere,dataElement%5Bid,name,code,formName,valueType,optionSetValue,optionSet%5Bid,name,options%5Bcode,name%5D%5D%5D%5D%5D" ) ) .andRespond( withSuccess( IOUtils.resourceToString( "/org/dhis2/fhir/adapter/dhis/test/single-program.json", StandardCharsets.UTF_8 ), MediaType.APPLICATION_JSON ) ); systemDhis2Server.expect( ExpectedCount.between( 0, 1 ), method( HttpMethod.GET ) ).andExpect( header( "Authorization", testConfiguration.getDhis2SystemAuthorization() ) ) - .andExpect( requestTo( dhis2BaseUrl + "/api/" + dhis2ApiVersion + "/programs.json?paging=false&fields=id,name,code,description,selectIncidentDatesInFuture,selectEnrollmentDatesInFuture,displayIncidentDate,registration," + + .andExpect( requestTo( dhis2BaseUrl + "/api/" + dhis2ApiVersion + "/programs.json?paging=false&fields=id,name,code,description,lastUpdated,selectIncidentDatesInFuture,selectEnrollmentDatesInFuture,displayIncidentDate,registration," + "withoutRegistration,captureCoordinates,trackedEntityType%5Bid%5D,programTrackedEntityAttributes%5Bid,name,valueType,mandatory,allowFutureDate,trackedEntityAttribute%5Bid,name,code,valueType,generated%5D%5D,programStages%5Bid,name," + "description,repeatable,captureCoordinates,generatedByEnrollmentDate,minDaysFromStart,programStageDataElements%5Bid,compulsory,allowProvidedElsewhere,dataElement%5Bid,name,code,formName,valueType,optionSetValue,optionSet%5Bid,name," + "options%5Bcode,name%5D%5D%5D%5D%5D&filter=name:eq:Child%20Programme" ) ) diff --git a/app/src/test/java/org/dhis2/fhir/adapter/fhir/server/r4/R4ProgramMetadataFhirRestAppTest.java b/app/src/test/java/org/dhis2/fhir/adapter/fhir/server/r4/R4ProgramMetadataFhirRestAppTest.java index 1402d045..695d91d2 100644 --- a/app/src/test/java/org/dhis2/fhir/adapter/fhir/server/r4/R4ProgramMetadataFhirRestAppTest.java +++ b/app/src/test/java/org/dhis2/fhir/adapter/fhir/server/r4/R4ProgramMetadataFhirRestAppTest.java @@ -78,7 +78,8 @@ public void getPlanDefinitionWithInvalidAuthorization() { userDhis2Server.expect( ExpectedCount.once(), method( HttpMethod.GET ) ).andExpect( header( "Authorization", "Basic Zmhpcl9jbGllbnQ6aW52YWxpZF8x" ) ) .andExpect( requestTo( dhis2BaseUrl + "/api/" + dhis2ApiVersion + "/programs/EPDyQuoRnXk.json?" + - "fields=id,name,code,description,selectIncidentDatesInFuture,selectEnrollmentDatesInFuture,displayIncidentDate,registration,withoutRegistration,captureCoordinates,trackedEntityType%5Bid%5D,programTrackedEntityAttributes%5Bid,name,valueType," + + "fields=id,name,code,description,lastUpdated,selectIncidentDatesInFuture,selectEnrollmentDatesInFuture,displayIncidentDate,registration,withoutRegistration,captureCoordinates,trackedEntityType%5Bid%5D,programTrackedEntityAttributes%5Bid," + + "name,valueType," + "mandatory,allowFutureDate,trackedEntityAttribute%5Bid,name,code,valueType,generated%5D%5D,programStages%5Bid,name,description,repeatable,captureCoordinates,generatedByEnrollmentDate,minDaysFromStart,programStageDataElements%5Bid,compulsory," + "allowProvidedElsewhere,dataElement%5Bid,name,code,formName,valueType,optionSetValue,optionSet%5Bid,name,options%5Bcode,name%5D%5D%5D%5D%5D" ) ) .andRespond( withStatus( HttpStatus.UNAUTHORIZED ) ); @@ -105,13 +106,13 @@ private void getPlanDefinition() throws Exception "fields=id,name,trackedEntityTypeAttributes%5Bid,name,valueType,mandatory,trackedEntityAttribute%5Bid,name,code,valueType,generated,optionSetValue,optionSet%5Bid,name,options%5Bcode,name%5D%5D%5D%5D" ) ) .andRespond( withSuccess( IOUtils.resourceToString( "/org/dhis2/fhir/adapter/dhis/test/single-tracked-entity-type.json", StandardCharsets.UTF_8 ), MediaType.APPLICATION_JSON ) ); userDhis2Server.expect( ExpectedCount.once(), method( HttpMethod.GET ) ).andExpect( header( "Authorization", "Basic Zmhpcl9jbGllbnQ6Zmhpcl9jbGllbnRfMQ==" ) ) - .andExpect( requestTo( dhis2BaseUrl + "/api/" + dhis2ApiVersion + "/programs/EPDyQuoRnXk.json?fields=id,name,code,description,selectIncidentDatesInFuture,selectEnrollmentDatesInFuture,displayIncidentDate,registration," + + .andExpect( requestTo( dhis2BaseUrl + "/api/" + dhis2ApiVersion + "/programs/EPDyQuoRnXk.json?fields=id,name,code,description,lastUpdated,selectIncidentDatesInFuture,selectEnrollmentDatesInFuture,displayIncidentDate,registration," + "withoutRegistration,captureCoordinates,trackedEntityType%5Bid%5D,programTrackedEntityAttributes%5Bid,name,valueType,mandatory,allowFutureDate,trackedEntityAttribute%5Bid,name,code,valueType,generated%5D%5D,programStages%5Bid,name," + "description,repeatable,captureCoordinates,generatedByEnrollmentDate,minDaysFromStart,programStageDataElements%5Bid,compulsory,allowProvidedElsewhere,dataElement%5Bid,name,code,formName,valueType,optionSetValue,optionSet%5Bid,name," + "options%5Bcode,name%5D%5D%5D%5D%5D" ) ) .andRespond( withSuccess( IOUtils.resourceToString( "/org/dhis2/fhir/adapter/dhis/test/single-program.json", StandardCharsets.UTF_8 ), MediaType.APPLICATION_JSON ) ); userDhis2Server.expect( ExpectedCount.between( 0, 1 ), method( HttpMethod.GET ) ).andExpect( header( "Authorization", "Basic Zmhpcl9jbGllbnQ6Zmhpcl9jbGllbnRfMQ==" ) ) - .andExpect( requestTo( dhis2BaseUrl + "/api/" + dhis2ApiVersion + "/programs.json?paging=false&fields=id,name,code,description,selectIncidentDatesInFuture,selectEnrollmentDatesInFuture,displayIncidentDate,registration," + + .andExpect( requestTo( dhis2BaseUrl + "/api/" + dhis2ApiVersion + "/programs.json?paging=false&fields=id,name,code,description,lastUpdated,selectIncidentDatesInFuture,selectEnrollmentDatesInFuture,displayIncidentDate,registration," + "withoutRegistration,captureCoordinates,trackedEntityType%5Bid%5D,programTrackedEntityAttributes%5Bid,name,valueType,mandatory,allowFutureDate,trackedEntityAttribute%5Bid,name,code,valueType,generated%5D%5D,programStages%5Bid,name," + "description,repeatable,captureCoordinates,generatedByEnrollmentDate,minDaysFromStart,programStageDataElements%5Bid,compulsory,allowProvidedElsewhere,dataElement%5Bid,name,code,formName,valueType,optionSetValue,optionSet%5Bid,name," + "options%5Bcode,name%5D%5D%5D%5D%5D" ) ) @@ -146,7 +147,7 @@ private void getPlanDefinitionNotExists() userDhis2Server.reset(); userDhis2Server.expect( ExpectedCount.once(), method( HttpMethod.GET ) ).andExpect( header( "Authorization", "Basic Zmhpcl9jbGllbnQ6Zmhpcl9jbGllbnRfMQ==" ) ) - .andExpect( requestTo( dhis2BaseUrl + "/api/" + dhis2ApiVersion + "/programs/0dXIdLNUNEn.json?fields=id,name,code,description,selectIncidentDatesInFuture,selectEnrollmentDatesInFuture,displayIncidentDate,registration," + + .andExpect( requestTo( dhis2BaseUrl + "/api/" + dhis2ApiVersion + "/programs/0dXIdLNUNEn.json?fields=id,name,code,description,lastUpdated,selectIncidentDatesInFuture,selectEnrollmentDatesInFuture,displayIncidentDate,registration," + "withoutRegistration,captureCoordinates,trackedEntityType%5Bid%5D,programTrackedEntityAttributes%5Bid,name,valueType,mandatory,allowFutureDate,trackedEntityAttribute%5Bid,name,code,valueType,generated%5D%5D,programStages%5Bid,name," + "description,repeatable,captureCoordinates,generatedByEnrollmentDate,minDaysFromStart,programStageDataElements%5Bid,compulsory,allowProvidedElsewhere,dataElement%5Bid,name,code,formName,valueType,optionSetValue,optionSet%5Bid,name," + "options%5Bcode,name%5D%5D%5D%5D%5D" ) ) @@ -210,7 +211,7 @@ public void getPlanDefinitionByIdentifierWithoutAuthorization() public void getPlanDefinitionByIdentifierInvalidAuthorization() { userDhis2Server.expect( ExpectedCount.once(), method( HttpMethod.GET ) ).andExpect( header( "Authorization", "Basic Zmhpcl9jbGllbnQ6aW52YWxpZF8x" ) ) - .andExpect( requestTo( dhis2BaseUrl + "/api/" + dhis2ApiVersion + "/programs.json?paging=false&fields=id,name,code,description,selectIncidentDatesInFuture,selectEnrollmentDatesInFuture,displayIncidentDate,registration,withoutRegistration," + + .andExpect( requestTo( dhis2BaseUrl + "/api/" + dhis2ApiVersion + "/programs.json?paging=false&fields=id,name,code,description,lastUpdated,selectIncidentDatesInFuture,selectEnrollmentDatesInFuture,displayIncidentDate,registration,withoutRegistration," + "captureCoordinates,trackedEntityType%5Bid%5D,programTrackedEntityAttributes%5Bid,name,valueType,mandatory,allowFutureDate,trackedEntityAttribute%5Bid,name,code,valueType,generated%5D%5D,programStages%5Bid,name,description,repeatable," + "captureCoordinates,generatedByEnrollmentDate,minDaysFromStart,programStageDataElements%5Bid,compulsory,allowProvidedElsewhere,dataElement%5Bid,name,code,formName,valueType,optionSetValue,optionSet%5Bid,name,options%5Bcode," + "name%5D%5D%5D%5D%5D&filter=code:eq:OU_1234" ) ) @@ -238,7 +239,7 @@ private void getPlanDefinitionByIdentifier() throws Exception "fields=id,name,trackedEntityTypeAttributes%5Bid,name,valueType,mandatory,trackedEntityAttribute%5Bid,name,code,valueType,generated,optionSetValue,optionSet%5Bid,name,options%5Bcode,name%5D%5D%5D%5D" ) ) .andRespond( withSuccess( IOUtils.resourceToString( "/org/dhis2/fhir/adapter/dhis/test/single-tracked-entity-type.json", StandardCharsets.UTF_8 ), MediaType.APPLICATION_JSON ) ); userDhis2Server.expect( ExpectedCount.between( 1, 2 ), method( HttpMethod.GET ) ).andExpect( header( "Authorization", "Basic Zmhpcl9jbGllbnQ6Zmhpcl9jbGllbnRfMQ==" ) ) - .andExpect( requestTo( dhis2BaseUrl + "/api/" + dhis2ApiVersion + "/programs.json?paging=false&fields=id,name,code,description,selectIncidentDatesInFuture,selectEnrollmentDatesInFuture,displayIncidentDate,registration,withoutRegistration," + + .andExpect( requestTo( dhis2BaseUrl + "/api/" + dhis2ApiVersion + "/programs.json?paging=false&fields=id,name,code,description,lastUpdated,selectIncidentDatesInFuture,selectEnrollmentDatesInFuture,displayIncidentDate,registration,withoutRegistration," + "captureCoordinates,trackedEntityType%5Bid%5D,programTrackedEntityAttributes%5Bid,name,valueType,mandatory,allowFutureDate,trackedEntityAttribute%5Bid,name,code,valueType,generated%5D%5D,programStages%5Bid,name,description,repeatable," + "captureCoordinates,generatedByEnrollmentDate,minDaysFromStart,programStageDataElements%5Bid,compulsory,allowProvidedElsewhere,dataElement%5Bid,name,code,formName,valueType,optionSetValue,optionSet%5Bid,name,options%5Bcode," + "name%5D%5D%5D%5D%5D&filter=code:eq:PD_1234" ) ) @@ -267,7 +268,7 @@ public void searchPlanDefinitionWithoutAuthorization() public void searchPlanDefinitionInvalidAuthorization() { userDhis2Server.expect( ExpectedCount.once(), method( HttpMethod.GET ) ).andExpect( header( "Authorization", "Basic Zmhpcl9jbGllbnQ6aW52YWxpZF8x" ) ) - .andExpect( requestTo( dhis2BaseUrl + "/api/" + dhis2ApiVersion + "/programs.json?filter=name:$ilike:Test&paging=true&page=1&pageSize=10&order=id&fields=id,name,code,description,selectIncidentDatesInFuture,selectEnrollmentDatesInFuture," + + .andExpect( requestTo( dhis2BaseUrl + "/api/" + dhis2ApiVersion + "/programs.json?filter=name:$ilike:Test&paging=true&page=1&pageSize=10&order=id&fields=id,name,code,description,lastUpdated,selectIncidentDatesInFuture,selectEnrollmentDatesInFuture," + "displayIncidentDate,registration,withoutRegistration,captureCoordinates,trackedEntityType%5Bid%5D,programTrackedEntityAttributes%5Bid,name,valueType,mandatory,allowFutureDate,trackedEntityAttribute%5Bid,name,code,valueType,generated%5D%5D," + "programStages%5Bid,name,description,repeatable,captureCoordinates,generatedByEnrollmentDate,minDaysFromStart,programStageDataElements%5Bid,compulsory,allowProvidedElsewhere,dataElement%5Bid,name,code,formName,valueType,optionSetValue," + "optionSet%5Bid,name,options%5Bcode,name%5D%5D%5D%5D%5D" ) ) @@ -286,7 +287,7 @@ public void searchPlanDefinition() throws Exception "fields=id,name,trackedEntityTypeAttributes%5Bid,name,valueType,mandatory,trackedEntityAttribute%5Bid,name,code,valueType,generated,optionSetValue,optionSet%5Bid,name,options%5Bcode,name%5D%5D%5D%5D" ) ) .andRespond( withSuccess( IOUtils.resourceToString( "/org/dhis2/fhir/adapter/dhis/test/single-tracked-entity-type.json", StandardCharsets.UTF_8 ), MediaType.APPLICATION_JSON ) ); userDhis2Server.expect( ExpectedCount.once(), method( HttpMethod.GET ) ).andExpect( header( "Authorization", "Basic Zmhpcl9jbGllbnQ6Zmhpcl9jbGllbnRfMQ==" ) ) - .andExpect( requestTo( dhis2BaseUrl + "/api/" + dhis2ApiVersion + "/programs.json?filter=name:$ilike:Child%20Programme&paging=true&page=1&pageSize=10&order=id&fields=id,name,code,description,selectIncidentDatesInFuture," + + .andExpect( requestTo( dhis2BaseUrl + "/api/" + dhis2ApiVersion + "/programs.json?filter=name:$ilike:Child%20Programme&paging=true&page=1&pageSize=10&order=id&fields=id,name,code,description,lastUpdated,selectIncidentDatesInFuture," + "selectEnrollmentDatesInFuture,displayIncidentDate,registration,withoutRegistration,captureCoordinates,trackedEntityType%5Bid%5D,programTrackedEntityAttributes%5Bid,name,valueType,mandatory,allowFutureDate,trackedEntityAttribute%5Bid,name," + "code,valueType,generated%5D%5D,programStages%5Bid,name,description,repeatable,captureCoordinates,generatedByEnrollmentDate,minDaysFromStart,programStageDataElements%5Bid,compulsory,allowProvidedElsewhere,dataElement%5Bid,name,code,formName," + "valueType,optionSetValue,optionSet%5Bid,name,options%5Bcode,name%5D%5D%5D%5D%5D" ) ) diff --git a/app/src/test/java/org/dhis2/fhir/adapter/fhir/transform/fhir/impl/enrollment/AbstractEnrollmentTransformationAppTest.java b/app/src/test/java/org/dhis2/fhir/adapter/fhir/transform/fhir/impl/enrollment/AbstractEnrollmentTransformationAppTest.java index f96cac5b..7b0009fc 100644 --- a/app/src/test/java/org/dhis2/fhir/adapter/fhir/transform/fhir/impl/enrollment/AbstractEnrollmentTransformationAppTest.java +++ b/app/src/test/java/org/dhis2/fhir/adapter/fhir/transform/fhir/impl/enrollment/AbstractEnrollmentTransformationAppTest.java @@ -49,13 +49,14 @@ public abstract class AbstractEnrollmentTransformationAppTest extends AbstractAp protected void expectMetadataRequests() throws Exception { systemDhis2Server.expect( ExpectedCount.between( 0, 1 ), method( HttpMethod.GET ) ).andExpect( header( "Authorization", testConfiguration.getDhis2SystemAuthorization() ) ) - .andExpect( requestTo( dhis2BaseUrl + "/api/" + dhis2ApiVersion + "/programs/EPDyQuoRnXk.json?fields=id,name,code,description,selectIncidentDatesInFuture,selectEnrollmentDatesInFuture,displayIncidentDate,registration," + + .andExpect( requestTo( dhis2BaseUrl + "/api/" + dhis2ApiVersion + "/programs/EPDyQuoRnXk.json?fields=id,name,code,description,lastUpdated,selectIncidentDatesInFuture,selectEnrollmentDatesInFuture,displayIncidentDate,registration," + "withoutRegistration,captureCoordinates,trackedEntityType%5Bid%5D,programTrackedEntityAttributes%5Bid,name,valueType,mandatory,allowFutureDate,trackedEntityAttribute%5Bid,name,code,valueType,generated%5D%5D,programStages%5Bid,name," + "description,repeatable,captureCoordinates,generatedByEnrollmentDate,minDaysFromStart,programStageDataElements%5Bid,compulsory,allowProvidedElsewhere,dataElement%5Bid,name,code,formName,valueType,optionSetValue,optionSet%5Bid,name," + "options%5Bcode,name%5D%5D%5D%5D%5D" ) ) .andRespond( withSuccess( IOUtils.resourceToString( "/org/dhis2/fhir/adapter/dhis/test/single-program.json", StandardCharsets.UTF_8 ), MediaType.APPLICATION_JSON ) ); systemDhis2Server.expect( ExpectedCount.between( 0, 1 ), method( HttpMethod.GET ) ).andExpect( header( "Authorization", testConfiguration.getDhis2SystemAuthorization() ) ) - .andExpect( requestTo( dhis2BaseUrl + "/api/" + dhis2ApiVersion + "/programs.json?paging=false&fields=id,name,code,description,selectIncidentDatesInFuture,selectEnrollmentDatesInFuture,displayIncidentDate,registration,withoutRegistration," + + .andExpect( requestTo( dhis2BaseUrl + "/api/" + dhis2ApiVersion + "/programs.json?paging=false&fields=id,name,code,description,lastUpdated,selectIncidentDatesInFuture,selectEnrollmentDatesInFuture,displayIncidentDate,registration," + + "withoutRegistration," + "captureCoordinates," + "trackedEntityType%5Bid%5D,programTrackedEntityAttributes%5Bid,name,valueType,mandatory,allowFutureDate,trackedEntityAttribute%5Bid,name,code,valueType,generated%5D%5D,programStages%5Bid,name,description,repeatable,captureCoordinates," + "generatedByEnrollmentDate,minDaysFromStart,programStageDataElements%5Bid,compulsory,allowProvidedElsewhere,dataElement%5Bid,name,code,description,formName,valueType,optionSetValue,optionSet%5Bid,name,options%5Bcode," + @@ -74,7 +75,7 @@ protected void expectMetadataRequests() throws Exception .andRespond( withSuccess( IOUtils.resourceToString( "/org/dhis2/fhir/adapter/dhis/test/default-tracked-entity-type.json", StandardCharsets.UTF_8 ), MediaType.APPLICATION_JSON ) ); systemDhis2Server.expect( ExpectedCount.between( 0, 1 ), method( HttpMethod.GET ) ).andExpect( header( "Authorization", testConfiguration.getDhis2SystemAuthorization() ) ) .andExpect( requestTo( dhis2BaseUrl + "/api/" + dhis2ApiVersion + "/programs.json?paging=false&" + - "fields=id,name,code,description,selectIncidentDatesInFuture,selectEnrollmentDatesInFuture,displayIncidentDate,registration,withoutRegistration,captureCoordinates,trackedEntityType%5Bid%5D,programTrackedEntityAttributes%5Bid,name,valueType," + + "fields=id,name,code,description,lastUpdated,selectIncidentDatesInFuture,selectEnrollmentDatesInFuture,displayIncidentDate,registration,withoutRegistration,captureCoordinates,trackedEntityType%5Bid%5D,programTrackedEntityAttributes%5Bid,name,valueType," + "mandatory,allowFutureDate,trackedEntityAttribute%5Bid,name,code,valueType,generated%5D%5D,programStages%5Bid,name,description,repeatable,captureCoordinates,generatedByEnrollmentDate,minDaysFromStart,programStageDataElements%5Bid,compulsory," + "allowProvidedElsewhere,dataElement%5Bid,name,code,description,formName,valueType,optionSetValue,optionSet%5Bid,name,options%5Bcode,name%5D%5D%5D%5D%5D&filter=name:eq:Child%20Programme" ) ) .andRespond( withSuccess( IOUtils.resourceToString( "/org/dhis2/fhir/adapter/dhis/test/single-program.json", StandardCharsets.UTF_8 ), MediaType.APPLICATION_JSON ) ); diff --git a/app/src/test/java/org/dhis2/fhir/adapter/fhir/transform/fhir/impl/programstage/AbstractProgramStageTransformationAppTest.java b/app/src/test/java/org/dhis2/fhir/adapter/fhir/transform/fhir/impl/programstage/AbstractProgramStageTransformationAppTest.java index b104bc05..742bfca8 100644 --- a/app/src/test/java/org/dhis2/fhir/adapter/fhir/transform/fhir/impl/programstage/AbstractProgramStageTransformationAppTest.java +++ b/app/src/test/java/org/dhis2/fhir/adapter/fhir/transform/fhir/impl/programstage/AbstractProgramStageTransformationAppTest.java @@ -50,7 +50,8 @@ public abstract class AbstractProgramStageTransformationAppTest extends Abstract protected void expectMetadataRequests() throws Exception { systemDhis2Server.expect( ExpectedCount.between( 0, 1 ), method( HttpMethod.GET ) ).andExpect( header( "Authorization", testConfiguration.getDhis2SystemAuthorization() ) ) - .andExpect( requestTo( dhis2BaseUrl + "/api/" + dhis2ApiVersion + "/programs.json?paging=false&fields=id,name,code,description,selectIncidentDatesInFuture,selectEnrollmentDatesInFuture,displayIncidentDate,registration,withoutRegistration," + + .andExpect( requestTo( dhis2BaseUrl + "/api/" + dhis2ApiVersion + "/programs.json?paging=false&fields=id,name,code,description,lastUpdated,selectIncidentDatesInFuture,selectEnrollmentDatesInFuture,displayIncidentDate,registration," + + "withoutRegistration," + "captureCoordinates,trackedEntityType%5Bid%5D,programTrackedEntityAttributes%5Bid,name,valueType,mandatory,allowFutureDate,trackedEntityAttribute%5Bid,name,code,valueType,generated%5D%5D,programStages%5Bid,name,description,repeatable," + "captureCoordinates," + "generatedByEnrollmentDate,minDaysFromStart,programStageDataElements%5Bid,compulsory,allowProvidedElsewhere,dataElement%5Bid,name,code,formName,valueType,optionSetValue,optionSet%5Bid,name,options%5Bcode," + @@ -69,7 +70,7 @@ protected void expectMetadataRequests() throws Exception .andRespond( withSuccess( IOUtils.resourceToString( "/org/dhis2/fhir/adapter/dhis/test/default-tracked-entity-type.json", StandardCharsets.UTF_8 ), MediaType.APPLICATION_JSON ) ); systemDhis2Server.expect( ExpectedCount.between( 0, 1 ), method( HttpMethod.GET ) ).andExpect( header( "Authorization", testConfiguration.getDhis2SystemAuthorization() ) ) .andExpect( requestTo( dhis2BaseUrl + "/api/" + dhis2ApiVersion + "/programs.json?paging=false&" + - "fields=id,name,code,description,selectIncidentDatesInFuture,selectEnrollmentDatesInFuture,displayIncidentDate,registration,withoutRegistration,captureCoordinates,trackedEntityType%5Bid%5D,programTrackedEntityAttributes%5Bid,name,valueType," + + "fields=id,name,code,description,lastUpdated,selectIncidentDatesInFuture,selectEnrollmentDatesInFuture,displayIncidentDate,registration,withoutRegistration,captureCoordinates,trackedEntityType%5Bid%5D,programTrackedEntityAttributes%5Bid,name,valueType," + "mandatory,allowFutureDate,trackedEntityAttribute%5Bid,name,code,valueType,generated%5D%5D,programStages%5Bid,name,description,repeatable,captureCoordinates,generatedByEnrollmentDate,minDaysFromStart,programStageDataElements%5Bid,compulsory," + "allowProvidedElsewhere,dataElement%5Bid,name,code,description,formName,valueType,optionSetValue,optionSet%5Bid,name,options%5Bcode,name%5D%5D%5D%5D%5D&filter=name:eq:Child%20Programme" ) ) .andRespond( withSuccess( IOUtils.resourceToString( "/org/dhis2/fhir/adapter/dhis/test/single-program.json", StandardCharsets.UTF_8 ), MediaType.APPLICATION_JSON ) ); diff --git a/dhis/src/main/java/org/dhis2/fhir/adapter/dhis/model/DhisResourceType.java b/dhis/src/main/java/org/dhis2/fhir/adapter/dhis/model/DhisResourceType.java index 80d8238f..30e669e9 100644 --- a/dhis/src/main/java/org/dhis2/fhir/adapter/dhis/model/DhisResourceType.java +++ b/dhis/src/main/java/org/dhis2/fhir/adapter/dhis/model/DhisResourceType.java @@ -36,20 +36,16 @@ /** * Contains the different types of DHIS2 Resources that are can be created. + * The enum values should be listed in the order of the dependencies. * * @author volsch */ public enum DhisResourceType { /** - * Resource is a tracked entity instance. - */ - TRACKED_ENTITY( "trackedEntityInstances", "te", "TrackedEntityRule" ), - - /** - * Resource is a tracked entity type. + * Resource is a organisation unit. */ - TRACKED_ENTITY_TYPE( "trackedEntityTypes", "tt", "TrackedEntityTypeRule" ), + ORGANIZATION_UNIT( "organisationUnits", "ou", "OrganizationUnitRule" ), /** * The program metadata. @@ -61,20 +57,26 @@ public enum DhisResourceType */ PROGRAM_STAGE_METADATA( "programStages", "sm", "ProgramStageMetadataRule" ), + /** - * Resource is a program instance (aka enrollment). + * Resource is a tracked entity type. */ - ENROLLMENT( "enrollments", "en", "EnrollmentRule" ), + TRACKED_ENTITY_TYPE( "trackedEntityTypes", "tt", "TrackedEntityTypeRule" ), /** - * Resource is a program stage instance (aka event of a program instance). + * Resource is a tracked entity instance. */ - PROGRAM_STAGE_EVENT( "events", "ps", "ProgramStageRule" ), + TRACKED_ENTITY( "trackedEntityInstances", "te", "TrackedEntityRule" ), /** - * Resource is a organisation unit. + * Resource is a program instance (aka enrollment). + */ + ENROLLMENT( "enrollments", "en", "EnrollmentRule" ), + + /** + * Resource is a program stage instance (aka event of a program instance). */ - ORGANIZATION_UNIT( "organisationUnits", "ou", "OrganizationUnitRule" ); + PROGRAM_STAGE_EVENT( "events", "ps", "ProgramStageRule" ); private static final Map byTypeName = Arrays.stream( values() ).collect( Collectors.toMap( DhisResourceType::getTypeName, v -> v ) ); diff --git a/dhis/src/main/java/org/dhis2/fhir/adapter/dhis/orgunit/impl/OrganizationUnitServiceImpl.java b/dhis/src/main/java/org/dhis2/fhir/adapter/dhis/orgunit/impl/OrganizationUnitServiceImpl.java index c0839e5d..da9839d4 100644 --- a/dhis/src/main/java/org/dhis2/fhir/adapter/dhis/orgunit/impl/OrganizationUnitServiceImpl.java +++ b/dhis/src/main/java/org/dhis2/fhir/adapter/dhis/orgunit/impl/OrganizationUnitServiceImpl.java @@ -58,7 +58,7 @@ public OrganizationUnitServiceImpl( @Nonnull @Qualifier( "systemDhis2RestTemplat @Nonnull @Override - protected DhisResourceType getDhisResourceType() + public DhisResourceType getDhisResourceType() { return DhisResourceType.ORGANIZATION_UNIT; } diff --git a/dhis/src/main/java/org/dhis2/fhir/adapter/dhis/service/DhisMetadataService.java b/dhis/src/main/java/org/dhis2/fhir/adapter/dhis/service/DhisMetadataService.java index e36d5ce3..2e2e08ae 100644 --- a/dhis/src/main/java/org/dhis2/fhir/adapter/dhis/service/DhisMetadataService.java +++ b/dhis/src/main/java/org/dhis2/fhir/adapter/dhis/service/DhisMetadataService.java @@ -28,8 +28,6 @@ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -import org.dhis2.fhir.adapter.data.model.ProcessedItemInfo; -import org.dhis2.fhir.adapter.dhis.metadata.model.DhisSyncGroup; import org.dhis2.fhir.adapter.dhis.model.DhisMetadata; import org.dhis2.fhir.adapter.dhis.model.DhisResource; import org.dhis2.fhir.adapter.dhis.model.DhisResourceResult; @@ -37,11 +35,7 @@ import org.dhis2.fhir.adapter.dhis.model.UriFilterApplier; import javax.annotation.Nonnull; -import java.time.Instant; -import java.util.Collection; import java.util.Optional; -import java.util.Set; -import java.util.function.Consumer; /** * Service that provides access to DHIS2 metadata. @@ -49,7 +43,7 @@ * @param the concrete type of the metadata. * @author volsch */ -public interface DhisMetadataService +public interface DhisMetadataService extends DhisPolledService, DhisService { @Nonnull Optional findMetadataByReference( @Nonnull Reference reference ); @@ -62,8 +56,4 @@ public interface DhisMetadataService @Nonnull DhisResourceResult find( @Nonnull UriFilterApplier uriFilterApplier, int from, int max ); - - @Nonnull - Instant poll( @Nonnull DhisSyncGroup group, @Nonnull Instant lastUpdated, int toleranceMillis, - int maxSearchCount, @Nonnull Set excludedStoredBy, @Nonnull Consumer> consumer ); } diff --git a/dhis/src/main/java/org/dhis2/fhir/adapter/dhis/service/DhisPolledService.java b/dhis/src/main/java/org/dhis2/fhir/adapter/dhis/service/DhisPolledService.java new file mode 100644 index 00000000..87a7481f --- /dev/null +++ b/dhis/src/main/java/org/dhis2/fhir/adapter/dhis/service/DhisPolledService.java @@ -0,0 +1,52 @@ +package org.dhis2.fhir.adapter.dhis.service; + +/* + * Copyright (c) 2004-2019, University of Oslo + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * Neither the name of the HISP project nor the names of its contributors may + * be used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +import org.dhis2.fhir.adapter.data.model.ProcessedItemInfo; +import org.dhis2.fhir.adapter.dhis.metadata.model.DhisSyncGroup; +import org.dhis2.fhir.adapter.dhis.model.DhisResource; + +import javax.annotation.Nonnull; +import java.time.Instant; +import java.util.Collection; +import java.util.Set; +import java.util.function.Consumer; + +/** + * Service that provides polled access to DHIS2 data. + * + * @param the concrete type of the data. + * @author volsch + */ +public interface DhisPolledService extends DhisService +{ + @Nonnull + Instant poll( @Nonnull DhisSyncGroup group, @Nonnull Instant lastUpdated, int toleranceMillis, + int maxSearchCount, @Nonnull Set excludedStoredBy, @Nonnull Consumer> consumer ); +} diff --git a/dhis/src/main/java/org/dhis2/fhir/adapter/dhis/service/DhisService.java b/dhis/src/main/java/org/dhis2/fhir/adapter/dhis/service/DhisService.java new file mode 100644 index 00000000..9eaf55ba --- /dev/null +++ b/dhis/src/main/java/org/dhis2/fhir/adapter/dhis/service/DhisService.java @@ -0,0 +1,46 @@ +package org.dhis2.fhir.adapter.dhis.service; + +/* + * Copyright (c) 2004-2019, University of Oslo + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * Neither the name of the HISP project nor the names of its contributors may + * be used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +import org.dhis2.fhir.adapter.dhis.model.DhisResource; +import org.dhis2.fhir.adapter.dhis.model.DhisResourceType; + +import javax.annotation.Nonnull; + +/** + * Service that provides access to DHIS2 data. + * + * @param the concrete type of the DHIS2 resource. + * @author volsch + */ +public interface DhisService +{ + @Nonnull + DhisResourceType getDhisResourceType(); +} diff --git a/dhis/src/main/java/org/dhis2/fhir/adapter/dhis/service/impl/AbstractDhisMetadataServiceImpl.java b/dhis/src/main/java/org/dhis2/fhir/adapter/dhis/service/impl/AbstractDhisMetadataServiceImpl.java index 767102db..fc466c7b 100644 --- a/dhis/src/main/java/org/dhis2/fhir/adapter/dhis/service/impl/AbstractDhisMetadataServiceImpl.java +++ b/dhis/src/main/java/org/dhis2/fhir/adapter/dhis/service/impl/AbstractDhisMetadataServiceImpl.java @@ -91,7 +91,7 @@ protected AbstractDhisMetadataServiceImpl( @Nonnull @Qualifier( "systemDhis2Rest } @Nonnull - protected abstract DhisResourceType getDhisResourceType(); + public abstract DhisResourceType getDhisResourceType(); @Nonnull protected abstract Class getItemClass(); diff --git a/dhis/src/main/java/org/dhis2/fhir/adapter/dhis/sync/impl/DhisDataProcessorItemRetrieverImpl.java b/dhis/src/main/java/org/dhis2/fhir/adapter/dhis/sync/impl/DhisDataProcessorItemRetrieverImpl.java index 6a655afa..9f59da2c 100644 --- a/dhis/src/main/java/org/dhis2/fhir/adapter/dhis/sync/impl/DhisDataProcessorItemRetrieverImpl.java +++ b/dhis/src/main/java/org/dhis2/fhir/adapter/dhis/sync/impl/DhisDataProcessorItemRetrieverImpl.java @@ -36,20 +36,25 @@ import org.dhis2.fhir.adapter.data.processor.DataProcessorItemRetriever; import org.dhis2.fhir.adapter.dhis.config.DhisConfig; import org.dhis2.fhir.adapter.dhis.metadata.model.DhisSyncGroup; +import org.dhis2.fhir.adapter.dhis.model.DhisResource; import org.dhis2.fhir.adapter.dhis.model.DhisResourceType; -import org.dhis2.fhir.adapter.dhis.orgunit.OrganizationUnitService; +import org.dhis2.fhir.adapter.dhis.service.DhisPolledService; +import org.dhis2.fhir.adapter.dhis.service.DhisService; import org.dhis2.fhir.adapter.dhis.sync.SyncExcludedDhisUsernameRetriever; -import org.dhis2.fhir.adapter.dhis.tracker.program.EventService; -import org.dhis2.fhir.adapter.dhis.tracker.program.ProgramMetadataService; -import org.dhis2.fhir.adapter.dhis.tracker.trackedentity.TrackedEntityService; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.stereotype.Component; import javax.annotation.Nonnull; import java.time.Instant; import java.util.Collection; +import java.util.List; +import java.util.Map; +import java.util.Objects; import java.util.Set; +import java.util.concurrent.atomic.AtomicReference; import java.util.function.Consumer; +import java.util.stream.Collectors; +import java.util.stream.Stream; /** * The item retriever that polls DHIS2 resources. All relevant resources are @@ -66,13 +71,7 @@ public class DhisDataProcessorItemRetrieverImpl implements DataProcessorItemRetr private final Authorization systemDhis2Authorization; - private final OrganizationUnitService organizationUnitService; - - private final ProgramMetadataService programMetadataService; - - private final TrackedEntityService trackedEntityService; - - private final EventService eventService; + private final Map> polledServices; private final DhisSyncProcessorConfig processorConfig; @@ -80,20 +79,14 @@ public DhisDataProcessorItemRetrieverImpl( @Nonnull AuthorizationContext authorizationContext, @Nonnull @Qualifier( "systemDhis2Authorization" ) Authorization systemDhis2Authorization, @Nonnull SyncExcludedDhisUsernameRetriever excludedDhisUsernameRetriever, - @Nonnull OrganizationUnitService organizationUnitService, - @Nonnull ProgramMetadataService programMetadataService, - @Nonnull TrackedEntityService trackedEntityService, - @Nonnull EventService eventService, + @Nonnull List> polledServices, @Nonnull DhisSyncProcessorConfig processorConfig, @Nonnull DhisConfig config ) { this.authorizationContext = authorizationContext; this.systemDhis2Authorization = systemDhis2Authorization; this.excludedDhisUsernameRetriever = excludedDhisUsernameRetriever; - this.trackedEntityService = trackedEntityService; - this.eventService = eventService; - this.organizationUnitService = organizationUnitService; - this.programMetadataService = programMetadataService; + this.polledServices = polledServices.stream().collect( Collectors.toMap( DhisService::getDhisResourceType, ps -> ps ) ); this.processorConfig = processorConfig; } @@ -103,38 +96,21 @@ public Instant poll( @Nonnull DhisSyncGroup group, @Nonnull Instant lastUpdated, { final int toleranceMillis = processorConfig.getToleranceMillis(); final Set resourceTypes = processorConfig.getResourceTypes(); + authorizationContext.setAuthorization( systemDhis2Authorization ); try { final Set excludedDhisUsernames = excludedDhisUsernameRetriever.findAllDhisUsernames(); - Instant result = Instant.now(); + final AtomicReference result = new AtomicReference<>( Instant.now() ); - if ( resourceTypes.contains( DhisResourceType.ORGANIZATION_UNIT ) ) - { - final Instant currentResult = organizationUnitService.poll( group, lastUpdated, toleranceMillis, maxSearchCount, - excludedDhisUsernames, consumer ); - result = ObjectUtils.min( result, currentResult ); - } - if ( resourceTypes.contains( DhisResourceType.PROGRAM_METADATA ) ) - { - final Instant currentResult = programMetadataService.poll( group, lastUpdated, toleranceMillis, maxSearchCount, - excludedDhisUsernames, consumer ); - result = ObjectUtils.min( result, currentResult ); - } - if ( resourceTypes.contains( DhisResourceType.TRACKED_ENTITY ) ) - { - final Instant currentResult = trackedEntityService.poll( group, lastUpdated, toleranceMillis, maxSearchCount, - excludedDhisUsernames, consumer ); - result = ObjectUtils.min( result, currentResult ); - } - if ( resourceTypes.contains( DhisResourceType.PROGRAM_STAGE_EVENT ) ) - { - final Instant currentResult = eventService.poll( group, lastUpdated, toleranceMillis, maxSearchCount, - excludedDhisUsernames, consumer ); - result = ObjectUtils.min( result, currentResult ); - } + Stream.of( DhisResourceType.values() ).filter( resourceTypes::contains ).map( polledServices::get ).filter( Objects::nonNull ) + .forEach( polledService -> { + final Instant currentResult = polledService.poll( group, lastUpdated, toleranceMillis, maxSearchCount, + excludedDhisUsernames, consumer ); + result.set( ObjectUtils.min( result.get(), currentResult ) ); + } ); - return result; + return result.get(); } finally { diff --git a/dhis/src/main/java/org/dhis2/fhir/adapter/dhis/tracker/program/EnrollmentService.java b/dhis/src/main/java/org/dhis2/fhir/adapter/dhis/tracker/program/EnrollmentService.java index 4326ea09..fa3db68c 100644 --- a/dhis/src/main/java/org/dhis2/fhir/adapter/dhis/tracker/program/EnrollmentService.java +++ b/dhis/src/main/java/org/dhis2/fhir/adapter/dhis/tracker/program/EnrollmentService.java @@ -28,6 +28,8 @@ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ +import org.dhis2.fhir.adapter.dhis.service.DhisService; + import javax.annotation.Nonnull; import java.util.Optional; @@ -38,7 +40,7 @@ * @author volsch * @author Charles Chigoriwa (ITINORDIC) */ -public interface EnrollmentService +public interface EnrollmentService extends DhisService { @Nonnull Optional findLatestActiveRefreshed( @Nonnull String programId, @Nonnull String trackedEntityInstanceId, boolean localOnly ); diff --git a/dhis/src/main/java/org/dhis2/fhir/adapter/dhis/tracker/program/EventService.java b/dhis/src/main/java/org/dhis2/fhir/adapter/dhis/tracker/program/EventService.java index 99582bae..80215e74 100644 --- a/dhis/src/main/java/org/dhis2/fhir/adapter/dhis/tracker/program/EventService.java +++ b/dhis/src/main/java/org/dhis2/fhir/adapter/dhis/tracker/program/EventService.java @@ -28,17 +28,14 @@ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -import org.dhis2.fhir.adapter.data.model.ProcessedItemInfo; -import org.dhis2.fhir.adapter.dhis.metadata.model.DhisSyncGroup; import org.dhis2.fhir.adapter.dhis.model.DhisResourceResult; import org.dhis2.fhir.adapter.dhis.model.UriFilterApplier; +import org.dhis2.fhir.adapter.dhis.service.DhisPolledService; +import org.dhis2.fhir.adapter.dhis.service.DhisService; import javax.annotation.Nonnull; -import java.time.Instant; import java.util.Collection; import java.util.Optional; -import java.util.Set; -import java.util.function.Consumer; /** * Service to create, update and read DHIS2 Program Stage Instances (aka events) @@ -46,7 +43,7 @@ * * @author volsch */ -public interface EventService +public interface EventService extends DhisService, DhisPolledService { @Nonnull Collection findRefreshed( @Nonnull String programId, @Nonnull String programStageId, @@ -69,8 +66,4 @@ Collection find( @Nonnull String programId, @Nonnull String programStageI @Nonnull DhisResourceResult find( @Nonnull String programId, @Nonnull String programStageId, @Nonnull UriFilterApplier uriFilterApplier, int from, int max ); - - @Nonnull - Instant poll( @Nonnull DhisSyncGroup group, @Nonnull Instant lastUpdated, int toleranceMillis, - int maxSearchCount, @Nonnull Set excludedStoredBy, @Nonnull Consumer> consumer ); } diff --git a/dhis/src/main/java/org/dhis2/fhir/adapter/dhis/tracker/program/WritableProgram.java b/dhis/src/main/java/org/dhis2/fhir/adapter/dhis/tracker/program/WritableProgram.java index b6327539..457168ae 100644 --- a/dhis/src/main/java/org/dhis2/fhir/adapter/dhis/tracker/program/WritableProgram.java +++ b/dhis/src/main/java/org/dhis2/fhir/adapter/dhis/tracker/program/WritableProgram.java @@ -50,6 +50,8 @@ public class WritableProgram extends AbstractDhisMetadata implements Program, Se { private static final long serialVersionUID = -4906529875383953995L; + private ZonedDateTime lastUpdated; + @JsonInclude( JsonInclude.Include.NON_NULL ) private String id; @@ -294,7 +296,7 @@ public boolean isDeleted() @Override public ZonedDateTime getLastUpdated() { - return null; + return lastUpdated; } @Override diff --git a/dhis/src/main/java/org/dhis2/fhir/adapter/dhis/tracker/program/impl/EnrollmentServiceImpl.java b/dhis/src/main/java/org/dhis2/fhir/adapter/dhis/tracker/program/impl/EnrollmentServiceImpl.java index 1e33b3a5..b5067391 100644 --- a/dhis/src/main/java/org/dhis2/fhir/adapter/dhis/tracker/program/impl/EnrollmentServiceImpl.java +++ b/dhis/src/main/java/org/dhis2/fhir/adapter/dhis/tracker/program/impl/EnrollmentServiceImpl.java @@ -39,6 +39,7 @@ import org.dhis2.fhir.adapter.dhis.local.LocalDhisRepositoryPersistStatus; import org.dhis2.fhir.adapter.dhis.local.LocalDhisResourceRepositoryTemplate; import org.dhis2.fhir.adapter.dhis.model.DhisResourceComparator; +import org.dhis2.fhir.adapter.dhis.model.DhisResourceType; import org.dhis2.fhir.adapter.dhis.model.ImportStatus; import org.dhis2.fhir.adapter.dhis.model.ImportSummaries; import org.dhis2.fhir.adapter.dhis.model.ImportSummary; @@ -117,6 +118,13 @@ public EnrollmentServiceImpl( @Nonnull @Qualifier( "userDhis2RestTemplate" ) Res this.resourceRepositoryTemplate = new LocalDhisResourceRepositoryTemplate<>( Enrollment.class, requestCacheService, this ); } + @Nonnull + @Override + public DhisResourceType getDhisResourceType() + { + return DhisResourceType.ENROLLMENT; + } + @HystrixCommand( ignoreExceptions = UnauthorizedException.class ) @Nonnull @Override diff --git a/dhis/src/main/java/org/dhis2/fhir/adapter/dhis/tracker/program/impl/EventServiceImpl.java b/dhis/src/main/java/org/dhis2/fhir/adapter/dhis/tracker/program/impl/EventServiceImpl.java index fc9b8fc7..5799e5b4 100644 --- a/dhis/src/main/java/org/dhis2/fhir/adapter/dhis/tracker/program/impl/EventServiceImpl.java +++ b/dhis/src/main/java/org/dhis2/fhir/adapter/dhis/tracker/program/impl/EventServiceImpl.java @@ -45,6 +45,7 @@ import org.dhis2.fhir.adapter.dhis.model.DataValue; import org.dhis2.fhir.adapter.dhis.model.DhisResourceComparator; import org.dhis2.fhir.adapter.dhis.model.DhisResourceResult; +import org.dhis2.fhir.adapter.dhis.model.DhisResourceType; import org.dhis2.fhir.adapter.dhis.model.ImportStatus; import org.dhis2.fhir.adapter.dhis.model.ImportSummary; import org.dhis2.fhir.adapter.dhis.model.ImportSummaryWebMessage; @@ -136,6 +137,13 @@ public EventServiceImpl( @Nonnull @Qualifier( "userDhis2RestTemplate" ) RestTemp this.resourceRepositoryTemplate = new LocalDhisResourceRepositoryTemplate<>( Event.class, requestCacheService, this ); } + @Nonnull + @Override + public DhisResourceType getDhisResourceType() + { + return DhisResourceType.PROGRAM_STAGE_EVENT; + } + @HystrixCommand( ignoreExceptions = { DhisConflictException.class, UnauthorizedException.class } ) @Override public boolean delete( @Nonnull String eventId ) diff --git a/dhis/src/main/java/org/dhis2/fhir/adapter/dhis/tracker/program/impl/ProgramMetadataServiceImpl.java b/dhis/src/main/java/org/dhis2/fhir/adapter/dhis/tracker/program/impl/ProgramMetadataServiceImpl.java index 070f1d71..a17179c8 100644 --- a/dhis/src/main/java/org/dhis2/fhir/adapter/dhis/tracker/program/impl/ProgramMetadataServiceImpl.java +++ b/dhis/src/main/java/org/dhis2/fhir/adapter/dhis/tracker/program/impl/ProgramMetadataServiceImpl.java @@ -49,7 +49,7 @@ public class ProgramMetadataServiceImpl extends AbstractDhisMetadataServiceImpl implements ProgramMetadataService { protected static final String FIELDS = - "id,name,code,description,selectIncidentDatesInFuture,selectEnrollmentDatesInFuture,displayIncidentDate," + + "id,name,code,description,lastUpdated,selectIncidentDatesInFuture,selectEnrollmentDatesInFuture,displayIncidentDate," + "registration,withoutRegistration,captureCoordinates,trackedEntityType[id]," + "programTrackedEntityAttributes[id,name,valueType,mandatory,allowFutureDate," + "trackedEntityAttribute[id,name,code,valueType,generated]]," + @@ -64,7 +64,7 @@ public ProgramMetadataServiceImpl( @Nonnull @Qualifier( "systemDhis2RestTemplate @Nonnull @Override - protected DhisResourceType getDhisResourceType() + public DhisResourceType getDhisResourceType() { return DhisResourceType.PROGRAM_METADATA; } diff --git a/dhis/src/main/java/org/dhis2/fhir/adapter/dhis/tracker/trackedentity/TrackedEntityService.java b/dhis/src/main/java/org/dhis2/fhir/adapter/dhis/tracker/trackedentity/TrackedEntityService.java index 34688f99..c8348eac 100644 --- a/dhis/src/main/java/org/dhis2/fhir/adapter/dhis/tracker/trackedentity/TrackedEntityService.java +++ b/dhis/src/main/java/org/dhis2/fhir/adapter/dhis/tracker/trackedentity/TrackedEntityService.java @@ -28,25 +28,22 @@ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -import org.dhis2.fhir.adapter.data.model.ProcessedItemInfo; -import org.dhis2.fhir.adapter.dhis.metadata.model.DhisSyncGroup; import org.dhis2.fhir.adapter.dhis.model.DhisResourceResult; import org.dhis2.fhir.adapter.dhis.model.UriFilterApplier; +import org.dhis2.fhir.adapter.dhis.service.DhisPolledService; +import org.dhis2.fhir.adapter.dhis.service.DhisService; import javax.annotation.Nonnull; -import java.time.Instant; import java.util.Collection; import java.util.Map; import java.util.Optional; -import java.util.Set; -import java.util.function.Consumer; /** * Service that allows to create, read and update tracked entity instances. * * @author volsch */ -public interface TrackedEntityService +public interface TrackedEntityService extends DhisService, DhisPolledService { void updateGeneratedValues( @Nonnull TrackedEntityInstance trackedEntityInstance, @Nonnull TrackedEntityType type, @Nonnull Map requiredValues ); @@ -74,8 +71,4 @@ Collection findByAttrValue( @Nonnull String typeId, @Nonnull DhisResourceResult find( @Nonnull String trackedEntityTypeId, @Nonnull UriFilterApplier uriFilterApplier, int from, int max ); - - @Nonnull - Instant poll( @Nonnull DhisSyncGroup group, @Nonnull Instant lastUpdated, int toleranceMillis, - int maxSearchCount, @Nonnull Set excludedStoredBy, @Nonnull Consumer> consumer ); } diff --git a/dhis/src/main/java/org/dhis2/fhir/adapter/dhis/tracker/trackedentity/impl/TrackedEntityServiceImpl.java b/dhis/src/main/java/org/dhis2/fhir/adapter/dhis/tracker/trackedentity/impl/TrackedEntityServiceImpl.java index beda50e6..7c8102f0 100644 --- a/dhis/src/main/java/org/dhis2/fhir/adapter/dhis/tracker/trackedentity/impl/TrackedEntityServiceImpl.java +++ b/dhis/src/main/java/org/dhis2/fhir/adapter/dhis/tracker/trackedentity/impl/TrackedEntityServiceImpl.java @@ -154,6 +154,13 @@ public TrackedEntityServiceImpl( @Nonnull @Qualifier( "userDhis2RestTemplate" ) this.resourceRepositoryTemplate = new LocalDhisResourceRepositoryTemplate<>( TrackedEntityInstance.class, requestCacheService, this ); } + @Nonnull + @Override + public DhisResourceType getDhisResourceType() + { + return DhisResourceType.TRACKED_ENTITY; + } + @HystrixCommand( ignoreExceptions = { DhisConflictException.class, UnauthorizedException.class } ) @Override public void updateGeneratedValues( @Nonnull TrackedEntityInstance trackedEntityInstance, @Nonnull TrackedEntityType type, @Nonnull Map requiredValues ) diff --git a/dhis/src/test/java/org/dhis2/fhir/adapter/dhis/tracker/program/impl/ProgramMetadataServiceImplTest.java b/dhis/src/test/java/org/dhis2/fhir/adapter/dhis/tracker/program/impl/ProgramMetadataServiceImplTest.java index b1aabe2f..27653d09 100644 --- a/dhis/src/test/java/org/dhis2/fhir/adapter/dhis/tracker/program/impl/ProgramMetadataServiceImplTest.java +++ b/dhis/src/test/java/org/dhis2/fhir/adapter/dhis/tracker/program/impl/ProgramMetadataServiceImplTest.java @@ -81,7 +81,8 @@ public void setUp() @Test public void findMetadataByReferenceId() throws IOException { - mockServer.expect( requestTo( "http://localhost:8080/api/programs/93783.json?fields=id,name,code,description,selectIncidentDatesInFuture,selectEnrollmentDatesInFuture,displayIncidentDate,registration,withoutRegistration,captureCoordinates," + + mockServer.expect( requestTo( "http://localhost:8080/api/programs/93783.json?fields=id,name,code,description,lastUpdated,selectIncidentDatesInFuture,selectEnrollmentDatesInFuture,displayIncidentDate,registration,withoutRegistration," + + "captureCoordinates," + "trackedEntityType%5Bid%5D,programTrackedEntityAttributes%5Bid,name,valueType,mandatory,allowFutureDate,trackedEntityAttribute%5Bid,name,code,valueType,generated%5D%5D,programStages%5Bid,name,description,repeatable,captureCoordinates," + "generatedByEnrollmentDate,minDaysFromStart,programStageDataElements%5Bid,compulsory,allowProvidedElsewhere,dataElement%5Bid,name,code,formName,valueType,optionSetValue,optionSet%5Bid,name,options%5Bcode,name%5D%5D%5D%5D%5D" ) ) .andExpect( method( HttpMethod.GET ) ).andRespond( withSuccess( IOUtils.resourceToByteArray( "/org/dhis2/fhir/adapter/dhis/tracker/program/impl/program.json" ), MediaType.APPLICATION_JSON ) ); @@ -94,7 +95,7 @@ public void findMetadataByReferenceId() throws IOException @Test public void findMetadataByReferenceIdNotFound() { - mockServer.expect( requestTo( "http://localhost:8080/api/programs/93783.json?fields=id,name,code,description,selectIncidentDatesInFuture,selectEnrollmentDatesInFuture,displayIncidentDate,registration,withoutRegistration,captureCoordinates," + + mockServer.expect( requestTo( "http://localhost:8080/api/programs/93783.json?fields=id,name,code,description,lastUpdated,selectIncidentDatesInFuture,selectEnrollmentDatesInFuture,displayIncidentDate,registration,withoutRegistration,captureCoordinates," + "trackedEntityType%5Bid%5D,programTrackedEntityAttributes%5Bid,name,valueType,mandatory,allowFutureDate,trackedEntityAttribute%5Bid,name,code,valueType,generated%5D%5D,programStages%5Bid,name,description,repeatable,captureCoordinates," + "generatedByEnrollmentDate,minDaysFromStart,programStageDataElements%5Bid,compulsory,allowProvidedElsewhere,dataElement%5Bid,name,code,formName,valueType,optionSetValue,optionSet%5Bid,name,options%5Bcode,name%5D%5D%5D%5D%5D" ) ).andExpect( method( HttpMethod.GET ) ) .andRespond( MockRestResponseCreators.withStatus( HttpStatus.NOT_FOUND ).body( "{}" ).contentType( MediaType.APPLICATION_JSON ) ); @@ -106,7 +107,7 @@ public void findMetadataByReferenceIdNotFound() @Test( expected = HttpServerErrorException.class ) public void findMetadataByReferenceIdServerError() { - mockServer.expect( requestTo( "http://localhost:8080/api/programs/93783.json?fields=id,name,code,description,selectIncidentDatesInFuture,selectEnrollmentDatesInFuture,displayIncidentDate,registration,withoutRegistration,captureCoordinates," + + mockServer.expect( requestTo( "http://localhost:8080/api/programs/93783.json?fields=id,name,code,description,lastUpdated,selectIncidentDatesInFuture,selectEnrollmentDatesInFuture,displayIncidentDate,registration,withoutRegistration,captureCoordinates," + "trackedEntityType%5Bid%5D,programTrackedEntityAttributes%5Bid,name,valueType,mandatory,allowFutureDate,trackedEntityAttribute%5Bid,name,code,valueType,generated%5D%5D,programStages%5Bid,name,description,repeatable,captureCoordinates," + "generatedByEnrollmentDate,minDaysFromStart,programStageDataElements%5Bid,compulsory,allowProvidedElsewhere,dataElement%5Bid,name,code,formName,valueType,optionSetValue,optionSet%5Bid,name,options%5Bcode,name%5D%5D%5D%5D%5D" ) ).andExpect( method( HttpMethod.GET ) ) .andRespond( MockRestResponseCreators.withServerError() ); @@ -116,7 +117,7 @@ public void findMetadataByReferenceIdServerError() @Test public void findMetadataByReferenceCode() throws IOException { - mockServer.expect( requestTo( "http://localhost:8080/api/programs.json?paging=false&fields=id,name,code,description,selectIncidentDatesInFuture,selectEnrollmentDatesInFuture,displayIncidentDate,registration,withoutRegistration,captureCoordinates," + + mockServer.expect( requestTo( "http://localhost:8080/api/programs.json?paging=false&fields=id,name,code,description,lastUpdated,selectIncidentDatesInFuture,selectEnrollmentDatesInFuture,displayIncidentDate,registration,withoutRegistration,captureCoordinates," + "trackedEntityType%5Bid%5D,programTrackedEntityAttributes%5Bid,name,valueType,mandatory,allowFutureDate,trackedEntityAttribute%5Bid,name,code,valueType,generated%5D%5D,programStages%5Bid,name,description,repeatable,captureCoordinates," + "generatedByEnrollmentDate,minDaysFromStart,programStageDataElements%5Bid,compulsory,allowProvidedElsewhere,dataElement%5Bid,name,code,formName,valueType,optionSetValue,optionSet%5Bid,name,options%5Bcode," + "name%5D%5D%5D%5D%5D&filter=code:eq:OU_3783" ) ).andExpect( method( HttpMethod.GET ) ) @@ -130,7 +131,7 @@ public void findMetadataByReferenceCode() throws IOException @Test public void findMetadataByReferenceCodeNotFound() throws IOException { - mockServer.expect( requestTo( "http://localhost:8080/api/programs.json?paging=false&fields=id,name,code,description,selectIncidentDatesInFuture,selectEnrollmentDatesInFuture,displayIncidentDate,registration,withoutRegistration,captureCoordinates," + + mockServer.expect( requestTo( "http://localhost:8080/api/programs.json?paging=false&fields=id,name,code,description,lastUpdated,selectIncidentDatesInFuture,selectEnrollmentDatesInFuture,displayIncidentDate,registration,withoutRegistration,captureCoordinates," + "trackedEntityType%5Bid%5D,programTrackedEntityAttributes%5Bid,name,valueType,mandatory,allowFutureDate,trackedEntityAttribute%5Bid,name,code,valueType,generated%5D%5D,programStages%5Bid,name,description,repeatable,captureCoordinates," + "generatedByEnrollmentDate,minDaysFromStart,programStageDataElements%5Bid,compulsory,allowProvidedElsewhere,dataElement%5Bid,name,code,formName,valueType,optionSetValue,optionSet%5Bid,name,options%5Bcode," + "name%5D%5D%5D%5D%5D&filter=code:eq:OU_3783" ) ).andExpect( method( HttpMethod.GET ) ) @@ -143,7 +144,7 @@ public void findMetadataByReferenceCodeNotFound() throws IOException @Test public void findMetadataByReferenceName() throws IOException { - mockServer.expect( requestTo( "http://localhost:8080/api/programs.json?paging=false&fields=id,name,code,description,selectIncidentDatesInFuture,selectEnrollmentDatesInFuture,displayIncidentDate,registration,withoutRegistration,captureCoordinates," + + mockServer.expect( requestTo( "http://localhost:8080/api/programs.json?paging=false&fields=id,name,code,description,lastUpdated,selectIncidentDatesInFuture,selectEnrollmentDatesInFuture,displayIncidentDate,registration,withoutRegistration,captureCoordinates," + "trackedEntityType%5Bid%5D,programTrackedEntityAttributes%5Bid,name,valueType,mandatory,allowFutureDate,trackedEntityAttribute%5Bid,name,code,valueType,generated%5D%5D,programStages%5Bid,name,description,repeatable,captureCoordinates," + "generatedByEnrollmentDate,minDaysFromStart,programStageDataElements%5Bid,compulsory,allowProvidedElsewhere,dataElement%5Bid,name,code,formName,valueType,optionSetValue,optionSet%5Bid,name,options%5Bcode," + "name%5D%5D%5D%5D%5D&filter=name:eq:Freetown" ) ).andExpect( method( HttpMethod.GET ) ) @@ -157,7 +158,7 @@ public void findMetadataByReferenceName() throws IOException @Test public void findMetadataByReferenceNameNotFound() throws IOException { - mockServer.expect( requestTo( "http://localhost:8080/api/programs.json?paging=false&fields=id,name,code,description,selectIncidentDatesInFuture,selectEnrollmentDatesInFuture,displayIncidentDate,registration,withoutRegistration,captureCoordinates," + + mockServer.expect( requestTo( "http://localhost:8080/api/programs.json?paging=false&fields=id,name,code,description,lastUpdated,selectIncidentDatesInFuture,selectEnrollmentDatesInFuture,displayIncidentDate,registration,withoutRegistration,captureCoordinates," + "trackedEntityType%5Bid%5D,programTrackedEntityAttributes%5Bid,name,valueType,mandatory,allowFutureDate,trackedEntityAttribute%5Bid,name,code,valueType,generated%5D%5D,programStages%5Bid,name,description,repeatable,captureCoordinates," + "generatedByEnrollmentDate,minDaysFromStart,programStageDataElements%5Bid,compulsory,allowProvidedElsewhere,dataElement%5Bid,name,code,formName,valueType,optionSetValue,optionSet%5Bid,name,options%5Bcode," + "name%5D%5D%5D%5D%5D&filter=name:eq:Freetown" ) ).andExpect( method( HttpMethod.GET ) ) diff --git a/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/AbstractDhisToFhirTransformer.java b/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/AbstractDhisToFhirTransformer.java index ed19b435..91e0517c 100644 --- a/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/AbstractDhisToFhirTransformer.java +++ b/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/AbstractDhisToFhirTransformer.java @@ -250,7 +250,7 @@ protected Optional getResource( @Nonnull FhirClient fhi } @Nonnull - protected String createDhisFhirResourceId( @Nonnull RuleInfo ruleInfo, @Nonnull ScriptedDhisResource dhisResource ) + protected String createDhisFhirResourceId( @Nonnull RuleInfo ruleInfo, @Nonnull ScriptedDhisResource dhisResource ) { if ( ruleInfo.getRule().isSimpleFhirId() ) { @@ -532,10 +532,9 @@ protected boolean evaluateNotModified( @Nonnull DhisToFhirTransformerContext con } @Nonnull - protected String createAdapterIdentifierValue( @Nonnull RuleInfo rule, @Nonnull ScriptedDhisResource dhisResource ) + protected String createAdapterIdentifierValue( @Nonnull RuleInfo ruleInfo, @Nonnull ScriptedDhisResource dhisResource ) { - return DhisFhirResourceId.toString( rule.getRule().getDhisResourceType(), - Objects.requireNonNull( dhisResource.getId() ), rule.getRule().getId() ); + return createDhisFhirResourceId( ruleInfo, dhisResource ); } @Nullable From 87d3054480d389faebf2102d9398e0ab9a316b6a Mon Sep 17 00:00:00 2001 From: Volker Schmidt Date: Tue, 30 Jul 2019 14:26:00 +0200 Subject: [PATCH 13/16] Adds support for DHIS2 program stage to FHIR Questionnaire. --- .../main/resources/default-application.yml | 1 + .../AbstractProgramStageFhirRestAppTest.java | 6 +- .../r4/R4ProgramMetadataFhirRestAppTest.java | 35 +-- ...R4ProgramStageMetadataFhirRestAppTest.java | 221 ++++++++++++++++++ ...stractEnrollmentTransformationAppTest.java | 22 +- ...ractProgramStageTransformationAppTest.java | 10 +- .../adapter/dhis/test/all-program-stages.json | 185 +++++++++++++++ .../dhis/test/default-program-stage.json | 101 ++++++++ .../dhis/test/single-program-stage.json | 99 ++++++++ .../sync/impl/DhisResourceRepositoryImpl.java | 10 +- .../program/ImmutableProgramStage.java | 7 + .../dhis/tracker/program/ProgramStage.java | 2 + .../program/ProgramStageMetadataService.java | 40 ++++ .../dhis/tracker/program/WritableProgram.java | 4 +- .../tracker/program/WritableProgramStage.java | 19 +- .../program/impl/DhisProgramStages.java | 62 +++++ .../impl/ProgramMetadataServiceImpl.java | 4 +- .../impl/ProgramStageMetadataServiceImpl.java | 86 +++++++ .../impl/ProgramMetadataServiceImplTest.java | 38 +-- .../ProgramStageMetadataServiceImplTest.java | 156 +++++++++++++ .../program/impl/emptyProgramStages.json | 3 + .../tracker/program/impl/programStage.json | 11 + .../tracker/program/impl/programStages.json | 24 ++ ...gramPlanDefinition.StructureDefinition.xml | 6 +- ...ogramQuestionnaire.StructureDefinition.xml | 16 +- .../R4ProgramMetadataToFhirDataProvider.java | 3 +- ...tadataToFhirPlanDefinitionTransformer.java | 3 +- ...rogramStageMetadataToFhirDataProvider.java | 70 ++++++ ...etadataToFhirQuestionnaireTransformer.java | 158 +++++++++++++ ...ProgramMetadataToFhirDataProviderTest.java | 2 +- ...taToFhirPlanDefinitionTransformerTest.java | 2 +- ...amStageMetadataToFhirDataProviderTest.java | 93 ++++++++ ...ataToFhirQuestionnaireTransformerTest.java | 170 ++++++++++++++ ...rogramStageMetadataToFhirDataProvider.java | 66 ++++++ ...etadataToFhirQuestionnaireTransformer.java | 93 ++++++++ ...ramStageMetadataToFhirRequestResolver.java | 76 ++++++ ...0.41_10_0__Plan_Definition_Sample_Data.sql | 2 + 37 files changed, 1843 insertions(+), 63 deletions(-) create mode 100644 app/src/test/java/org/dhis2/fhir/adapter/fhir/server/r4/R4ProgramStageMetadataFhirRestAppTest.java create mode 100644 app/src/test/resources/org/dhis2/fhir/adapter/dhis/test/all-program-stages.json create mode 100644 app/src/test/resources/org/dhis2/fhir/adapter/dhis/test/default-program-stage.json create mode 100644 app/src/test/resources/org/dhis2/fhir/adapter/dhis/test/single-program-stage.json create mode 100644 dhis/src/main/java/org/dhis2/fhir/adapter/dhis/tracker/program/ProgramStageMetadataService.java create mode 100644 dhis/src/main/java/org/dhis2/fhir/adapter/dhis/tracker/program/impl/DhisProgramStages.java create mode 100644 dhis/src/main/java/org/dhis2/fhir/adapter/dhis/tracker/program/impl/ProgramStageMetadataServiceImpl.java create mode 100644 dhis/src/test/java/org/dhis2/fhir/adapter/dhis/tracker/program/impl/ProgramStageMetadataServiceImplTest.java create mode 100644 dhis/src/test/resources/org/dhis2/fhir/adapter/dhis/tracker/program/impl/emptyProgramStages.json create mode 100644 dhis/src/test/resources/org/dhis2/fhir/adapter/dhis/tracker/program/impl/programStage.json create mode 100644 dhis/src/test/resources/org/dhis2/fhir/adapter/dhis/tracker/program/impl/programStages.json create mode 100644 fhir-r4/src/main/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/metadata/r4/R4ProgramStageMetadataToFhirDataProvider.java create mode 100644 fhir-r4/src/main/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/metadata/r4/R4ProgramStageMetadataToFhirQuestionnaireTransformer.java create mode 100644 fhir-r4/src/test/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/metadata/r4/R4ProgramStageMetadataToFhirDataProviderTest.java create mode 100644 fhir-r4/src/test/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/metadata/r4/R4ProgramStageMetadataToFhirQuestionnaireTransformerTest.java create mode 100644 fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/metadata/program/AbstractProgramStageMetadataToFhirDataProvider.java create mode 100644 fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/metadata/program/AbstractProgramStageMetadataToFhirQuestionnaireTransformer.java create mode 100644 fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/metadata/program/ProgramStageMetadataToFhirRequestResolver.java diff --git a/app/src/main/resources/default-application.yml b/app/src/main/resources/default-application.yml index 1feb5c15..2fb496db 100644 --- a/app/src/main/resources/default-application.yml +++ b/app/src/main/resources/default-application.yml @@ -357,6 +357,7 @@ dhis2.fhir-adapter: resource-types: - ORGANIZATION_UNIT - PROGRAM_METADATA + - PROGRAM_STAGE_METADATA - TRACKED_ENTITY - PROGRAM_STAGE_EVENT # The queue that is used to store distributed requests to synchronize the data from diff --git a/app/src/test/java/org/dhis2/fhir/adapter/fhir/server/AbstractProgramStageFhirRestAppTest.java b/app/src/test/java/org/dhis2/fhir/adapter/fhir/server/AbstractProgramStageFhirRestAppTest.java index b7ed5cd6..a5683b12 100644 --- a/app/src/test/java/org/dhis2/fhir/adapter/fhir/server/AbstractProgramStageFhirRestAppTest.java +++ b/app/src/test/java/org/dhis2/fhir/adapter/fhir/server/AbstractProgramStageFhirRestAppTest.java @@ -69,12 +69,14 @@ protected void expectProgramStageMetadataRequests() throws Exception .andExpect( requestTo( dhis2BaseUrl + "/api/" + dhis2ApiVersion + "/programs/EPDyQuoRnXk.json?" + "fields=id,name,code,description,lastUpdated,selectIncidentDatesInFuture,selectEnrollmentDatesInFuture,displayIncidentDate,registration,withoutRegistration,captureCoordinates,trackedEntityType%5Bid%5D,programTrackedEntityAttributes%5Bid," + "name,valueType," + - "mandatory,allowFutureDate,trackedEntityAttribute%5Bid,name,code,valueType,generated%5D%5D,programStages%5Bid,name,description,repeatable,captureCoordinates,generatedByEnrollmentDate,minDaysFromStart,programStageDataElements%5Bid,compulsory," + + "mandatory,allowFutureDate,trackedEntityAttribute%5Bid,name,code,valueType,generated%5D%5D,programStages%5Bid,program%5Bid%5D,lastUpdated,name,description,repeatable,captureCoordinates,generatedByEnrollmentDate,minDaysFromStart," + + "programStageDataElements%5Bid,compulsory," + "allowProvidedElsewhere,dataElement%5Bid,name,code,formName,valueType,optionSetValue,optionSet%5Bid,name,options%5Bcode,name%5D%5D%5D%5D%5D" ) ) .andRespond( withSuccess( IOUtils.resourceToString( "/org/dhis2/fhir/adapter/dhis/test/single-program.json", StandardCharsets.UTF_8 ), MediaType.APPLICATION_JSON ) ); systemDhis2Server.expect( ExpectedCount.between( 0, 1 ), method( HttpMethod.GET ) ).andExpect( header( "Authorization", testConfiguration.getDhis2SystemAuthorization() ) ) .andExpect( requestTo( dhis2BaseUrl + "/api/" + dhis2ApiVersion + "/programs.json?paging=false&fields=id,name,code,description,lastUpdated,selectIncidentDatesInFuture,selectEnrollmentDatesInFuture,displayIncidentDate,registration," + - "withoutRegistration,captureCoordinates,trackedEntityType%5Bid%5D,programTrackedEntityAttributes%5Bid,name,valueType,mandatory,allowFutureDate,trackedEntityAttribute%5Bid,name,code,valueType,generated%5D%5D,programStages%5Bid,name," + + "withoutRegistration,captureCoordinates,trackedEntityType%5Bid%5D,programTrackedEntityAttributes%5Bid,name,valueType,mandatory,allowFutureDate,trackedEntityAttribute%5Bid,name,code,valueType,generated%5D%5D,programStages%5Bid," + + "program%5Bid%5D,lastUpdated,name," + "description,repeatable,captureCoordinates,generatedByEnrollmentDate,minDaysFromStart,programStageDataElements%5Bid,compulsory,allowProvidedElsewhere,dataElement%5Bid,name,code,formName,valueType,optionSetValue,optionSet%5Bid,name," + "options%5Bcode,name%5D%5D%5D%5D%5D&filter=name:eq:Child%20Programme" ) ) .andRespond( withSuccess( IOUtils.resourceToString( "/org/dhis2/fhir/adapter/dhis/test/default-program.json", StandardCharsets.UTF_8 ), MediaType.APPLICATION_JSON ) ); diff --git a/app/src/test/java/org/dhis2/fhir/adapter/fhir/server/r4/R4ProgramMetadataFhirRestAppTest.java b/app/src/test/java/org/dhis2/fhir/adapter/fhir/server/r4/R4ProgramMetadataFhirRestAppTest.java index 695d91d2..e86e33b7 100644 --- a/app/src/test/java/org/dhis2/fhir/adapter/fhir/server/r4/R4ProgramMetadataFhirRestAppTest.java +++ b/app/src/test/java/org/dhis2/fhir/adapter/fhir/server/r4/R4ProgramMetadataFhirRestAppTest.java @@ -80,7 +80,8 @@ public void getPlanDefinitionWithInvalidAuthorization() .andExpect( requestTo( dhis2BaseUrl + "/api/" + dhis2ApiVersion + "/programs/EPDyQuoRnXk.json?" + "fields=id,name,code,description,lastUpdated,selectIncidentDatesInFuture,selectEnrollmentDatesInFuture,displayIncidentDate,registration,withoutRegistration,captureCoordinates,trackedEntityType%5Bid%5D,programTrackedEntityAttributes%5Bid," + "name,valueType," + - "mandatory,allowFutureDate,trackedEntityAttribute%5Bid,name,code,valueType,generated%5D%5D,programStages%5Bid,name,description,repeatable,captureCoordinates,generatedByEnrollmentDate,minDaysFromStart,programStageDataElements%5Bid,compulsory," + + "mandatory,allowFutureDate,trackedEntityAttribute%5Bid,name,code,valueType,generated%5D%5D,programStages%5Bid,program%5Bid%5D,lastUpdated,name,description,repeatable,captureCoordinates,generatedByEnrollmentDate,minDaysFromStart," + + "programStageDataElements%5Bid,compulsory," + "allowProvidedElsewhere,dataElement%5Bid,name,code,formName,valueType,optionSetValue,optionSet%5Bid,name,options%5Bcode,name%5D%5D%5D%5D%5D" ) ) .andRespond( withStatus( HttpStatus.UNAUTHORIZED ) ); @@ -107,13 +108,14 @@ private void getPlanDefinition() throws Exception .andRespond( withSuccess( IOUtils.resourceToString( "/org/dhis2/fhir/adapter/dhis/test/single-tracked-entity-type.json", StandardCharsets.UTF_8 ), MediaType.APPLICATION_JSON ) ); userDhis2Server.expect( ExpectedCount.once(), method( HttpMethod.GET ) ).andExpect( header( "Authorization", "Basic Zmhpcl9jbGllbnQ6Zmhpcl9jbGllbnRfMQ==" ) ) .andExpect( requestTo( dhis2BaseUrl + "/api/" + dhis2ApiVersion + "/programs/EPDyQuoRnXk.json?fields=id,name,code,description,lastUpdated,selectIncidentDatesInFuture,selectEnrollmentDatesInFuture,displayIncidentDate,registration," + - "withoutRegistration,captureCoordinates,trackedEntityType%5Bid%5D,programTrackedEntityAttributes%5Bid,name,valueType,mandatory,allowFutureDate,trackedEntityAttribute%5Bid,name,code,valueType,generated%5D%5D,programStages%5Bid,name," + + "withoutRegistration,captureCoordinates,trackedEntityType%5Bid%5D,programTrackedEntityAttributes%5Bid,name,valueType,mandatory,allowFutureDate,trackedEntityAttribute%5Bid,name,code,valueType,generated%5D%5D,programStages%5Bid," + + "program%5Bid%5D,lastUpdated,name," + "description,repeatable,captureCoordinates,generatedByEnrollmentDate,minDaysFromStart,programStageDataElements%5Bid,compulsory,allowProvidedElsewhere,dataElement%5Bid,name,code,formName,valueType,optionSetValue,optionSet%5Bid,name," + "options%5Bcode,name%5D%5D%5D%5D%5D" ) ) .andRespond( withSuccess( IOUtils.resourceToString( "/org/dhis2/fhir/adapter/dhis/test/single-program.json", StandardCharsets.UTF_8 ), MediaType.APPLICATION_JSON ) ); userDhis2Server.expect( ExpectedCount.between( 0, 1 ), method( HttpMethod.GET ) ).andExpect( header( "Authorization", "Basic Zmhpcl9jbGllbnQ6Zmhpcl9jbGllbnRfMQ==" ) ) .andExpect( requestTo( dhis2BaseUrl + "/api/" + dhis2ApiVersion + "/programs.json?paging=false&fields=id,name,code,description,lastUpdated,selectIncidentDatesInFuture,selectEnrollmentDatesInFuture,displayIncidentDate,registration," + - "withoutRegistration,captureCoordinates,trackedEntityType%5Bid%5D,programTrackedEntityAttributes%5Bid,name,valueType,mandatory,allowFutureDate,trackedEntityAttribute%5Bid,name,code,valueType,generated%5D%5D,programStages%5Bid,name," + + "withoutRegistration,captureCoordinates,trackedEntityType%5Bid%5D,programTrackedEntityAttributes%5Bid,name,valueType,mandatory,allowFutureDate,trackedEntityAttribute%5Bid,name,code,valueType,generated%5D%5D,programStages%5Bid,program%5Bid%5D,lastUpdated,name," + "description,repeatable,captureCoordinates,generatedByEnrollmentDate,minDaysFromStart,programStageDataElements%5Bid,compulsory,allowProvidedElsewhere,dataElement%5Bid,name,code,formName,valueType,optionSetValue,optionSet%5Bid,name," + "options%5Bcode,name%5D%5D%5D%5D%5D" ) ) .andRespond( withSuccess( IOUtils.resourceToString( "/org/dhis2/fhir/adapter/dhis/test/all-programs.json", StandardCharsets.UTF_8 ), MediaType.APPLICATION_JSON ) ); @@ -121,7 +123,7 @@ private void getPlanDefinition() throws Exception final IGenericClient client = createGenericClient(); client.registerInterceptor( new BasicAuthInterceptor( "fhir_client", "fhir_client_1" ) ); PlanDefinition planDefinition = client.read().resource( PlanDefinition.class ).withId( "EPDyQuoRnXk" ).execute(); - Assert.assertEquals( "Child Programme", planDefinition.getName() ); + Assert.assertEquals( "Child Programme", planDefinition.getTitle() ); systemDhis2Server.verify(); userDhis2Server.verify(); @@ -148,7 +150,7 @@ private void getPlanDefinitionNotExists() userDhis2Server.expect( ExpectedCount.once(), method( HttpMethod.GET ) ).andExpect( header( "Authorization", "Basic Zmhpcl9jbGllbnQ6Zmhpcl9jbGllbnRfMQ==" ) ) .andExpect( requestTo( dhis2BaseUrl + "/api/" + dhis2ApiVersion + "/programs/0dXIdLNUNEn.json?fields=id,name,code,description,lastUpdated,selectIncidentDatesInFuture,selectEnrollmentDatesInFuture,displayIncidentDate,registration," + - "withoutRegistration,captureCoordinates,trackedEntityType%5Bid%5D,programTrackedEntityAttributes%5Bid,name,valueType,mandatory,allowFutureDate,trackedEntityAttribute%5Bid,name,code,valueType,generated%5D%5D,programStages%5Bid,name," + + "withoutRegistration,captureCoordinates,trackedEntityType%5Bid%5D,programTrackedEntityAttributes%5Bid,name,valueType,mandatory,allowFutureDate,trackedEntityAttribute%5Bid,name,code,valueType,generated%5D%5D,programStages%5Bid,program%5Bid%5D,lastUpdated,name," + "description,repeatable,captureCoordinates,generatedByEnrollmentDate,minDaysFromStart,programStageDataElements%5Bid,compulsory,allowProvidedElsewhere,dataElement%5Bid,name,code,formName,valueType,optionSetValue,optionSet%5Bid,name," + "options%5Bcode,name%5D%5D%5D%5D%5D" ) ) .andRespond( withStatus( HttpStatus.NOT_FOUND ).contentType( MediaType.APPLICATION_JSON ).body( "{}" ) ); @@ -211,8 +213,9 @@ public void getPlanDefinitionByIdentifierWithoutAuthorization() public void getPlanDefinitionByIdentifierInvalidAuthorization() { userDhis2Server.expect( ExpectedCount.once(), method( HttpMethod.GET ) ).andExpect( header( "Authorization", "Basic Zmhpcl9jbGllbnQ6aW52YWxpZF8x" ) ) - .andExpect( requestTo( dhis2BaseUrl + "/api/" + dhis2ApiVersion + "/programs.json?paging=false&fields=id,name,code,description,lastUpdated,selectIncidentDatesInFuture,selectEnrollmentDatesInFuture,displayIncidentDate,registration,withoutRegistration," + - "captureCoordinates,trackedEntityType%5Bid%5D,programTrackedEntityAttributes%5Bid,name,valueType,mandatory,allowFutureDate,trackedEntityAttribute%5Bid,name,code,valueType,generated%5D%5D,programStages%5Bid,name,description,repeatable," + + .andExpect( requestTo( dhis2BaseUrl + "/api/" + dhis2ApiVersion + + "/programs.json?paging=false&fields=id,name,code,description,lastUpdated,selectIncidentDatesInFuture,selectEnrollmentDatesInFuture,displayIncidentDate,registration,withoutRegistration," + + "captureCoordinates,trackedEntityType%5Bid%5D,programTrackedEntityAttributes%5Bid,name,valueType,mandatory,allowFutureDate,trackedEntityAttribute%5Bid,name,code,valueType,generated%5D%5D,programStages%5Bid,program%5Bid%5D,lastUpdated,name,description,repeatable," + "captureCoordinates,generatedByEnrollmentDate,minDaysFromStart,programStageDataElements%5Bid,compulsory,allowProvidedElsewhere,dataElement%5Bid,name,code,formName,valueType,optionSetValue,optionSet%5Bid,name,options%5Bcode," + "name%5D%5D%5D%5D%5D&filter=code:eq:OU_1234" ) ) .andRespond( withStatus( HttpStatus.UNAUTHORIZED ) ); @@ -239,8 +242,9 @@ private void getPlanDefinitionByIdentifier() throws Exception "fields=id,name,trackedEntityTypeAttributes%5Bid,name,valueType,mandatory,trackedEntityAttribute%5Bid,name,code,valueType,generated,optionSetValue,optionSet%5Bid,name,options%5Bcode,name%5D%5D%5D%5D" ) ) .andRespond( withSuccess( IOUtils.resourceToString( "/org/dhis2/fhir/adapter/dhis/test/single-tracked-entity-type.json", StandardCharsets.UTF_8 ), MediaType.APPLICATION_JSON ) ); userDhis2Server.expect( ExpectedCount.between( 1, 2 ), method( HttpMethod.GET ) ).andExpect( header( "Authorization", "Basic Zmhpcl9jbGllbnQ6Zmhpcl9jbGllbnRfMQ==" ) ) - .andExpect( requestTo( dhis2BaseUrl + "/api/" + dhis2ApiVersion + "/programs.json?paging=false&fields=id,name,code,description,lastUpdated,selectIncidentDatesInFuture,selectEnrollmentDatesInFuture,displayIncidentDate,registration,withoutRegistration," + - "captureCoordinates,trackedEntityType%5Bid%5D,programTrackedEntityAttributes%5Bid,name,valueType,mandatory,allowFutureDate,trackedEntityAttribute%5Bid,name,code,valueType,generated%5D%5D,programStages%5Bid,name,description,repeatable," + + .andExpect( requestTo( dhis2BaseUrl + "/api/" + dhis2ApiVersion + "/programs.json?paging=false&fields=id,name,code,description,lastUpdated,selectIncidentDatesInFuture,selectEnrollmentDatesInFuture,displayIncidentDate,registration," + + "withoutRegistration," + + "captureCoordinates,trackedEntityType%5Bid%5D,programTrackedEntityAttributes%5Bid,name,valueType,mandatory,allowFutureDate,trackedEntityAttribute%5Bid,name,code,valueType,generated%5D%5D,programStages%5Bid,program%5Bid%5D,lastUpdated,name,description,repeatable," + "captureCoordinates,generatedByEnrollmentDate,minDaysFromStart,programStageDataElements%5Bid,compulsory,allowProvidedElsewhere,dataElement%5Bid,name,code,formName,valueType,optionSetValue,optionSet%5Bid,name,options%5Bcode," + "name%5D%5D%5D%5D%5D&filter=code:eq:PD_1234" ) ) .andRespond( withSuccess( IOUtils.resourceToString( "/org/dhis2/fhir/adapter/dhis/test/default-program.json", StandardCharsets.UTF_8 ), MediaType.APPLICATION_JSON ) ); @@ -251,7 +255,7 @@ private void getPlanDefinitionByIdentifier() throws Exception client.search().forResource( PlanDefinition.class ).where( PlanDefinition.IDENTIFIER.exactly().systemAndIdentifier( "http://www.dhis2.org/dhis2-fhir-adapter/systems/plan-definition-identifier", "PD_1234" ) ).returnBundle( Bundle.class ).execute(); Assert.assertEquals( 1, bundle.getEntry().size() ); PlanDefinition planDefinition = (PlanDefinition) bundle.getEntry().get( 0 ).getResource(); - Assert.assertEquals( "Child Programme", planDefinition.getName() ); + Assert.assertEquals( "Child Programme", planDefinition.getTitle() ); systemDhis2Server.verify(); userDhis2Server.verify(); @@ -270,13 +274,14 @@ public void searchPlanDefinitionInvalidAuthorization() userDhis2Server.expect( ExpectedCount.once(), method( HttpMethod.GET ) ).andExpect( header( "Authorization", "Basic Zmhpcl9jbGllbnQ6aW52YWxpZF8x" ) ) .andExpect( requestTo( dhis2BaseUrl + "/api/" + dhis2ApiVersion + "/programs.json?filter=name:$ilike:Test&paging=true&page=1&pageSize=10&order=id&fields=id,name,code,description,lastUpdated,selectIncidentDatesInFuture,selectEnrollmentDatesInFuture," + "displayIncidentDate,registration,withoutRegistration,captureCoordinates,trackedEntityType%5Bid%5D,programTrackedEntityAttributes%5Bid,name,valueType,mandatory,allowFutureDate,trackedEntityAttribute%5Bid,name,code,valueType,generated%5D%5D," + - "programStages%5Bid,name,description,repeatable,captureCoordinates,generatedByEnrollmentDate,minDaysFromStart,programStageDataElements%5Bid,compulsory,allowProvidedElsewhere,dataElement%5Bid,name,code,formName,valueType,optionSetValue," + + "programStages%5Bid,program%5Bid%5D,lastUpdated,name,description,repeatable,captureCoordinates,generatedByEnrollmentDate,minDaysFromStart,programStageDataElements%5Bid,compulsory,allowProvidedElsewhere,dataElement%5Bid,name,code,formName," + + "valueType,optionSetValue," + "optionSet%5Bid,name,options%5Bcode,name%5D%5D%5D%5D%5D" ) ) .andRespond( withStatus( HttpStatus.UNAUTHORIZED ) ); final IGenericClient client = createGenericClient(); client.registerInterceptor( new BasicAuthInterceptor( "fhir_client", "invalid_1" ) ); - client.search().forResource( PlanDefinition.class ).where( PlanDefinition.NAME.matches().value( "Test" ) ).returnBundle( Bundle.class ).execute(); + client.search().forResource( PlanDefinition.class ).where( PlanDefinition.TITLE.matches().value( "Test" ) ).returnBundle( Bundle.class ).execute(); } @Test @@ -289,15 +294,15 @@ public void searchPlanDefinition() throws Exception userDhis2Server.expect( ExpectedCount.once(), method( HttpMethod.GET ) ).andExpect( header( "Authorization", "Basic Zmhpcl9jbGllbnQ6Zmhpcl9jbGllbnRfMQ==" ) ) .andExpect( requestTo( dhis2BaseUrl + "/api/" + dhis2ApiVersion + "/programs.json?filter=name:$ilike:Child%20Programme&paging=true&page=1&pageSize=10&order=id&fields=id,name,code,description,lastUpdated,selectIncidentDatesInFuture," + "selectEnrollmentDatesInFuture,displayIncidentDate,registration,withoutRegistration,captureCoordinates,trackedEntityType%5Bid%5D,programTrackedEntityAttributes%5Bid,name,valueType,mandatory,allowFutureDate,trackedEntityAttribute%5Bid,name," + - "code,valueType,generated%5D%5D,programStages%5Bid,name,description,repeatable,captureCoordinates,generatedByEnrollmentDate,minDaysFromStart,programStageDataElements%5Bid,compulsory,allowProvidedElsewhere,dataElement%5Bid,name,code,formName," + + "code,valueType,generated%5D%5D,programStages%5Bid,program%5Bid%5D,lastUpdated,name,description,repeatable,captureCoordinates,generatedByEnrollmentDate,minDaysFromStart,programStageDataElements%5Bid,compulsory,allowProvidedElsewhere,dataElement%5Bid,name,code,formName," + "valueType,optionSetValue,optionSet%5Bid,name,options%5Bcode,name%5D%5D%5D%5D%5D" ) ) .andRespond( withSuccess( IOUtils.resourceToString( "/org/dhis2/fhir/adapter/dhis/test/default-program.json", StandardCharsets.UTF_8 ), MediaType.APPLICATION_JSON ) ); final IGenericClient client = createGenericClient(); client.registerInterceptor( new BasicAuthInterceptor( "fhir_client", "fhir_client_1" ) ); - Bundle bundle = client.search().forResource( PlanDefinition.class ).where( PlanDefinition.NAME.matches().value( "Child Programme" ) ).returnBundle( Bundle.class ).execute(); + Bundle bundle = client.search().forResource( PlanDefinition.class ).where( PlanDefinition.TITLE.matches().value( "Child Programme" ) ).returnBundle( Bundle.class ).execute(); Assert.assertEquals( 1, bundle.getEntry().size() ); PlanDefinition planDefinition = (PlanDefinition) bundle.getEntry().get( 0 ).getResource(); - Assert.assertEquals( "Child Programme", planDefinition.getName() ); + Assert.assertEquals( "Child Programme", planDefinition.getTitle() ); } } diff --git a/app/src/test/java/org/dhis2/fhir/adapter/fhir/server/r4/R4ProgramStageMetadataFhirRestAppTest.java b/app/src/test/java/org/dhis2/fhir/adapter/fhir/server/r4/R4ProgramStageMetadataFhirRestAppTest.java new file mode 100644 index 00000000..b86a8b22 --- /dev/null +++ b/app/src/test/java/org/dhis2/fhir/adapter/fhir/server/r4/R4ProgramStageMetadataFhirRestAppTest.java @@ -0,0 +1,221 @@ +package org.dhis2.fhir.adapter.fhir.server.r4; + +/* + * Copyright (c) 2004-2019, University of Oslo + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * Neither the name of the HISP project nor the names of its contributors may + * be used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +import ca.uhn.fhir.rest.client.api.IGenericClient; +import ca.uhn.fhir.rest.client.interceptor.BasicAuthInterceptor; +import ca.uhn.fhir.rest.server.exceptions.AuthenticationException; +import ca.uhn.fhir.rest.server.exceptions.ResourceNotFoundException; +import org.apache.commons.io.IOUtils; +import org.dhis2.fhir.adapter.AbstractAppTest; +import org.dhis2.fhir.adapter.fhir.model.FhirVersion; +import org.hl7.fhir.r4.model.Bundle; +import org.hl7.fhir.r4.model.Questionnaire; +import org.junit.Assert; +import org.junit.Test; +import org.springframework.http.HttpMethod; +import org.springframework.http.HttpStatus; +import org.springframework.http.MediaType; +import org.springframework.test.web.client.ExpectedCount; + +import javax.annotation.Nonnull; +import java.nio.charset.StandardCharsets; + +import static org.springframework.test.web.client.match.MockRestRequestMatchers.*; +import static org.springframework.test.web.client.response.MockRestResponseCreators.withStatus; +import static org.springframework.test.web.client.response.MockRestResponseCreators.withSuccess; + +/** + * R4 tests for rest interfaces that access + * DHIS2 program stage metadata. + * + * @author volsch + */ +public class R4ProgramStageMetadataFhirRestAppTest extends AbstractAppTest +{ + @Nonnull + @Override + protected FhirVersion getFhirVersion() + { + return FhirVersion.R4; + } + + @Test( expected = AuthenticationException.class ) + public void getQuestionnaireWithoutAuthorization() + { + final IGenericClient client = createGenericClient(); + client.read().resource( Questionnaire.class ).withId( "MsWxkiY6tMS" ).execute(); + } + + @Test( expected = AuthenticationException.class ) + public void getQuestionnaireWithInvalidAuthorization() + { + userDhis2Server.expect( ExpectedCount.once(), method( HttpMethod.GET ) ).andExpect( header( "Authorization", "Basic Zmhpcl9jbGllbnQ6aW52YWxpZF8x" ) ) + .andExpect( requestTo( dhis2BaseUrl + "/api/" + dhis2ApiVersion + "/programStages/MsWxkiY6tMS.json?fields=id,program%5Bid%5D,lastUpdated,name,description,repeatable,captureCoordinates,generatedByEnrollmentDate,minDaysFromStart," + + "programStageDataElements%5Bid,compulsory,allowProvidedElsewhere,dataElement%5Bid,name,code,formName,valueType,optionSetValue,optionSet%5Bid,name,options%5Bcode,name%5D%5D%5D%5D" ) ) + .andRespond( withStatus( HttpStatus.UNAUTHORIZED ) ); + + final IGenericClient client = createGenericClient(); + client.registerInterceptor( new BasicAuthInterceptor( "fhir_client", "invalid_1" ) ); + client.read().resource( Questionnaire.class ).withId( "MsWxkiY6tMS" ).execute(); + } + + @Test + public void getQuestionnaireRepeated() throws Exception + { + getQuestionnaire(); + getQuestionnaire(); + } + + private void getQuestionnaire() throws Exception + { + systemDhis2Server.reset(); + userDhis2Server.reset(); + + userDhis2Server.expect( ExpectedCount.once(), method( HttpMethod.GET ) ).andExpect( header( "Authorization", "Basic Zmhpcl9jbGllbnQ6Zmhpcl9jbGllbnRfMQ==" ) ) + .andExpect( requestTo( dhis2BaseUrl + "/api/" + dhis2ApiVersion + "/programStages/MsWxkiY6tMS.json?fields=id,program%5Bid%5D,lastUpdated,name,description,repeatable,captureCoordinates,generatedByEnrollmentDate,minDaysFromStart," + + "programStageDataElements%5Bid,compulsory,allowProvidedElsewhere,dataElement%5Bid,name,code,formName,valueType,optionSetValue,optionSet%5Bid,name,options%5Bcode,name%5D%5D%5D%5D" ) ) + .andRespond( withSuccess( IOUtils.resourceToString( "/org/dhis2/fhir/adapter/dhis/test/single-program-stage.json", StandardCharsets.UTF_8 ), MediaType.APPLICATION_JSON ) ); + + final IGenericClient client = createGenericClient(); + client.registerInterceptor( new BasicAuthInterceptor( "fhir_client", "fhir_client_1" ) ); + Questionnaire planDefinition = client.read().resource( Questionnaire.class ).withId( "MsWxkiY6tMS" ).execute(); + Assert.assertEquals( "Birth", planDefinition.getTitle() ); + + systemDhis2Server.verify(); + userDhis2Server.verify(); + } + + @Test( expected = ResourceNotFoundException.class ) + public void getQuestionnaireNotExistsRepeated() + { + try + { + getQuestionnaireNotExists(); + Assert.fail( "Exception expected also an first invocation." ); + } + catch ( ResourceNotFoundException e ) + { + getQuestionnaireNotExists(); + } + } + + private void getQuestionnaireNotExists() + { + systemDhis2Server.reset(); + userDhis2Server.reset(); + + userDhis2Server.expect( ExpectedCount.once(), method( HttpMethod.GET ) ).andExpect( header( "Authorization", "Basic Zmhpcl9jbGllbnQ6Zmhpcl9jbGllbnRfMQ==" ) ) + .andExpect( requestTo( dhis2BaseUrl + "/api/" + dhis2ApiVersion + "/programStages/0dXIdLNUNEn.json?fields=id,program%5Bid%5D,lastUpdated,name,description,repeatable,captureCoordinates,generatedByEnrollmentDate,minDaysFromStart," + + "programStageDataElements%5Bid,compulsory,allowProvidedElsewhere,dataElement%5Bid,name,code,formName,valueType,optionSetValue,optionSet%5Bid,name,options%5Bcode,name%5D%5D%5D%5D" ) ) + .andRespond( withStatus( HttpStatus.NOT_FOUND ).contentType( MediaType.APPLICATION_JSON ).body( "{}" ) ); + + try + { + final IGenericClient client = createGenericClient(); + client.registerInterceptor( new BasicAuthInterceptor( "fhir_client", "fhir_client_1" ) ); + client.read().resource( Questionnaire.class ).withId( "0dXIdLNUNEn" ).execute(); + } + catch ( ResourceNotFoundException e ) + { + systemDhis2Server.verify(); + userDhis2Server.verify(); + throw e; + } + } + + @Test( expected = ResourceNotFoundException.class ) + public void getQuestionnaireRuleNotFoundRepeated() + { + try + { + getQuestionnaireNotExists(); + Assert.fail( "Exception expected also an first invocation." ); + } + catch ( ResourceNotFoundException e ) + { + getQuestionnaireNotExists(); + } + } + + private void getQuestionnaireRuleNotFound() + { + systemDhis2Server.reset(); + userDhis2Server.reset(); + + try + { + final IGenericClient client = createGenericClient(); + client.registerInterceptor( new BasicAuthInterceptor( "fhir_client", "fhir_client_1" ) ); + client.read().resource( Questionnaire.class ).withId( "ldXIdLNUNEn" ).execute(); + } + catch ( ResourceNotFoundException e ) + { + systemDhis2Server.verify(); + userDhis2Server.verify(); + throw e; + } + } + + @Test( expected = AuthenticationException.class ) + public void searchQuestionnaireWithoutAuthorization() + { + final IGenericClient client = createGenericClient(); + client.search().forResource( Questionnaire.class ).where( Questionnaire.TITLE.matches().value( "Test" ) ).returnBundle( Bundle.class ).execute(); + } + + @Test( expected = AuthenticationException.class ) + public void searchQuestionnaireInvalidAuthorization() + { + userDhis2Server.expect( ExpectedCount.once(), method( HttpMethod.GET ) ).andExpect( header( "Authorization", "Basic Zmhpcl9jbGllbnQ6aW52YWxpZF8x" ) ) + .andExpect( requestTo( dhis2BaseUrl + "/api/" + dhis2ApiVersion + "/programStages.json?filter=name:$ilike:Test&paging=true&page=1&pageSize=10&order=id&fields=id,program%5Bid%5D,lastUpdated,name,description,repeatable,captureCoordinates," + + "generatedByEnrollmentDate,minDaysFromStart,programStageDataElements%5Bid,compulsory,allowProvidedElsewhere,dataElement%5Bid,name,code,formName,valueType,optionSetValue,optionSet%5Bid,name,options%5Bcode,name%5D%5D%5D%5D" ) ) + .andRespond( withStatus( HttpStatus.UNAUTHORIZED ) ); + + final IGenericClient client = createGenericClient(); + client.registerInterceptor( new BasicAuthInterceptor( "fhir_client", "invalid_1" ) ); + client.search().forResource( Questionnaire.class ).where( Questionnaire.TITLE.matches().value( "Test" ) ).returnBundle( Bundle.class ).execute(); + } + + @Test + public void searchQuestionnaire() throws Exception + { + userDhis2Server.expect( ExpectedCount.once(), method( HttpMethod.GET ) ).andExpect( header( "Authorization", "Basic Zmhpcl9jbGllbnQ6Zmhpcl9jbGllbnRfMQ==" ) ) + .andExpect( requestTo( dhis2BaseUrl + "/api/" + dhis2ApiVersion + "/programStages.json?filter=name:$ilike:Birth&paging=true&page=1&pageSize=10&order=id&fields=id,program%5Bid%5D,lastUpdated,name,description,repeatable,captureCoordinates," + + "generatedByEnrollmentDate,minDaysFromStart,programStageDataElements%5Bid,compulsory,allowProvidedElsewhere,dataElement%5Bid,name,code,formName,valueType,optionSetValue,optionSet%5Bid,name,options%5Bcode,name%5D%5D%5D%5D" ) ) + .andRespond( withSuccess( IOUtils.resourceToString( "/org/dhis2/fhir/adapter/dhis/test/default-program-stage.json", StandardCharsets.UTF_8 ), MediaType.APPLICATION_JSON ) ); + + final IGenericClient client = createGenericClient(); + client.registerInterceptor( new BasicAuthInterceptor( "fhir_client", "fhir_client_1" ) ); + Bundle bundle = client.search().forResource( Questionnaire.class ).where( Questionnaire.TITLE.matches().value( "Birth" ) ).returnBundle( Bundle.class ).execute(); + Assert.assertEquals( 1, bundle.getEntry().size() ); + Questionnaire planDefinition = (Questionnaire) bundle.getEntry().get( 0 ).getResource(); + Assert.assertEquals( "Birth", planDefinition.getTitle() ); + } +} diff --git a/app/src/test/java/org/dhis2/fhir/adapter/fhir/transform/fhir/impl/enrollment/AbstractEnrollmentTransformationAppTest.java b/app/src/test/java/org/dhis2/fhir/adapter/fhir/transform/fhir/impl/enrollment/AbstractEnrollmentTransformationAppTest.java index 7b0009fc..aec89b39 100644 --- a/app/src/test/java/org/dhis2/fhir/adapter/fhir/transform/fhir/impl/enrollment/AbstractEnrollmentTransformationAppTest.java +++ b/app/src/test/java/org/dhis2/fhir/adapter/fhir/transform/fhir/impl/enrollment/AbstractEnrollmentTransformationAppTest.java @@ -50,17 +50,16 @@ protected void expectMetadataRequests() throws Exception { systemDhis2Server.expect( ExpectedCount.between( 0, 1 ), method( HttpMethod.GET ) ).andExpect( header( "Authorization", testConfiguration.getDhis2SystemAuthorization() ) ) .andExpect( requestTo( dhis2BaseUrl + "/api/" + dhis2ApiVersion + "/programs/EPDyQuoRnXk.json?fields=id,name,code,description,lastUpdated,selectIncidentDatesInFuture,selectEnrollmentDatesInFuture,displayIncidentDate,registration," + - "withoutRegistration,captureCoordinates,trackedEntityType%5Bid%5D,programTrackedEntityAttributes%5Bid,name,valueType,mandatory,allowFutureDate,trackedEntityAttribute%5Bid,name,code,valueType,generated%5D%5D,programStages%5Bid,name," + - "description,repeatable,captureCoordinates,generatedByEnrollmentDate,minDaysFromStart,programStageDataElements%5Bid,compulsory,allowProvidedElsewhere,dataElement%5Bid,name,code,formName,valueType,optionSetValue,optionSet%5Bid,name," + - "options%5Bcode,name%5D%5D%5D%5D%5D" ) ) + "withoutRegistration,captureCoordinates,trackedEntityType%5Bid%5D,programTrackedEntityAttributes%5Bid,name,valueType,mandatory,allowFutureDate,trackedEntityAttribute%5Bid,name,code,valueType,generated%5D%5D,programStages%5Bid," + + "program%5Bid%5D,lastUpdated,name,description,repeatable,captureCoordinates,generatedByEnrollmentDate,minDaysFromStart,programStageDataElements%5Bid,compulsory,allowProvidedElsewhere,dataElement%5Bid,name,code,formName,valueType," + + "optionSetValue,optionSet%5Bid,name,options%5Bcode,name%5D%5D%5D%5D%5D" ) ) .andRespond( withSuccess( IOUtils.resourceToString( "/org/dhis2/fhir/adapter/dhis/test/single-program.json", StandardCharsets.UTF_8 ), MediaType.APPLICATION_JSON ) ); systemDhis2Server.expect( ExpectedCount.between( 0, 1 ), method( HttpMethod.GET ) ).andExpect( header( "Authorization", testConfiguration.getDhis2SystemAuthorization() ) ) .andExpect( requestTo( dhis2BaseUrl + "/api/" + dhis2ApiVersion + "/programs.json?paging=false&fields=id,name,code,description,lastUpdated,selectIncidentDatesInFuture,selectEnrollmentDatesInFuture,displayIncidentDate,registration," + - "withoutRegistration," + - "captureCoordinates," + - "trackedEntityType%5Bid%5D,programTrackedEntityAttributes%5Bid,name,valueType,mandatory,allowFutureDate,trackedEntityAttribute%5Bid,name,code,valueType,generated%5D%5D,programStages%5Bid,name,description,repeatable,captureCoordinates," + - "generatedByEnrollmentDate,minDaysFromStart,programStageDataElements%5Bid,compulsory,allowProvidedElsewhere,dataElement%5Bid,name,code,description,formName,valueType,optionSetValue,optionSet%5Bid,name,options%5Bcode," + - "name%5D%5D%5D%5D%5D&filter=name:eq:Child%20Programme" ) ) + "withoutRegistration,captureCoordinates,trackedEntityType%5Bid%5D,programTrackedEntityAttributes%5Bid,name,valueType,mandatory,allowFutureDate,trackedEntityAttribute%5Bid,name,code,valueType,generated%5D%5D,programStages%5Bid," + + "program%5Bid%5D," + + "lastUpdated,name,description,repeatable,captureCoordinates,generatedByEnrollmentDate,minDaysFromStart,programStageDataElements%5Bid,compulsory,allowProvidedElsewhere,dataElement%5Bid,name,code,description,formName,valueType,optionSetValue," + + "optionSet%5Bid,name,options%5Bcode,name%5D%5D%5D%5D%5D&filter=name:eq:Child%20Programme" ) ) .andRespond( withSuccess( IOUtils.resourceToString( "/org/dhis2/fhir/adapter/dhis/test/all-programs.json", StandardCharsets.UTF_8 ), MediaType.APPLICATION_JSON ) ); systemDhis2Server.expect( ExpectedCount.between( 0, 1 ), method( HttpMethod.GET ) ).andExpect( header( "Authorization", testConfiguration.getDhis2SystemAuthorization() ) ) .andExpect( requestTo( dhis2BaseUrl + "/api/" + dhis2ApiVersion + "/trackedEntityTypes/MCPQUTHX1Ze.json?fields=id,name,trackedEntityTypeAttributes%5Bid,name,valueType,mandatory,trackedEntityAttribute%5Bid,name,code,valueType,generated," + @@ -75,9 +74,10 @@ protected void expectMetadataRequests() throws Exception .andRespond( withSuccess( IOUtils.resourceToString( "/org/dhis2/fhir/adapter/dhis/test/default-tracked-entity-type.json", StandardCharsets.UTF_8 ), MediaType.APPLICATION_JSON ) ); systemDhis2Server.expect( ExpectedCount.between( 0, 1 ), method( HttpMethod.GET ) ).andExpect( header( "Authorization", testConfiguration.getDhis2SystemAuthorization() ) ) .andExpect( requestTo( dhis2BaseUrl + "/api/" + dhis2ApiVersion + "/programs.json?paging=false&" + - "fields=id,name,code,description,lastUpdated,selectIncidentDatesInFuture,selectEnrollmentDatesInFuture,displayIncidentDate,registration,withoutRegistration,captureCoordinates,trackedEntityType%5Bid%5D,programTrackedEntityAttributes%5Bid,name,valueType," + - "mandatory,allowFutureDate,trackedEntityAttribute%5Bid,name,code,valueType,generated%5D%5D,programStages%5Bid,name,description,repeatable,captureCoordinates,generatedByEnrollmentDate,minDaysFromStart,programStageDataElements%5Bid,compulsory," + - "allowProvidedElsewhere,dataElement%5Bid,name,code,description,formName,valueType,optionSetValue,optionSet%5Bid,name,options%5Bcode,name%5D%5D%5D%5D%5D&filter=name:eq:Child%20Programme" ) ) + "fields=id,name,code,description,lastUpdated,selectIncidentDatesInFuture,selectEnrollmentDatesInFuture,displayIncidentDate,registration,withoutRegistration,captureCoordinates,trackedEntityType%5Bid%5D,programTrackedEntityAttributes%5Bid," + + "name,valueType,mandatory,allowFutureDate,trackedEntityAttribute%5Bid,name,code,valueType,generated%5D%5D,programStages%5Bid,program%5Bid%5D,lastUpdated,name,description,repeatable,captureCoordinates,generatedByEnrollmentDate," + + "minDaysFromStart,programStageDataElements%5Bid,compulsory,allowProvidedElsewhere,dataElement%5Bid,name,code,description,formName,valueType,optionSetValue,optionSet%5Bid,name,options%5Bcode,name%5D%5D%5D%5D%5D" + + "&filter=name:eq:Child%20Programme" ) ) .andRespond( withSuccess( IOUtils.resourceToString( "/org/dhis2/fhir/adapter/dhis/test/single-program.json", StandardCharsets.UTF_8 ), MediaType.APPLICATION_JSON ) ); } } diff --git a/app/src/test/java/org/dhis2/fhir/adapter/fhir/transform/fhir/impl/programstage/AbstractProgramStageTransformationAppTest.java b/app/src/test/java/org/dhis2/fhir/adapter/fhir/transform/fhir/impl/programstage/AbstractProgramStageTransformationAppTest.java index 742bfca8..1cfadd70 100644 --- a/app/src/test/java/org/dhis2/fhir/adapter/fhir/transform/fhir/impl/programstage/AbstractProgramStageTransformationAppTest.java +++ b/app/src/test/java/org/dhis2/fhir/adapter/fhir/transform/fhir/impl/programstage/AbstractProgramStageTransformationAppTest.java @@ -52,7 +52,9 @@ protected void expectMetadataRequests() throws Exception systemDhis2Server.expect( ExpectedCount.between( 0, 1 ), method( HttpMethod.GET ) ).andExpect( header( "Authorization", testConfiguration.getDhis2SystemAuthorization() ) ) .andExpect( requestTo( dhis2BaseUrl + "/api/" + dhis2ApiVersion + "/programs.json?paging=false&fields=id,name,code,description,lastUpdated,selectIncidentDatesInFuture,selectEnrollmentDatesInFuture,displayIncidentDate,registration," + "withoutRegistration," + - "captureCoordinates,trackedEntityType%5Bid%5D,programTrackedEntityAttributes%5Bid,name,valueType,mandatory,allowFutureDate,trackedEntityAttribute%5Bid,name,code,valueType,generated%5D%5D,programStages%5Bid,name,description,repeatable," + + "captureCoordinates,trackedEntityType%5Bid%5D,programTrackedEntityAttributes%5Bid,name,valueType,mandatory,allowFutureDate,trackedEntityAttribute%5Bid,name,code,valueType,generated%5D%5D,programStages%5Bid,program%5Bid%5D,lastUpdated,name," + + "description," + + "repeatable," + "captureCoordinates," + "generatedByEnrollmentDate,minDaysFromStart,programStageDataElements%5Bid,compulsory,allowProvidedElsewhere,dataElement%5Bid,name,code,formName,valueType,optionSetValue,optionSet%5Bid,name,options%5Bcode," + "name%5D%5D%5D%5D%5D&filter=name:eq:Child%20Programme" ) ) @@ -70,8 +72,10 @@ protected void expectMetadataRequests() throws Exception .andRespond( withSuccess( IOUtils.resourceToString( "/org/dhis2/fhir/adapter/dhis/test/default-tracked-entity-type.json", StandardCharsets.UTF_8 ), MediaType.APPLICATION_JSON ) ); systemDhis2Server.expect( ExpectedCount.between( 0, 1 ), method( HttpMethod.GET ) ).andExpect( header( "Authorization", testConfiguration.getDhis2SystemAuthorization() ) ) .andExpect( requestTo( dhis2BaseUrl + "/api/" + dhis2ApiVersion + "/programs.json?paging=false&" + - "fields=id,name,code,description,lastUpdated,selectIncidentDatesInFuture,selectEnrollmentDatesInFuture,displayIncidentDate,registration,withoutRegistration,captureCoordinates,trackedEntityType%5Bid%5D,programTrackedEntityAttributes%5Bid,name,valueType," + - "mandatory,allowFutureDate,trackedEntityAttribute%5Bid,name,code,valueType,generated%5D%5D,programStages%5Bid,name,description,repeatable,captureCoordinates,generatedByEnrollmentDate,minDaysFromStart,programStageDataElements%5Bid,compulsory," + + "fields=id,name,code,description,lastUpdated,selectIncidentDatesInFuture,selectEnrollmentDatesInFuture,displayIncidentDate,registration,withoutRegistration,captureCoordinates,trackedEntityType%5Bid%5D,programTrackedEntityAttributes%5Bid," + + "name,valueType," + + "mandatory,allowFutureDate,trackedEntityAttribute%5Bid,name,code,valueType,generated%5D%5D,programStages%5Bid,program%5Bid%5D,lastUpdated,name,description,repeatable,captureCoordinates,generatedByEnrollmentDate,minDaysFromStart," + + "programStageDataElements%5Bid,compulsory," + "allowProvidedElsewhere,dataElement%5Bid,name,code,description,formName,valueType,optionSetValue,optionSet%5Bid,name,options%5Bcode,name%5D%5D%5D%5D%5D&filter=name:eq:Child%20Programme" ) ) .andRespond( withSuccess( IOUtils.resourceToString( "/org/dhis2/fhir/adapter/dhis/test/single-program.json", StandardCharsets.UTF_8 ), MediaType.APPLICATION_JSON ) ); } diff --git a/app/src/test/resources/org/dhis2/fhir/adapter/dhis/test/all-program-stages.json b/app/src/test/resources/org/dhis2/fhir/adapter/dhis/test/all-program-stages.json new file mode 100644 index 00000000..99a77892 --- /dev/null +++ b/app/src/test/resources/org/dhis2/fhir/adapter/dhis/test/all-program-stages.json @@ -0,0 +1,185 @@ +{ + "programStages": [ + { + "name": "Birth", + "id": "MsWxkiY6tMS", + "program": { + "id": "EPDyQuoRnXk" + }, + "generatedByEnrollmentDate": false, + "repeatable": false, + "minDaysFromStart": 0, + "programStageDataElements": [ + { + "id": "uRAfo19lLtY", + "compulsory": false, + "allowProvidedElsewhere": false, + "dataElement": { + "code": "DE_391382", + "name": "MCH Apgar comment", + "id": "wquzcS4X6He", + "formName": "Apgar comment", + "valueType": "TEXT", + "optionSetValue": false + } + }, + { + "id": "Ije8HSPh71d", + "compulsory": false, + "allowProvidedElsewhere": false, + "dataElement": { + "code": "DE_2006098", + "name": "MCH Apgar Score", + "id": "uzKeaNjgQz9", + "formName": "Apgar Score", + "valueType": "NUMBER", + "optionSetValue": false + } + }, + { + "id": "GLrVTw75zSK", + "compulsory": false, + "allowProvidedElsewhere": false, + "dataElement": { + "code": "DE_2005736", + "name": "MCH Weight (g)", + "id": "BnplxU2jGvX", + "formName": "Weight (g)", + "valueType": "NUMBER", + "optionSetValue": false + } + }, + { + "id": "aQd5KKFge1U", + "compulsory": false, + "allowProvidedElsewhere": false, + "dataElement": { + "code": "DE_2006101", + "name": "MCH BCG dose", + "id": "Lon4lYDF0SH", + "formName": "BCG dose", + "valueType": "BOOLEAN", + "optionSetValue": false + } + }, + { + "id": "lm3Cxju7CL0", + "compulsory": false, + "allowProvidedElsewhere": false, + "dataElement": { + "code": "DE_2006104", + "name": "MCH OPV dose", + "id": "ft7iD5ZzPxJ", + "formName": "OPV dose", + "valueType": "TEXT", + "optionSetValue": true, + "optionSet": { + "name": "MNCH Polio doses (0-3)", + "id": "kzgQRhOCadd", + "options": [ + { + "code": "0", + "name": "Dose 0" + }, + { + "code": "1", + "name": "Dose 1" + }, + { + "code": "2", + "name": "Dose 2" + }, + { + "code": "3", + "name": "Dose 3" + } + ] + } + } + } + ] + }, + { + "name": "Baby Postnatal", + "id": "qowTSevVSkd", + "program": { + "id": "EPDyQuoRnXk" + }, + "generatedByEnrollmentDate": false, + "repeatable": false, + "minDaysFromStart": 6, + "programStageDataElements": [ + { + "id": "s7bKmA7gfXq", + "compulsory": false, + "allowProvidedElsewhere": false, + "dataElement": { + "code": "DE_2006099", + "name": "MCH Infant Weight (g)", + "id": "UnN0Jdrfr4o", + "formName": "Infant Weight (g)", + "valueType": "NUMBER", + "optionSetValue": false + } + }, + { + "id": "g1eWTx5nsNx", + "compulsory": false, + "allowProvidedElsewhere": false, + "dataElement": { + "code": "DE_2006125", + "name": "MCH Measles dose", + "id": "KBLBjsTfNSS", + "formName": "Measles dose", + "valueType": "BOOLEAN", + "optionSetValue": false + } + }, + { + "id": "TKnlmmxUEss", + "compulsory": false, + "allowProvidedElsewhere": false, + "dataElement": { + "code": "DE_2006126", + "name": "MCH Yellow fever dose", + "id": "TNg7ABaHeP7", + "formName": "Yellow fever dose", + "valueType": "BOOLEAN", + "optionSetValue": false + } + }, + { + "id": "mx5k0I1SLDt", + "compulsory": false, + "allowProvidedElsewhere": false, + "dataElement": { + "code": "DE_2006105", + "name": "MCH DPT dose", + "id": "vNGGmCPNfky", + "formName": "DPT dose", + "valueType": "TEXT", + "optionSetValue": true, + "optionSet": { + "name": "MNCH DPT doses (1-3)", + "id": "udkr3ihaeD3", + "options": [ + { + "code": "1", + "name": "Dose 1" + }, + { + "code": "2", + "name": "Dose 2" + }, + { + "code": "3", + "name": "Dose 3" + } + ] + } + } + } + ] + } + ] +} diff --git a/app/src/test/resources/org/dhis2/fhir/adapter/dhis/test/default-program-stage.json b/app/src/test/resources/org/dhis2/fhir/adapter/dhis/test/default-program-stage.json new file mode 100644 index 00000000..15a57544 --- /dev/null +++ b/app/src/test/resources/org/dhis2/fhir/adapter/dhis/test/default-program-stage.json @@ -0,0 +1,101 @@ +{ + "pager": { "page": 1, "pageCount": 1, "total": 0, "pageSize": 1 }, + "programStages": [ + { + "name": "Birth", + "id": "MsWxkiY6tMS", + "generatedByEnrollmentDate": false, + "repeatable": false, + "minDaysFromStart": 0, + "programStageDataElements": [ + { + "id": "uRAfo19lLtY", + "compulsory": false, + "allowProvidedElsewhere": false, + "dataElement": { + "code": "DE_391382", + "name": "MCH Apgar comment", + "id": "wquzcS4X6He", + "formName": "Apgar comment", + "valueType": "TEXT", + "optionSetValue": false + } + }, + { + "id": "Ije8HSPh71d", + "compulsory": false, + "allowProvidedElsewhere": false, + "dataElement": { + "code": "DE_2006098", + "name": "MCH Apgar Score", + "id": "uzKeaNjgQz9", + "formName": "Apgar Score", + "valueType": "NUMBER", + "optionSetValue": false + } + }, + { + "id": "GLrVTw75zSK", + "compulsory": false, + "allowProvidedElsewhere": false, + "dataElement": { + "code": "DE_2005736", + "name": "MCH Weight (g)", + "id": "BnplxU2jGvX", + "formName": "Weight (g)", + "valueType": "NUMBER", + "optionSetValue": false + } + }, + { + "id": "aQd5KKFge1U", + "compulsory": false, + "allowProvidedElsewhere": false, + "dataElement": { + "code": "DE_2006101", + "name": "MCH BCG dose", + "id": "Lon4lYDF0SH", + "formName": "BCG dose", + "valueType": "BOOLEAN", + "optionSetValue": false + } + }, + { + "id": "lm3Cxju7CL0", + "compulsory": false, + "allowProvidedElsewhere": false, + "dataElement": { + "code": "DE_2006104", + "name": "MCH OPV dose", + "id": "ft7iD5ZzPxJ", + "formName": "OPV dose", + "valueType": "TEXT", + "optionSetValue": true, + "optionSet": { + "name": "MNCH Polio doses (0-3)", + "id": "kzgQRhOCadd", + "options": [ + { + "code": "0", + "name": "Dose 0" + }, + { + "code": "1", + "name": "Dose 1" + }, + { + "code": "2", + "name": "Dose 2" + }, + { + "code": "3", + "name": "Dose 3" + } + ] + } + } + } + ] + } + ] +} diff --git a/app/src/test/resources/org/dhis2/fhir/adapter/dhis/test/single-program-stage.json b/app/src/test/resources/org/dhis2/fhir/adapter/dhis/test/single-program-stage.json new file mode 100644 index 00000000..1bad997c --- /dev/null +++ b/app/src/test/resources/org/dhis2/fhir/adapter/dhis/test/single-program-stage.json @@ -0,0 +1,99 @@ +{ + "name": "Birth", + "id": "MsWxkiY6tMS", + "program": { + "id": "EPDyQuoRnXk" + }, + "generatedByEnrollmentDate": false, + "repeatable": false, + "minDaysFromStart": 0, + "programStageDataElements": [ + { + "id": "uRAfo19lLtY", + "compulsory": false, + "allowProvidedElsewhere": false, + "dataElement": { + "code": "DE_391382", + "name": "MCH Apgar comment", + "id": "wquzcS4X6He", + "formName": "Apgar comment", + "valueType": "TEXT", + "optionSetValue": false + } + }, + { + "id": "Ije8HSPh71d", + "compulsory": false, + "allowProvidedElsewhere": false, + "dataElement": { + "code": "DE_2006098", + "name": "MCH Apgar Score", + "id": "uzKeaNjgQz9", + "formName": "Apgar Score", + "valueType": "NUMBER", + "optionSetValue": false + } + }, + { + "id": "GLrVTw75zSK", + "compulsory": false, + "allowProvidedElsewhere": false, + "dataElement": { + "code": "DE_2005736", + "name": "MCH Weight (g)", + "id": "BnplxU2jGvX", + "formName": "Weight (g)", + "valueType": "NUMBER", + "optionSetValue": false + } + }, + { + "id": "aQd5KKFge1U", + "compulsory": false, + "allowProvidedElsewhere": false, + "dataElement": { + "code": "DE_2006101", + "name": "MCH BCG dose", + "id": "Lon4lYDF0SH", + "formName": "BCG dose", + "valueType": "BOOLEAN", + "optionSetValue": false + } + }, + { + "id": "lm3Cxju7CL0", + "compulsory": false, + "allowProvidedElsewhere": false, + "dataElement": { + "code": "DE_2006104", + "name": "MCH OPV dose", + "id": "ft7iD5ZzPxJ", + "formName": "OPV dose", + "valueType": "TEXT", + "optionSetValue": true, + "optionSet": { + "name": "MNCH Polio doses (0-3)", + "id": "kzgQRhOCadd", + "options": [ + { + "code": "0", + "name": "Dose 0" + }, + { + "code": "1", + "name": "Dose 1" + }, + { + "code": "2", + "name": "Dose 2" + }, + { + "code": "3", + "name": "Dose 3" + } + ] + } + } + } + ] +} diff --git a/dhis/src/main/java/org/dhis2/fhir/adapter/dhis/sync/impl/DhisResourceRepositoryImpl.java b/dhis/src/main/java/org/dhis2/fhir/adapter/dhis/sync/impl/DhisResourceRepositoryImpl.java index fef873ad..5d44677c 100644 --- a/dhis/src/main/java/org/dhis2/fhir/adapter/dhis/sync/impl/DhisResourceRepositoryImpl.java +++ b/dhis/src/main/java/org/dhis2/fhir/adapter/dhis/sync/impl/DhisResourceRepositoryImpl.java @@ -39,6 +39,7 @@ import org.dhis2.fhir.adapter.dhis.tracker.program.Event; import org.dhis2.fhir.adapter.dhis.tracker.program.EventService; import org.dhis2.fhir.adapter.dhis.tracker.program.ProgramMetadataService; +import org.dhis2.fhir.adapter.dhis.tracker.program.ProgramStageMetadataService; import org.dhis2.fhir.adapter.dhis.tracker.trackedentity.TrackedEntityInstance; import org.dhis2.fhir.adapter.dhis.tracker.trackedentity.TrackedEntityService; import org.slf4j.Logger; @@ -65,6 +66,8 @@ public class DhisResourceRepositoryImpl implements DhisResourceRepository private final ProgramMetadataService programMetadataService; + private final ProgramStageMetadataService programStageMetadataService; + private final TrackedEntityService trackedEntityService; private final EnrollmentService enrollmentService; @@ -72,10 +75,12 @@ public class DhisResourceRepositoryImpl implements DhisResourceRepository private final EventService eventService; public DhisResourceRepositoryImpl( @Nonnull OrganizationUnitService organizationUnitService, ProgramMetadataService programMetadataService, - @Nonnull TrackedEntityService trackedEntityService, @Nonnull EnrollmentService enrollmentService, @Nonnull EventService eventService ) + @Nonnull ProgramStageMetadataService programStageMetadataService, @Nonnull TrackedEntityService trackedEntityService, + @Nonnull EnrollmentService enrollmentService, @Nonnull EventService eventService ) { this.organizationUnitService = organizationUnitService; this.programMetadataService = programMetadataService; + this.programStageMetadataService = programStageMetadataService; this.trackedEntityService = trackedEntityService; this.enrollmentService = enrollmentService; this.eventService = eventService; @@ -91,6 +96,8 @@ public Optional findRefreshed( @Nonnull DhisResourceId d return organizationUnitService.findOneByReference( new Reference( dhisResourceId.getId(), ReferenceType.ID ) ); case PROGRAM_METADATA: return programMetadataService.findOneByReference( new Reference( dhisResourceId.getId(), ReferenceType.ID ) ); + case PROGRAM_STAGE_METADATA: + return programStageMetadataService.findOneByReference( new Reference( dhisResourceId.getId(), ReferenceType.ID ) ); case TRACKED_ENTITY: return trackedEntityService.findOneByIdRefreshed( dhisResourceId.getId() ); case PROGRAM_STAGE_EVENT: @@ -112,6 +119,7 @@ public Optional findRefreshedDeleted( @Nonnull DhisResou return eventService.findOneDeletedById( dhisResourceId.getId() ); case ORGANIZATION_UNIT: case PROGRAM_METADATA: + case PROGRAM_STAGE_METADATA: case TRACKED_ENTITY: case ENROLLMENT: throw new UnsupportedOperationException( "Retrieving deleted " + dhisResourceId.getType() + " DHIS resource items is not supported." ); diff --git a/dhis/src/main/java/org/dhis2/fhir/adapter/dhis/tracker/program/ImmutableProgramStage.java b/dhis/src/main/java/org/dhis2/fhir/adapter/dhis/tracker/program/ImmutableProgramStage.java index 405d180e..bb461091 100644 --- a/dhis/src/main/java/org/dhis2/fhir/adapter/dhis/tracker/program/ImmutableProgramStage.java +++ b/dhis/src/main/java/org/dhis2/fhir/adapter/dhis/tracker/program/ImmutableProgramStage.java @@ -66,6 +66,13 @@ public Set getAllReferences() return delegate.getAllReferences(); } + @JsonIgnore + @Override + public String getProgramId() + { + return delegate.getProgramId(); + } + @JsonIgnore @Override public String getId() diff --git a/dhis/src/main/java/org/dhis2/fhir/adapter/dhis/tracker/program/ProgramStage.java b/dhis/src/main/java/org/dhis2/fhir/adapter/dhis/tracker/program/ProgramStage.java index 435f596b..0535b110 100644 --- a/dhis/src/main/java/org/dhis2/fhir/adapter/dhis/tracker/program/ProgramStage.java +++ b/dhis/src/main/java/org/dhis2/fhir/adapter/dhis/tracker/program/ProgramStage.java @@ -48,6 +48,8 @@ @Scriptable public interface ProgramStage extends DhisResource, DhisMetadata { + String getProgramId(); + boolean isRepeatable(); boolean isCaptureCoordinates(); diff --git a/dhis/src/main/java/org/dhis2/fhir/adapter/dhis/tracker/program/ProgramStageMetadataService.java b/dhis/src/main/java/org/dhis2/fhir/adapter/dhis/tracker/program/ProgramStageMetadataService.java new file mode 100644 index 00000000..ac5093e7 --- /dev/null +++ b/dhis/src/main/java/org/dhis2/fhir/adapter/dhis/tracker/program/ProgramStageMetadataService.java @@ -0,0 +1,40 @@ +package org.dhis2.fhir.adapter.dhis.tracker.program; + +/* + * Copyright (c) 2004-2019, University of Oslo + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * Neither the name of the HISP project nor the names of its contributors may + * be used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +import org.dhis2.fhir.adapter.dhis.service.DhisMetadataService; + +/** + * Metadata service for DHIS2 program stages. + * + * @author volsch + */ +public interface ProgramStageMetadataService extends DhisMetadataService +{ +} diff --git a/dhis/src/main/java/org/dhis2/fhir/adapter/dhis/tracker/program/WritableProgram.java b/dhis/src/main/java/org/dhis2/fhir/adapter/dhis/tracker/program/WritableProgram.java index 457168ae..ceecf229 100644 --- a/dhis/src/main/java/org/dhis2/fhir/adapter/dhis/tracker/program/WritableProgram.java +++ b/dhis/src/main/java/org/dhis2/fhir/adapter/dhis/tracker/program/WritableProgram.java @@ -139,12 +139,12 @@ public void setDescription( String description ) @Override public String getTrackedEntityTypeId() { - return (trackedEntityType == null) ? null : trackedEntityType.getId(); + return trackedEntityType == null ? null : trackedEntityType.getId(); } public void setTrackedEntityTypeId( String trackedEntityTypeId ) { - this.trackedEntityType = (trackedEntityTypeId == null) ? null : new Id( trackedEntityTypeId ); + this.trackedEntityType = trackedEntityTypeId == null ? null : new Id( trackedEntityTypeId ); } @Override diff --git a/dhis/src/main/java/org/dhis2/fhir/adapter/dhis/tracker/program/WritableProgramStage.java b/dhis/src/main/java/org/dhis2/fhir/adapter/dhis/tracker/program/WritableProgramStage.java index 588b057c..d0474ce4 100644 --- a/dhis/src/main/java/org/dhis2/fhir/adapter/dhis/tracker/program/WritableProgramStage.java +++ b/dhis/src/main/java/org/dhis2/fhir/adapter/dhis/tracker/program/WritableProgramStage.java @@ -34,6 +34,7 @@ import org.dhis2.fhir.adapter.dhis.model.DhisResourceId; import org.dhis2.fhir.adapter.dhis.model.DhisResourceType; import org.dhis2.fhir.adapter.dhis.model.Reference; +import org.dhis2.fhir.adapter.model.Id; import javax.annotation.Nonnull; import javax.annotation.Nullable; @@ -48,6 +49,11 @@ public class WritableProgramStage extends AbstractDhisMetadata implements Progra { private static final long serialVersionUID = -7544648580734783374L; + private ZonedDateTime lastUpdated; + + @JsonProperty + private Id program; + private String id; private String name; @@ -74,6 +80,17 @@ public class WritableProgramStage extends AbstractDhisMetadata implements Progra @JsonIgnore private transient volatile Map dataElementsByCode; + @JsonIgnore + public String getProgramId() + { + return program == null ? null : program.getId(); + } + + public void setProgramId( String programId ) + { + program = programId == null ? null : new Id( programId ); + } + @Override public String getId() { @@ -262,7 +279,7 @@ public boolean isDeleted() @Override public ZonedDateTime getLastUpdated() { - return null; + return lastUpdated; } @Override diff --git a/dhis/src/main/java/org/dhis2/fhir/adapter/dhis/tracker/program/impl/DhisProgramStages.java b/dhis/src/main/java/org/dhis2/fhir/adapter/dhis/tracker/program/impl/DhisProgramStages.java new file mode 100644 index 00000000..8757310e --- /dev/null +++ b/dhis/src/main/java/org/dhis2/fhir/adapter/dhis/tracker/program/impl/DhisProgramStages.java @@ -0,0 +1,62 @@ +package org.dhis2.fhir.adapter.dhis.tracker.program.impl; + +/* + * Copyright (c) 2004-2019, University of Oslo + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * Neither the name of the HISP project nor the names of its contributors may + * be used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +import com.fasterxml.jackson.annotation.JsonProperty; +import org.dhis2.fhir.adapter.dhis.service.impl.DhisMetadataItems; +import org.dhis2.fhir.adapter.dhis.tracker.program.WritableProgramStage; + +import java.io.Serializable; +import java.util.List; + +/** + * Container object used for DHIS2 program stages. + * + * @author volsch + */ +public class DhisProgramStages extends DhisMetadataItems implements Serializable +{ + private static final long serialVersionUID = 1084527285362478422L; + + @JsonProperty( "programStages" ) + public List getProgramStages() + { + return getItems(); + } + + public void setProgramStages( List programStages ) + { + setItems( programStages ); + } + + public List toModel() + { + return getItems(); + } +} diff --git a/dhis/src/main/java/org/dhis2/fhir/adapter/dhis/tracker/program/impl/ProgramMetadataServiceImpl.java b/dhis/src/main/java/org/dhis2/fhir/adapter/dhis/tracker/program/impl/ProgramMetadataServiceImpl.java index a17179c8..eaa0e56d 100644 --- a/dhis/src/main/java/org/dhis2/fhir/adapter/dhis/tracker/program/impl/ProgramMetadataServiceImpl.java +++ b/dhis/src/main/java/org/dhis2/fhir/adapter/dhis/tracker/program/impl/ProgramMetadataServiceImpl.java @@ -53,9 +53,7 @@ public class ProgramMetadataServiceImpl extends AbstractDhisMetadataServiceImpl< "registration,withoutRegistration,captureCoordinates,trackedEntityType[id]," + "programTrackedEntityAttributes[id,name,valueType,mandatory,allowFutureDate," + "trackedEntityAttribute[id,name,code,valueType,generated]]," + - "programStages[id,name,description,repeatable,captureCoordinates,generatedByEnrollmentDate,minDaysFromStart," + - "programStageDataElements[id,compulsory,allowProvidedElsewhere," + - "dataElement[id,name,code,formName,valueType,optionSetValue,optionSet[id,name,options[code,name]]]]]"; + "programStages[" + ProgramStageMetadataServiceImpl.FIELDS + "]"; public ProgramMetadataServiceImpl( @Nonnull @Qualifier( "systemDhis2RestTemplate" ) RestTemplate systemRestTemplate, @Nonnull @Qualifier( "userDhis2RestTemplate" ) RestTemplate userRestTemplate ) { diff --git a/dhis/src/main/java/org/dhis2/fhir/adapter/dhis/tracker/program/impl/ProgramStageMetadataServiceImpl.java b/dhis/src/main/java/org/dhis2/fhir/adapter/dhis/tracker/program/impl/ProgramStageMetadataServiceImpl.java new file mode 100644 index 00000000..554f04bd --- /dev/null +++ b/dhis/src/main/java/org/dhis2/fhir/adapter/dhis/tracker/program/impl/ProgramStageMetadataServiceImpl.java @@ -0,0 +1,86 @@ +package org.dhis2.fhir.adapter.dhis.tracker.program.impl; + +/* + * Copyright (c) 2004-2019, University of Oslo + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * Neither the name of the HISP project nor the names of its contributors may + * be used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +import org.dhis2.fhir.adapter.dhis.model.DhisResourceType; +import org.dhis2.fhir.adapter.dhis.service.impl.AbstractDhisMetadataServiceImpl; +import org.dhis2.fhir.adapter.dhis.service.impl.DhisMetadataItems; +import org.dhis2.fhir.adapter.dhis.tracker.program.ProgramStage; +import org.dhis2.fhir.adapter.dhis.tracker.program.ProgramStageMetadataService; +import org.dhis2.fhir.adapter.dhis.tracker.program.WritableProgramStage; +import org.springframework.beans.factory.annotation.Qualifier; +import org.springframework.stereotype.Service; +import org.springframework.web.client.RestTemplate; + +import javax.annotation.Nonnull; + +/** + * Implementation of a {@link ProgramStageMetadataService}. + * + * @author volsch + */ +@Service +public class ProgramStageMetadataServiceImpl extends AbstractDhisMetadataServiceImpl implements ProgramStageMetadataService +{ + protected static final String FIELDS = "id,program[id],lastUpdated,name,description,repeatable,captureCoordinates,generatedByEnrollmentDate,minDaysFromStart," + + "programStageDataElements[id,compulsory,allowProvidedElsewhere,dataElement[id,name,code,formName,valueType,optionSetValue,optionSet[id,name,options[code,name]]]]"; + + public ProgramStageMetadataServiceImpl( @Nonnull @Qualifier( "systemDhis2RestTemplate" ) RestTemplate systemRestTemplate, @Nonnull @Qualifier( "userDhis2RestTemplate" ) RestTemplate userRestTemplate ) + { + super( systemRestTemplate, userRestTemplate ); + } + + @Nonnull + @Override + public DhisResourceType getDhisResourceType() + { + return DhisResourceType.PROGRAM_STAGE_METADATA; + } + + @Nonnull + @Override + protected Class getItemClass() + { + return WritableProgramStage.class; + } + + @Nonnull + @Override + protected Class> getItemsClass() + { + return DhisProgramStages.class; + } + + @Nonnull + @Override + protected String getFieldNames() + { + return FIELDS; + } +} diff --git a/dhis/src/test/java/org/dhis2/fhir/adapter/dhis/tracker/program/impl/ProgramMetadataServiceImplTest.java b/dhis/src/test/java/org/dhis2/fhir/adapter/dhis/tracker/program/impl/ProgramMetadataServiceImplTest.java index 27653d09..c12402da 100644 --- a/dhis/src/test/java/org/dhis2/fhir/adapter/dhis/tracker/program/impl/ProgramMetadataServiceImplTest.java +++ b/dhis/src/test/java/org/dhis2/fhir/adapter/dhis/tracker/program/impl/ProgramMetadataServiceImplTest.java @@ -82,9 +82,10 @@ public void setUp() public void findMetadataByReferenceId() throws IOException { mockServer.expect( requestTo( "http://localhost:8080/api/programs/93783.json?fields=id,name,code,description,lastUpdated,selectIncidentDatesInFuture,selectEnrollmentDatesInFuture,displayIncidentDate,registration,withoutRegistration," + - "captureCoordinates," + - "trackedEntityType%5Bid%5D,programTrackedEntityAttributes%5Bid,name,valueType,mandatory,allowFutureDate,trackedEntityAttribute%5Bid,name,code,valueType,generated%5D%5D,programStages%5Bid,name,description,repeatable,captureCoordinates," + - "generatedByEnrollmentDate,minDaysFromStart,programStageDataElements%5Bid,compulsory,allowProvidedElsewhere,dataElement%5Bid,name,code,formName,valueType,optionSetValue,optionSet%5Bid,name,options%5Bcode,name%5D%5D%5D%5D%5D" ) ) + "captureCoordinates,trackedEntityType%5Bid%5D,programTrackedEntityAttributes%5Bid,name,valueType,mandatory,allowFutureDate,trackedEntityAttribute%5Bid,name,code,valueType,generated%5D%5D,programStages%5Bid,program%5Bid%5D,lastUpdated,name," + + "description," + + "repeatable,captureCoordinates,generatedByEnrollmentDate,minDaysFromStart,programStageDataElements%5Bid,compulsory,allowProvidedElsewhere,dataElement%5Bid,name,code,formName,valueType,optionSetValue,optionSet%5Bid,name," + + "options%5Bcode,name%5D%5D%5D%5D%5D" ) ) .andExpect( method( HttpMethod.GET ) ).andRespond( withSuccess( IOUtils.resourceToByteArray( "/org/dhis2/fhir/adapter/dhis/tracker/program/impl/program.json" ), MediaType.APPLICATION_JSON ) ); Optional ou = service.findMetadataByReference( new Reference( "93783", ReferenceType.ID ) ); @@ -95,8 +96,10 @@ public void findMetadataByReferenceId() throws IOException @Test public void findMetadataByReferenceIdNotFound() { - mockServer.expect( requestTo( "http://localhost:8080/api/programs/93783.json?fields=id,name,code,description,lastUpdated,selectIncidentDatesInFuture,selectEnrollmentDatesInFuture,displayIncidentDate,registration,withoutRegistration,captureCoordinates," + - "trackedEntityType%5Bid%5D,programTrackedEntityAttributes%5Bid,name,valueType,mandatory,allowFutureDate,trackedEntityAttribute%5Bid,name,code,valueType,generated%5D%5D,programStages%5Bid,name,description,repeatable,captureCoordinates," + + mockServer.expect( requestTo( + "http://localhost:8080/api/programs/93783.json?fields=id,name,code,description,lastUpdated,selectIncidentDatesInFuture,selectEnrollmentDatesInFuture,displayIncidentDate,registration,withoutRegistration,captureCoordinates," + + "trackedEntityType%5Bid%5D,programTrackedEntityAttributes%5Bid,name,valueType,mandatory,allowFutureDate,trackedEntityAttribute%5Bid,name,code,valueType,generated%5D%5D,programStages%5Bid,program%5Bid%5D,lastUpdated,name,description,repeatable," + + "captureCoordinates," + "generatedByEnrollmentDate,minDaysFromStart,programStageDataElements%5Bid,compulsory,allowProvidedElsewhere,dataElement%5Bid,name,code,formName,valueType,optionSetValue,optionSet%5Bid,name,options%5Bcode,name%5D%5D%5D%5D%5D" ) ).andExpect( method( HttpMethod.GET ) ) .andRespond( MockRestResponseCreators.withStatus( HttpStatus.NOT_FOUND ).body( "{}" ).contentType( MediaType.APPLICATION_JSON ) ); @@ -107,8 +110,9 @@ public void findMetadataByReferenceIdNotFound() @Test( expected = HttpServerErrorException.class ) public void findMetadataByReferenceIdServerError() { - mockServer.expect( requestTo( "http://localhost:8080/api/programs/93783.json?fields=id,name,code,description,lastUpdated,selectIncidentDatesInFuture,selectEnrollmentDatesInFuture,displayIncidentDate,registration,withoutRegistration,captureCoordinates," + - "trackedEntityType%5Bid%5D,programTrackedEntityAttributes%5Bid,name,valueType,mandatory,allowFutureDate,trackedEntityAttribute%5Bid,name,code,valueType,generated%5D%5D,programStages%5Bid,name,description,repeatable,captureCoordinates," + + mockServer.expect( requestTo( + "http://localhost:8080/api/programs/93783.json?fields=id,name,code,description,lastUpdated,selectIncidentDatesInFuture,selectEnrollmentDatesInFuture,displayIncidentDate,registration,withoutRegistration,captureCoordinates," + + "trackedEntityType%5Bid%5D,programTrackedEntityAttributes%5Bid,name,valueType,mandatory,allowFutureDate,trackedEntityAttribute%5Bid,name,code,valueType,generated%5D%5D,programStages%5Bid,program%5Bid%5D,lastUpdated,name,description,repeatable,captureCoordinates," + "generatedByEnrollmentDate,minDaysFromStart,programStageDataElements%5Bid,compulsory,allowProvidedElsewhere,dataElement%5Bid,name,code,formName,valueType,optionSetValue,optionSet%5Bid,name,options%5Bcode,name%5D%5D%5D%5D%5D" ) ).andExpect( method( HttpMethod.GET ) ) .andRespond( MockRestResponseCreators.withServerError() ); service.findMetadataByReference( new Reference( "93783", ReferenceType.ID ) ); @@ -117,8 +121,9 @@ public void findMetadataByReferenceIdServerError() @Test public void findMetadataByReferenceCode() throws IOException { - mockServer.expect( requestTo( "http://localhost:8080/api/programs.json?paging=false&fields=id,name,code,description,lastUpdated,selectIncidentDatesInFuture,selectEnrollmentDatesInFuture,displayIncidentDate,registration,withoutRegistration,captureCoordinates," + - "trackedEntityType%5Bid%5D,programTrackedEntityAttributes%5Bid,name,valueType,mandatory,allowFutureDate,trackedEntityAttribute%5Bid,name,code,valueType,generated%5D%5D,programStages%5Bid,name,description,repeatable,captureCoordinates," + + mockServer.expect( requestTo( "http://localhost:8080/api/programs.json?paging=false&fields=id,name,code,description,lastUpdated,selectIncidentDatesInFuture,selectEnrollmentDatesInFuture,displayIncidentDate,registration,withoutRegistration," + + "captureCoordinates," + + "trackedEntityType%5Bid%5D,programTrackedEntityAttributes%5Bid,name,valueType,mandatory,allowFutureDate,trackedEntityAttribute%5Bid,name,code,valueType,generated%5D%5D,programStages%5Bid,program%5Bid%5D,lastUpdated,name,description,repeatable,captureCoordinates," + "generatedByEnrollmentDate,minDaysFromStart,programStageDataElements%5Bid,compulsory,allowProvidedElsewhere,dataElement%5Bid,name,code,formName,valueType,optionSetValue,optionSet%5Bid,name,options%5Bcode," + "name%5D%5D%5D%5D%5D&filter=code:eq:OU_3783" ) ).andExpect( method( HttpMethod.GET ) ) .andRespond( withSuccess( IOUtils.resourceToByteArray( "/org/dhis2/fhir/adapter/dhis/tracker/program/impl/programs.json" ), MediaType.APPLICATION_JSON ) ); @@ -131,8 +136,9 @@ public void findMetadataByReferenceCode() throws IOException @Test public void findMetadataByReferenceCodeNotFound() throws IOException { - mockServer.expect( requestTo( "http://localhost:8080/api/programs.json?paging=false&fields=id,name,code,description,lastUpdated,selectIncidentDatesInFuture,selectEnrollmentDatesInFuture,displayIncidentDate,registration,withoutRegistration,captureCoordinates," + - "trackedEntityType%5Bid%5D,programTrackedEntityAttributes%5Bid,name,valueType,mandatory,allowFutureDate,trackedEntityAttribute%5Bid,name,code,valueType,generated%5D%5D,programStages%5Bid,name,description,repeatable,captureCoordinates," + + mockServer.expect( requestTo( "http://localhost:8080/api/programs.json?paging=false&fields=id,name,code,description,lastUpdated,selectIncidentDatesInFuture,selectEnrollmentDatesInFuture,displayIncidentDate,registration,withoutRegistration," + + "captureCoordinates," + + "trackedEntityType%5Bid%5D,programTrackedEntityAttributes%5Bid,name,valueType,mandatory,allowFutureDate,trackedEntityAttribute%5Bid,name,code,valueType,generated%5D%5D,programStages%5Bid,program%5Bid%5D,lastUpdated,name,description,repeatable,captureCoordinates," + "generatedByEnrollmentDate,minDaysFromStart,programStageDataElements%5Bid,compulsory,allowProvidedElsewhere,dataElement%5Bid,name,code,formName,valueType,optionSetValue,optionSet%5Bid,name,options%5Bcode," + "name%5D%5D%5D%5D%5D&filter=code:eq:OU_3783" ) ).andExpect( method( HttpMethod.GET ) ) .andRespond( withSuccess( IOUtils.resourceToByteArray( "/org/dhis2/fhir/adapter/dhis/tracker/program/impl/emptyPrograms.json" ), MediaType.APPLICATION_JSON ) ); @@ -144,8 +150,9 @@ public void findMetadataByReferenceCodeNotFound() throws IOException @Test public void findMetadataByReferenceName() throws IOException { - mockServer.expect( requestTo( "http://localhost:8080/api/programs.json?paging=false&fields=id,name,code,description,lastUpdated,selectIncidentDatesInFuture,selectEnrollmentDatesInFuture,displayIncidentDate,registration,withoutRegistration,captureCoordinates," + - "trackedEntityType%5Bid%5D,programTrackedEntityAttributes%5Bid,name,valueType,mandatory,allowFutureDate,trackedEntityAttribute%5Bid,name,code,valueType,generated%5D%5D,programStages%5Bid,name,description,repeatable,captureCoordinates," + + mockServer.expect( requestTo( "http://localhost:8080/api/programs.json?paging=false&fields=id,name,code,description,lastUpdated,selectIncidentDatesInFuture,selectEnrollmentDatesInFuture,displayIncidentDate,registration,withoutRegistration," + + "captureCoordinates," + + "trackedEntityType%5Bid%5D,programTrackedEntityAttributes%5Bid,name,valueType,mandatory,allowFutureDate,trackedEntityAttribute%5Bid,name,code,valueType,generated%5D%5D,programStages%5Bid,program%5Bid%5D,lastUpdated,name,description,repeatable,captureCoordinates," + "generatedByEnrollmentDate,minDaysFromStart,programStageDataElements%5Bid,compulsory,allowProvidedElsewhere,dataElement%5Bid,name,code,formName,valueType,optionSetValue,optionSet%5Bid,name,options%5Bcode," + "name%5D%5D%5D%5D%5D&filter=name:eq:Freetown" ) ).andExpect( method( HttpMethod.GET ) ) .andRespond( withSuccess( IOUtils.resourceToByteArray( "/org/dhis2/fhir/adapter/dhis/tracker/program/impl/programs.json" ), MediaType.APPLICATION_JSON ) ); @@ -158,8 +165,9 @@ public void findMetadataByReferenceName() throws IOException @Test public void findMetadataByReferenceNameNotFound() throws IOException { - mockServer.expect( requestTo( "http://localhost:8080/api/programs.json?paging=false&fields=id,name,code,description,lastUpdated,selectIncidentDatesInFuture,selectEnrollmentDatesInFuture,displayIncidentDate,registration,withoutRegistration,captureCoordinates," + - "trackedEntityType%5Bid%5D,programTrackedEntityAttributes%5Bid,name,valueType,mandatory,allowFutureDate,trackedEntityAttribute%5Bid,name,code,valueType,generated%5D%5D,programStages%5Bid,name,description,repeatable,captureCoordinates," + + mockServer.expect( requestTo( "http://localhost:8080/api/programs.json?paging=false&fields=id,name,code,description,lastUpdated,selectIncidentDatesInFuture,selectEnrollmentDatesInFuture,displayIncidentDate,registration,withoutRegistration," + + "captureCoordinates," + + "trackedEntityType%5Bid%5D,programTrackedEntityAttributes%5Bid,name,valueType,mandatory,allowFutureDate,trackedEntityAttribute%5Bid,name,code,valueType,generated%5D%5D,programStages%5Bid,program%5Bid%5D,lastUpdated,name,description,repeatable,captureCoordinates," + "generatedByEnrollmentDate,minDaysFromStart,programStageDataElements%5Bid,compulsory,allowProvidedElsewhere,dataElement%5Bid,name,code,formName,valueType,optionSetValue,optionSet%5Bid,name,options%5Bcode," + "name%5D%5D%5D%5D%5D&filter=name:eq:Freetown" ) ).andExpect( method( HttpMethod.GET ) ) .andRespond( withSuccess( IOUtils.resourceToByteArray( "/org/dhis2/fhir/adapter/dhis/tracker/program/impl/emptyPrograms.json" ), MediaType.APPLICATION_JSON ) ); diff --git a/dhis/src/test/java/org/dhis2/fhir/adapter/dhis/tracker/program/impl/ProgramStageMetadataServiceImplTest.java b/dhis/src/test/java/org/dhis2/fhir/adapter/dhis/tracker/program/impl/ProgramStageMetadataServiceImplTest.java new file mode 100644 index 00000000..eba3e0b4 --- /dev/null +++ b/dhis/src/test/java/org/dhis2/fhir/adapter/dhis/tracker/program/impl/ProgramStageMetadataServiceImplTest.java @@ -0,0 +1,156 @@ +package org.dhis2.fhir.adapter.dhis.tracker.program.impl; + +/* + * Copyright (c) 2004-2019, University of Oslo + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * Neither the name of the HISP project nor the names of its contributors may + * be used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +import org.apache.commons.io.IOUtils; +import org.dhis2.fhir.adapter.dhis.model.Reference; +import org.dhis2.fhir.adapter.dhis.model.ReferenceType; +import org.dhis2.fhir.adapter.dhis.tracker.program.ProgramStage; +import org.dhis2.fhir.adapter.dhis.tracker.program.ProgramStageMetadataService; +import org.dhis2.fhir.adapter.model.ValueType; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; +import org.springframework.boot.web.client.RestTemplateBuilder; +import org.springframework.http.HttpMethod; +import org.springframework.http.HttpStatus; +import org.springframework.http.MediaType; +import org.springframework.test.web.client.MockRestServiceServer; +import org.springframework.test.web.client.response.MockRestResponseCreators; +import org.springframework.web.client.HttpServerErrorException; +import org.springframework.web.client.RestTemplate; + +import java.io.IOException; +import java.util.Optional; + +import static org.junit.Assert.*; +import static org.springframework.test.web.client.match.MockRestRequestMatchers.method; +import static org.springframework.test.web.client.match.MockRestRequestMatchers.requestTo; +import static org.springframework.test.web.client.response.MockRestResponseCreators.withSuccess; + +/** + * Unit tests for {@link ProgramStageMetadataServiceImpl}. + * + * @author volsch + */ +public class ProgramStageMetadataServiceImplTest +{ + private RestTemplate systemRestTemplate; + + private RestTemplate userRestTemplate; + + private MockRestServiceServer mockServer; + + private ProgramStageMetadataService service; + + @Before + public void setUp() + { + systemRestTemplate = new RestTemplateBuilder().rootUri( "http://localhost:8080/api" ).build(); + userRestTemplate = new RestTemplateBuilder().rootUri( "http://localhost:8080/api" ).build(); + mockServer = MockRestServiceServer.createServer( systemRestTemplate ); + service = new ProgramStageMetadataServiceImpl( systemRestTemplate, userRestTemplate ); + } + + @Test + public void findMetadataByReferenceId() throws IOException + { + mockServer.expect( requestTo( "http://localhost:8080/api/programStages/93783.json?fields=id,program%5Bid%5D,lastUpdated,name,description,repeatable,captureCoordinates,generatedByEnrollmentDate,minDaysFromStart,programStageDataElements%5Bid," + + "compulsory," + + "allowProvidedElsewhere,dataElement%5Bid,name,code,formName,valueType,optionSetValue,optionSet%5Bid,name,options%5Bcode,name%5D%5D%5D%5D" ) ) + .andExpect( method( HttpMethod.GET ) ).andRespond( withSuccess( IOUtils.resourceToByteArray( "/org/dhis2/fhir/adapter/dhis/tracker/program/impl/programStage.json" ), MediaType.APPLICATION_JSON ) ); + + Optional ou = service.findMetadataByReference( new Reference( "93783", ReferenceType.ID ) ); + Assert.assertTrue( ou.isPresent() ); + assertProgramStage( ou.get() ); + } + + @Test + public void findMetadataByReferenceIdNotFound() + { + mockServer.expect( requestTo( "http://localhost:8080/api/programStages/93783.json?fields=id,program%5Bid%5D,lastUpdated,name,description,repeatable,captureCoordinates,generatedByEnrollmentDate,minDaysFromStart,programStageDataElements%5Bid,compulsory," + + "allowProvidedElsewhere,dataElement%5Bid,name,code,formName,valueType,optionSetValue,optionSet%5Bid,name,options%5Bcode,name%5D%5D%5D%5D" ) ).andExpect( method( HttpMethod.GET ) ) + .andRespond( MockRestResponseCreators.withStatus( HttpStatus.NOT_FOUND ).body( "{}" ).contentType( MediaType.APPLICATION_JSON ) ); + + Optional ou = service.findMetadataByReference( new Reference( "93783", ReferenceType.ID ) ); + Assert.assertFalse( ou.isPresent() ); + } + + @Test( expected = HttpServerErrorException.class ) + public void findMetadataByReferenceIdServerError() + { + mockServer.expect( requestTo( "http://localhost:8080/api/programStages/93783.json?fields=id,program%5Bid%5D,lastUpdated,name,description,repeatable,captureCoordinates,generatedByEnrollmentDate,minDaysFromStart,programStageDataElements%5Bid,compulsory," + + "allowProvidedElsewhere,dataElement%5Bid,name,code,formName,valueType,optionSetValue,optionSet%5Bid,name,options%5Bcode,name%5D%5D%5D%5D" ) ).andExpect( method( HttpMethod.GET ) ) + .andRespond( MockRestResponseCreators.withServerError() ); + service.findMetadataByReference( new Reference( "93783", ReferenceType.ID ) ); + } + + @Test + public void findMetadataByReferenceName() throws IOException + { + mockServer.expect( requestTo( "http://localhost:8080/api/programStages.json?paging=false&fields=id,program%5Bid%5D,lastUpdated,name,description,repeatable,captureCoordinates,generatedByEnrollmentDate,minDaysFromStart,programStageDataElements%5Bid,compulsory," + + "allowProvidedElsewhere,dataElement%5Bid,name,code,formName,valueType,optionSetValue,optionSet%5Bid,name,options%5Bcode,name%5D%5D%5D%5D&filter=name:eq:Birth" ) ).andExpect( method( HttpMethod.GET ) ) + .andRespond( withSuccess( IOUtils.resourceToByteArray( "/org/dhis2/fhir/adapter/dhis/tracker/program/impl/programStages.json" ), MediaType.APPLICATION_JSON ) ); + + Optional ou = service.findMetadataByReference( new Reference( "Birth", ReferenceType.NAME ) ); + Assert.assertTrue( ou.isPresent() ); + assertProgramStage( ou.get() ); + } + + @Test + public void findMetadataByReferenceNameNotFound() throws IOException + { + mockServer.expect( requestTo( "http://localhost:8080/api/programStages.json?paging=false&fields=id,program%5Bid%5D,lastUpdated,name,description,repeatable,captureCoordinates,generatedByEnrollmentDate,minDaysFromStart,programStageDataElements%5Bid,compulsory," + + "allowProvidedElsewhere,dataElement%5Bid,name,code,formName,valueType,optionSetValue,optionSet%5Bid,name,options%5Bcode,name%5D%5D%5D%5D&filter=name:eq:xBirth" ) ).andExpect( method( HttpMethod.GET ) ) + .andRespond( withSuccess( IOUtils.resourceToByteArray( "/org/dhis2/fhir/adapter/dhis/tracker/program/impl/emptyPrograms.json" ), MediaType.APPLICATION_JSON ) ); + + Optional ou = service.findMetadataByReference( new Reference( "xBirth", ReferenceType.NAME ) ); + Assert.assertFalse( ou.isPresent() ); + } + + private void assertProgramStage( ProgramStage programStage ) + { + assertNotNull( programStage ); + assertEquals( "A03MvHHogjR", programStage.getId() ); + assertEquals( "IpHINAT79UW", programStage.getProgramId() ); + assertEquals( "Birth", programStage.getName() ); + assertEquals( 2, programStage.getMinDaysFromStart() ); + assertNotNull( programStage.getDataElements() ); + assertEquals( "a3kGcGDCuk6", programStage.getDataElements().get( 0 ).getElement().getId() ); + assertEquals( "MCH Apgar Score", programStage.getDataElements().get( 0 ).getElement().getName() ); + assertEquals( "DE_2006098", programStage.getDataElements().get( 0 ).getElement().getCode() ); + assertEquals( ValueType.NUMBER, programStage.getDataElements().get( 0 ).getElement().getValueType() ); + assertTrue( programStage.getDataElements().get( 0 ).isAllowProvidedElsewhere() ); + assertTrue( programStage.getDataElements().get( 0 ).isCompulsory() ); + assertNull( programStage.getDataElements().get( 0 ).getElement().getOptionSet() ); + assertTrue( programStage.isGeneratedByEnrollmentDate() ); + assertTrue( programStage.isCaptureCoordinates() ); + assertTrue( programStage.isRepeatable() ); + } +} \ No newline at end of file diff --git a/dhis/src/test/resources/org/dhis2/fhir/adapter/dhis/tracker/program/impl/emptyProgramStages.json b/dhis/src/test/resources/org/dhis2/fhir/adapter/dhis/tracker/program/impl/emptyProgramStages.json new file mode 100644 index 00000000..6c232bc6 --- /dev/null +++ b/dhis/src/test/resources/org/dhis2/fhir/adapter/dhis/tracker/program/impl/emptyProgramStages.json @@ -0,0 +1,3 @@ +{ + "programStages": [ ] +} \ No newline at end of file diff --git a/dhis/src/test/resources/org/dhis2/fhir/adapter/dhis/tracker/program/impl/programStage.json b/dhis/src/test/resources/org/dhis2/fhir/adapter/dhis/tracker/program/impl/programStage.json new file mode 100644 index 00000000..93b9829f --- /dev/null +++ b/dhis/src/test/resources/org/dhis2/fhir/adapter/dhis/tracker/program/impl/programStage.json @@ -0,0 +1,11 @@ +{ + "name": "Birth", "id": "A03MvHHogjR", "program": { "id": "IpHINAT79UW" }, "generatedByEnrollmentDate": true, "repeatable": true, "captureCoordinates": true, "minDaysFromStart": 2, "programStageDataElements": [ + { "id": "LBNxoXdMnkv", "compulsory": true, "allowProvidedElsewhere": true, "dataElement": { "code": "DE_2006098", "name": "MCH Apgar Score", "id": "a3kGcGDCuk6", "valueType": "NUMBER", "optionSetValue": false } }, + { + "id": "O4dwFWakvGO", "compulsory": false, "allowProvidedElsewhere": false, "dataElement": { + "code": "DE_2006104", "name": "MCH OPV dose", "id": "ebaJjqltK5N", "valueType": "TEXT", "optionSetValue": true, + "optionSet": { "name": "MNCH Polio doses (0-3)", "id": "kzgQRhOCadd", "options": [ { "code": "0", "name": "Dose 0" }, { "code": "1", "name": "Dose 1" } ] } + } + } +] +} diff --git a/dhis/src/test/resources/org/dhis2/fhir/adapter/dhis/tracker/program/impl/programStages.json b/dhis/src/test/resources/org/dhis2/fhir/adapter/dhis/tracker/program/impl/programStages.json new file mode 100644 index 00000000..8be57dd1 --- /dev/null +++ b/dhis/src/test/resources/org/dhis2/fhir/adapter/dhis/tracker/program/impl/programStages.json @@ -0,0 +1,24 @@ +{ + "programStages": [ + { + "name": "Birth", "id": "A03MvHHogjR", "program": { "id": "IpHINAT79UW" }, "generatedByEnrollmentDate": true, "repeatable": true, "captureCoordinates": true, "minDaysFromStart": 2, "programStageDataElements": [ + { "id": "LBNxoXdMnkv", "compulsory": true, "allowProvidedElsewhere": true, "dataElement": { "code": "DE_2006098", "name": "MCH Apgar Score", "id": "a3kGcGDCuk6", "valueType": "NUMBER", "optionSetValue": false } }, + { + "id": "O4dwFWakvGO", "compulsory": false, "allowProvidedElsewhere": false, "dataElement": { + "code": "DE_2006104", "name": "MCH OPV dose", "id": "ebaJjqltK5N", "valueType": "TEXT", "optionSetValue": true, + "optionSet": { "name": "MNCH Polio doses (0-3)", "id": "kzgQRhOCadd", "options": [ { "code": "0", "name": "Dose 0" }, { "code": "1", "name": "Dose 1" } ] } + } + } + ] + }, { + "name": "Baby Postnatal", "id": "ZzYYXq4fJie", "generatedByEnrollmentDate": false, "repeatable": false, "captureCoordinates": false, "minDaysFromStart": 6, "programStageDataElements": [ + { "id": "ztoQtbuXzsI", "compulsory": false, "allowProvidedElsewhere": true, "dataElement": { "code": "DE_2006099", "name": "MCH Infant Weight (g)", "id": "GQY2lXrypjO", "valueType": "NUMBER", "optionSetValue": false } } + ] + } + ], "programTrackedEntityAttributes": [ + { + "name": "Child Programme First name", "id": "l2T72XzBCLd", "program": { "id": "IpHINAT79UW" }, "valueType": "TEXT", "allowFutureDate": true, "mandatory": true, + "trackedEntityAttribute": { "code": "MMD_PER_NAM", "name": "First name", "id": "w75KJ2mc4zz", "generated": true, "valueType": "TEXT" } + }, { "name": "Child Programme Last name", "id": "XmRd2ZDjWhF", "valueType": "TEXT", "allowFutureDate": false, "mandatory": false, "trackedEntityAttribute": { "name": "Last name", "id": "zDhUuAYrxNC", "generated": false, "valueType": "TEXT" } } +] +} diff --git a/docs/fhir/profiles/TrackerProgramPlanDefinition.StructureDefinition.xml b/docs/fhir/profiles/TrackerProgramPlanDefinition.StructureDefinition.xml index 38b47048..bf33d0bb 100644 --- a/docs/fhir/profiles/TrackerProgramPlanDefinition.StructureDefinition.xml +++ b/docs/fhir/profiles/TrackerProgramPlanDefinition.StructureDefinition.xml @@ -96,13 +96,9 @@ - - - - - + diff --git a/docs/fhir/profiles/TrackerProgramQuestionnaire.StructureDefinition.xml b/docs/fhir/profiles/TrackerProgramQuestionnaire.StructureDefinition.xml index 0702ebad..eeef7fc0 100644 --- a/docs/fhir/profiles/TrackerProgramQuestionnaire.StructureDefinition.xml +++ b/docs/fhir/profiles/TrackerProgramQuestionnaire.StructureDefinition.xml @@ -96,9 +96,13 @@ + + + + - + @@ -186,6 +190,10 @@ + + + + @@ -200,6 +208,12 @@ + + + + + + diff --git a/fhir-r4/src/main/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/metadata/r4/R4ProgramMetadataToFhirDataProvider.java b/fhir-r4/src/main/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/metadata/r4/R4ProgramMetadataToFhirDataProvider.java index 180a4560..da5594af 100644 --- a/fhir-r4/src/main/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/metadata/r4/R4ProgramMetadataToFhirDataProvider.java +++ b/fhir-r4/src/main/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/metadata/r4/R4ProgramMetadataToFhirDataProvider.java @@ -65,6 +65,7 @@ public Set getFhirVersions() @Override protected void initSearchFilter( @Nonnull FhirVersion fhirVersion, @Nonnull RuleInfo ruleInfo, @Nonnull SearchFilter searchFilter ) { - searchFilter.add( PlanDefinition.SP_NAME, SearchParamType.STRING, "name" ); + searchFilter.add( PlanDefinition.SP_NAME, SearchParamType.STRING, "code" ); + searchFilter.add( PlanDefinition.SP_TITLE, SearchParamType.STRING, "name" ); } } diff --git a/fhir-r4/src/main/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/metadata/r4/R4ProgramMetadataToFhirPlanDefinitionTransformer.java b/fhir-r4/src/main/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/metadata/r4/R4ProgramMetadataToFhirPlanDefinitionTransformer.java index 6002c267..44333c79 100644 --- a/fhir-r4/src/main/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/metadata/r4/R4ProgramMetadataToFhirPlanDefinitionTransformer.java +++ b/fhir-r4/src/main/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/metadata/r4/R4ProgramMetadataToFhirPlanDefinitionTransformer.java @@ -92,7 +92,8 @@ protected boolean transformInternal( @Nonnull FhirClient fhirClient, @Nonnull Dh } fhirPlanDefinition.setUrl( dhisProgram.getId() ); - fhirPlanDefinition.setName( dhisProgram.getName() ); + fhirPlanDefinition.setName( dhisProgram.getCode() ); + fhirPlanDefinition.setTitle( dhisProgram.getName() ); fhirPlanDefinition.setStatus( Enumerations.PublicationStatus.ACTIVE ); fhirPlanDefinition.setDescription( dhisProgram.getDescription() ); fhirPlanDefinition.setAction( null ); diff --git a/fhir-r4/src/main/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/metadata/r4/R4ProgramStageMetadataToFhirDataProvider.java b/fhir-r4/src/main/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/metadata/r4/R4ProgramStageMetadataToFhirDataProvider.java new file mode 100644 index 00000000..5801cc6b --- /dev/null +++ b/fhir-r4/src/main/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/metadata/r4/R4ProgramStageMetadataToFhirDataProvider.java @@ -0,0 +1,70 @@ +package org.dhis2.fhir.adapter.fhir.transform.dhis.impl.metadata.r4; + +/* + * Copyright (c) 2004-2019, University of Oslo + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * Neither the name of the HISP project nor the names of its contributors may + * be used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +import org.dhis2.fhir.adapter.dhis.tracker.program.ProgramStageMetadataService; +import org.dhis2.fhir.adapter.fhir.metadata.model.ProgramStageMetadataRule; +import org.dhis2.fhir.adapter.fhir.metadata.model.RuleInfo; +import org.dhis2.fhir.adapter.fhir.model.FhirVersion; +import org.dhis2.fhir.adapter.fhir.script.ScriptExecutor; +import org.dhis2.fhir.adapter.fhir.transform.dhis.impl.metadata.program.AbstractProgramStageMetadataToFhirDataProvider; +import org.dhis2.fhir.adapter.fhir.transform.dhis.search.SearchFilter; +import org.dhis2.fhir.adapter.fhir.transform.dhis.search.SearchParamType; +import org.hl7.fhir.r4.model.Questionnaire; +import org.springframework.stereotype.Component; + +import javax.annotation.Nonnull; +import java.util.Set; + +/** + * R4 specific version of DHIS2 Program Stage Metadata data provider. + * + * @author volsch + */ +@Component +public class R4ProgramStageMetadataToFhirDataProvider extends AbstractProgramStageMetadataToFhirDataProvider +{ + public R4ProgramStageMetadataToFhirDataProvider( @Nonnull ScriptExecutor scriptExecutor, @Nonnull ProgramStageMetadataService programStageMetadataService ) + { + super( scriptExecutor, programStageMetadataService ); + } + + @Nonnull + @Override + public Set getFhirVersions() + { + return FhirVersion.R4_ONLY; + } + + @Override + protected void initSearchFilter( @Nonnull FhirVersion fhirVersion, @Nonnull RuleInfo ruleInfo, @Nonnull SearchFilter searchFilter ) + { + searchFilter.add( Questionnaire.SP_TITLE, SearchParamType.STRING, "name" ); + } +} diff --git a/fhir-r4/src/main/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/metadata/r4/R4ProgramStageMetadataToFhirQuestionnaireTransformer.java b/fhir-r4/src/main/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/metadata/r4/R4ProgramStageMetadataToFhirQuestionnaireTransformer.java new file mode 100644 index 00000000..2575b8ee --- /dev/null +++ b/fhir-r4/src/main/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/metadata/r4/R4ProgramStageMetadataToFhirQuestionnaireTransformer.java @@ -0,0 +1,158 @@ +package org.dhis2.fhir.adapter.fhir.transform.dhis.impl.metadata.r4; + +/* + * Copyright (c) 2004-2019, University of Oslo + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * Neither the name of the HISP project nor the names of its contributors may + * be used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +import org.dhis2.fhir.adapter.dhis.orgunit.OrganizationUnitService; +import org.dhis2.fhir.adapter.dhis.tracker.program.ProgramStage; +import org.dhis2.fhir.adapter.fhir.data.repository.FhirDhisAssignmentRepository; +import org.dhis2.fhir.adapter.fhir.metadata.model.FhirClient; +import org.dhis2.fhir.adapter.fhir.metadata.model.ProgramStageMetadataRule; +import org.dhis2.fhir.adapter.fhir.metadata.model.RuleInfo; +import org.dhis2.fhir.adapter.fhir.metadata.repository.SystemRepository; +import org.dhis2.fhir.adapter.fhir.model.FhirVersion; +import org.dhis2.fhir.adapter.fhir.repository.FhirResourceRepository; +import org.dhis2.fhir.adapter.fhir.script.ScriptExecutor; +import org.dhis2.fhir.adapter.fhir.transform.dhis.DhisToFhirTransformerContext; +import org.dhis2.fhir.adapter.fhir.transform.dhis.impl.metadata.program.AbstractProgramStageMetadataToFhirQuestionnaireTransformer; +import org.dhis2.fhir.adapter.fhir.transform.scripted.AccessibleScriptedDhisMetadata; +import org.dhis2.fhir.adapter.lock.LockManager; +import org.dhis2.fhir.adapter.model.ValueType; +import org.hl7.fhir.instance.model.api.IBaseResource; +import org.hl7.fhir.r4.model.Coding; +import org.hl7.fhir.r4.model.Enumerations; +import org.hl7.fhir.r4.model.Questionnaire; +import org.hl7.fhir.r4.model.Questionnaire.QuestionnaireItemComponent; +import org.hl7.fhir.r4.model.Questionnaire.QuestionnaireItemType; +import org.springframework.stereotype.Component; + +import javax.annotation.Nonnull; +import javax.annotation.Nullable; +import java.util.Map; +import java.util.Set; + +import static org.hl7.fhir.r4.model.Questionnaire.QuestionnaireItemType.*; + +/** + * R4 specific version of DHIS2 Program Stage Metadata to FHIR Questionnaire transformer. + * + * @author volsch + */ +@Component +public class R4ProgramStageMetadataToFhirQuestionnaireTransformer extends AbstractProgramStageMetadataToFhirQuestionnaireTransformer +{ + public R4ProgramStageMetadataToFhirQuestionnaireTransformer( @Nonnull ScriptExecutor scriptExecutor, @Nonnull LockManager lockManager, @Nonnull SystemRepository systemRepository, @Nonnull FhirResourceRepository fhirResourceRepository, + @Nonnull FhirDhisAssignmentRepository fhirDhisAssignmentRepository, @Nonnull OrganizationUnitService organizationUnitService ) + { + super( scriptExecutor, lockManager, systemRepository, fhirResourceRepository, fhirDhisAssignmentRepository, organizationUnitService ); + } + + @Nonnull + @Override + public Set getFhirVersions() + { + return FhirVersion.R4_ONLY; + } + + @Override + protected boolean transformInternal( @Nonnull FhirClient fhirClient, @Nonnull DhisToFhirTransformerContext context, @Nonnull RuleInfo ruleInfo, @Nonnull Map scriptVariables, + @Nonnull AccessibleScriptedDhisMetadata input, @Nonnull IBaseResource output ) + { + final ProgramStage dhisProgramStage = (ProgramStage) input.getDhisResource(); + final Questionnaire fhirQuestionnaire = (Questionnaire) output; + + fhirQuestionnaire.setUrl( dhisProgramStage.getId() ); + fhirQuestionnaire.setTitle( dhisProgramStage.getName() ); + fhirQuestionnaire.setStatus( Enumerations.PublicationStatus.ACTIVE ); + fhirQuestionnaire.setDescription( dhisProgramStage.getDescription() ); + fhirQuestionnaire.setItem( null ); + + dhisProgramStage.getDataElements().forEach( dataElement -> { + final QuestionnaireItemType type = convertValueType( dataElement.getElement().getValueType() ); + + if ( type != null ) + { + final QuestionnaireItemComponent itemComponent = fhirQuestionnaire.addItem(); + + itemComponent.setLinkId( dataElement.getElementId() ); + itemComponent.setText( dataElement.getElement().getName() ); + itemComponent.setRequired( dataElement.isCompulsory() ); + itemComponent.setType( type ); + + if ( dataElement.getElement().isOptionSetValue() ) + { + dataElement.getElement().getOptionSet().getOptions().forEach( option -> itemComponent.addAnswerOption().setValue( + new Coding().setCode( option.getCode() ).setDisplay( option.getName() ) ) ); + } + } + } ); + + return true; + } + + @Nullable + protected QuestionnaireItemType convertValueType( @Nonnull ValueType valueType ) + { + switch ( valueType ) + { + case TEXT: + case EMAIL: + case LETTER: + case ORGANISATION_UNIT: + case PHONE_NUMBER: + case TRACKER_ASSOCIATE: + case URL: + case USERNAME: + return STRING; + case LONG_TEXT: + return TEXT; + case INTEGER: + case INTEGER_POSITIVE: + case INTEGER_NEGATIVE: + case INTEGER_ZERO_OR_POSITIVE: + return INTEGER; + case NUMBER: + case PERCENTAGE: + case UNIT_INTERVAL: + return DECIMAL; + case DATETIME: + case AGE: + return DATETIME; + case DATE: + return DATE; + case TIME: + return TIME; + case BOOLEAN: + case TRUE_ONLY: + return BOOLEAN; + } + + // unhandled data type + return null; + } +} diff --git a/fhir-r4/src/test/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/metadata/r4/R4ProgramMetadataToFhirDataProviderTest.java b/fhir-r4/src/test/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/metadata/r4/R4ProgramMetadataToFhirDataProviderTest.java index 4798edd1..6ed3a090 100644 --- a/fhir-r4/src/test/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/metadata/r4/R4ProgramMetadataToFhirDataProviderTest.java +++ b/fhir-r4/src/test/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/metadata/r4/R4ProgramMetadataToFhirDataProviderTest.java @@ -79,7 +79,7 @@ public void getFhirVersions() @Test public void searchFilterName() throws URISyntaxException { - final SearchFilterCollector searchFilterCollector = new SearchFilterCollector( Collections.singletonMap( "name", Collections.singletonList( "Child Programme" ) ) ); + final SearchFilterCollector searchFilterCollector = new SearchFilterCollector( Collections.singletonMap( "title", Collections.singletonList( "Child Programme" ) ) ); final SearchFilter searchFilter = new SearchFilter( searchFilterCollector, false ); provider.initSearchFilter( FhirVersion.R4, new RuleInfo<>( new ProgramMetadataRule(), Collections.emptyList() ), searchFilter ); diff --git a/fhir-r4/src/test/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/metadata/r4/R4ProgramMetadataToFhirPlanDefinitionTransformerTest.java b/fhir-r4/src/test/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/metadata/r4/R4ProgramMetadataToFhirPlanDefinitionTransformerTest.java index 3d14b07b..adccd703 100644 --- a/fhir-r4/src/test/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/metadata/r4/R4ProgramMetadataToFhirPlanDefinitionTransformerTest.java +++ b/fhir-r4/src/test/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/metadata/r4/R4ProgramMetadataToFhirPlanDefinitionTransformerTest.java @@ -152,7 +152,7 @@ public void transformInternal() transformer.transformInternal( fhirClient, context, new RuleInfo<>( new ProgramMetadataRule(), Collections.emptyList() ), new HashMap<>(), new WritableScriptedDhisMetadata( program, scriptExecutionContext ), fhirPlanDefinition ); - Assert.assertEquals( "Test Program", fhirPlanDefinition.getName() ); + Assert.assertEquals( "Test Program", fhirPlanDefinition.getTitle() ); Assert.assertEquals( "Test Description", fhirPlanDefinition.getDescription() ); Assert.assertEquals( 2, fhirPlanDefinition.getAction().size() ); diff --git a/fhir-r4/src/test/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/metadata/r4/R4ProgramStageMetadataToFhirDataProviderTest.java b/fhir-r4/src/test/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/metadata/r4/R4ProgramStageMetadataToFhirDataProviderTest.java new file mode 100644 index 00000000..8b2c37f7 --- /dev/null +++ b/fhir-r4/src/test/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/metadata/r4/R4ProgramStageMetadataToFhirDataProviderTest.java @@ -0,0 +1,93 @@ +package org.dhis2.fhir.adapter.fhir.transform.dhis.impl.metadata.r4; + +/* + * Copyright (c) 2004-2019, University of Oslo + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * Neither the name of the HISP project nor the names of its contributors may + * be used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +import org.dhis2.fhir.adapter.dhis.tracker.program.ProgramStageMetadataService; +import org.dhis2.fhir.adapter.fhir.metadata.model.ProgramStageMetadataRule; +import org.dhis2.fhir.adapter.fhir.metadata.model.RuleInfo; +import org.dhis2.fhir.adapter.fhir.model.FhirVersion; +import org.dhis2.fhir.adapter.fhir.script.ScriptExecutor; +import org.dhis2.fhir.adapter.fhir.transform.dhis.search.SearchFilter; +import org.dhis2.fhir.adapter.fhir.transform.dhis.search.SearchFilterCollector; +import org.junit.Assert; +import org.junit.Rule; +import org.junit.Test; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.junit.MockitoJUnit; +import org.mockito.junit.MockitoRule; +import org.springframework.web.util.DefaultUriBuilderFactory; +import org.springframework.web.util.UriBuilder; + +import java.net.URI; +import java.net.URISyntaxException; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +/** + * Unit tests for {@link R4ProgramStageMetadataToFhirDataProvider}. + * + * @author volsch + */ +public class R4ProgramStageMetadataToFhirDataProviderTest +{ + @Mock + private ScriptExecutor scriptExecutor; + + @Mock + private ProgramStageMetadataService programStageMetadataService; + + @InjectMocks + private R4ProgramStageMetadataToFhirDataProvider provider; + + @Rule + public MockitoRule rule = MockitoJUnit.rule(); + + @Test + public void getFhirVersions() + { + Assert.assertEquals( FhirVersion.R4_ONLY, provider.getFhirVersions() ); + } + + @Test + public void searchFilterName() throws URISyntaxException + { + final SearchFilterCollector searchFilterCollector = new SearchFilterCollector( Collections.singletonMap( "title", Collections.singletonList( "Birth" ) ) ); + final SearchFilter searchFilter = new SearchFilter( searchFilterCollector, false ); + + provider.initSearchFilter( FhirVersion.R4, new RuleInfo<>( new ProgramStageMetadataRule(), Collections.emptyList() ), searchFilter ); + + final List variables = new ArrayList<>(); + UriBuilder uriBuilder = new DefaultUriBuilderFactory().builder(); + uriBuilder = searchFilterCollector.add( uriBuilder, variables ); + + Assert.assertEquals( new URI( "?filter=%5Bname:$ilike:Birth%5D" ), uriBuilder.build( variables ) ); + } +} diff --git a/fhir-r4/src/test/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/metadata/r4/R4ProgramStageMetadataToFhirQuestionnaireTransformerTest.java b/fhir-r4/src/test/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/metadata/r4/R4ProgramStageMetadataToFhirQuestionnaireTransformerTest.java new file mode 100644 index 00000000..3cf0b25c --- /dev/null +++ b/fhir-r4/src/test/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/metadata/r4/R4ProgramStageMetadataToFhirQuestionnaireTransformerTest.java @@ -0,0 +1,170 @@ +package org.dhis2.fhir.adapter.fhir.transform.dhis.impl.metadata.r4; + +/* + * Copyright (c) 2004-2019, University of Oslo + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * Neither the name of the HISP project nor the names of its contributors may + * be used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +import org.dhis2.fhir.adapter.dhis.model.WritableDataElement; +import org.dhis2.fhir.adapter.dhis.model.WritableOption; +import org.dhis2.fhir.adapter.dhis.model.WritableOptionSet; +import org.dhis2.fhir.adapter.dhis.orgunit.OrganizationUnitService; +import org.dhis2.fhir.adapter.dhis.tracker.program.WritableProgramStage; +import org.dhis2.fhir.adapter.dhis.tracker.program.WritableProgramStageDataElement; +import org.dhis2.fhir.adapter.fhir.data.repository.FhirDhisAssignmentRepository; +import org.dhis2.fhir.adapter.fhir.metadata.model.FhirClient; +import org.dhis2.fhir.adapter.fhir.metadata.model.ProgramStageMetadataRule; +import org.dhis2.fhir.adapter.fhir.metadata.model.RuleInfo; +import org.dhis2.fhir.adapter.fhir.metadata.repository.SystemRepository; +import org.dhis2.fhir.adapter.fhir.model.FhirVersion; +import org.dhis2.fhir.adapter.fhir.repository.FhirResourceRepository; +import org.dhis2.fhir.adapter.fhir.script.ScriptExecutionContext; +import org.dhis2.fhir.adapter.fhir.script.ScriptExecutor; +import org.dhis2.fhir.adapter.fhir.transform.dhis.DhisToFhirTransformerContext; +import org.dhis2.fhir.adapter.fhir.transform.scripted.WritableScriptedDhisMetadata; +import org.dhis2.fhir.adapter.lock.LockManager; +import org.dhis2.fhir.adapter.model.ValueType; +import org.hl7.fhir.r4.model.Coding; +import org.hl7.fhir.r4.model.Questionnaire; +import org.junit.Assert; +import org.junit.Rule; +import org.junit.Test; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.junit.MockitoJUnit; +import org.mockito.junit.MockitoRule; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; + +/** + * Unit tests for {@link R4ProgramStageMetadataToFhirQuestionnaireTransformer}. + * + * @author volsch + */ +public class R4ProgramStageMetadataToFhirQuestionnaireTransformerTest +{ + @Mock + private ScriptExecutor scriptExecutor; + + @Mock + private LockManager lockManager; + + @Mock + private SystemRepository systemRepository; + + @Mock + private FhirResourceRepository fhirResourceRepository; + + @Mock + private FhirDhisAssignmentRepository fhirDhisAssignmentRepository; + + @Mock + private OrganizationUnitService organizationUnitService; + + @Mock + private DhisToFhirTransformerContext context; + + @Mock + private FhirClient fhirClient; + + @Mock + private ScriptExecutionContext scriptExecutionContext; + + @InjectMocks + private R4ProgramStageMetadataToFhirQuestionnaireTransformer transformer; + + @Rule + public MockitoRule rule = MockitoJUnit.rule(); + + @Test + public void getFhirVersions() + { + Assert.assertEquals( FhirVersion.R4_ONLY, transformer.getFhirVersions() ); + } + + @Test + public void transformInternal() + { + final WritableProgramStage programStage = new WritableProgramStage(); + programStage.setId( "b1234567890" ); + programStage.setName( "Test Stage 1" ); + programStage.setDescription( "Test Description 1" ); + programStage.setRepeatable( false ); + programStage.setDataElements( new ArrayList<>() ); + + WritableProgramStageDataElement programStageDataElement = new WritableProgramStageDataElement(); + WritableDataElement dataElement = new WritableDataElement(); + dataElement.setId( "d0123456789" ); + dataElement.setName( "Value 1" ); + dataElement.setValueType( ValueType.TEXT ); + programStageDataElement.setCompulsory( false ); + programStageDataElement.setElement( dataElement ); + programStage.getDataElements().add( programStageDataElement ); + + WritableOptionSet optionSet = new WritableOptionSet(); + optionSet.setOptions( new ArrayList<>() ); + optionSet.getOptions().add( new WritableOption( "5", "Test Value 1" ) ); + optionSet.getOptions().add( new WritableOption( "7", "Test Value 2" ) ); + + programStageDataElement = new WritableProgramStageDataElement(); + dataElement = new WritableDataElement(); + dataElement.setId( "d1123456789" ); + dataElement.setName( "Value 2" ); + dataElement.setValueType( ValueType.INTEGER ); + dataElement.setOptionSetValue( true ); + dataElement.setOptionSet( optionSet ); + programStageDataElement.setCompulsory( true ); + programStageDataElement.setElement( dataElement ); + programStage.getDataElements().add( programStageDataElement ); + + final Questionnaire fhirQuestionnaire = new Questionnaire(); + + transformer.transformInternal( fhirClient, context, new RuleInfo<>( new ProgramStageMetadataRule(), Collections.emptyList() ), new HashMap<>(), + new WritableScriptedDhisMetadata( programStage, scriptExecutionContext ), fhirQuestionnaire ); + + Assert.assertEquals( "b1234567890", fhirQuestionnaire.getUrl() ); + Assert.assertEquals( "Test Stage 1", fhirQuestionnaire.getTitle() ); + Assert.assertEquals( "Test Description 1", fhirQuestionnaire.getDescription() ); + Assert.assertEquals( 2, fhirQuestionnaire.getItem().size() ); + + Assert.assertEquals( "d0123456789", fhirQuestionnaire.getItem().get( 0 ).getLinkId() ); + Assert.assertEquals( "Value 1", fhirQuestionnaire.getItem().get( 0 ).getText() ); + Assert.assertEquals( 0, fhirQuestionnaire.getItem().get( 0 ).getAnswerOption().size() ); + Assert.assertFalse( fhirQuestionnaire.getItem().get( 0 ).getRequired() ); + + Assert.assertEquals( "d1123456789", fhirQuestionnaire.getItem().get( 1 ).getLinkId() ); + Assert.assertEquals( "Value 2", fhirQuestionnaire.getItem().get( 1 ).getText() ); + Assert.assertEquals( 2, fhirQuestionnaire.getItem().get( 1 ).getAnswerOption().size() ); + Assert.assertTrue( fhirQuestionnaire.getItem().get( 1 ).getAnswerOption().get( 0 ).getValue() instanceof Coding ); + Assert.assertEquals( "5", ( (Coding) fhirQuestionnaire.getItem().get( 1 ).getAnswerOption().get( 0 ).getValue() ).getCode() ); + Assert.assertEquals( "Test Value 1", ( (Coding) fhirQuestionnaire.getItem().get( 1 ).getAnswerOption().get( 0 ).getValue() ).getDisplay() ); + Assert.assertEquals( "7", ( (Coding) fhirQuestionnaire.getItem().get( 1 ).getAnswerOption().get( 1 ).getValue() ).getCode() ); + Assert.assertEquals( "Test Value 2", ( (Coding) fhirQuestionnaire.getItem().get( 1 ).getAnswerOption().get( 1 ).getValue() ).getDisplay() ); + Assert.assertTrue( fhirQuestionnaire.getItem().get( 1 ).getRequired() ); + } +} diff --git a/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/metadata/program/AbstractProgramStageMetadataToFhirDataProvider.java b/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/metadata/program/AbstractProgramStageMetadataToFhirDataProvider.java new file mode 100644 index 00000000..05062a36 --- /dev/null +++ b/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/metadata/program/AbstractProgramStageMetadataToFhirDataProvider.java @@ -0,0 +1,66 @@ +package org.dhis2.fhir.adapter.fhir.transform.dhis.impl.metadata.program; + +/* + * Copyright (c) 2004-2019, University of Oslo + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * Neither the name of the HISP project nor the names of its contributors may + * be used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +import org.dhis2.fhir.adapter.dhis.model.DhisResourceType; +import org.dhis2.fhir.adapter.dhis.tracker.program.ProgramStage; +import org.dhis2.fhir.adapter.dhis.tracker.program.ProgramStageMetadataService; +import org.dhis2.fhir.adapter.fhir.metadata.model.ProgramStageMetadataRule; +import org.dhis2.fhir.adapter.fhir.script.ScriptExecutor; +import org.dhis2.fhir.adapter.fhir.transform.dhis.DhisToFhirDataProvider; +import org.dhis2.fhir.adapter.fhir.transform.dhis.impl.metadata.AbstractDhisMetadataToFhirDataProvider; + +import javax.annotation.Nonnull; + +/** + * Abstract implementation of {@link DhisToFhirDataProvider} for DHIS2 Program Stage Metadata. + * + * @author volsch + */ +public abstract class AbstractProgramStageMetadataToFhirDataProvider extends AbstractDhisMetadataToFhirDataProvider +{ + public AbstractProgramStageMetadataToFhirDataProvider( @Nonnull ScriptExecutor scriptExecutor, @Nonnull ProgramStageMetadataService programStageMetadataService ) + { + super( scriptExecutor, programStageMetadataService ); + } + + @Nonnull + @Override + public DhisResourceType getDhisResourceType() + { + return DhisResourceType.PROGRAM_STAGE_METADATA; + } + + @Nonnull + @Override + protected Class getRuleClass() + { + return ProgramStageMetadataRule.class; + } +} diff --git a/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/metadata/program/AbstractProgramStageMetadataToFhirQuestionnaireTransformer.java b/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/metadata/program/AbstractProgramStageMetadataToFhirQuestionnaireTransformer.java new file mode 100644 index 00000000..0bc8ba45 --- /dev/null +++ b/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/metadata/program/AbstractProgramStageMetadataToFhirQuestionnaireTransformer.java @@ -0,0 +1,93 @@ +package org.dhis2.fhir.adapter.fhir.transform.dhis.impl.metadata.program; + +/* + * Copyright (c) 2004-2019, University of Oslo + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * Neither the name of the HISP project nor the names of its contributors may + * be used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +import org.dhis2.fhir.adapter.dhis.model.DhisResourceType; +import org.dhis2.fhir.adapter.dhis.orgunit.OrganizationUnitService; +import org.dhis2.fhir.adapter.fhir.data.repository.FhirDhisAssignmentRepository; +import org.dhis2.fhir.adapter.fhir.metadata.model.FhirResourceType; +import org.dhis2.fhir.adapter.fhir.metadata.model.ProgramStageMetadataRule; +import org.dhis2.fhir.adapter.fhir.metadata.repository.SystemRepository; +import org.dhis2.fhir.adapter.fhir.repository.FhirResourceRepository; +import org.dhis2.fhir.adapter.fhir.script.ScriptExecutor; +import org.dhis2.fhir.adapter.fhir.transform.dhis.impl.DhisToFhirTransformer; +import org.dhis2.fhir.adapter.fhir.transform.dhis.impl.metadata.AbstractReadOnlyDhisMetadataToTypedFhirTransformer; +import org.dhis2.fhir.adapter.fhir.transform.scripted.AccessibleScriptedDhisMetadata; +import org.dhis2.fhir.adapter.lock.LockManager; +import org.hl7.fhir.instance.model.api.IBaseResource; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import javax.annotation.Nonnull; + +/** + * Implementation of {@link DhisToFhirTransformer} for transforming DHIS2 + * program stage metadata to FHIR Questionnaire. + * + * @param the concrete type of the FHIR resource into which the DHIS2 resource should be transformed. + * @author volsch + */ +public abstract class AbstractProgramStageMetadataToFhirQuestionnaireTransformer extends AbstractReadOnlyDhisMetadataToTypedFhirTransformer +{ + private final Logger logger = LoggerFactory.getLogger( getClass() ); + + public AbstractProgramStageMetadataToFhirQuestionnaireTransformer( @Nonnull ScriptExecutor scriptExecutor, @Nonnull LockManager lockManager, @Nonnull SystemRepository systemRepository, @Nonnull FhirResourceRepository fhirResourceRepository, + @Nonnull FhirDhisAssignmentRepository fhirDhisAssignmentRepository, @Nonnull OrganizationUnitService organizationUnitService ) + { + super( scriptExecutor, lockManager, systemRepository, fhirResourceRepository, fhirDhisAssignmentRepository ); + } + + @Nonnull + @Override + public DhisResourceType getDhisResourceType() + { + return DhisResourceType.PROGRAM_STAGE_METADATA; + } + + @Nonnull + @Override + protected FhirResourceType getFhirResourceType() + { + return FhirResourceType.QUESTIONNAIRE; + } + + @Nonnull + @Override + public Class getDhisResourceClass() + { + return AccessibleScriptedDhisMetadata.class; + } + + @Nonnull + @Override + public Class getRuleClass() + { + return ProgramStageMetadataRule.class; + } +} diff --git a/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/metadata/program/ProgramStageMetadataToFhirRequestResolver.java b/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/metadata/program/ProgramStageMetadataToFhirRequestResolver.java new file mode 100644 index 00000000..4186ebae --- /dev/null +++ b/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/metadata/program/ProgramStageMetadataToFhirRequestResolver.java @@ -0,0 +1,76 @@ +package org.dhis2.fhir.adapter.fhir.transform.dhis.impl.metadata.program; + +/* + * Copyright (c) 2004-2019, University of Oslo + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * Neither the name of the HISP project nor the names of its contributors may + * be used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +import org.dhis2.fhir.adapter.dhis.model.DhisMetadata; +import org.dhis2.fhir.adapter.dhis.model.DhisResource; +import org.dhis2.fhir.adapter.dhis.model.DhisResourceType; +import org.dhis2.fhir.adapter.fhir.metadata.repository.FhirClientRepository; +import org.dhis2.fhir.adapter.fhir.metadata.repository.RuleRepository; +import org.dhis2.fhir.adapter.fhir.script.ScriptExecutionContext; +import org.dhis2.fhir.adapter.fhir.transform.dhis.impl.DhisToFhirRequestResolver; +import org.dhis2.fhir.adapter.fhir.transform.dhis.impl.metadata.AbstractDhisMetadataToFhirRequestResolver; +import org.dhis2.fhir.adapter.fhir.transform.dhis.model.DhisRequest; +import org.dhis2.fhir.adapter.fhir.transform.scripted.ImmutableScriptedDhisMetadata; +import org.dhis2.fhir.adapter.fhir.transform.scripted.ScriptedDhisResource; +import org.dhis2.fhir.adapter.fhir.transform.scripted.WritableScriptedDhisMetadata; +import org.springframework.stereotype.Component; + +import javax.annotation.Nonnull; + +/** + * Implementation of {@link DhisToFhirRequestResolver} for DHIS2 program stage metadata. + * + * @author volsch + */ +@Component +public class ProgramStageMetadataToFhirRequestResolver extends AbstractDhisMetadataToFhirRequestResolver +{ + private final ScriptExecutionContext scriptExecutionContext; + + public ProgramStageMetadataToFhirRequestResolver( @Nonnull FhirClientRepository fhirClientRepository, @Nonnull RuleRepository ruleRepository, @Nonnull ScriptExecutionContext scriptExecutionContext ) + { + super( fhirClientRepository, ruleRepository ); + this.scriptExecutionContext = scriptExecutionContext; + } + + @Nonnull + @Override + public DhisResourceType getDhisResourceType() + { + return DhisResourceType.PROGRAM_STAGE_METADATA; + } + + @Nonnull + @Override + public ScriptedDhisResource convert( @Nonnull DhisResource dhisResource, @Nonnull DhisRequest dhisRequest ) + { + return new ImmutableScriptedDhisMetadata( new WritableScriptedDhisMetadata( (DhisMetadata) dhisResource, scriptExecutionContext ) ); + } +} diff --git a/fhir/src/main/resources/db/migration/sample/V1.1.0.41_10_0__Plan_Definition_Sample_Data.sql b/fhir/src/main/resources/db/migration/sample/V1.1.0.41_10_0__Plan_Definition_Sample_Data.sql index e21ec8fd..e3918522 100644 --- a/fhir/src/main/resources/db/migration/sample/V1.1.0.41_10_0__Plan_Definition_Sample_Data.sql +++ b/fhir/src/main/resources/db/migration/sample/V1.1.0.41_10_0__Plan_Definition_Sample_Data.sql @@ -34,5 +34,7 @@ WHERE id='73cd99c5-0ca8-42ad-a53b-1891fccce08f'; -- virtual subscription for FHIR organizations INSERT INTO fhir_client_resource (id, version, fhir_client_id, fhir_resource_type, exp_only, fhir_criteria_parameters, description) VALUES ('2520acc5-86b4-4716-ae0f-ea0531eb885a', 0, '73cd99c5-0ca8-42ad-a53b-1891fccce08f', 'PLAN_DEFINITION', TRUE, NULL, 'Virtual subscription for all Plan Definitions.'); +INSERT INTO fhir_client_resource (id, version, fhir_client_id, fhir_resource_type, exp_only, fhir_criteria_parameters, description) +VALUES ('0918c4f3-995c-4130-b607-b28023a1a3a0', 0, '73cd99c5-0ca8-42ad-a53b-1891fccce08f', 'QUESTIONNAIRE', TRUE, NULL, 'Virtual subscription for all Questionnaires.'); INSERT INTO fhir_client_resource_update(id) VALUES ('2520acc5-86b4-4716-ae0f-ea0531eb885a'); From bf65003480550c55712e7dcce128eb90ca454be8 Mon Sep 17 00:00:00 2001 From: Volker Schmidt Date: Wed, 31 Jul 2019 12:06:35 +0200 Subject: [PATCH 14/16] Fixes issue with generated FHIR server resource IDs. --- .../adapter/dhis/model/DhisResourceType.java | 9 +- .../fhir/metadata/model/FhirResourceType.java | 46 ++-- .../repository/FhirResourceRepository.java | 2 +- .../repository/impl/DhisRepositoryImpl.java | 5 +- .../impl/FhirResourceRepositoryImpl.java | 33 ++- .../impl/FhirResourceRepositoryImplTest.java | 207 ++++++++++++++++++ 6 files changed, 273 insertions(+), 29 deletions(-) create mode 100644 fhir/src/test/java/org/dhis2/fhir/adapter/fhir/repository/impl/FhirResourceRepositoryImplTest.java diff --git a/dhis/src/main/java/org/dhis2/fhir/adapter/dhis/model/DhisResourceType.java b/dhis/src/main/java/org/dhis2/fhir/adapter/dhis/model/DhisResourceType.java index 30e669e9..8b6ca64a 100644 --- a/dhis/src/main/java/org/dhis2/fhir/adapter/dhis/model/DhisResourceType.java +++ b/dhis/src/main/java/org/dhis2/fhir/adapter/dhis/model/DhisResourceType.java @@ -47,16 +47,15 @@ public enum DhisResourceType */ ORGANIZATION_UNIT( "organisationUnits", "ou", "OrganizationUnitRule" ), - /** - * The program metadata. - */ - PROGRAM_METADATA( "programs", "pm", "ProgramMetadataRule" ), - /** * The program stage metadata. */ PROGRAM_STAGE_METADATA( "programStages", "sm", "ProgramStageMetadataRule" ), + /** + * The program metadata. + */ + PROGRAM_METADATA( "programs", "pm", "ProgramMetadataRule" ), /** * Resource is a tracked entity type. diff --git a/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/metadata/model/FhirResourceType.java b/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/metadata/model/FhirResourceType.java index 6bf41c17..ed0b04c6 100644 --- a/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/metadata/model/FhirResourceType.java +++ b/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/metadata/model/FhirResourceType.java @@ -58,22 +58,22 @@ */ public enum FhirResourceType { - CONDITION( FhirVersion.ALL, "Condition", 19, Collections.emptySet(), Collections.singleton( "Condition" ) ), - DIAGNOSTIC_REPORT( FhirVersion.ALL, "DiagnosticReport", 30, Collections.emptySet(), Collections.singleton( "DiagnosticReport" ) ), - ENCOUNTER( FhirVersion.ALL, "Encounter", 4, Collections.emptySet(), Collections.singleton( "Encounter" ) ), - IMMUNIZATION( FhirVersion.ALL, "Immunization", 22, Collections.emptySet(), Collections.singleton( "Immunization" ) ), - LOCATION( FhirVersion.ALL, "Location", 2, Collections.emptySet(), Collections.singleton( "Location" ) ), - MEDICATION_REQUEST( FhirVersion.ALL, "MedicationRequest", 21, Collections.emptySet(), Collections.singleton( "MedicationRequest" ) ), - OBSERVATION( FhirVersion.ALL, "Observation", 20, Collections.emptySet(), Collections.singleton( "Observation" ) ), - ORGANIZATION( FhirVersion.ALL, "Organization", 1, Collections.emptySet(), Collections.singleton( "Organization" ) ), - PATIENT( FhirVersion.ALL, "Patient", 10, Collections.emptySet(), Collections.singleton( "Patient" ) ), - RELATED_PERSON( FhirVersion.ALL, "RelatedPerson", 11, Collections.emptySet(), Collections.singleton( "RelatedPerson" ) ), - PRACTITIONER( FhirVersion.ALL, "Practitioner", 9, Collections.emptySet(), Collections.singleton( "Practitioner" ) ), - MEASURE_REPORT( FhirVersion.ALL, "MeasureReport", 30, Collections.singleton( "MeasureReport" ), Collections.singleton( "MeasureReport" ) ), - PLAN_DEFINITION( FhirVersion.R4_ONLY, "PlanDefinition", 30, Collections.emptySet(), Collections.singleton( "PlanDefinition" ) ), - QUESTIONNAIRE( FhirVersion.R4_ONLY, "Questionnaire", 31, Collections.emptySet(), Collections.singleton( "Questionnaire" ) ), - CARE_PLAN( FhirVersion.R4_ONLY, "CarePlan", 35, Collections.emptySet(), Collections.singleton( "CarePlan" ) ), - QUESTIONNAIRE_RESPONSE( FhirVersion.R4_ONLY, "QuestionnaireResponse", 40, Collections.emptySet(), Collections.singleton( "QuestionnaireResponse" ) ); + CONDITION( FhirVersion.ALL, "Condition", false, 19, Collections.emptySet(), Collections.singleton( "Condition" ) ), + DIAGNOSTIC_REPORT( FhirVersion.ALL, "DiagnosticReport", false, 30, Collections.emptySet(), Collections.singleton( "DiagnosticReport" ) ), + ENCOUNTER( FhirVersion.ALL, "Encounter", false, 4, Collections.emptySet(), Collections.singleton( "Encounter" ) ), + IMMUNIZATION( FhirVersion.ALL, "Immunization", false, 22, Collections.emptySet(), Collections.singleton( "Immunization" ) ), + LOCATION( FhirVersion.ALL, "Location", false, 2, Collections.emptySet(), Collections.singleton( "Location" ) ), + MEDICATION_REQUEST( FhirVersion.ALL, "MedicationRequest", false, 21, Collections.emptySet(), Collections.singleton( "MedicationRequest" ) ), + OBSERVATION( FhirVersion.ALL, "Observation", false, 20, Collections.emptySet(), Collections.singleton( "Observation" ) ), + ORGANIZATION( FhirVersion.ALL, "Organization", false, 1, Collections.emptySet(), Collections.singleton( "Organization" ) ), + PATIENT( FhirVersion.ALL, "Patient", false, 10, Collections.emptySet(), Collections.singleton( "Patient" ) ), + RELATED_PERSON( FhirVersion.ALL, "RelatedPerson", false, 11, Collections.emptySet(), Collections.singleton( "RelatedPerson" ) ), + PRACTITIONER( FhirVersion.ALL, "Practitioner", false, 9, Collections.emptySet(), Collections.singleton( "Practitioner" ) ), + MEASURE_REPORT( FhirVersion.ALL, "MeasureReport", false, 30, Collections.singleton( "MeasureReport" ), Collections.singleton( "MeasureReport" ) ), + PLAN_DEFINITION( FhirVersion.R4_ONLY, "PlanDefinition", true, 30, Collections.emptySet(), Collections.singleton( "PlanDefinition" ) ), + QUESTIONNAIRE( FhirVersion.R4_ONLY, "Questionnaire", true, 31, Collections.emptySet(), Collections.singleton( "Questionnaire" ) ), + CARE_PLAN( FhirVersion.R4_ONLY, "CarePlan", false, 35, Collections.emptySet(), Collections.singleton( "CarePlan" ) ), + QUESTIONNAIRE_RESPONSE( FhirVersion.R4_ONLY, "QuestionnaireResponse", false, 40, Collections.emptySet(), Collections.singleton( "QuestionnaireResponse" ) ); private static final Map resourcesBySimpleClassName = Arrays.stream( values() ).flatMap( v -> v.getSimpleClassNames().stream().map( scn -> new SimpleEntry<>( scn, v ) ) ) .collect( Collectors.toMap( SimpleEntry::getKey, SimpleEntry::getValue ) ); @@ -85,17 +85,21 @@ public static FhirResourceType getByResource( @Nullable IBaseResource resource ) { return null; } + FhirResourceType frt; Class c = resource.getClass(); + do { frt = resourcesBySimpleClassName.get( c.getSimpleName() ); + if ( frt == null ) { c = c.getSuperclass(); } } while ( (frt == null) && (c != null) && (c != Object.class) ); + return frt; } @@ -115,6 +119,8 @@ public static FhirResourceType getByResourceTypeName( @Nullable String resourceT private final String resourceTypeName; + private final boolean syncDhisId; + private final int order; private final Set transactionalWith; @@ -123,10 +129,11 @@ public static FhirResourceType getByResourceTypeName( @Nullable String resourceT private volatile Set transactionalWithTypes; - FhirResourceType( Set fhirVersions, String resourceTypeName, int order, Collection transactionalWith, Collection simpleClassNames ) + FhirResourceType( Set fhirVersions, String resourceTypeName, boolean syncDhisId, int order, Collection transactionalWith, Collection simpleClassNames ) { this.fhirVersions = fhirVersions; this.resourceTypeName = resourceTypeName; + this.syncDhisId = syncDhisId; this.order = order; this.transactionalWith = new HashSet<>( transactionalWith ); this.simpleClassNames = Collections.unmodifiableSet( new HashSet<>( simpleClassNames ) ); @@ -144,6 +151,11 @@ public String getResourceTypeName() return resourceTypeName; } + public boolean isSyncDhisId() + { + return syncDhisId; + } + public int getOrder() { return order; diff --git a/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/repository/FhirResourceRepository.java b/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/repository/FhirResourceRepository.java index 48cfc16d..8ca0374a 100644 --- a/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/repository/FhirResourceRepository.java +++ b/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/repository/FhirResourceRepository.java @@ -75,7 +75,7 @@ public interface FhirResourceRepository IBaseResource transform( @Nonnull UUID fhirClientId, @Nonnull FhirVersion fhirVersion, @Nullable IBaseResource resource ); @Nonnull - IBaseResource save( @Nonnull FhirClient fhirClient, @Nonnull IBaseResource resource ); + IBaseResource save( @Nonnull FhirClient fhirClient, @Nonnull IBaseResource resource, @Nullable String dhisResourceId ); boolean delete( @Nonnull FhirClient fhirClient, @Nonnull IBaseResource resource ); } diff --git a/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/repository/impl/DhisRepositoryImpl.java b/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/repository/impl/DhisRepositoryImpl.java index a613d7a2..b2c17a31 100644 --- a/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/repository/impl/DhisRepositoryImpl.java +++ b/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/repository/impl/DhisRepositoryImpl.java @@ -451,7 +451,8 @@ protected boolean saveInternally( @Nonnull DhisResource resource ) } else { - final IBaseResource resultingResource = fhirResourceRepository.save( transformerRequest.getFhirClient(), outcome.getResource() ); + final IBaseResource resultingResource = fhirResourceRepository + .save( transformerRequest.getFhirClient(), outcome.getResource(), resource.getId() ); // resource may have been set as attribute in transformer context (e.g. shared encounter) outcome.getResource().setId( resultingResource.getIdElement() ); fhirDhisAssignmentRepository.saveFhirResourceId( outcome.getRule(), transformerRequest.getFhirClient(), @@ -482,6 +483,7 @@ public Optional read( @Nonnull FhirClient fhirClient, @Nonnull Fh if ( dhisResource == null ) { logger.debug( "DHIS resource could not be found." ); + return Optional.empty(); } @@ -493,6 +495,7 @@ public Optional read( @Nonnull FhirClient fhirClient, @Nonnull Fh if ( transformerRequest == null ) { logger.debug( "No matching rule has been found." ); + return Optional.empty(); } diff --git a/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/repository/impl/FhirResourceRepositoryImpl.java b/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/repository/impl/FhirResourceRepositoryImpl.java index 086f7b9b..fbbb7f7c 100644 --- a/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/repository/impl/FhirResourceRepositoryImpl.java +++ b/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/repository/impl/FhirResourceRepositoryImpl.java @@ -276,7 +276,7 @@ public boolean delete( @Nonnull FhirClient fhirClient, @Nonnull IBaseResource re "T(org.dhis2.fhir.adapter.fhir.metadata.model.FhirResourceType).getByResource(#resource).getResourceTypeName(), #resource.getIdElement().getIdPart(), true}" ) @Nonnull @Override - public IBaseResource save( @Nonnull FhirClient fhirClient, @Nonnull IBaseResource resource ) + public IBaseResource save( @Nonnull FhirClient fhirClient, @Nonnull IBaseResource resource, @Nullable String dhisResourceId ) { if ( !fhirClient.getFhirEndpoint().isUseRemote() ) { @@ -287,25 +287,28 @@ public IBaseResource save( @Nonnull FhirClient fhirClient, @Nonnull IBaseResourc final FhirContext fhirContext = fhirContexts.get( fhirClient.getFhirVersion() ); final IGenericClient client = FhirClientUtils.createClient( fhirContext, fhirClient.getFhirEndpoint() ); + final IBaseResource preparedResource = prepareResource( resource, dhisResourceId ); final MethodOutcome methodOutcome; + if ( resource.getIdElement().hasIdPart() ) { try { - methodOutcome = client.update().resource( resource ).prefer( PreferReturnEnum.REPRESENTATION ).execute(); + methodOutcome = client.update().resource( preparedResource ).prefer( PreferReturnEnum.REPRESENTATION ).execute(); } catch ( PreconditionFailedException e ) { throw new OptimisticFhirResourceLockException( "Could not update FHIR resource " + - resource.getIdElement() + " because of an optimistic locking failure.", e ); + preparedResource.getIdElement() + " because of an optimistic locking failure.", e ); } } else { - methodOutcome = client.create().resource( resource ).prefer( PreferReturnEnum.REPRESENTATION ).execute(); + methodOutcome = client.create().resource( preparedResource ).prefer( PreferReturnEnum.REPRESENTATION ).execute(); } ProcessedItemInfo processedItemInfo = null; + if ( (methodOutcome.getResource() != null) && (methodOutcome.getResource().getMeta() != null) ) { // resource itself may contain old version ID (even if it should not) @@ -313,7 +316,7 @@ public IBaseResource save( @Nonnull FhirClient fhirClient, @Nonnull IBaseResourc } else if ( (methodOutcome.getId() != null) && methodOutcome.getId().hasVersionIdPart() ) { - processedItemInfo = ProcessedFhirItemInfoUtils.create( resource, methodOutcome.getId() ); + processedItemInfo = ProcessedFhirItemInfoUtils.create( preparedResource, methodOutcome.getId() ); } if ( processedItemInfo == null ) @@ -327,9 +330,11 @@ else if ( (methodOutcome.getId() != null) && methodOutcome.getId().hasVersionIdP } final IBaseResource result; + if ( methodOutcome.getResource() == null ) { result = resource; + if ( methodOutcome.getId() != null ) { result.setId( methodOutcome.getId() ); @@ -343,6 +348,24 @@ else if ( (methodOutcome.getId() != null) && methodOutcome.getId().hasVersionIdP return result; } + @Nonnull + protected T prepareResource( @Nonnull T resource, @Nullable String dhisResourceId ) + { + final FhirResourceType fhirResourceType = FhirResourceType.getByResource( resource ); + + if ( fhirResourceType == null ) + { + throw new FhirResourceTransformationException( "Could not determine FHIR resource type for " + resource.getClass().getSimpleName() ); + } + + if ( fhirResourceType.isSyncDhisId() && dhisResourceId != null && !resource.getIdElement().hasIdPart() ) + { + resource.setId( dhisResourceId ); + } + + return resource; + } + @TransactionalEventListener( phase = TransactionPhase.BEFORE_COMMIT, classes = AutoCreatedFhirClientResourceEvent.class ) public void autoCreatedSubscriptionResource( @Nonnull AutoCreatedFhirClientResourceEvent event ) { diff --git a/fhir/src/test/java/org/dhis2/fhir/adapter/fhir/repository/impl/FhirResourceRepositoryImplTest.java b/fhir/src/test/java/org/dhis2/fhir/adapter/fhir/repository/impl/FhirResourceRepositoryImplTest.java new file mode 100644 index 00000000..6fcd4ef9 --- /dev/null +++ b/fhir/src/test/java/org/dhis2/fhir/adapter/fhir/repository/impl/FhirResourceRepositoryImplTest.java @@ -0,0 +1,207 @@ +package org.dhis2.fhir.adapter.fhir.repository.impl; + +/* + * Copyright (c) 2004-2019, University of Oslo + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * Neither the name of the HISP project nor the names of its contributors may + * be used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +import ca.uhn.fhir.context.FhirContext; +import ca.uhn.fhir.context.FhirVersionEnum; +import ca.uhn.fhir.model.api.IFhirVersion; +import ca.uhn.fhir.model.primitive.IdDt; +import org.dhis2.fhir.adapter.fhir.client.StoredFhirResourceService; +import org.dhis2.fhir.adapter.fhir.metadata.repository.FhirClientResourceRepository; +import org.dhis2.fhir.adapter.fhir.script.ScriptExecutor; +import org.dhis2.fhir.adapter.spring.StaticObjectProvider; +import org.hl7.fhir.instance.model.api.IBaseMetaType; +import org.hl7.fhir.instance.model.api.IBaseResource; +import org.hl7.fhir.instance.model.api.IIdType; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.mockito.Mock; +import org.mockito.Mockito; +import org.mockito.junit.MockitoJUnit; +import org.mockito.junit.MockitoRule; + +import java.util.Arrays; +import java.util.Collections; +import java.util.List; + +/** + * Implementation of {@link FhirResourceRepositoryImpl}. + * + * @author volsch + */ +public class FhirResourceRepositoryImplTest +{ + @Mock + private ScriptExecutor scriptExecutor; + + @Mock + private StoredFhirResourceService storedItemService; + + @Mock + private FhirClientResourceRepository fhirClientResourceRepository; + + @Mock + private FhirContext fhirContextDstu3; + + @Mock + private IFhirVersion fhirVersionDstu3; + + @Mock + private FhirContext fhirContextR4; + + @Mock + private IFhirVersion fhirVersionR4; + + private FhirResourceRepositoryImpl repository; + + @Rule + public MockitoRule mockitoRule = MockitoJUnit.rule(); + + @Before + public void setUp() + { + Mockito.when( fhirContextDstu3.getVersion() ).thenReturn( fhirVersionDstu3 ); + Mockito.when( fhirVersionDstu3.getVersion() ).thenReturn( FhirVersionEnum.DSTU3 ); + Mockito.when( fhirContextR4.getVersion() ).thenReturn( fhirVersionR4 ); + Mockito.when( fhirVersionR4.getVersion() ).thenReturn( FhirVersionEnum.R4 ); + + repository = new FhirResourceRepositoryImpl( scriptExecutor, storedItemService, fhirClientResourceRepository, + new StaticObjectProvider<>( Arrays.asList( fhirContextDstu3, fhirContextR4 ) ), new StaticObjectProvider<>( Collections.emptyList() ) ); + } + + @Test + public void prepareResourceWithoutAnyId() + { + Patient patient = new Patient(); + patient = repository.prepareResource( patient, null ); + Assert.assertTrue( patient.getIdElement().isEmpty() ); + } + + @Test + public void prepareResourceNonMatchingWithDhisId() + { + Patient patient = new Patient(); + patient = repository.prepareResource( patient, "a0123456789" ); + Assert.assertTrue( patient.getIdElement().isEmpty() ); + } + + @Test + public void prepareResourceWithDhisId() + { + PlanDefinition planDefinition = new PlanDefinition(); + planDefinition = repository.prepareResource( planDefinition, "a0123456789" ); + Assert.assertEquals( "a0123456789", planDefinition.getIdElement().getIdPart() ); + } + + @Test + public void prepareResourceWithAllIds() + { + PlanDefinition planDefinition = new PlanDefinition(); + planDefinition.setId( "d0123456789" ); + planDefinition = repository.prepareResource( planDefinition, "a0123456789" ); + Assert.assertEquals( "d0123456789", planDefinition.getIdElement().getIdPart() ); + } + + public static class Patient extends AbstractBaseResource + { + private static final long serialVersionUID = -1428885428508171576L; + } + + public static class PlanDefinition extends AbstractBaseResource + { + private static final long serialVersionUID = -1428885428508171576L; + } + + public static abstract class AbstractBaseResource implements IBaseResource + { + private static final long serialVersionUID = 7566894473303706042L; + + private IIdType id = new IdDt(); + + @Override + public IBaseMetaType getMeta() + { + return null; + } + + @Override + public IIdType getIdElement() + { + return id; + } + + @Override + public IBaseResource setId( String theId ) + { + id = new IdDt( theId ); + + return this; + } + + @Override + public IBaseResource setId( IIdType theId ) + { + this.id = theId; + + return this; + } + + @Override + public FhirVersionEnum getStructureFhirVersionEnum() + { + return FhirVersionEnum.R4; + } + + @Override + public boolean isEmpty() + { + return false; + } + + @Override + public boolean hasFormatComment() + { + return false; + } + + @Override + public List getFormatCommentsPre() + { + return null; + } + + @Override + public List getFormatCommentsPost() + { + return null; + } + } +} \ No newline at end of file From 46299cede66daae51d6e3df38f6fc2c40e55c716 Mon Sep 17 00:00:00 2001 From: Volker Schmidt Date: Wed, 31 Jul 2019 14:31:05 +0200 Subject: [PATCH 15/16] Adds support for tracked entity resource type in Plan Definitions. --- .../r4/R4ProgramMetadataFhirRestAppTest.java | 3 + .../LocationExtension.StructureDefinition.xml | 17 +-- ...ourceTypeExtension.StructureDefinition.xml | 57 ++++++++++ ...gramPlanDefinition.StructureDefinition.xml | 21 ++++ ...tadataToFhirPlanDefinitionTransformer.java | 12 +- ...taToFhirPlanDefinitionTransformerTest.java | 14 ++- .../extension/ResourceTypeExtensionUtils.java | 68 +++++++++++ ...tadataToFhirPlanDefinitionTransformer.java | 33 +++++- .../ResourceTypeExtensionUtilsTest.java | 107 ++++++++++++++++++ ...taToFhirPlanDefinitionTransformerTest.java | 88 ++++++++++---- 10 files changed, 372 insertions(+), 48 deletions(-) create mode 100644 docs/fhir/profiles/ResourceTypeExtension.StructureDefinition.xml create mode 100644 fhir/src/main/java/org/dhis2/fhir/adapter/fhir/extension/ResourceTypeExtensionUtils.java create mode 100644 fhir/src/test/java/org/dhis2/fhir/adapter/fhir/extension/ResourceTypeExtensionUtilsTest.java diff --git a/app/src/test/java/org/dhis2/fhir/adapter/fhir/server/r4/R4ProgramMetadataFhirRestAppTest.java b/app/src/test/java/org/dhis2/fhir/adapter/fhir/server/r4/R4ProgramMetadataFhirRestAppTest.java index e86e33b7..e828ae5b 100644 --- a/app/src/test/java/org/dhis2/fhir/adapter/fhir/server/r4/R4ProgramMetadataFhirRestAppTest.java +++ b/app/src/test/java/org/dhis2/fhir/adapter/fhir/server/r4/R4ProgramMetadataFhirRestAppTest.java @@ -34,6 +34,7 @@ import ca.uhn.fhir.rest.server.exceptions.ResourceNotFoundException; import org.apache.commons.io.IOUtils; import org.dhis2.fhir.adapter.AbstractAppTest; +import org.dhis2.fhir.adapter.fhir.extension.ResourceTypeExtensionUtils; import org.dhis2.fhir.adapter.fhir.model.FhirVersion; import org.hl7.fhir.r4.model.Bundle; import org.hl7.fhir.r4.model.PlanDefinition; @@ -124,6 +125,8 @@ private void getPlanDefinition() throws Exception client.registerInterceptor( new BasicAuthInterceptor( "fhir_client", "fhir_client_1" ) ); PlanDefinition planDefinition = client.read().resource( PlanDefinition.class ).withId( "EPDyQuoRnXk" ).execute(); Assert.assertEquals( "Child Programme", planDefinition.getTitle() ); + Assert.assertNotNull( planDefinition.getExtensionByUrl( ResourceTypeExtensionUtils.URL ) ); + Assert.assertEquals( "Patient", planDefinition.getExtensionByUrl( ResourceTypeExtensionUtils.URL ).getValue().primitiveValue() ); systemDhis2Server.verify(); userDhis2Server.verify(); diff --git a/docs/fhir/profiles/LocationExtension.StructureDefinition.xml b/docs/fhir/profiles/LocationExtension.StructureDefinition.xml index 4211ca46..51891d76 100644 --- a/docs/fhir/profiles/LocationExtension.StructureDefinition.xml +++ b/docs/fhir/profiles/LocationExtension.StructureDefinition.xml @@ -28,7 +28,7 @@ --> - + @@ -47,21 +47,6 @@ - - - - - - - - - - - - - - - diff --git a/docs/fhir/profiles/ResourceTypeExtension.StructureDefinition.xml b/docs/fhir/profiles/ResourceTypeExtension.StructureDefinition.xml new file mode 100644 index 00000000..76f90869 --- /dev/null +++ b/docs/fhir/profiles/ResourceTypeExtension.StructureDefinition.xml @@ -0,0 +1,57 @@ + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/docs/fhir/profiles/TrackerProgramPlanDefinition.StructureDefinition.xml b/docs/fhir/profiles/TrackerProgramPlanDefinition.StructureDefinition.xml index bf33d0bb..25e9ae28 100644 --- a/docs/fhir/profiles/TrackerProgramPlanDefinition.StructureDefinition.xml +++ b/docs/fhir/profiles/TrackerProgramPlanDefinition.StructureDefinition.xml @@ -50,6 +50,27 @@ + + + + + + + + + + + + + + + + + + + + + diff --git a/fhir-r4/src/main/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/metadata/r4/R4ProgramMetadataToFhirPlanDefinitionTransformer.java b/fhir-r4/src/main/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/metadata/r4/R4ProgramMetadataToFhirPlanDefinitionTransformer.java index 44333c79..90f5a217 100644 --- a/fhir-r4/src/main/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/metadata/r4/R4ProgramMetadataToFhirPlanDefinitionTransformer.java +++ b/fhir-r4/src/main/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/metadata/r4/R4ProgramMetadataToFhirPlanDefinitionTransformer.java @@ -28,6 +28,7 @@ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ +import ca.uhn.fhir.model.api.IElement; import org.dhis2.fhir.adapter.dhis.orgunit.OrganizationUnitService; import org.dhis2.fhir.adapter.dhis.tracker.program.Program; import org.dhis2.fhir.adapter.dhis.tracker.trackedentity.TrackedEntityMetadataService; @@ -50,11 +51,13 @@ import org.hl7.fhir.r4.model.Enumerations; import org.hl7.fhir.r4.model.PlanDefinition; import org.hl7.fhir.r4.model.PlanDefinition.PlanDefinitionActionComponent; +import org.hl7.fhir.r4.model.ResourceFactory; import org.springframework.stereotype.Component; import javax.annotation.Nonnull; import java.util.Map; import java.util.Set; +import java.util.function.Function; /** * R4 specific version of DHIS2 Program Metadata to FHIR Plan Definition transformer. @@ -86,7 +89,7 @@ protected boolean transformInternal( @Nonnull FhirClient fhirClient, @Nonnull Dh final Program dhisProgram = (Program) input.getDhisResource(); final PlanDefinition fhirPlanDefinition = (PlanDefinition) output; - if ( !isApplicableProgram( dhisProgram ) ) + if ( !addSubjectResourceType( dhisProgram, fhirPlanDefinition ) ) { return false; } @@ -111,4 +114,11 @@ protected boolean transformInternal( @Nonnull FhirClient fhirClient, @Nonnull Dh return true; } + + @Nonnull + @Override + protected Function getTypeFactory() + { + return ResourceFactory::createType; + } } diff --git a/fhir-r4/src/test/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/metadata/r4/R4ProgramMetadataToFhirPlanDefinitionTransformerTest.java b/fhir-r4/src/test/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/metadata/r4/R4ProgramMetadataToFhirPlanDefinitionTransformerTest.java index adccd703..a795ad71 100644 --- a/fhir-r4/src/test/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/metadata/r4/R4ProgramMetadataToFhirPlanDefinitionTransformerTest.java +++ b/fhir-r4/src/test/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/metadata/r4/R4ProgramMetadataToFhirPlanDefinitionTransformerTest.java @@ -36,9 +36,12 @@ import org.dhis2.fhir.adapter.dhis.tracker.trackedentity.TrackedEntityMetadataService; import org.dhis2.fhir.adapter.dhis.tracker.trackedentity.WritableTrackedEntityType; import org.dhis2.fhir.adapter.fhir.data.repository.FhirDhisAssignmentRepository; +import org.dhis2.fhir.adapter.fhir.extension.ResourceTypeExtensionUtils; import org.dhis2.fhir.adapter.fhir.metadata.model.FhirClient; +import org.dhis2.fhir.adapter.fhir.metadata.model.FhirResourceType; import org.dhis2.fhir.adapter.fhir.metadata.model.ProgramMetadataRule; import org.dhis2.fhir.adapter.fhir.metadata.model.RuleInfo; +import org.dhis2.fhir.adapter.fhir.metadata.model.TrackedEntityRule; import org.dhis2.fhir.adapter.fhir.metadata.repository.SystemRepository; import org.dhis2.fhir.adapter.fhir.metadata.repository.TrackedEntityRuleRepository; import org.dhis2.fhir.adapter.fhir.model.FhirVersion; @@ -120,10 +123,13 @@ public void getFhirVersions() @Test public void transformInternal() { + final TrackedEntityRule rule2 = new TrackedEntityRule(); + rule2.setEvaluationOrder( 100 ); + rule2.setFhirResourceType( FhirResourceType.PATIENT ); + Mockito.doReturn( Optional.of( new WritableTrackedEntityType( "a1234567890", "Test", Collections.emptyList() ) ) ) .when( trackedEntityMetadataService ).findTypeByReference( Mockito.eq( new Reference( "a1234567890", ReferenceType.ID ) ) ); - Mockito.doReturn( Collections.singletonList( new RuleInfo<>( new ProgramMetadataRule(), Collections.emptyList() ) ) ) - .when( trackedEntityRuleRepository ).findByTypeRefs( Mockito.eq( new HashSet<>( + Mockito.doReturn( Collections.singletonList( rule2 ) ).when( trackedEntityRuleRepository ).findByTypeRefs( Mockito.eq( new HashSet<>( Arrays.asList( new Reference( "a1234567890", ReferenceType.ID ), new Reference( "Test", ReferenceType.NAME ) ) ) ) ); final WritableProgram program = new WritableProgram(); @@ -156,6 +162,10 @@ public void transformInternal() Assert.assertEquals( "Test Description", fhirPlanDefinition.getDescription() ); Assert.assertEquals( 2, fhirPlanDefinition.getAction().size() ); + Assert.assertEquals( 1, fhirPlanDefinition.getExtension().size() ); + Assert.assertEquals( ResourceTypeExtensionUtils.URL, fhirPlanDefinition.getExtension().get( 0 ).getUrl() ); + Assert.assertEquals( "Patient", fhirPlanDefinition.getExtension().get( 0 ).getValue().toString() ); + Assert.assertEquals( "b1234567890", fhirPlanDefinition.getAction().get( 0 ).getId() ); Assert.assertEquals( "Test Stage 1", fhirPlanDefinition.getAction().get( 0 ).getTitle() ); Assert.assertEquals( "Test Description 1", fhirPlanDefinition.getAction().get( 0 ).getDescription() ); diff --git a/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/extension/ResourceTypeExtensionUtils.java b/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/extension/ResourceTypeExtensionUtils.java new file mode 100644 index 00000000..595cc28f --- /dev/null +++ b/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/extension/ResourceTypeExtensionUtils.java @@ -0,0 +1,68 @@ +package org.dhis2.fhir.adapter.fhir.extension; + +/* + * Copyright (c) 2004-2019, University of Oslo + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * Neither the name of the HISP project nor the names of its contributors may + * be used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +import ca.uhn.fhir.model.api.IElement; +import org.dhis2.fhir.adapter.fhir.metadata.model.FhirResourceType; +import org.hl7.fhir.instance.model.api.IBaseExtension; +import org.hl7.fhir.instance.model.api.IBaseHasExtensions; +import org.hl7.fhir.instance.model.api.IPrimitiveType; + +import javax.annotation.Nonnull; +import javax.annotation.Nullable; +import java.util.function.Function; + +/** + * Utility class to process FHIR resource type extension. + * + * @author volsch + */ +public abstract class ResourceTypeExtensionUtils +{ + public static final String URL = "http://www.dhis2.org/dhis2-fhir-adapter/fhir/extensions/resource-type"; + + @SuppressWarnings( "unchecked" ) + public static void setValue( @Nonnull IBaseHasExtensions resource, @Nullable FhirResourceType fhirResourceType, @Nonnull Function typeFactory ) + { + resource.getExtension().removeIf( e -> URL.equals( e.getUrl() ) ); + + if ( fhirResourceType != null ) + { + final IBaseExtension extension = resource.addExtension(); + + extension.setUrl( URL ); + extension.setValue( ( (IPrimitiveType) typeFactory.apply( "string" ) ).setValue( fhirResourceType.getResourceTypeName() ) ); + } + } + + private ResourceTypeExtensionUtils() + { + super(); + } +} diff --git a/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/metadata/program/AbstractProgramMetadataToFhirPlanDefinitionTransformer.java b/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/metadata/program/AbstractProgramMetadataToFhirPlanDefinitionTransformer.java index bb7aaefe..a2b48af7 100644 --- a/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/metadata/program/AbstractProgramMetadataToFhirPlanDefinitionTransformer.java +++ b/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/metadata/program/AbstractProgramMetadataToFhirPlanDefinitionTransformer.java @@ -28,6 +28,7 @@ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ +import ca.uhn.fhir.model.api.IElement; import org.dhis2.fhir.adapter.dhis.model.DhisResourceType; import org.dhis2.fhir.adapter.dhis.model.Reference; import org.dhis2.fhir.adapter.dhis.model.ReferenceType; @@ -36,6 +37,8 @@ import org.dhis2.fhir.adapter.dhis.tracker.trackedentity.TrackedEntityMetadataService; import org.dhis2.fhir.adapter.dhis.tracker.trackedentity.TrackedEntityType; import org.dhis2.fhir.adapter.fhir.data.repository.FhirDhisAssignmentRepository; +import org.dhis2.fhir.adapter.fhir.extension.ResourceTypeExtensionUtils; +import org.dhis2.fhir.adapter.fhir.metadata.model.AbstractRule; import org.dhis2.fhir.adapter.fhir.metadata.model.FhirResourceType; import org.dhis2.fhir.adapter.fhir.metadata.model.ProgramMetadataRule; import org.dhis2.fhir.adapter.fhir.metadata.repository.SystemRepository; @@ -46,11 +49,13 @@ import org.dhis2.fhir.adapter.fhir.transform.dhis.impl.metadata.AbstractReadOnlyDhisMetadataToTypedFhirTransformer; import org.dhis2.fhir.adapter.fhir.transform.scripted.AccessibleScriptedDhisMetadata; import org.dhis2.fhir.adapter.lock.LockManager; +import org.hl7.fhir.instance.model.api.IBaseHasExtensions; import org.hl7.fhir.instance.model.api.IBaseResource; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import javax.annotation.Nonnull; +import java.util.function.Function; /** * Implementation of {@link DhisToFhirTransformer} for transforming DHIS2 @@ -63,6 +68,8 @@ public abstract class AbstractProgramMetadataToFhirPlanDefinitionTransformer getRuleClass() return ProgramMetadataRule.class; } - protected boolean isApplicableProgram( @Nonnull Program program ) + protected boolean addSubjectResourceType( @Nonnull Program program, @Nonnull IBaseHasExtensions resource ) { - if ( program.isWithoutRegistration() || program.getTrackedEntityTypeId() == null ) + if ( program.getTrackedEntityTypeId() == null ) { - return false; + ResourceTypeExtensionUtils.setValue( resource, null, getTypeFactory() ); + + return true; } - final TrackedEntityType trackedEntityType = trackedEntityMetadataService.findTypeByReference( new Reference( program.getTrackedEntityTypeId(), ReferenceType.ID ) ).orElse( null ); + final TrackedEntityType trackedEntityType = trackedEntityMetadataService.findTypeByReference( + new Reference( program.getTrackedEntityTypeId(), ReferenceType.ID ) ).orElse( null ); if ( trackedEntityType == null ) { return false; } - return !trackedEntityRuleRepository.findByTypeRefs( trackedEntityType.getAllReferences() ).isEmpty(); + final FhirResourceType fhirResourceType = trackedEntityRuleRepository.findByTypeRefs( trackedEntityType.getAllReferences() ).stream() + .sorted( ( o1, o2 ) -> o2.getEvaluationOrder() - o1.getEvaluationOrder() ).map( AbstractRule::getFhirResourceType ).findFirst().orElse( null ); + + if ( fhirResourceType == null ) + { + return false; + } + + ResourceTypeExtensionUtils.setValue( resource, fhirResourceType, getTypeFactory() ); + + return true; } + + @Nonnull + protected abstract Function getTypeFactory(); } diff --git a/fhir/src/test/java/org/dhis2/fhir/adapter/fhir/extension/ResourceTypeExtensionUtilsTest.java b/fhir/src/test/java/org/dhis2/fhir/adapter/fhir/extension/ResourceTypeExtensionUtilsTest.java new file mode 100644 index 00000000..d245ec1d --- /dev/null +++ b/fhir/src/test/java/org/dhis2/fhir/adapter/fhir/extension/ResourceTypeExtensionUtilsTest.java @@ -0,0 +1,107 @@ +package org.dhis2.fhir.adapter.fhir.extension; + +/* + * Copyright (c) 2004-2019, University of Oslo + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * Neither the name of the HISP project nor the names of its contributors may + * be used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +import ca.uhn.fhir.model.api.ExtensionDt; +import ca.uhn.fhir.model.api.IElement; +import ca.uhn.fhir.model.primitive.StringDt; +import org.dhis2.fhir.adapter.fhir.metadata.model.FhirResourceType; +import org.hl7.fhir.instance.model.api.IBaseExtension; +import org.hl7.fhir.instance.model.api.IBaseHasExtensions; +import org.junit.Assert; +import org.junit.Test; + +import java.util.ArrayList; +import java.util.List; + +/** + * Unit tests for {@link ResourceTypeExtensionUtils}. + * + * @author volsch + */ +public class ResourceTypeExtensionUtilsTest +{ + @Test + public void resetValue() + { + PlanDefinition planDefinition = new PlanDefinition(); + + ResourceTypeExtensionUtils.setValue( planDefinition, FhirResourceType.PATIENT, TypeFactory::createType ); + ResourceTypeExtensionUtils.setValue( planDefinition, null, TypeFactory::createType ); + + Assert.assertTrue( planDefinition.getExtension().isEmpty() ); + } + + @Test + public void setValue() + { + PlanDefinition planDefinition = new PlanDefinition(); + + ResourceTypeExtensionUtils.setValue( planDefinition, FhirResourceType.PATIENT, TypeFactory::createType ); + + Assert.assertEquals( 1, planDefinition.getExtension().size() ); + Assert.assertEquals( ResourceTypeExtensionUtils.URL, planDefinition.getExtension().get( 0 ).getUrl() ); + Assert.assertEquals( "Patient", planDefinition.getExtension().get( 0 ).getValue().toString() ); + } + + public static class PlanDefinition implements IBaseHasExtensions + { + private final List> extensions = new ArrayList<>(); + + @Override + public IBaseExtension addExtension() + { + extensions.add( new ExtensionDt() ); + + return extensions.get( extensions.size() - 1 ); + } + + @Override + public List> getExtension() + { + return extensions; + } + + @Override + public boolean hasExtension() + { + return !extensions.isEmpty(); + } + } + + public static class TypeFactory + { + public static IElement createType( String name ) + { + Assert.assertEquals( "string", name ); + + return new StringDt(); + } + } +} \ No newline at end of file diff --git a/fhir/src/test/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/metadata/program/AbstractProgramMetadataToFhirPlanDefinitionTransformerTest.java b/fhir/src/test/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/metadata/program/AbstractProgramMetadataToFhirPlanDefinitionTransformerTest.java index 8ee2c709..5c473888 100644 --- a/fhir/src/test/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/metadata/program/AbstractProgramMetadataToFhirPlanDefinitionTransformerTest.java +++ b/fhir/src/test/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/metadata/program/AbstractProgramMetadataToFhirPlanDefinitionTransformerTest.java @@ -28,6 +28,8 @@ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ +import ca.uhn.fhir.model.api.ExtensionDt; +import ca.uhn.fhir.model.primitive.StringDt; import org.dhis2.fhir.adapter.dhis.model.DhisResourceType; import org.dhis2.fhir.adapter.dhis.model.Reference; import org.dhis2.fhir.adapter.dhis.model.ReferenceType; @@ -36,15 +38,17 @@ import org.dhis2.fhir.adapter.dhis.tracker.trackedentity.TrackedEntityMetadataService; import org.dhis2.fhir.adapter.dhis.tracker.trackedentity.WritableTrackedEntityType; import org.dhis2.fhir.adapter.fhir.data.repository.FhirDhisAssignmentRepository; +import org.dhis2.fhir.adapter.fhir.extension.ResourceTypeExtensionUtils; import org.dhis2.fhir.adapter.fhir.metadata.model.FhirResourceType; -import org.dhis2.fhir.adapter.fhir.metadata.model.ProgramMetadataRule; -import org.dhis2.fhir.adapter.fhir.metadata.model.RuleInfo; +import org.dhis2.fhir.adapter.fhir.metadata.model.TrackedEntityRule; import org.dhis2.fhir.adapter.fhir.metadata.repository.SystemRepository; import org.dhis2.fhir.adapter.fhir.metadata.repository.TrackedEntityRuleRepository; import org.dhis2.fhir.adapter.fhir.repository.FhirResourceRepository; import org.dhis2.fhir.adapter.fhir.script.ScriptExecutor; import org.dhis2.fhir.adapter.fhir.transform.scripted.AccessibleScriptedDhisMetadata; import org.dhis2.fhir.adapter.lock.LockManager; +import org.hl7.fhir.instance.model.api.IBaseExtension; +import org.hl7.fhir.instance.model.api.IBaseHasExtensions; import org.junit.Assert; import org.junit.Before; import org.junit.Rule; @@ -58,6 +62,7 @@ import java.util.Arrays; import java.util.Collections; import java.util.HashSet; +import java.util.List; import java.util.Optional; import static org.mockito.Mockito.CALLS_REAL_METHODS; @@ -97,7 +102,7 @@ public class AbstractProgramMetadataToFhirPlanDefinitionTransformerTest private AbstractProgramMetadataToFhirPlanDefinitionTransformer transformer; @Rule - public MockitoRule rule = MockitoJUnit.rule(); + public MockitoRule rule = MockitoJUnit.rule().silent(); @Before public void setUp() @@ -105,6 +110,8 @@ public void setUp() transformer = Mockito.mock( AbstractProgramMetadataToFhirPlanDefinitionTransformer.class, withSettings().useConstructor( scriptExecutor, lockManager, systemRepository, fhirResourceRepository, fhirDhisAssignmentRepository, organizationUnitService, trackedEntityMetadataService, trackedEntityRuleRepository ).defaultAnswer( CALLS_REAL_METHODS ) ); + + Mockito.when( transformer.getTypeFactory() ).thenReturn( name -> new StringDt() ); } @Test @@ -126,12 +133,19 @@ public void getDhisResourceClass() } @Test - public void isApplicableProgram() + public void addSubjectResourceType() { + final TrackedEntityRule rule1 = new TrackedEntityRule(); + rule1.setEvaluationOrder( 1 ); + rule1.setFhirResourceType( FhirResourceType.RELATED_PERSON ); + + final TrackedEntityRule rule2 = new TrackedEntityRule(); + rule2.setEvaluationOrder( 100 ); + rule2.setFhirResourceType( FhirResourceType.PATIENT ); + Mockito.doReturn( Optional.of( new WritableTrackedEntityType( "a1234567890", "Test", Collections.emptyList() ) ) ) .when( trackedEntityMetadataService ).findTypeByReference( Mockito.eq( new Reference( "a1234567890", ReferenceType.ID ) ) ); - Mockito.doReturn( Collections.singletonList( new RuleInfo<>( new ProgramMetadataRule(), Collections.emptyList() ) ) ) - .when( trackedEntityRuleRepository ).findByTypeRefs( Mockito.eq( new HashSet<>( + Mockito.doReturn( Arrays.asList( rule1, rule2 ) ).when( trackedEntityRuleRepository ).findByTypeRefs( Mockito.eq( new HashSet<>( Arrays.asList( new Reference( "a1234567890", ReferenceType.ID ), new Reference( "Test", ReferenceType.NAME ) ) ) ) ); final WritableProgram program = new WritableProgram(); @@ -141,24 +155,17 @@ public void isApplicableProgram() program.setTrackedEntityTypeId( "a1234567890" ); program.setStages( new ArrayList<>() ); - Assert.assertTrue( transformer.isApplicableProgram( program ) ); - } + final PlanDefinition planDefinition = new PlanDefinition(); - @Test - public void isApplicableProgramWithoutRegistration() - { - final WritableProgram program = new WritableProgram(); - program.setName( "Test Program" ); - program.setDescription( "Test Description" ); - program.setWithoutRegistration( true ); - program.setTrackedEntityTypeId( "a1234567890" ); - program.setStages( new ArrayList<>() ); + Assert.assertTrue( transformer.addSubjectResourceType( program, planDefinition ) ); - Assert.assertFalse( transformer.isApplicableProgram( program ) ); + Assert.assertEquals( 1, planDefinition.getExtension().size() ); + Assert.assertEquals( ResourceTypeExtensionUtils.URL, planDefinition.getExtension().get( 0 ).getUrl() ); + Assert.assertEquals( "Patient", planDefinition.getExtension().get( 0 ).getValue().toString() ); } @Test - public void isApplicableProgramWithoutTrackedEntityType() + public void addSubjectResourceTypeWithoutTrackedEntityType() { final WritableProgram program = new WritableProgram(); program.setName( "Test Program" ); @@ -166,11 +173,15 @@ public void isApplicableProgramWithoutTrackedEntityType() program.setWithoutRegistration( false ); program.setStages( new ArrayList<>() ); - Assert.assertFalse( transformer.isApplicableProgram( program ) ); + final PlanDefinition planDefinition = new PlanDefinition(); + + Assert.assertTrue( transformer.addSubjectResourceType( program, planDefinition ) ); + + Assert.assertEquals( 0, planDefinition.getExtension().size() ); } @Test - public void isApplicableProgramWithoutTrackedEntityMetadata() + public void addSubjectResourceTypeWithoutTrackedEntityMetadata() { Mockito.doReturn( Optional.empty() ) .when( trackedEntityMetadataService ).findTypeByReference( Mockito.eq( new Reference( "a1234567890", ReferenceType.ID ) ) ); @@ -182,11 +193,13 @@ public void isApplicableProgramWithoutTrackedEntityMetadata() program.setTrackedEntityTypeId( "a1234567890" ); program.setStages( new ArrayList<>() ); - Assert.assertFalse( transformer.isApplicableProgram( program ) ); + final PlanDefinition planDefinition = new PlanDefinition(); + + Assert.assertFalse( transformer.addSubjectResourceType( program, planDefinition ) ); } @Test - public void isApplicableProgramWithoutRules() + public void addSubjectResourceTypeWithoutRules() { Mockito.doReturn( Optional.of( new WritableTrackedEntityType( "a1234567890", "Test", Collections.emptyList() ) ) ) .when( trackedEntityMetadataService ).findTypeByReference( Mockito.eq( new Reference( "a1234567890", ReferenceType.ID ) ) ); @@ -201,6 +214,33 @@ public void isApplicableProgramWithoutRules() program.setTrackedEntityTypeId( "a1234567890" ); program.setStages( new ArrayList<>() ); - Assert.assertFalse( transformer.isApplicableProgram( program ) ); + final PlanDefinition planDefinition = new PlanDefinition(); + + Assert.assertFalse( transformer.addSubjectResourceType( program, planDefinition ) ); + } + + public static class PlanDefinition implements IBaseHasExtensions + { + private final List> extensions = new ArrayList<>(); + + @Override + public IBaseExtension addExtension() + { + extensions.add( new ExtensionDt() ); + + return extensions.get( extensions.size() - 1 ); + } + + @Override + public List> getExtension() + { + return extensions; + } + + @Override + public boolean hasExtension() + { + return !extensions.isEmpty(); + } } } \ No newline at end of file From 054de1fcbb2d94605343cd7dca7bf55be8405e7b Mon Sep 17 00:00:00 2001 From: Volker Schmidt Date: Wed, 31 Jul 2019 17:54:47 +0200 Subject: [PATCH 16/16] Adds value type extension for Questionnaire items. --- ...ourceTypeExtension.StructureDefinition.xml | 2 +- ...ogramQuestionnaire.StructureDefinition.xml | 20 ++++++ ...ValueTypeExtension.StructureDefinition.xml | 57 ++++++++++++++++ ...etadataToFhirQuestionnaireTransformer.java | 13 ++++ ...ataToFhirQuestionnaireTransformerTest.java | 12 ++++ .../fhir/extension/BaseExtensionUtils.java | 65 ++++++++++++++++++ .../extension/ResourceTypeExtensionUtils.java | 13 +--- .../extension/ValueTypeExtensionUtils.java | 56 ++++++++++++++++ ...etadataToFhirQuestionnaireTransformer.java | 5 ++ .../extension/BaseExtensionUtilsTest.java | 64 ++++++++++++++++++ .../ResourceTypeExtensionUtilsTest.java | 47 +------------ .../fhir/extension/TestPlanDefinition.java | 66 +++++++++++++++++++ .../adapter/fhir/extension/TypeFactory.java | 48 ++++++++++++++ .../ValueTypeExtensionUtilsTest.java | 53 +++++++++++++++ 14 files changed, 463 insertions(+), 58 deletions(-) create mode 100644 docs/fhir/profiles/ValueTypeExtension.StructureDefinition.xml create mode 100644 fhir/src/main/java/org/dhis2/fhir/adapter/fhir/extension/BaseExtensionUtils.java create mode 100644 fhir/src/main/java/org/dhis2/fhir/adapter/fhir/extension/ValueTypeExtensionUtils.java create mode 100644 fhir/src/test/java/org/dhis2/fhir/adapter/fhir/extension/BaseExtensionUtilsTest.java create mode 100644 fhir/src/test/java/org/dhis2/fhir/adapter/fhir/extension/TestPlanDefinition.java create mode 100644 fhir/src/test/java/org/dhis2/fhir/adapter/fhir/extension/TypeFactory.java create mode 100644 fhir/src/test/java/org/dhis2/fhir/adapter/fhir/extension/ValueTypeExtensionUtilsTest.java diff --git a/docs/fhir/profiles/ResourceTypeExtension.StructureDefinition.xml b/docs/fhir/profiles/ResourceTypeExtension.StructureDefinition.xml index 76f90869..fc42e483 100644 --- a/docs/fhir/profiles/ResourceTypeExtension.StructureDefinition.xml +++ b/docs/fhir/profiles/ResourceTypeExtension.StructureDefinition.xml @@ -36,7 +36,7 @@ - + diff --git a/docs/fhir/profiles/TrackerProgramQuestionnaire.StructureDefinition.xml b/docs/fhir/profiles/TrackerProgramQuestionnaire.StructureDefinition.xml index eeef7fc0..f7fa6224 100644 --- a/docs/fhir/profiles/TrackerProgramQuestionnaire.StructureDefinition.xml +++ b/docs/fhir/profiles/TrackerProgramQuestionnaire.StructureDefinition.xml @@ -164,6 +164,26 @@ + + + + + + + + + + + + + + + + + + + + diff --git a/docs/fhir/profiles/ValueTypeExtension.StructureDefinition.xml b/docs/fhir/profiles/ValueTypeExtension.StructureDefinition.xml new file mode 100644 index 00000000..5fae7e76 --- /dev/null +++ b/docs/fhir/profiles/ValueTypeExtension.StructureDefinition.xml @@ -0,0 +1,57 @@ + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/fhir-r4/src/main/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/metadata/r4/R4ProgramStageMetadataToFhirQuestionnaireTransformer.java b/fhir-r4/src/main/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/metadata/r4/R4ProgramStageMetadataToFhirQuestionnaireTransformer.java index 2575b8ee..8b113b97 100644 --- a/fhir-r4/src/main/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/metadata/r4/R4ProgramStageMetadataToFhirQuestionnaireTransformer.java +++ b/fhir-r4/src/main/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/metadata/r4/R4ProgramStageMetadataToFhirQuestionnaireTransformer.java @@ -28,9 +28,11 @@ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ +import ca.uhn.fhir.model.api.IElement; import org.dhis2.fhir.adapter.dhis.orgunit.OrganizationUnitService; import org.dhis2.fhir.adapter.dhis.tracker.program.ProgramStage; import org.dhis2.fhir.adapter.fhir.data.repository.FhirDhisAssignmentRepository; +import org.dhis2.fhir.adapter.fhir.extension.ValueTypeExtensionUtils; import org.dhis2.fhir.adapter.fhir.metadata.model.FhirClient; import org.dhis2.fhir.adapter.fhir.metadata.model.ProgramStageMetadataRule; import org.dhis2.fhir.adapter.fhir.metadata.model.RuleInfo; @@ -49,12 +51,14 @@ import org.hl7.fhir.r4.model.Questionnaire; import org.hl7.fhir.r4.model.Questionnaire.QuestionnaireItemComponent; import org.hl7.fhir.r4.model.Questionnaire.QuestionnaireItemType; +import org.hl7.fhir.r4.model.ResourceFactory; import org.springframework.stereotype.Component; import javax.annotation.Nonnull; import javax.annotation.Nullable; import java.util.Map; import java.util.Set; +import java.util.function.Function; import static org.hl7.fhir.r4.model.Questionnaire.QuestionnaireItemType.*; @@ -104,6 +108,8 @@ protected boolean transformInternal( @Nonnull FhirClient fhirClient, @Nonnull Dh itemComponent.setRequired( dataElement.isCompulsory() ); itemComponent.setType( type ); + ValueTypeExtensionUtils.setValue( itemComponent, dataElement.getElement().getValueType(), getTypeFactory() ); + if ( dataElement.getElement().isOptionSetValue() ) { dataElement.getElement().getOptionSet().getOptions().forEach( option -> itemComponent.addAnswerOption().setValue( @@ -155,4 +161,11 @@ protected QuestionnaireItemType convertValueType( @Nonnull ValueType valueType ) // unhandled data type return null; } + + @Nonnull + @Override + protected Function getTypeFactory() + { + return ResourceFactory::createType; + } } diff --git a/fhir-r4/src/test/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/metadata/r4/R4ProgramStageMetadataToFhirQuestionnaireTransformerTest.java b/fhir-r4/src/test/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/metadata/r4/R4ProgramStageMetadataToFhirQuestionnaireTransformerTest.java index 3cf0b25c..c27859e3 100644 --- a/fhir-r4/src/test/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/metadata/r4/R4ProgramStageMetadataToFhirQuestionnaireTransformerTest.java +++ b/fhir-r4/src/test/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/metadata/r4/R4ProgramStageMetadataToFhirQuestionnaireTransformerTest.java @@ -35,6 +35,7 @@ import org.dhis2.fhir.adapter.dhis.tracker.program.WritableProgramStage; import org.dhis2.fhir.adapter.dhis.tracker.program.WritableProgramStageDataElement; import org.dhis2.fhir.adapter.fhir.data.repository.FhirDhisAssignmentRepository; +import org.dhis2.fhir.adapter.fhir.extension.ValueTypeExtensionUtils; import org.dhis2.fhir.adapter.fhir.metadata.model.FhirClient; import org.dhis2.fhir.adapter.fhir.metadata.model.ProgramStageMetadataRule; import org.dhis2.fhir.adapter.fhir.metadata.model.RuleInfo; @@ -49,6 +50,7 @@ import org.dhis2.fhir.adapter.model.ValueType; import org.hl7.fhir.r4.model.Coding; import org.hl7.fhir.r4.model.Questionnaire; +import org.hl7.fhir.r4.model.StringType; import org.junit.Assert; import org.junit.Rule; import org.junit.Test; @@ -157,6 +159,11 @@ public void transformInternal() Assert.assertEquals( 0, fhirQuestionnaire.getItem().get( 0 ).getAnswerOption().size() ); Assert.assertFalse( fhirQuestionnaire.getItem().get( 0 ).getRequired() ); + Assert.assertEquals( 1, fhirQuestionnaire.getItem().get( 0 ).getExtension().size() ); + Assert.assertEquals( ValueTypeExtensionUtils.URL, fhirQuestionnaire.getItem().get( 0 ).getExtension().get( 0 ).getUrl() ); + Assert.assertTrue( fhirQuestionnaire.getItem().get( 0 ).getExtension().get( 0 ).getValue() instanceof StringType ); + Assert.assertEquals( "TEXT", ( (StringType) fhirQuestionnaire.getItem().get( 0 ).getExtension().get( 0 ).getValue() ).getValue() ); + Assert.assertEquals( "d1123456789", fhirQuestionnaire.getItem().get( 1 ).getLinkId() ); Assert.assertEquals( "Value 2", fhirQuestionnaire.getItem().get( 1 ).getText() ); Assert.assertEquals( 2, fhirQuestionnaire.getItem().get( 1 ).getAnswerOption().size() ); @@ -166,5 +173,10 @@ public void transformInternal() Assert.assertEquals( "7", ( (Coding) fhirQuestionnaire.getItem().get( 1 ).getAnswerOption().get( 1 ).getValue() ).getCode() ); Assert.assertEquals( "Test Value 2", ( (Coding) fhirQuestionnaire.getItem().get( 1 ).getAnswerOption().get( 1 ).getValue() ).getDisplay() ); Assert.assertTrue( fhirQuestionnaire.getItem().get( 1 ).getRequired() ); + + Assert.assertEquals( 1, fhirQuestionnaire.getItem().get( 1 ).getExtension().size() ); + Assert.assertEquals( ValueTypeExtensionUtils.URL, fhirQuestionnaire.getItem().get( 1 ).getExtension().get( 0 ).getUrl() ); + Assert.assertTrue( fhirQuestionnaire.getItem().get( 1 ).getExtension().get( 0 ).getValue() instanceof StringType ); + Assert.assertEquals( "INTEGER", ( (StringType) fhirQuestionnaire.getItem().get( 1 ).getExtension().get( 0 ).getValue() ).getValue() ); } } diff --git a/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/extension/BaseExtensionUtils.java b/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/extension/BaseExtensionUtils.java new file mode 100644 index 00000000..6811ec79 --- /dev/null +++ b/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/extension/BaseExtensionUtils.java @@ -0,0 +1,65 @@ +package org.dhis2.fhir.adapter.fhir.extension; + +/* + * Copyright (c) 2004-2019, University of Oslo + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * Neither the name of the HISP project nor the names of its contributors may + * be used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +import ca.uhn.fhir.model.api.IElement; +import org.hl7.fhir.instance.model.api.IBaseExtension; +import org.hl7.fhir.instance.model.api.IBaseHasExtensions; +import org.hl7.fhir.instance.model.api.IPrimitiveType; + +import javax.annotation.Nonnull; +import javax.annotation.Nullable; +import java.util.function.Function; + +/** + * Utility class to process FHIR resource extensions. + * + * @author volsch + */ +abstract class BaseExtensionUtils +{ + @SuppressWarnings( "unchecked" ) + protected static void setStringValue( @Nonnull String url, @Nonnull IBaseHasExtensions resource, @Nullable String value, @Nonnull Function typeFactory ) + { + resource.getExtension().removeIf( e -> url.equals( e.getUrl() ) ); + + if ( value != null ) + { + final IBaseExtension extension = resource.addExtension(); + + extension.setUrl( url ); + extension.setValue( ( (IPrimitiveType) typeFactory.apply( "string" ) ).setValue( value ) ); + } + } + + private BaseExtensionUtils() + { + super(); + } +} diff --git a/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/extension/ResourceTypeExtensionUtils.java b/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/extension/ResourceTypeExtensionUtils.java index 595cc28f..6a316e7d 100644 --- a/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/extension/ResourceTypeExtensionUtils.java +++ b/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/extension/ResourceTypeExtensionUtils.java @@ -30,9 +30,7 @@ import ca.uhn.fhir.model.api.IElement; import org.dhis2.fhir.adapter.fhir.metadata.model.FhirResourceType; -import org.hl7.fhir.instance.model.api.IBaseExtension; import org.hl7.fhir.instance.model.api.IBaseHasExtensions; -import org.hl7.fhir.instance.model.api.IPrimitiveType; import javax.annotation.Nonnull; import javax.annotation.Nullable; @@ -47,18 +45,9 @@ public abstract class ResourceTypeExtensionUtils { public static final String URL = "http://www.dhis2.org/dhis2-fhir-adapter/fhir/extensions/resource-type"; - @SuppressWarnings( "unchecked" ) public static void setValue( @Nonnull IBaseHasExtensions resource, @Nullable FhirResourceType fhirResourceType, @Nonnull Function typeFactory ) { - resource.getExtension().removeIf( e -> URL.equals( e.getUrl() ) ); - - if ( fhirResourceType != null ) - { - final IBaseExtension extension = resource.addExtension(); - - extension.setUrl( URL ); - extension.setValue( ( (IPrimitiveType) typeFactory.apply( "string" ) ).setValue( fhirResourceType.getResourceTypeName() ) ); - } + BaseExtensionUtils.setStringValue( URL, resource, fhirResourceType == null ? null : fhirResourceType.getResourceTypeName(), typeFactory ); } private ResourceTypeExtensionUtils() diff --git a/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/extension/ValueTypeExtensionUtils.java b/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/extension/ValueTypeExtensionUtils.java new file mode 100644 index 00000000..127b9c7c --- /dev/null +++ b/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/extension/ValueTypeExtensionUtils.java @@ -0,0 +1,56 @@ +package org.dhis2.fhir.adapter.fhir.extension; + +/* + * Copyright (c) 2004-2019, University of Oslo + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * Neither the name of the HISP project nor the names of its contributors may + * be used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +import ca.uhn.fhir.model.api.IElement; +import org.dhis2.fhir.adapter.model.ValueType; +import org.hl7.fhir.instance.model.api.IBaseHasExtensions; + +import javax.annotation.Nonnull; +import java.util.function.Function; + +/** + * Utility class to process FHIR value type extension. + * + * @author volsch + */ +public abstract class ValueTypeExtensionUtils +{ + public static final String URL = "http://www.dhis2.org/dhis2-fhir-adapter/fhir/extensions/value-type"; + + public static void setValue( @Nonnull IBaseHasExtensions resource, @Nonnull ValueType valueType, @Nonnull Function typeFactory ) + { + BaseExtensionUtils.setStringValue( URL, resource, valueType.name(), typeFactory ); + } + + private ValueTypeExtensionUtils() + { + super(); + } +} diff --git a/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/metadata/program/AbstractProgramStageMetadataToFhirQuestionnaireTransformer.java b/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/metadata/program/AbstractProgramStageMetadataToFhirQuestionnaireTransformer.java index 0bc8ba45..2252165a 100644 --- a/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/metadata/program/AbstractProgramStageMetadataToFhirQuestionnaireTransformer.java +++ b/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/metadata/program/AbstractProgramStageMetadataToFhirQuestionnaireTransformer.java @@ -28,6 +28,7 @@ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ +import ca.uhn.fhir.model.api.IElement; import org.dhis2.fhir.adapter.dhis.model.DhisResourceType; import org.dhis2.fhir.adapter.dhis.orgunit.OrganizationUnitService; import org.dhis2.fhir.adapter.fhir.data.repository.FhirDhisAssignmentRepository; @@ -45,6 +46,7 @@ import org.slf4j.LoggerFactory; import javax.annotation.Nonnull; +import java.util.function.Function; /** * Implementation of {@link DhisToFhirTransformer} for transforming DHIS2 @@ -90,4 +92,7 @@ public Class getRuleClass() { return ProgramStageMetadataRule.class; } + + @Nonnull + protected abstract Function getTypeFactory(); } diff --git a/fhir/src/test/java/org/dhis2/fhir/adapter/fhir/extension/BaseExtensionUtilsTest.java b/fhir/src/test/java/org/dhis2/fhir/adapter/fhir/extension/BaseExtensionUtilsTest.java new file mode 100644 index 00000000..b6c8758a --- /dev/null +++ b/fhir/src/test/java/org/dhis2/fhir/adapter/fhir/extension/BaseExtensionUtilsTest.java @@ -0,0 +1,64 @@ +package org.dhis2.fhir.adapter.fhir.extension; + +/* + * Copyright (c) 2004-2019, University of Oslo + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * Neither the name of the HISP project nor the names of its contributors may + * be used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +import org.junit.Assert; +import org.junit.Test; + +/** + * Unit tests for {@link BaseExtensionUtils}. + * + * @author volsch + */ +public class BaseExtensionUtilsTest +{ + @Test + public void resetValue() + { + TestPlanDefinition planDefinition = new TestPlanDefinition(); + + BaseExtensionUtils.setStringValue( "testUrl", planDefinition, "testValue", TypeFactory::createType ); + BaseExtensionUtils.setStringValue( "testUrl", planDefinition, null, TypeFactory::createType ); + + Assert.assertTrue( planDefinition.getExtension().isEmpty() ); + } + + @Test + public void setValue() + { + TestPlanDefinition planDefinition = new TestPlanDefinition(); + + BaseExtensionUtils.setStringValue( "testUrl", planDefinition, "testValue", TypeFactory::createType ); + + Assert.assertEquals( 1, planDefinition.getExtension().size() ); + Assert.assertEquals( "testUrl", planDefinition.getExtension().get( 0 ).getUrl() ); + Assert.assertEquals( "testValue", planDefinition.getExtension().get( 0 ).getValue().toString() ); + } + +} \ No newline at end of file diff --git a/fhir/src/test/java/org/dhis2/fhir/adapter/fhir/extension/ResourceTypeExtensionUtilsTest.java b/fhir/src/test/java/org/dhis2/fhir/adapter/fhir/extension/ResourceTypeExtensionUtilsTest.java index d245ec1d..b45787a9 100644 --- a/fhir/src/test/java/org/dhis2/fhir/adapter/fhir/extension/ResourceTypeExtensionUtilsTest.java +++ b/fhir/src/test/java/org/dhis2/fhir/adapter/fhir/extension/ResourceTypeExtensionUtilsTest.java @@ -28,18 +28,10 @@ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -import ca.uhn.fhir.model.api.ExtensionDt; -import ca.uhn.fhir.model.api.IElement; -import ca.uhn.fhir.model.primitive.StringDt; import org.dhis2.fhir.adapter.fhir.metadata.model.FhirResourceType; -import org.hl7.fhir.instance.model.api.IBaseExtension; -import org.hl7.fhir.instance.model.api.IBaseHasExtensions; import org.junit.Assert; import org.junit.Test; -import java.util.ArrayList; -import java.util.List; - /** * Unit tests for {@link ResourceTypeExtensionUtils}. * @@ -50,7 +42,7 @@ public class ResourceTypeExtensionUtilsTest @Test public void resetValue() { - PlanDefinition planDefinition = new PlanDefinition(); + TestPlanDefinition planDefinition = new TestPlanDefinition(); ResourceTypeExtensionUtils.setValue( planDefinition, FhirResourceType.PATIENT, TypeFactory::createType ); ResourceTypeExtensionUtils.setValue( planDefinition, null, TypeFactory::createType ); @@ -61,7 +53,7 @@ public void resetValue() @Test public void setValue() { - PlanDefinition planDefinition = new PlanDefinition(); + TestPlanDefinition planDefinition = new TestPlanDefinition(); ResourceTypeExtensionUtils.setValue( planDefinition, FhirResourceType.PATIENT, TypeFactory::createType ); @@ -69,39 +61,4 @@ public void setValue() Assert.assertEquals( ResourceTypeExtensionUtils.URL, planDefinition.getExtension().get( 0 ).getUrl() ); Assert.assertEquals( "Patient", planDefinition.getExtension().get( 0 ).getValue().toString() ); } - - public static class PlanDefinition implements IBaseHasExtensions - { - private final List> extensions = new ArrayList<>(); - - @Override - public IBaseExtension addExtension() - { - extensions.add( new ExtensionDt() ); - - return extensions.get( extensions.size() - 1 ); - } - - @Override - public List> getExtension() - { - return extensions; - } - - @Override - public boolean hasExtension() - { - return !extensions.isEmpty(); - } - } - - public static class TypeFactory - { - public static IElement createType( String name ) - { - Assert.assertEquals( "string", name ); - - return new StringDt(); - } - } } \ No newline at end of file diff --git a/fhir/src/test/java/org/dhis2/fhir/adapter/fhir/extension/TestPlanDefinition.java b/fhir/src/test/java/org/dhis2/fhir/adapter/fhir/extension/TestPlanDefinition.java new file mode 100644 index 00000000..b2f01a17 --- /dev/null +++ b/fhir/src/test/java/org/dhis2/fhir/adapter/fhir/extension/TestPlanDefinition.java @@ -0,0 +1,66 @@ +package org.dhis2.fhir.adapter.fhir.extension; + +/* + * Copyright (c) 2004-2019, University of Oslo + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * Neither the name of the HISP project nor the names of its contributors may + * be used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +import ca.uhn.fhir.model.api.ExtensionDt; +import org.hl7.fhir.instance.model.api.IBaseExtension; +import org.hl7.fhir.instance.model.api.IBaseHasExtensions; + +import java.util.ArrayList; +import java.util.List; + +/** + * Test plan definition FHIR resource. + * + * @author volsch + */ +class TestPlanDefinition implements IBaseHasExtensions +{ + private final List> extensions = new ArrayList<>(); + + @Override + public IBaseExtension addExtension() + { + extensions.add( new ExtensionDt() ); + + return extensions.get( extensions.size() - 1 ); + } + + @Override + public List> getExtension() + { + return extensions; + } + + @Override + public boolean hasExtension() + { + return !extensions.isEmpty(); + } +} diff --git a/fhir/src/test/java/org/dhis2/fhir/adapter/fhir/extension/TypeFactory.java b/fhir/src/test/java/org/dhis2/fhir/adapter/fhir/extension/TypeFactory.java new file mode 100644 index 00000000..32329ee4 --- /dev/null +++ b/fhir/src/test/java/org/dhis2/fhir/adapter/fhir/extension/TypeFactory.java @@ -0,0 +1,48 @@ +package org.dhis2.fhir.adapter.fhir.extension; + +/* + * Copyright (c) 2004-2019, University of Oslo + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * Neither the name of the HISP project nor the names of its contributors may + * be used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +import ca.uhn.fhir.model.api.IElement; +import ca.uhn.fhir.model.primitive.StringDt; +import org.junit.Assert; + +/** + * Test type factory. + * + * @author volsch + */ +class TypeFactory +{ + public static IElement createType( String name ) + { + Assert.assertEquals( "string", name ); + + return new StringDt(); + } +} diff --git a/fhir/src/test/java/org/dhis2/fhir/adapter/fhir/extension/ValueTypeExtensionUtilsTest.java b/fhir/src/test/java/org/dhis2/fhir/adapter/fhir/extension/ValueTypeExtensionUtilsTest.java new file mode 100644 index 00000000..6593491a --- /dev/null +++ b/fhir/src/test/java/org/dhis2/fhir/adapter/fhir/extension/ValueTypeExtensionUtilsTest.java @@ -0,0 +1,53 @@ +package org.dhis2.fhir.adapter.fhir.extension; + +/* + * Copyright (c) 2004-2019, University of Oslo + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * Neither the name of the HISP project nor the names of its contributors may + * be used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +import org.dhis2.fhir.adapter.model.ValueType; +import org.junit.Assert; +import org.junit.Test; + +/** + * Unit tests for {@link ValueTypeExtensionUtils}. + * + * @author volsch + */ +public class ValueTypeExtensionUtilsTest +{ + @Test + public void setValue() + { + TestPlanDefinition planDefinition = new TestPlanDefinition(); + + ValueTypeExtensionUtils.setValue( planDefinition, ValueType.INTEGER_POSITIVE, TypeFactory::createType ); + + Assert.assertEquals( 1, planDefinition.getExtension().size() ); + Assert.assertEquals( ValueTypeExtensionUtils.URL, planDefinition.getExtension().get( 0 ).getUrl() ); + Assert.assertEquals( "INTEGER_POSITIVE", planDefinition.getExtension().get( 0 ).getValue().toString() ); + } +} \ No newline at end of file