From d173b595159f026bc9eb95f41df81d8030f59413 Mon Sep 17 00:00:00 2001 From: Volker Schmidt Date: Fri, 9 Nov 2018 02:02:04 +0100 Subject: [PATCH] Implemented setup wizard. --- .../org/dhis2/fhir/adapter/DemoClient.java | 18 +- .../dhis2/fhir/adapter/WebSecurityConfig.java | 2 - .../fhir/adapter/setup/SetupController.java | 25 ++- .../fhir/adapter/setup/SetupService.java | 118 +++++++----- .../resources/templates/setup-completed.html | 39 ++++ app/src/main/resources/templates/setup.html | 15 +- ...emoteHierarchicallyFhirRepositoryImpl.java | 67 +++++++ ...scriptionResourceBundleRetrieverImpl.java} | 4 +- .../Dstu3OrganizationTransformerUtils.java | 101 ++++++++++ .../metadata/model/TrackedEntityRule.java | 12 -- .../ExecutableScriptArgRepository.java | 9 + .../fhir/repository/RemoteFhirRepository.java | 6 - .../RemoteHierarchicallyFhirRepository.java | 53 +++++ .../AbstractRemoteFhirRepositoryImpl.java | 27 +-- ...emoteHierarchicallyFhirRepositoryImpl.java | 112 +++++++++++ .../impl/AbstractFhirToDhisTransformer.java | 8 +- .../FhirToTrackedEntityTransformer.java | 2 +- .../AbstractOrganizationTransformerUtils.java | 181 ++++++++++++++++++ .../impl/util/BeanTransformerUtils.java | 17 ++ .../OrganizationUnitTransformerUtils.java | 106 ---------- .../production/V1.0.0.0_0_1__Initial.sql | 41 ++-- pom.xml | 2 +- 22 files changed, 729 insertions(+), 236 deletions(-) create mode 100644 app/src/main/resources/templates/setup-completed.html create mode 100644 fhir-dstu3/src/main/java/org/dhis2/fhir/adapter/fhir/remote/impl/dstu3/Dstu3RemoteHierarchicallyFhirRepositoryImpl.java rename fhir-dstu3/src/main/java/org/dhis2/fhir/adapter/fhir/remote/impl/dstu3/{Dstu3SubscriptionResourceBundleRetriever.java => Dstu3SubscriptionResourceBundleRetrieverImpl.java} (94%) create mode 100644 fhir-dstu3/src/main/java/org/dhis2/fhir/adapter/fhir/transform/impl/util/dstu3/Dstu3OrganizationTransformerUtils.java create mode 100644 fhir/src/main/java/org/dhis2/fhir/adapter/fhir/repository/RemoteHierarchicallyFhirRepository.java create mode 100644 fhir/src/main/java/org/dhis2/fhir/adapter/fhir/repository/impl/AbstractRemoteHierarchicallyFhirRepositoryImpl.java create mode 100644 fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/impl/util/AbstractOrganizationTransformerUtils.java delete mode 100644 fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/impl/util/OrganizationUnitTransformerUtils.java diff --git a/app/src/main/java/org/dhis2/fhir/adapter/DemoClient.java b/app/src/main/java/org/dhis2/fhir/adapter/DemoClient.java index b4c879c2..f7b5ed05 100644 --- a/app/src/main/java/org/dhis2/fhir/adapter/DemoClient.java +++ b/app/src/main/java/org/dhis2/fhir/adapter/DemoClient.java @@ -101,12 +101,20 @@ public static void main( String[] args ) // Organization /////////////// + Organization hospitalOrg = new Organization(); + hospitalOrg.setId( IdType.newRandomUuid() ); + hospitalOrg.setName( "Connaught Hospital" ); + hospitalOrg.addIdentifier() + .setSystem( "http://example.sl/organizations" ) + .setValue( orgCode ); + Organization org = new Organization(); org.setId( IdType.newRandomUuid() ); - org.setName( "Connaught Hospital" ); + org.setName( "Registration Unit" ); org.addIdentifier() .setSystem( "http://example.sl/organizations" ) - .setValue( orgCode ); + .setValue( "XZY123456" ); + org.setPartOf( new Reference( hospitalOrg.getIdElement() ) ); ////////////////////////////////// // Create Patient (new born child) @@ -371,6 +379,12 @@ public static void main( String[] args ) .getRequest() .setMethod( Bundle.HTTPVerb.PUT ) .setUrl( "Location?identifier=http://example.sl/locations|" + orgCode ); + bundle.addEntry() + .setResource( hospitalOrg ) + .setFullUrl( hospitalOrg.getId() ) + .getRequest() + .setMethod( Bundle.HTTPVerb.PUT ) + .setUrl( "Organization?identifier=http://example.sl/organizations|XZY123456" ); bundle.addEntry() .setResource( org ) .setFullUrl( org.getId() ) diff --git a/app/src/main/java/org/dhis2/fhir/adapter/WebSecurityConfig.java b/app/src/main/java/org/dhis2/fhir/adapter/WebSecurityConfig.java index b84b217b..2f4827a0 100644 --- a/app/src/main/java/org/dhis2/fhir/adapter/WebSecurityConfig.java +++ b/app/src/main/java/org/dhis2/fhir/adapter/WebSecurityConfig.java @@ -33,7 +33,6 @@ import org.dhis2.fhir.adapter.dhis.security.SecurityConfig; import org.springframework.boot.web.client.RestTemplateBuilder; import org.springframework.context.annotation.Configuration; -import org.springframework.http.HttpMethod; import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder; import org.springframework.security.config.annotation.web.builders.HttpSecurity; import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; @@ -76,7 +75,6 @@ protected void configure( @Nonnull HttpSecurity http ) throws Exception http .authorizeRequests() .antMatchers( "/remote-fhir-rest-hook/**" ).permitAll() - .antMatchers( HttpMethod.GET, "/setup" ).permitAll() .anyRequest().authenticated() .and() .httpBasic().realmName( DHIS_BASIC_REALM ); diff --git a/app/src/main/java/org/dhis2/fhir/adapter/setup/SetupController.java b/app/src/main/java/org/dhis2/fhir/adapter/setup/SetupController.java index 092bb306..0c8533ae 100644 --- a/app/src/main/java/org/dhis2/fhir/adapter/setup/SetupController.java +++ b/app/src/main/java/org/dhis2/fhir/adapter/setup/SetupController.java @@ -29,6 +29,9 @@ */ import org.apache.commons.codec.binary.Hex; +import org.dhis2.fhir.adapter.rest.RestResponseEntityException; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.stereotype.Controller; import org.springframework.ui.Model; @@ -47,6 +50,8 @@ @ConditionalOnProperty( "adapter-setup" ) public class SetupController { + private final Logger logger = LoggerFactory.getLogger( getClass() ); + protected static final int BEARER_TOKEN_BYTES = 40; private final SetupService setupService; @@ -82,8 +87,24 @@ public String submit( @Valid Setup setup, BindingResult bindingResult, Model mod return "setup"; } - setupService.apply( setup ); - return "result"; + try + { + setupService.apply( setup ); + } + catch ( SetupException | RestResponseEntityException e ) + { + logger.error( "An error occurred when performing the setup.", e ); + bindingResult.reject( "setup.error", new Object[]{ e.getMessage() }, "{0}" ); + return "setup"; + } + catch ( RuntimeException e ) + { + logger.error( "An error occurred when performing the setup.", e ); + bindingResult.reject( "setup.error", "An error occurred when performing setup. Please, check the log files for further information." ); + return "setup"; + } + + return "setup-completed"; } @Nonnull diff --git a/app/src/main/java/org/dhis2/fhir/adapter/setup/SetupService.java b/app/src/main/java/org/dhis2/fhir/adapter/setup/SetupService.java index 2fbca69c..66d67024 100644 --- a/app/src/main/java/org/dhis2/fhir/adapter/setup/SetupService.java +++ b/app/src/main/java/org/dhis2/fhir/adapter/setup/SetupService.java @@ -32,6 +32,7 @@ import org.dhis2.fhir.adapter.fhir.metadata.model.AuthenticationMethod; import org.dhis2.fhir.adapter.fhir.metadata.model.Code; import org.dhis2.fhir.adapter.fhir.metadata.model.CodeCategory; +import org.dhis2.fhir.adapter.fhir.metadata.model.ExecutableScriptArg; import org.dhis2.fhir.adapter.fhir.metadata.model.FhirResourceType; import org.dhis2.fhir.adapter.fhir.metadata.model.RemoteSubscription; import org.dhis2.fhir.adapter.fhir.metadata.model.RemoteSubscriptionSystem; @@ -43,6 +44,7 @@ import org.dhis2.fhir.adapter.fhir.metadata.model.SystemCode; import org.dhis2.fhir.adapter.fhir.metadata.repository.CodeCategoryRepository; import org.dhis2.fhir.adapter.fhir.metadata.repository.CodeRepository; +import org.dhis2.fhir.adapter.fhir.metadata.repository.ExecutableScriptArgRepository; import org.dhis2.fhir.adapter.fhir.metadata.repository.RemoteSubscriptionRepository; import org.dhis2.fhir.adapter.fhir.metadata.repository.SystemCodeRepository; import org.dhis2.fhir.adapter.fhir.metadata.repository.SystemRepository; @@ -71,8 +73,16 @@ @Service public class SetupService { + public static final String AUTHORIZATION_HEADER_NAME = "Authorization"; + public static final String ORGANIZATION_CODE_CATEGORY = "ORGANIZATION_UNIT"; + public static final String ORG_UNIT_CODE_EXECUTABLE_SCRIPT_CODE = "EXTRACT_FHIR_RESOURCE_DHIS_ORG_UNIT_CODE"; + + public static final String ORG_UNIT_CODE_FALLBACK_ARG_NAME = "useIdentifierCode"; + + public static final String ORG_UNIT_CODE_DEFAULT_CODE_ARG_NAME = "defaultCode"; + private final CodeCategoryRepository codeCategoryRepository; private final CodeRepository codeRepository; @@ -81,16 +91,19 @@ public class SetupService private final SystemCodeRepository systemCodeRepository; + private final ExecutableScriptArgRepository executableScriptArgRepository; + private final RemoteSubscriptionRepository remoteSubscriptionRepository; public SetupService( @Nonnull CodeCategoryRepository codeCategoryRepository, @Nonnull CodeRepository codeRepository, @Nonnull SystemRepository systemRepository, @Nonnull SystemCodeRepository systemCodeRepository, - @Nonnull RemoteSubscriptionRepository remoteSubscriptionRepository ) + @Nonnull ExecutableScriptArgRepository executableScriptArgRepository, @Nonnull RemoteSubscriptionRepository remoteSubscriptionRepository ) { this.codeCategoryRepository = codeCategoryRepository; this.codeRepository = codeRepository; this.systemRepository = systemRepository; this.systemCodeRepository = systemCodeRepository; + this.executableScriptArgRepository = executableScriptArgRepository; this.remoteSubscriptionRepository = remoteSubscriptionRepository; } @@ -122,45 +135,6 @@ public void apply( @Nonnull Setup setup ) createSystemCodes( setup.getOrganizationCodeSetup(), organizationSystem ); } - private void createSystemCodes( @Nonnull OrganizationCodeSetup setup, @Nonnull System organizationSystem ) - { - final List codeMappings = setup.getCodeMappings(); - if ( !codeMappings.isEmpty() ) - { - final CodeCategory organizationCodeCategory = findCodeCategory( ORGANIZATION_CODE_CATEGORY ); - final Map persistedCodes = codeRepository.saveAll( createCodes( codeMappings, organizationCodeCategory ).values() ) - .stream().collect( Collectors.toMap( Code::getMappedCode, c -> c ) ); - - final List systemCodes = codeMappings.stream().map( cm -> { - final SystemCode systemCode = new SystemCode(); - systemCode.setSystem( organizationSystem ); - systemCode.setSystemCode( cm.getFhirCode() ); - systemCode.setCode( persistedCodes.get( cm.getDhisCode() ) ); - return systemCode; - } ).collect( Collectors.toList() ); - systemCodeRepository.saveAll( systemCodes ); - } - } - - @Nonnull - private Map createCodes( @Nonnull List codeMappings, @Nonnull CodeCategory organizationCodeCategory ) - { - final Map codes = new HashMap<>(); - codeMappings.forEach( cm -> { - codes.computeIfAbsent( cm.getDhisCode(), c -> { - final Code code = new Code(); - code.setCodeCategory( organizationCodeCategory ); - code.setName( "Default DHIS2 Organization Unit Code " + c ); - code.setDescription( "Default DHIS2 organization unit code " + c ); - code.setCode( c.startsWith( OrganizationCodeSetup.DEFAULT_CODE_PREFIX ) ? c : - (OrganizationCodeSetup.DEFAULT_CODE_PREFIX + c) ); - code.setMappedCode( c ); - return code; - } ); - } ); - return codes; - } - private void createRemoteSubscription( @Nonnull RemoteSubscriptionSetup setup, @Nonnull System organizationSystem, @Nonnull System patientSystem ) { final RemoteSubscription remoteSubscription = new RemoteSubscription(); @@ -174,15 +148,15 @@ private void createRemoteSubscription( @Nonnull RemoteSubscriptionSetup setup, @ remoteSubscription.setToleranceMillis( setup.getFhirSetup().getToleranceMillis() ); final SubscriptionAdapterEndpoint adapterEndpoint = new SubscriptionAdapterEndpoint(); - adapterEndpoint.setBaseUrl( setup.getAdapterSetup().getBaseUrl() ); - adapterEndpoint.setAuthorizationHeader( setup.getAdapterSetup().getAuthorizationHeaderValue() ); + adapterEndpoint.setBaseUrl( StringUtils.trim( setup.getAdapterSetup().getBaseUrl() ) ); + adapterEndpoint.setAuthorizationHeader( StringUtils.trim( setup.getAdapterSetup().getAuthorizationHeaderValue() ) ); adapterEndpoint.setSubscriptionType( setup.getFhirSetup().getSubscriptionType() ); remoteSubscription.setAdapterEndpoint( adapterEndpoint ); final SubscriptionDhisEndpoint dhisEndpoint = new SubscriptionDhisEndpoint(); dhisEndpoint.setAuthenticationMethod( AuthenticationMethod.BASIC ); - dhisEndpoint.setUsername( setup.getDhisSetup().getUsername() ); - dhisEndpoint.setPassword( setup.getDhisSetup().getPassword() ); + dhisEndpoint.setUsername( StringUtils.trim( setup.getDhisSetup().getUsername() ) ); + dhisEndpoint.setPassword( StringUtils.trim( setup.getDhisSetup().getPassword() ) ); remoteSubscription.setDhisEndpoint( dhisEndpoint ); final SubscriptionFhirEndpoint fhirEndpoint = new SubscriptionFhirEndpoint(); @@ -191,8 +165,9 @@ private void createRemoteSubscription( @Nonnull RemoteSubscriptionSetup setup, @ StringUtils.isNotBlank( setup.getFhirSetup().getHeaderValue() ) ) { fhirEndpoint.setHeaders( new TreeSet<>( Collections.singleton( new RequestHeader( - setup.getFhirSetup().getHeaderName(), - setup.getFhirSetup().getHeaderValue(), true ) ) ) ); + StringUtils.trim( setup.getFhirSetup().getHeaderName() ), + StringUtils.trim( setup.getFhirSetup().getHeaderValue() ), + AUTHORIZATION_HEADER_NAME.equalsIgnoreCase( StringUtils.trim( setup.getFhirSetup().getHeaderName() ) ) ) ) ) ); } remoteSubscription.setFhirEndpoint( fhirEndpoint ); @@ -200,19 +175,66 @@ private void createRemoteSubscription( @Nonnull RemoteSubscriptionSetup setup, @ subscriptionOrganizationSystem.setRemoteSubscription( remoteSubscription ); subscriptionOrganizationSystem.setFhirResourceType( FhirResourceType.ORGANIZATION ); subscriptionOrganizationSystem.setSystem( organizationSystem ); - subscriptionOrganizationSystem.setCodePrefix( setup.getSystemUriSetup().getOrganizationCodePrefix() ); + subscriptionOrganizationSystem.setCodePrefix( StringUtils.trim( setup.getSystemUriSetup().getOrganizationCodePrefix() ) ); remoteSubscription.getSystems().add( subscriptionOrganizationSystem ); final RemoteSubscriptionSystem subscriptionPatientSystem = new RemoteSubscriptionSystem(); subscriptionPatientSystem.setRemoteSubscription( remoteSubscription ); subscriptionPatientSystem.setFhirResourceType( FhirResourceType.PATIENT ); subscriptionPatientSystem.setSystem( patientSystem ); - subscriptionPatientSystem.setCodePrefix( setup.getSystemUriSetup().getPatientCodePrefix() ); + subscriptionPatientSystem.setCodePrefix( StringUtils.trim( setup.getSystemUriSetup().getPatientCodePrefix() ) ); remoteSubscription.getSystems().add( subscriptionPatientSystem ); remoteSubscriptionRepository.saveAndFlush( remoteSubscription ); } + private void createSystemCodes( @Nonnull OrganizationCodeSetup setup, @Nonnull System organizationSystem ) + { + final List codeMappings = setup.getCodeMappings(); + if ( !codeMappings.isEmpty() ) + { + final CodeCategory organizationCodeCategory = findCodeCategory( ORGANIZATION_CODE_CATEGORY ); + final Map persistedCodes = codeRepository.saveAll( createCodes( codeMappings, organizationCodeCategory ).values() ) + .stream().collect( Collectors.toMap( Code::getMappedCode, c -> c ) ); + + final List systemCodes = codeMappings.stream().map( cm -> { + final SystemCode systemCode = new SystemCode(); + systemCode.setSystem( organizationSystem ); + systemCode.setSystemCode( cm.getFhirCode() ); + systemCode.setCode( persistedCodes.get( cm.getDhisCode() ) ); + return systemCode; + } ).collect( Collectors.toList() ); + systemCodeRepository.saveAll( systemCodes ); + } + + ExecutableScriptArg scriptArg = executableScriptArgRepository.findByCodeAndName( ORG_UNIT_CODE_EXECUTABLE_SCRIPT_CODE, ORG_UNIT_CODE_FALLBACK_ARG_NAME ) + .orElseThrow( () -> new SetupException( "Executable script with code " + ORG_UNIT_CODE_EXECUTABLE_SCRIPT_CODE + " and argument " + ORG_UNIT_CODE_FALLBACK_ARG_NAME + " exists." ) ); + scriptArg.setOverrideValue( Boolean.valueOf( setup.isFallback() ).toString() ); + executableScriptArgRepository.save( scriptArg ); + + scriptArg = executableScriptArgRepository.findByCodeAndName( ORG_UNIT_CODE_EXECUTABLE_SCRIPT_CODE, ORG_UNIT_CODE_DEFAULT_CODE_ARG_NAME ) + .orElseThrow( () -> new SetupException( "Executable script with code " + ORG_UNIT_CODE_EXECUTABLE_SCRIPT_CODE + " and argument " + ORG_UNIT_CODE_DEFAULT_CODE_ARG_NAME + " exists." ) ); + scriptArg.setOverrideValue( StringUtils.trimToNull( setup.getDefaultDhisCode() ) ); + executableScriptArgRepository.save( scriptArg ); + } + + @Nonnull + private Map createCodes( @Nonnull List codeMappings, @Nonnull CodeCategory organizationCodeCategory ) + { + final Map codes = new HashMap<>(); + codeMappings.forEach( cm -> codes.computeIfAbsent( cm.getDhisCode(), c -> { + final Code code = new Code(); + code.setCodeCategory( organizationCodeCategory ); + code.setName( "Default DHIS2 Organization Unit Code " + c ); + code.setDescription( "Default DHIS2 organization unit code " + c ); + code.setCode( c.startsWith( OrganizationCodeSetup.DEFAULT_CODE_PREFIX ) ? c : + (OrganizationCodeSetup.DEFAULT_CODE_PREFIX + c) ); + code.setMappedCode( c ); + return code; + } ) ); + return codes; + } + @Nonnull protected System findOrCreateSystem( @Nonnull String systemUri, @Nonnull String name, @Nonnull String code, @Nonnull String description ) { diff --git a/app/src/main/resources/templates/setup-completed.html b/app/src/main/resources/templates/setup-completed.html new file mode 100644 index 00000000..c7b1322b --- /dev/null +++ b/app/src/main/resources/templates/setup-completed.html @@ -0,0 +1,39 @@ + + + + + + DHIS2 FHIR Adapter - Initial Setup Completed + + + +

DHIS2 FHIR Adapter - Initial Setup Completed

+

The initial setup has been completed successfully!

+ + diff --git a/app/src/main/resources/templates/setup.html b/app/src/main/resources/templates/setup.html index 69e2a051..f47c9f79 100644 --- a/app/src/main/resources/templates/setup.html +++ b/app/src/main/resources/templates/setup.html @@ -59,9 +59,13 @@

DHIS2 FHIR Adapter - Initial Setup

In the following sections you can enter the initial setup data of the Adapter. All further adjustments must be applied by using the REST API of the Adapter.

+
+
    +
  • +
+

FHIR Subscriptions

This section contains the data that is used to setup the FHIR subscriptions.

-

DHIS2 Setup

This sub-section contains the data of DHIS2 that is required when storing mapped and transformed FHIR data on DHIS2.

@@ -69,7 +73,7 @@

DHIS2 Setup

create and update Tracker program instances and create and update tracker program stage instances.

DHIS2 Username

-

+

DHIS2 Password

@@ -152,7 +156,7 @@

Code Mappings

FHIR Organization to DHIS2 Organization Unit Mapping

The mapping uses the business identifier code that is defined by the FHIR organization resource and tries to lookup the code in the mapping table that is defined below. If the code cannot be found in the mapping table below, the code is optionally be used used to lookup the DHIS2 organization unit (including the code prefix that has been defined above) as a fallback. If such a DHIS2 organization unit does not exist, the default organization unit is used (when specified). If no existing DHIS2 - organization unit can be found, the transformation of the input data will be skipped.

+ organization unit can be found, the transformation of the input data will be skipped. Normally the mapping must be done for organization units at facility level (e.g. the hospital itself).

The mapping table below has two columns. The column values may be separated by spaces, tabulators, commas and semicolons. Every code mapping must be placed in a new line. The first column contains the FHIR business identifier code and the second column contains the DHIS2 organization unit code. The same FHIR identifier code must not be specified more than once. DHIS2 organization unit codes can be specified several times.

Example:

@@ -167,8 +171,9 @@

FHIR Organization to DHIS2 Organization Unit Mapping

Org Code Default

-

After pressing the button you will be asked for a username and password. Please, enter the username and password of a DHIS2 user that has all privileges.

-

+
+ +
diff --git a/fhir-dstu3/src/main/java/org/dhis2/fhir/adapter/fhir/remote/impl/dstu3/Dstu3RemoteHierarchicallyFhirRepositoryImpl.java b/fhir-dstu3/src/main/java/org/dhis2/fhir/adapter/fhir/remote/impl/dstu3/Dstu3RemoteHierarchicallyFhirRepositoryImpl.java new file mode 100644 index 00000000..d6601e8e --- /dev/null +++ b/fhir-dstu3/src/main/java/org/dhis2/fhir/adapter/fhir/remote/impl/dstu3/Dstu3RemoteHierarchicallyFhirRepositoryImpl.java @@ -0,0 +1,67 @@ +package org.dhis2.fhir.adapter.fhir.remote.impl.dstu3; + +/* + * 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 EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +import org.dhis2.fhir.adapter.fhir.repository.RemoteFhirRepository; +import org.dhis2.fhir.adapter.fhir.repository.impl.AbstractRemoteHierarchicallyFhirRepositoryImpl; +import org.hl7.fhir.dstu3.model.Bundle; +import org.hl7.fhir.dstu3.model.Resource; +import org.hl7.fhir.instance.model.api.IBaseBundle; +import org.hl7.fhir.instance.model.api.IBaseResource; +import org.springframework.stereotype.Component; + +import javax.annotation.Nonnull; +import java.util.List; + +/** + * Implementation of {@link AbstractRemoteHierarchicallyFhirRepositoryImpl} for DSTU3. + * + * @author volsch + */ +@Component +public class Dstu3RemoteHierarchicallyFhirRepositoryImpl extends AbstractRemoteHierarchicallyFhirRepositoryImpl +{ + public Dstu3RemoteHierarchicallyFhirRepositoryImpl( @Nonnull RemoteFhirRepository remoteFhirRepository ) + { + super( remoteFhirRepository ); + } + + @Nonnull + @Override + protected IBaseBundle createBundle( @Nonnull List resources ) + { + final Bundle bundle = new Bundle(); + resources.stream().map( r -> { + final Bundle.BundleEntryComponent component = new Bundle.BundleEntryComponent(); + component.setResource( (Resource) r ); + return component; + } ).forEach( bundle::addEntry ); + return bundle; + } +} diff --git a/fhir-dstu3/src/main/java/org/dhis2/fhir/adapter/fhir/remote/impl/dstu3/Dstu3SubscriptionResourceBundleRetriever.java b/fhir-dstu3/src/main/java/org/dhis2/fhir/adapter/fhir/remote/impl/dstu3/Dstu3SubscriptionResourceBundleRetrieverImpl.java similarity index 94% rename from fhir-dstu3/src/main/java/org/dhis2/fhir/adapter/fhir/remote/impl/dstu3/Dstu3SubscriptionResourceBundleRetriever.java rename to fhir-dstu3/src/main/java/org/dhis2/fhir/adapter/fhir/remote/impl/dstu3/Dstu3SubscriptionResourceBundleRetrieverImpl.java index 90b77ca2..f2e822f6 100644 --- a/fhir-dstu3/src/main/java/org/dhis2/fhir/adapter/fhir/remote/impl/dstu3/Dstu3SubscriptionResourceBundleRetriever.java +++ b/fhir-dstu3/src/main/java/org/dhis2/fhir/adapter/fhir/remote/impl/dstu3/Dstu3SubscriptionResourceBundleRetrieverImpl.java @@ -50,9 +50,9 @@ * @author volsch */ @Component -public class Dstu3SubscriptionResourceBundleRetriever extends AbstractSubscriptionResourceBundleRetriever +public class Dstu3SubscriptionResourceBundleRetrieverImpl extends AbstractSubscriptionResourceBundleRetriever { - public Dstu3SubscriptionResourceBundleRetriever( @Nonnull @Qualifier( "fhirContextDstu3" ) FhirContext fhirContext ) + public Dstu3SubscriptionResourceBundleRetrieverImpl( @Nonnull @Qualifier( "fhirContextDstu3" ) FhirContext fhirContext ) { super( fhirContext ); } diff --git a/fhir-dstu3/src/main/java/org/dhis2/fhir/adapter/fhir/transform/impl/util/dstu3/Dstu3OrganizationTransformerUtils.java b/fhir-dstu3/src/main/java/org/dhis2/fhir/adapter/fhir/transform/impl/util/dstu3/Dstu3OrganizationTransformerUtils.java new file mode 100644 index 00000000..54278e85 --- /dev/null +++ b/fhir-dstu3/src/main/java/org/dhis2/fhir/adapter/fhir/transform/impl/util/dstu3/Dstu3OrganizationTransformerUtils.java @@ -0,0 +1,101 @@ +package org.dhis2.fhir.adapter.fhir.transform.impl.util.dstu3; + +/* + * 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 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.Scriptable; +import org.dhis2.fhir.adapter.dhis.orgunit.OrganisationUnitService; +import org.dhis2.fhir.adapter.fhir.metadata.repository.RemoteSubscriptionResourceRepository; +import org.dhis2.fhir.adapter.fhir.model.FhirVersion; +import org.dhis2.fhir.adapter.fhir.repository.RemoteFhirRepository; +import org.dhis2.fhir.adapter.fhir.repository.RemoteHierarchicallyFhirRepository; +import org.dhis2.fhir.adapter.fhir.script.ScriptExecutionContext; +import org.dhis2.fhir.adapter.fhir.transform.impl.util.AbstractOrganizationTransformerUtils; +import org.hl7.fhir.dstu3.model.Bundle; +import org.hl7.fhir.dstu3.model.Organization; +import org.hl7.fhir.instance.model.api.IBaseBundle; +import org.hl7.fhir.instance.model.api.IBaseReference; +import org.hl7.fhir.instance.model.api.IBaseResource; +import org.springframework.stereotype.Component; + +import javax.annotation.Nonnull; +import javax.annotation.Nullable; +import java.util.Collections; +import java.util.List; +import java.util.Set; +import java.util.stream.Collectors; + +@Component +@Scriptable +public class Dstu3OrganizationTransformerUtils extends AbstractOrganizationTransformerUtils +{ + public Dstu3OrganizationTransformerUtils( @Nonnull ScriptExecutionContext scriptExecutionContext, + @Nonnull OrganisationUnitService organisationUnitService, + @Nonnull RemoteSubscriptionResourceRepository subscriptionResourceRepository, + @Nonnull RemoteFhirRepository remoteFhirRepository, + @Nonnull RemoteHierarchicallyFhirRepository remoteHierarchicallyFhirRepository ) + { + super( scriptExecutionContext, organisationUnitService, subscriptionResourceRepository, remoteFhirRepository, remoteHierarchicallyFhirRepository ); + } + + @Nonnull + @Override + public Set getFhirVersions() + { + return FhirVersion.DSTU3_ONLY; + } + + @Nullable + @Override + protected IBaseReference getParentReference( @Nullable IBaseResource resource ) + { + if ( resource == null ) + { + return null; + } + return ((Organization) resource).getPartOf(); + } + + @Nonnull + @Override + protected List extractResources( @Nullable IBaseBundle bundle ) + { + if ( bundle == null ) + { + return Collections.emptyList(); + } + + final Bundle b = (Bundle) bundle; + if ( b.getEntry() == null ) + { + return Collections.emptyList(); + } + + return b.getEntry().stream().map( Bundle.BundleEntryComponent::getResource ).collect( Collectors.toList() ); + } +} diff --git a/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/metadata/model/TrackedEntityRule.java b/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/metadata/model/TrackedEntityRule.java index 1b28584c..b2f4e6d9 100644 --- a/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/metadata/model/TrackedEntityRule.java +++ b/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/metadata/model/TrackedEntityRule.java @@ -111,16 +111,4 @@ public void setTrackedEntityIdentifierReference( Reference trackedEntityIdentifi { this.trackedEntityIdentifierReference = trackedEntityIdentifierReference; } - - @Basic - @Column( name = "tracked_entity_identifier_fq", nullable = false ) - public boolean isTrackedEntityIdentifierFq() - { - return trackedEntityIdentifierFq; - } - - public void setTrackedEntityIdentifierFq( boolean trackedEntityIdentifierFq ) - { - this.trackedEntityIdentifierFq = trackedEntityIdentifierFq; - } } diff --git a/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/metadata/repository/ExecutableScriptArgRepository.java b/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/metadata/repository/ExecutableScriptArgRepository.java index 0c50d97d..6054b910 100644 --- a/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/metadata/repository/ExecutableScriptArgRepository.java +++ b/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/metadata/repository/ExecutableScriptArgRepository.java @@ -33,9 +33,13 @@ import org.springframework.cache.annotation.CacheEvict; import org.springframework.cache.annotation.CachePut; import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.Query; +import org.springframework.data.repository.query.Param; +import org.springframework.data.rest.core.annotation.RestResource; import javax.annotation.Nonnull; import java.util.List; +import java.util.Optional; import java.util.UUID; /** @@ -46,6 +50,11 @@ @CacheConfig( cacheManager = "metadataCacheManager", cacheNames = "executableScriptArg" ) public interface ExecutableScriptArgRepository extends JpaRepository { + @RestResource( exported = false ) + @Query( "SELECT esa FROM #{#entityName} esa JOIN esa.script es ON es.code=:scriptCode JOIN esa.argument a ON a.name=:argName" ) + @Nonnull + Optional findByCodeAndName( @Param( "scriptCode" ) @Nonnull String executableScriptCode, @Param( "argName" ) @Nonnull String argName ); + @Override @Nonnull @CacheEvict( allEntries = true ) diff --git a/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/repository/RemoteFhirRepository.java b/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/repository/RemoteFhirRepository.java index b286e7b0..a2c1021b 100644 --- a/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/repository/RemoteFhirRepository.java +++ b/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/repository/RemoteFhirRepository.java @@ -50,12 +50,6 @@ public interface RemoteFhirRepository @Nonnull Optional findRefreshed( @Nonnull UUID remoteSubscriptionId, @Nonnull FhirVersion fhirVersion, @Nonnull SubscriptionFhirEndpoint fhirEndpoint, @Nonnull String resourceType, @Nonnull String resourceId ); - @Nonnull - Optional findRefreshed( @Nonnull UUID remoteSubscriptionId, @Nonnull String resourceType, @Nonnull String resourceId ); - @Nonnull Optional find( @Nonnull UUID remoteSubscriptionId, @Nonnull FhirVersion fhirVersion, @Nonnull SubscriptionFhirEndpoint fhirEndpoint, @Nonnull String resourceType, @Nonnull String resourceId ); - - @Nonnull - Optional find( @Nonnull UUID remoteSubscriptionId, @Nonnull String resourceType, @Nonnull String resourceId ); } diff --git a/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/repository/RemoteHierarchicallyFhirRepository.java b/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/repository/RemoteHierarchicallyFhirRepository.java new file mode 100644 index 00000000..62845759 --- /dev/null +++ b/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/repository/RemoteHierarchicallyFhirRepository.java @@ -0,0 +1,53 @@ +package org.dhis2.fhir.adapter.fhir.repository; + +/* + * 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 EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +import org.dhis2.fhir.adapter.fhir.metadata.model.SubscriptionFhirEndpoint; +import org.dhis2.fhir.adapter.fhir.model.FhirVersion; +import org.hl7.fhir.instance.model.api.IBaseBundle; +import org.hl7.fhir.instance.model.api.IBaseReference; +import org.hl7.fhir.instance.model.api.IBaseResource; + +import javax.annotation.Nonnull; +import javax.annotation.Nullable; +import java.util.UUID; +import java.util.function.Function; + +/** + * Facade for {@link RemoteFhirRepository} to handle parent child relationships on + * to request the complete hierarchy up to the root. + * + * @author volsch + */ +public interface RemoteHierarchicallyFhirRepository +{ + @Nonnull + IBaseBundle findWithParents( @Nonnull UUID remoteSubscriptionId, @Nonnull FhirVersion fhirVersion, @Nonnull SubscriptionFhirEndpoint fhirEndpoint, + @Nonnull String resourceType, @Nullable String resourceId, @Nonnull String hierarchyType, @Nonnull Function parentReferenceFunction ); +} diff --git a/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/repository/impl/AbstractRemoteFhirRepositoryImpl.java b/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/repository/impl/AbstractRemoteFhirRepositoryImpl.java index 7baa6295..610ef4f1 100644 --- a/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/repository/impl/AbstractRemoteFhirRepositoryImpl.java +++ b/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/repository/impl/AbstractRemoteFhirRepositoryImpl.java @@ -35,14 +35,12 @@ import ca.uhn.fhir.rest.server.exceptions.BaseServerResponseException; import ca.uhn.fhir.rest.server.exceptions.ResourceNotFoundException; import com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand; -import org.dhis2.fhir.adapter.fhir.metadata.model.RemoteSubscription; import org.dhis2.fhir.adapter.fhir.metadata.model.RemoteSubscriptionResource; import org.dhis2.fhir.adapter.fhir.metadata.model.SubscriptionFhirEndpoint; import org.dhis2.fhir.adapter.fhir.metadata.repository.RemoteSubscriptionRepository; import org.dhis2.fhir.adapter.fhir.metadata.repository.event.AutoCreatedRemoteSubscriptionResourceEvent; import org.dhis2.fhir.adapter.fhir.model.FhirVersion; import org.dhis2.fhir.adapter.fhir.repository.FhirClientUtils; -import org.dhis2.fhir.adapter.fhir.repository.FhirRepositoryException; import org.dhis2.fhir.adapter.fhir.repository.RemoteFhirRepository; import org.dhis2.fhir.adapter.rest.RestBadRequestException; import org.hl7.fhir.instance.model.api.IAnyResource; @@ -96,7 +94,7 @@ public Optional findFhirContext( @Nonnull FhirVersion fhirVersion ) } @HystrixCommand - @CachePut( key = "{#remoteSubscriptionId, #resourceType, #resourceId}", unless = "#result==null" ) + @CachePut( key = "{#remoteSubscriptionId, #fhirVersion, #resourceType, #resourceId}", unless = "#result==null" ) @Nonnull @Override public Optional findRefreshed( @Nonnull UUID remoteSubscriptionId, @Nonnull FhirVersion fhirVersion, @Nonnull SubscriptionFhirEndpoint fhirEndpoint, @Nonnull String resourceType, @Nonnull String resourceId ) @@ -119,19 +117,7 @@ public Optional findRefreshed( @Nonnull UUID remoteSubscriptionId } @HystrixCommand - @CachePut( key = "{#remoteSubscriptionId, #resourceType, #resourceId}", unless = "#result==null" ) - @Nonnull - @Override - public Optional findRefreshed( @Nonnull UUID remoteSubscriptionId, @Nonnull String resourceType, @Nonnull String resourceId ) - { - final RemoteSubscription remoteSubscription = repository.findOneByIdCached( remoteSubscriptionId ) - .orElseThrow( () -> new FhirRepositoryException( "Remote subscription with ID " + remoteSubscriptionId + " does not longer exist." ) ); - return findRefreshed( remoteSubscription.getId(), remoteSubscription.getFhirVersion(), remoteSubscription.getFhirEndpoint(), - resourceType, resourceId ); - } - - @HystrixCommand - @Cacheable( key = "{#remoteSubscriptionId, #resourceType, #resourceId}", unless = "#result==null" ) + @Cacheable( key = "{#remoteSubscriptionId, #fhirVersion, #resourceType, #resourceId}", unless = "#result==null" ) @Nonnull @Override public Optional find( @Nonnull UUID remoteSubscriptionId, @Nonnull FhirVersion fhirVersion, @Nonnull SubscriptionFhirEndpoint fhirEndpoint, @Nonnull String resourceType, @Nonnull String resourceId ) @@ -139,15 +125,6 @@ public Optional find( @Nonnull UUID remoteSubscriptionId, @Nonnul return findRefreshed( remoteSubscriptionId, fhirVersion, fhirEndpoint, resourceType, resourceId ); } - @HystrixCommand - @Cacheable( key = "{#remoteSubscriptionId, #resourceType, #resourceId}", unless = "#result==null" ) - @Nonnull - @Override - public Optional find( @Nonnull UUID remoteSubscriptionId, @Nonnull String resourceType, @Nonnull String resourceId ) - { - return findRefreshed( remoteSubscriptionId, resourceType, resourceId ); - } - @TransactionalEventListener( phase = TransactionPhase.BEFORE_COMMIT, classes = AutoCreatedRemoteSubscriptionResourceEvent.class ) public void autoCreatedSubscriptionResource( @Nonnull AutoCreatedRemoteSubscriptionResourceEvent event ) { diff --git a/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/repository/impl/AbstractRemoteHierarchicallyFhirRepositoryImpl.java b/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/repository/impl/AbstractRemoteHierarchicallyFhirRepositoryImpl.java new file mode 100644 index 00000000..6e27020a --- /dev/null +++ b/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/repository/impl/AbstractRemoteHierarchicallyFhirRepositoryImpl.java @@ -0,0 +1,112 @@ +package org.dhis2.fhir.adapter.fhir.repository.impl; + +/* + * 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 EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +import org.dhis2.fhir.adapter.fhir.metadata.model.SubscriptionFhirEndpoint; +import org.dhis2.fhir.adapter.fhir.model.FhirVersion; +import org.dhis2.fhir.adapter.fhir.repository.RemoteFhirRepository; +import org.dhis2.fhir.adapter.fhir.repository.RemoteHierarchicallyFhirRepository; +import org.hl7.fhir.instance.model.api.IBaseBundle; +import org.hl7.fhir.instance.model.api.IBaseReference; +import org.hl7.fhir.instance.model.api.IBaseResource; +import org.springframework.cache.annotation.CacheConfig; +import org.springframework.cache.annotation.Cacheable; + +import javax.annotation.Nonnull; +import javax.annotation.Nullable; +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashSet; +import java.util.List; +import java.util.Set; +import java.util.UUID; +import java.util.function.Function; + +/** + * Implementation of {@link RemoteHierarchicallyFhirRepository}. + * + * @author volsch + */ +@CacheConfig( cacheNames = "hierarchicallyFhirResources", cacheManager = "fhirCacheManager" ) +public abstract class AbstractRemoteHierarchicallyFhirRepositoryImpl implements RemoteHierarchicallyFhirRepository +{ + private final RemoteFhirRepository remoteFhirRepository; + + public AbstractRemoteHierarchicallyFhirRepositoryImpl( @Nonnull RemoteFhirRepository remoteFhirRepository ) + { + this.remoteFhirRepository = remoteFhirRepository; + } + + @Nonnull + @Cacheable( key = "{#remoteSubscriptionId, #fhirVersion, #resourceType, #resourceId, #hierarchyType}" ) + @Override + public IBaseBundle findWithParents( @Nonnull UUID remoteSubscriptionId, @Nonnull FhirVersion fhirVersion, @Nonnull SubscriptionFhirEndpoint fhirEndpoint, + @Nonnull String resourceType, @Nullable String resourceId, @Nonnull String hierarchyType, + @Nonnull Function parentReferenceFunction ) + { + if ( resourceId == null ) + { + return createBundle( Collections.emptyList() ); + } + + IBaseResource child = remoteFhirRepository.find( remoteSubscriptionId, fhirVersion, fhirEndpoint, resourceType, resourceId ).orElse( null ); + if ( child == null ) + { + return createBundle( Collections.emptyList() ); + } + + final Set processedResourceIds = new HashSet<>(); + processedResourceIds.add( resourceType + "/" + resourceId ); + final List result = new ArrayList<>(); + result.add( child ); + + IBaseReference parentReference; + while ( (child != null) && ((parentReference = parentReferenceFunction.apply( child )) != null) && !parentReference.isEmpty() && parentReference.getReferenceElement().hasIdPart() ) + { + final String childResourceType = parentReference.getReferenceElement().hasResourceType() ? + parentReference.getReferenceElement().getResourceType() : resourceType; + if ( !processedResourceIds.add( childResourceType + "/" + parentReference.getReferenceElement().getIdPart() ) ) + { + // there is a dependency loop and search must be interrupted + break; + } + child = remoteFhirRepository.find( remoteSubscriptionId, fhirVersion, fhirEndpoint, + childResourceType, parentReference.getReferenceElement().getIdPart() ).orElse( null ); + if ( child != null ) + { + result.add( child ); + } + } + + return createBundle( result ); + } + + @Nonnull + protected abstract IBaseBundle createBundle( @Nonnull List resources ); +} diff --git a/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/impl/AbstractFhirToDhisTransformer.java b/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/impl/AbstractFhirToDhisTransformer.java index 2fad23bc..b12b32c4 100644 --- a/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/impl/AbstractFhirToDhisTransformer.java +++ b/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/impl/AbstractFhirToDhisTransformer.java @@ -225,7 +225,7 @@ protected IBaseResource getTrackedEntityInstanceResource( @Nonnull FhirToDhisTra protected Optional getTrackedEntityInstanceByIdentifier( @Nonnull FhirToDhisTransformerContext context, @Nonnull TrackedEntityRule rule, @Nonnull IBaseResource baseResource, @Nonnull Map scriptVariables, boolean sync ) throws TransformerException { - final String identifier = getIdentifier( context, baseResource, rule.isTrackedEntityIdentifierFq(), scriptVariables ); + final String identifier = getIdentifier( context, baseResource, scriptVariables ); if ( identifier == null ) { return Optional.empty(); @@ -256,7 +256,7 @@ protected Optional getTrackedEntityInstanceByIdentifier( } @Nullable - protected String getIdentifier( @Nonnull FhirToDhisTransformerContext context, IBaseResource baseResource, boolean identifierFullQualified, @Nonnull Map scriptVariables ) + protected String getIdentifier( @Nonnull FhirToDhisTransformerContext context, IBaseResource baseResource, @Nonnull Map scriptVariables ) { if ( !(baseResource instanceof IDomainResource) ) { @@ -279,10 +279,6 @@ protected String getIdentifier( @Nonnull FhirToDhisTransformerContext context, I logger.info( "Resource " + resourceType + " does not include the required identifier with system: " + resourceSystem.getSystem() ); return null; } - if ( identifierFullQualified ) - { - throw new TransformerMappingException( "Full qualified identifiers are required that are not yet supported." ); - } return identifier; } diff --git a/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/impl/trackedentity/FhirToTrackedEntityTransformer.java b/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/impl/trackedentity/FhirToTrackedEntityTransformer.java index bdac64ca..9dca1042 100644 --- a/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/impl/trackedentity/FhirToTrackedEntityTransformer.java +++ b/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/impl/trackedentity/FhirToTrackedEntityTransformer.java @@ -206,6 +206,6 @@ protected TrackedEntityInstance createResource( @Nonnull FhirToDhisTransformerCo protected String getIdentifier( @Nonnull FhirToDhisTransformerContext context, @Nonnull TrackedEntityRule rule, @Nonnull Map scriptVariables ) { final IBaseResource baseResource = getScriptVariable( scriptVariables, ScriptVariable.INPUT, IBaseResource.class ); - return getIdentifier( context, baseResource, rule.isTrackedEntityIdentifierFq(), scriptVariables ); + return getIdentifier( context, baseResource, scriptVariables ); } } diff --git a/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/impl/util/AbstractOrganizationTransformerUtils.java b/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/impl/util/AbstractOrganizationTransformerUtils.java new file mode 100644 index 00000000..a9cafd81 --- /dev/null +++ b/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/impl/util/AbstractOrganizationTransformerUtils.java @@ -0,0 +1,181 @@ +package org.dhis2.fhir.adapter.fhir.transform.impl.util; + +/* + * 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 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 org.apache.commons.lang.StringUtils; +import org.dhis2.fhir.adapter.dhis.model.Reference; +import org.dhis2.fhir.adapter.dhis.model.ReferenceType; +import org.dhis2.fhir.adapter.dhis.orgunit.OrganisationUnitService; +import org.dhis2.fhir.adapter.fhir.metadata.model.FhirResourceType; +import org.dhis2.fhir.adapter.fhir.metadata.model.RemoteSubscription; +import org.dhis2.fhir.adapter.fhir.metadata.model.RemoteSubscriptionResource; +import org.dhis2.fhir.adapter.fhir.metadata.model.ScriptVariable; +import org.dhis2.fhir.adapter.fhir.metadata.repository.RemoteSubscriptionResourceRepository; +import org.dhis2.fhir.adapter.fhir.repository.RemoteFhirRepository; +import org.dhis2.fhir.adapter.fhir.repository.RemoteHierarchicallyFhirRepository; +import org.dhis2.fhir.adapter.fhir.script.ScriptExecutionContext; +import org.dhis2.fhir.adapter.fhir.script.ScriptExecutionRequired; +import org.dhis2.fhir.adapter.fhir.transform.FatalTransformerException; +import org.dhis2.fhir.adapter.fhir.transform.FhirToDhisTransformerContext; +import org.dhis2.fhir.adapter.fhir.transform.TransformerDataException; +import org.dhis2.fhir.adapter.fhir.transform.TransformerMappingException; +import org.dhis2.fhir.adapter.fhir.transform.model.ResourceSystem; +import org.hl7.fhir.instance.model.api.IBaseBundle; +import org.hl7.fhir.instance.model.api.IBaseReference; +import org.hl7.fhir.instance.model.api.IBaseResource; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import javax.annotation.Nonnull; +import javax.annotation.Nullable; +import java.util.List; +import java.util.UUID; + +/** + * Transformer utilities for organization. + * + * @author volsch + */ +public abstract class AbstractOrganizationTransformerUtils extends AbstractFhirToDhisTransformerUtils +{ + private static final String SCRIPT_ATTR_NAME = "organizationUtils"; + + private final Logger logger = LoggerFactory.getLogger( getClass() ); + + private final OrganisationUnitService organisationUnitService; + + private final RemoteSubscriptionResourceRepository subscriptionResourceRepository; + + private final RemoteFhirRepository remoteFhirRepository; + + private final RemoteHierarchicallyFhirRepository remoteHierarchicallyFhirRepository; + + public AbstractOrganizationTransformerUtils( @Nonnull ScriptExecutionContext scriptExecutionContext, + @Nonnull OrganisationUnitService organisationUnitService, + @Nonnull RemoteSubscriptionResourceRepository subscriptionResourceRepository, + @Nonnull RemoteFhirRepository remoteFhirRepository, + @Nonnull RemoteHierarchicallyFhirRepository remoteHierarchicallyFhirRepository ) + { + super( scriptExecutionContext ); + this.organisationUnitService = organisationUnitService; + this.subscriptionResourceRepository = subscriptionResourceRepository; + this.remoteFhirRepository = remoteFhirRepository; + this.remoteHierarchicallyFhirRepository = remoteHierarchicallyFhirRepository; + } + + @Nonnull + @Override + public String getScriptAttrName() + { + return SCRIPT_ATTR_NAME; + } + + @ScriptExecutionRequired + public boolean exists( @Nullable String code ) + { + if ( code == null ) + { + return false; + } + + final FhirToDhisTransformerContext context = getScriptVariable( ScriptVariable.CONTEXT.getVariableName(), FhirToDhisTransformerContext.class ); + final ResourceSystem resourceSystem = context.getFhirRequest().getOptionalResourceSystem( FhirResourceType.ORGANIZATION ) + .orElseThrow( () -> new TransformerMappingException( "No system has been defined for resource type " + FhirResourceType.ORGANIZATION + "." ) ); + + return organisationUnitService.findOneByReference( new Reference( code, ReferenceType.CODE ) ).isPresent(); + } + + @ScriptExecutionRequired + @Nullable + public String existsWithPrefix( @Nullable String code ) + { + if ( code == null ) + { + return null; + } + + final FhirToDhisTransformerContext context = getScriptVariable( ScriptVariable.CONTEXT.getVariableName(), FhirToDhisTransformerContext.class ); + final ResourceSystem resourceSystem = context.getFhirRequest().getOptionalResourceSystem( FhirResourceType.ORGANIZATION ) + .orElseThrow( () -> new TransformerMappingException( "No system has been defined for resource type " + FhirResourceType.ORGANIZATION + "." ) ); + + final String resultingCode = StringUtils.defaultString( resourceSystem.getCodePrefix() ) + code; + if ( organisationUnitService.findOneByReference( new Reference( resultingCode, ReferenceType.CODE ) ).isPresent() ) + { + return resultingCode; + } + return null; + } + + @Nullable + public List findHierarchy( @Nullable IBaseReference childReference ) + { + if ( (childReference == null) || childReference.isEmpty() ) + { + return null; + } + + if ( childReference.getReferenceElement().isLocal() ) + { + throw new TransformerDataException( "Reference element refers to an embedded resource, but no resource is specified: " + childReference.getReferenceElement() ); + } + if ( childReference.getReferenceElement().hasResourceType() && !FhirResourceType.ORGANIZATION.getResourceTypeName().equals( childReference.getReferenceElement().getResourceType() ) ) + { + throw new TransformerDataException( "Reference element does not refer to a FHIR Organization: " + childReference.getReferenceElement() ); + } + if ( !childReference.getReferenceElement().hasIdPart() ) + { + throw new TransformerDataException( "Reference element does not include an ID part: " + childReference.getReferenceElement() ); + } + + final FhirToDhisTransformerContext context = getScriptVariable( ScriptVariable.CONTEXT.getVariableName(), FhirToDhisTransformerContext.class ); + final UUID resourceId = context.getFhirRequest().getRemoteSubscriptionResourceId(); + if ( resourceId == null ) + { + throw new TransformerMappingException( "FHIR client cannot be created without having a remote request." ); + } + final RemoteSubscriptionResource subscriptionResource = subscriptionResourceRepository.findById( resourceId ) + .orElseThrow( () -> new TransformerMappingException( "Could not find remote subscription resource with ID " + resourceId ) ); + + final FhirContext fhirContext = remoteFhirRepository.findFhirContext( context.getFhirRequest().getVersion() ) + .orElseThrow( () -> new FatalTransformerException( "FHIR context for FHIR version " + context.getFhirRequest().getVersion() + " is not available." ) ); + final RemoteSubscription remoteSubscription = subscriptionResource.getRemoteSubscription(); + + final IBaseBundle hierarchyBundle = remoteHierarchicallyFhirRepository.findWithParents( remoteSubscription.getId(), remoteSubscription.getFhirVersion(), remoteSubscription.getFhirEndpoint(), + FhirResourceType.ORGANIZATION.getResourceTypeName(), childReference.getReferenceElement().getIdPart(), "organizationPartOf", this::getParentReference ); + final List hierarchy = extractResources( hierarchyBundle ); + return hierarchy.isEmpty() ? null : BeanTransformerUtils.clone( fhirContext, hierarchy ); + } + + @Nullable + protected abstract IBaseReference getParentReference( @Nullable IBaseResource resource ); + + @Nonnull + protected abstract List extractResources( @Nullable IBaseBundle bundle ); +} \ No newline at end of file diff --git a/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/impl/util/BeanTransformerUtils.java b/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/impl/util/BeanTransformerUtils.java index 9843b2fc..6f9b5daf 100644 --- a/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/impl/util/BeanTransformerUtils.java +++ b/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/impl/util/BeanTransformerUtils.java @@ -34,6 +34,9 @@ import javax.annotation.Nonnull; import javax.annotation.Nullable; +import java.util.Collections; +import java.util.List; +import java.util.stream.Collectors; /** * Transformer utilities that clone a bean (cached instances must not be modified @@ -55,6 +58,20 @@ public static T clone( @Nonnull FhirContext fhirContex return (T) parser.parseResource( object.getClass(), parser.encodeResourceToString( object ) ); } + @Nullable + public static List clone( @Nonnull FhirContext fhirContext, @Nullable List objects ) + { + if ( objects == null ) + { + return null; + } + if ( objects.isEmpty() ) + { + return Collections.emptyList(); + } + return objects.stream().map( o -> clone( fhirContext, o ) ).collect( Collectors.toList() ); + } + private BeanTransformerUtils() { super(); diff --git a/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/impl/util/OrganizationUnitTransformerUtils.java b/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/impl/util/OrganizationUnitTransformerUtils.java deleted file mode 100644 index 62dc3543..00000000 --- a/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/impl/util/OrganizationUnitTransformerUtils.java +++ /dev/null @@ -1,106 +0,0 @@ -package org.dhis2.fhir.adapter.fhir.transform.impl.util; - -/* - * 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 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.lang.StringUtils; -import org.dhis2.fhir.adapter.dhis.model.Reference; -import org.dhis2.fhir.adapter.dhis.model.ReferenceType; -import org.dhis2.fhir.adapter.dhis.orgunit.OrganisationUnitService; -import org.dhis2.fhir.adapter.fhir.metadata.model.FhirResourceType; -import org.dhis2.fhir.adapter.fhir.metadata.model.ScriptVariable; -import org.dhis2.fhir.adapter.fhir.model.FhirVersion; -import org.dhis2.fhir.adapter.fhir.script.ScriptExecutionContext; -import org.dhis2.fhir.adapter.fhir.script.ScriptExecutionRequired; -import org.dhis2.fhir.adapter.fhir.transform.FhirToDhisTransformerContext; -import org.dhis2.fhir.adapter.fhir.transform.TransformerMappingException; -import org.dhis2.fhir.adapter.fhir.transform.model.ResourceSystem; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.stereotype.Component; - -import javax.annotation.Nonnull; -import javax.annotation.Nullable; -import java.util.Set; - -/** - * Transformer utilities for organization units. - * - * @author volsch - */ -@Component -public class OrganizationUnitTransformerUtils extends AbstractFhirToDhisTransformerUtils -{ - private static final String SCRIPT_ATTR_NAME = "organizationUnitUtils"; - - private final Logger logger = LoggerFactory.getLogger( getClass() ); - - private final OrganisationUnitService organisationUnitService; - - public OrganizationUnitTransformerUtils( @Nonnull ScriptExecutionContext scriptExecutionContext, - @Nonnull OrganisationUnitService organisationUnitService ) - { - super( scriptExecutionContext ); - this.organisationUnitService = organisationUnitService; - } - - @Nonnull - @Override - public Set getFhirVersions() - { - return FhirVersion.ALL; - } - - @Nonnull - @Override - public String getScriptAttrName() - { - return SCRIPT_ATTR_NAME; - } - - @ScriptExecutionRequired - @Nullable - public String existsWithPrefix( @Nullable String code ) - { - if ( code == null ) - { - return null; - } - - final FhirToDhisTransformerContext context = getScriptVariable( ScriptVariable.CONTEXT.getVariableName(), FhirToDhisTransformerContext.class ); - final ResourceSystem resourceSystem = context.getFhirRequest().getOptionalResourceSystem( FhirResourceType.ORGANIZATION ) - .orElseThrow( () -> new TransformerMappingException( "No system has been defined for resource type " + FhirResourceType.ORGANIZATION + "." ) ); - - final String resultingCode = StringUtils.defaultString( resourceSystem.getCodePrefix() ) + code; - if ( organisationUnitService.findOneByReference( new Reference( resultingCode, ReferenceType.CODE ) ).isPresent() ) - { - return resultingCode; - } - return null; - } -} \ No newline at end of file diff --git a/fhir/src/main/resources/db/migration/production/V1.0.0.0_0_1__Initial.sql b/fhir/src/main/resources/db/migration/production/V1.0.0.0_0_1__Initial.sql index d4e1644a..1ba3dce8 100644 --- a/fhir/src/main/resources/db/migration/production/V1.0.0.0_0_1__Initial.sql +++ b/fhir/src/main/resources/db/migration/production/V1.0.0.0_0_1__Initial.sql @@ -97,13 +97,13 @@ CREATE TABLE fhir_version_enum ( COMMENT ON TABLE fhir_version_enum IS 'Enumeration values with supported FHIR versions.'; INSERT INTO fhir_version_enum VALUES('DSTU3'); -CREATE TABLE subscription_type_enum ( +CREATE TABLE fhir_subscription_type_enum ( value VARCHAR(30) NOT NULL, - CONSTRAINT subscription_type_enum_pk PRIMARY KEY(value) + CONSTRAINT fhir_subscription_type_enum_pk PRIMARY KEY(value) ); -COMMENT ON TABLE subscription_type_enum IS 'Enumeration values with supported FHIR subscription types.'; -INSERT INTO subscription_type_enum VALUES('REST_HOOK'); -INSERT INTO subscription_type_enum VALUES('REST_HOOK_WITH_JSON_PAYLOAD'); +COMMENT ON TABLE fhir_subscription_type_enum IS 'Enumeration values with supported FHIR subscription types.'; +INSERT INTO fhir_subscription_type_enum VALUES('REST_HOOK'); +INSERT INTO fhir_subscription_type_enum VALUES('REST_HOOK_WITH_JSON_PAYLOAD'); CREATE TABLE fhir_event_status_enum ( value VARCHAR(30) NOT NULL, @@ -572,7 +572,6 @@ CREATE TABLE fhir_tracked_entity_rule ( org_lookup_script_id UUID NOT NULL, loc_lookup_script_id UUID NOT NULL, tracked_entity_identifier_ref VARCHAR(230) NOT NULL, - tracked_entity_identifier_fq BOOLEAN NOT NULL DEFAULT FALSE, CONSTRAINT fhir_tracked_entity_rule_pk PRIMARY KEY (id), CONSTRAINT fhir_tracked_entity_rule_fk1 FOREIGN KEY (id) REFERENCES fhir_rule (id) ON DELETE CASCADE, CONSTRAINT fhir_tracked_entity_rule_fk2 FOREIGN KEY (org_lookup_script_id) REFERENCES fhir_executable_script (id), @@ -588,7 +587,6 @@ COMMENT ON COLUMN fhir_tracked_entity_rule.tracked_entity_ref IS 'The reference COMMENT ON COLUMN fhir_tracked_entity_rule.org_lookup_script_id IS 'References the executable lookup script for DHIS2 Organization Units.'; COMMENT ON COLUMN fhir_tracked_entity_rule.loc_lookup_script_id IS 'References the executable lookup script for DHIS2 coordinates (longitude and latitude).'; COMMENT ON COLUMN fhir_tracked_entity_rule.tracked_entity_identifier_ref IS 'The reference of the DHIS2 Tracked Entity Attribute that includes the identifier value that is also used by FHIR resources (e.g. NAME:National identifier).'; -COMMENT ON COLUMN fhir_tracked_entity_rule.tracked_entity_identifier_fq IS 'Specifies if the identifier that is stored in the DHIS2 Tracked Entity Attribute contains also the system URI.'; CREATE TABLE fhir_tracker_program ( id UUID NOT NULL DEFAULT UUID_GENERATE_V4(), @@ -850,7 +848,7 @@ CREATE TABLE fhir_remote_subscription ( CONSTRAINT fhir_remote_subscription_uk_code UNIQUE (code), CONSTRAINT fhir_remote_subscription_fk1 FOREIGN KEY (fhir_version) REFERENCES fhir_version_enum(value), CONSTRAINT fhir_remote_subscription_fk2 FOREIGN KEY (dhis_authentication_method) REFERENCES fhir_authentication_method_enum(value), - CONSTRAINT fhir_remote_subscription_fk3 FOREIGN KEY (subscription_type) REFERENCES subscription_type_enum(value) + CONSTRAINT fhir_remote_subscription_fk3 FOREIGN KEY (subscription_type) REFERENCES fhir_subscription_type_enum(value) ); COMMENT ON TABLE fhir_remote_subscription IS 'Contains FHIR Services on which the adapter has one or more FHIR Subscriptions.'; COMMENT ON COLUMN fhir_remote_subscription.id IS 'Unique ID of entity (also used as first part of web hook URI).'; @@ -1594,15 +1592,22 @@ if (((organizationReference == null) || organizationReference.isEmpty()) && args } if (organizationReference != null) { - var code = identifierUtils.getReferenceIdentifier(organizationReference, ''ORGANIZATION''); - var mappedCode; - if (code != null) + var mappedCode = null; + var hierarchy = organizationUtils.findHierarchy(organizationReference); + if (hierarchy != null) { - mappedCode = codeUtils.getMappedCode(code, ''ORGANIZATION''); - if ((mappedCode == null) && args[''useIdentifierCode'']) - { - mappedCode = organizationUnitUtils.existsWithPrefix(code); - } + for (var i = 0; (mappedCode == null) && (i < hierarchy.size()); i++) + { + var code = identifierUtils.getResourceIdentifier(hierarchy.get(i), ''ORGANIZATION''); + if (code != null) + { + mappedCode = codeUtils.getMappedCode(code, ''ORGANIZATION''); + if ((mappedCode == null) && args[''useIdentifierCode'']) + { + mappedCode = organizationUtils.existsWithPrefix(code); + } + } + } } if (mappedCode == null) { @@ -2005,8 +2010,8 @@ VALUES ('081c4642-bb83-44ab-b90f-aa206ad347aa', 'DSTU3'); -- Rule FHIR Patient to tracked entity type Person INSERT INTO fhir_rule (id, version, name, description, enabled, evaluation_order, fhir_resource_type, dhis_resource_type, applicable_in_script_id, transform_in_script_id) VALUES ('5f9ebdc9-852e-4c83-87ca-795946aabc35', 0, 'FHIR Patient to Person', NULL, TRUE, 0, 'PATIENT', 'TRACKED_ENTITY', '9299b82e-b90a-4542-8b78-200cadff3d7d', '72451c8f-7492-4707-90b8-a3e0796de19e'); -INSERT INTO fhir_tracked_entity_rule (id, tracked_entity_ref, org_lookup_script_id, loc_lookup_script_id, tracked_entity_identifier_ref, tracked_entity_identifier_fq) -VALUES ('5f9ebdc9-852e-4c83-87ca-795946aabc35', 'NAME:Person', '25a97bb4-7b39-4ed4-8677-db4bcaa28ccf', 'ef90531f-4438-48bd-83b3-6370dd65875a', 'CODE:National identifier', FALSE); +INSERT INTO fhir_tracked_entity_rule (id, tracked_entity_ref, org_lookup_script_id, loc_lookup_script_id, tracked_entity_identifier_ref) +VALUES ('5f9ebdc9-852e-4c83-87ca-795946aabc35', 'NAME:Person', '25a97bb4-7b39-4ed4-8677-db4bcaa28ccf', 'ef90531f-4438-48bd-83b3-6370dd65875a', 'CODE:National identifier'); UPDATE fhir_system_code sc SET system_code_value = (SELECT system_uri FROM fhir_system s WHERE s.id=sc.system_id) || '|' || system_code; ALTER TABLE fhir_system_code ALTER COLUMN system_code_value SET NOT NULL; diff --git a/pom.xml b/pom.xml index d2a37af5..42325063 100644 --- a/pom.xml +++ b/pom.xml @@ -53,7 +53,7 @@ - classpath:db/migration/production,classpath:db/migration/programs + classpath:db/migration/production jdbc:postgresql://localhost/dhis2-fhir dhis-fhir dhis-fhir