diff --git a/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/repository/impl/FhirRepositoryImpl.java b/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/repository/impl/FhirRepositoryImpl.java index 23e74435..0f6a3ef3 100644 --- a/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/repository/impl/FhirRepositoryImpl.java +++ b/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/repository/impl/FhirRepositoryImpl.java @@ -55,6 +55,7 @@ import org.dhis2.fhir.adapter.fhir.repository.FhirRepositoryOperation; import org.dhis2.fhir.adapter.fhir.repository.FhirRepositoryOperationOutcome; import org.dhis2.fhir.adapter.fhir.repository.FhirResourceRepository; +import org.dhis2.fhir.adapter.fhir.transform.DhisDataExistsException; import org.dhis2.fhir.adapter.fhir.transform.FatalTransformerException; import org.dhis2.fhir.adapter.fhir.transform.TransformerDataException; import org.dhis2.fhir.adapter.fhir.transform.TransformerMappingException; @@ -147,7 +148,7 @@ public FhirRepositoryImpl( @Nonnull AuthorizationContext authorizationContext, @Override @Nullable - @HystrixCommand( ignoreExceptions = { DhisConflictException.class, TransformerDataException.class, TransformerMappingException.class, TrackedEntityInstanceNotFoundException.class } ) + @HystrixCommand( ignoreExceptions = { DhisConflictException.class, TransformerDataException.class, TransformerMappingException.class, TrackedEntityInstanceNotFoundException.class, DhisDataExistsException.class } ) @Transactional( propagation = Propagation.NOT_SUPPORTED ) public FhirRepositoryOperationOutcome save( @Nonnull FhirClientResource fhirClientResource, @Nonnull IBaseResource resource, @Nullable FhirRepositoryOperation fhirRepositoryOperation ) { diff --git a/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/server/RepositoryExceptionInterceptor.java b/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/server/RepositoryExceptionInterceptor.java index 62d634b6..9eb35f59 100644 --- a/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/server/RepositoryExceptionInterceptor.java +++ b/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/server/RepositoryExceptionInterceptor.java @@ -34,6 +34,7 @@ import ca.uhn.fhir.rest.server.exceptions.UnprocessableEntityException; import ca.uhn.fhir.rest.server.interceptor.InterceptorAdapter; import org.dhis2.fhir.adapter.dhis.DhisConflictException; +import org.dhis2.fhir.adapter.fhir.transform.DhisDataExistsException; import org.dhis2.fhir.adapter.fhir.transform.TransformerDataException; import org.dhis2.fhir.adapter.fhir.transform.dhis.DhisToFhirDataProviderException; import org.dhis2.fhir.adapter.fhir.transform.fhir.TrackedEntityInstanceNotFoundException; @@ -62,7 +63,7 @@ public BaseServerResponseException preProcessOutgoingException( RequestDetails t { final BaseServerResponseException result; final Throwable unprocessableEntityException = ExceptionUtils.findCause( theException, DhisConflictException.class, - TransformerDataException.class, TrackedEntityInstanceNotFoundException.class, DhisToFhirDataProviderException.class ); + TransformerDataException.class, TrackedEntityInstanceNotFoundException.class, DhisToFhirDataProviderException.class, DhisDataExistsException.class ); if ( unprocessableEntityException != null ) { log.info( "Request could not be processed because of an error: {}", unprocessableEntityException.getMessage() ); diff --git a/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/DhisDataExistsException.java b/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/DhisDataExistsException.java new file mode 100644 index 00000000..c48b4c66 --- /dev/null +++ b/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/DhisDataExistsException.java @@ -0,0 +1,44 @@ +package org.dhis2.fhir.adapter.fhir.transform; + +/* + * 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. + */ + +/** + * Thrown if the data to be transformed exists already and cannot be created again. + * + * @author volsch + */ +public class DhisDataExistsException extends RuntimeException +{ + private static final long serialVersionUID = 507760404334717694L; + + public DhisDataExistsException( String message ) + { + super( message ); + } +} diff --git a/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/fhir/impl/AbstractFhirToDhisTransformer.java b/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/fhir/impl/AbstractFhirToDhisTransformer.java index 9d74adfa..9a518470 100644 --- a/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/fhir/impl/AbstractFhirToDhisTransformer.java +++ b/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/fhir/impl/AbstractFhirToDhisTransformer.java @@ -136,30 +136,34 @@ public Set getFhirVersions() protected Optional getResource( @Nonnull FhirClientResource fhirClientResource, @Nonnull FhirToDhisTransformerContext context, @Nonnull RuleInfo ruleInfo, @Nonnull Map scriptVariables ) throws TransformerException { - if ( context.getFhirRequest().getRequestMethod() != FhirRequestMethod.CREATE ) + boolean activeResource = true; + if ( context.getFhirRequest().isDhisFhirId() ) { - if ( context.getFhirRequest().isDhisFhirId() ) + final String id = getDhisId( context, ruleInfo ); + if ( id != null ) { - final String id = getDhisId( context, ruleInfo ); R resource = getResourceById( id ).orElse( null ); if ( resource != null ) { return Optional.of( resource ); } + activeResource = false; } - else + } + else + { + final R resource = getResourceByAssignment( fhirClientResource, context, ruleInfo, scriptVariables ).orElse( null ); + if ( resource != null ) { - R resource = getResourceByAssignment( fhirClientResource, context, ruleInfo, scriptVariables ).orElse( null ); - if ( resource != null ) - { - return Optional.of( resource ); - } - - resource = getActiveResource( context, ruleInfo, scriptVariables, false ).orElse( null ); - if ( resource != null ) - { - return Optional.of( resource ); - } + return Optional.of( resource ); + } + } + if ( activeResource ) + { + final R resource = getActiveResource( context, ruleInfo, scriptVariables, false ).orElse( null ); + if ( resource != null ) + { + return Optional.of( resource ); } } diff --git a/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/fhir/impl/program/FhirToProgramStageTransformer.java b/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/fhir/impl/program/FhirToProgramStageTransformer.java index de3fe5a9..44aecef7 100644 --- a/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/fhir/impl/program/FhirToProgramStageTransformer.java +++ b/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/fhir/impl/program/FhirToProgramStageTransformer.java @@ -28,6 +28,7 @@ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ +import org.apache.commons.lang3.StringUtils; import org.dhis2.fhir.adapter.dhis.converter.ValueConverter; import org.dhis2.fhir.adapter.dhis.model.DhisResourceType; import org.dhis2.fhir.adapter.dhis.model.Reference; @@ -43,6 +44,7 @@ import org.dhis2.fhir.adapter.dhis.tracker.program.Program; import org.dhis2.fhir.adapter.dhis.tracker.program.ProgramMetadataService; import org.dhis2.fhir.adapter.dhis.tracker.program.ProgramStage; +import org.dhis2.fhir.adapter.dhis.tracker.program.ProgramStageDataElement; import org.dhis2.fhir.adapter.dhis.tracker.program.ProgramTrackedEntityAttribute; import org.dhis2.fhir.adapter.dhis.tracker.trackedentity.TrackedEntityAttributes; import org.dhis2.fhir.adapter.dhis.tracker.trackedentity.TrackedEntityInstance; @@ -54,17 +56,20 @@ import org.dhis2.fhir.adapter.fhir.metadata.model.FhirResourceMapping; import org.dhis2.fhir.adapter.fhir.metadata.model.MappedTrackerProgram; import org.dhis2.fhir.adapter.fhir.metadata.model.ProgramStageRule; +import org.dhis2.fhir.adapter.fhir.metadata.model.RuleDhisDataReference; import org.dhis2.fhir.adapter.fhir.metadata.model.RuleInfo; import org.dhis2.fhir.adapter.fhir.metadata.model.ScriptVariable; import org.dhis2.fhir.adapter.fhir.metadata.repository.FhirResourceMappingRepository; import org.dhis2.fhir.adapter.fhir.model.EventDecisionType; import org.dhis2.fhir.adapter.fhir.script.ScriptExecutor; +import org.dhis2.fhir.adapter.fhir.transform.DhisDataExistsException; import org.dhis2.fhir.adapter.fhir.transform.FatalTransformerException; import org.dhis2.fhir.adapter.fhir.transform.TransformerException; import org.dhis2.fhir.adapter.fhir.transform.TransformerMappingException; import org.dhis2.fhir.adapter.fhir.transform.fhir.FhirToDhisTransformOutcome; import org.dhis2.fhir.adapter.fhir.transform.fhir.FhirToDhisTransformerContext; import org.dhis2.fhir.adapter.fhir.transform.fhir.impl.AbstractFhirToDhisTransformer; +import org.dhis2.fhir.adapter.fhir.transform.fhir.model.FhirRequestMethod; import org.dhis2.fhir.adapter.fhir.transform.scripted.ImmutableScriptedEnrollment; import org.dhis2.fhir.adapter.fhir.transform.scripted.ScriptedEnrollment; import org.dhis2.fhir.adapter.fhir.transform.scripted.ScriptedTrackedEntityInstance; @@ -200,6 +205,12 @@ public FhirToDhisTransformOutcome transform( @Nonnull FhirClientResource return null; } + boolean oldEmpty = true; + if ( !event.isNewResource() ) + { + oldEmpty = !hasRequiredDataElements( context, ruleInfo, programStage, event ); + } + if ( !isStatusApplicable( ruleInfo, event ) ) { return null; @@ -218,6 +229,10 @@ public FhirToDhisTransformOutcome transform( @Nonnull FhirClientResource { return null; } + if ( !oldEmpty && ( context.getFhirRequest().getRequestMethod() == FhirRequestMethod.CREATE ) ) + { + throw new DhisDataExistsException( "Required data elements contain data already." ); + } updateCoordinates( context, ruleInfo, resourceMapping, program, scriptedEnrollment, programStage, scriptedEvent, variables ); if ( !afterEvent( context, ruleInfo, programStage, scriptVariables ) || @@ -290,6 +305,35 @@ protected void updateCoordinates( @Nonnull FhirToDhisTransformerContext context, } } + protected boolean hasRequiredDataElements( @Nonnull FhirToDhisTransformerContext context, @Nonnull RuleInfo ruleInfo, @Nonnull ProgramStage programStage, @Nonnull Event event ) + { + if ( event.isNewResource() ) + { + return false; + } + boolean anyRequired = false; + for ( RuleDhisDataReference r : ruleInfo.getDhisDataReferences() ) + { + if ( r.isRequired() ) + { + if ( !hasDataElementValue( context, ruleInfo, programStage, event, r.getDataReference() ) ) + { + return false; + } + anyRequired = true; + } + } + return anyRequired; + } + + protected boolean hasDataElementValue( @Nonnull FhirToDhisTransformerContext context, @Nonnull RuleInfo ruleInfo, @Nonnull ProgramStage programStage, @Nonnull Event event, @Nonnull Reference dataElementRef ) + { + final ProgramStageDataElement dataElement = programStage.getOptionalDataElement( dataElementRef ) + .orElseThrow( () -> new TransformerMappingException( "Rule " + ruleInfo + " contains data element reference " + dataElementRef + " that is not defined by program stage \"" + programStage.getName() + "\"." ) ); + final Object value = event.getDataValue( dataElement.getElementId() ).getValue(); + return ( value != null ) && ( !( value instanceof String ) || StringUtils.isNotBlank( (String) value ) ); + } + @Override protected boolean isSyncRequired( @Nonnull FhirToDhisTransformerContext context, @Nonnull RuleInfo ruleInfo, @Nonnull Map scriptVariables ) throws TransformerException {