diff --git a/dhis/src/main/java/org/dhis2/fhir/adapter/dhis/model/ImmutableDataElement.java b/dhis/src/main/java/org/dhis2/fhir/adapter/dhis/model/ImmutableDataElement.java index 31570d67..cc2cd94a 100644 --- a/dhis/src/main/java/org/dhis2/fhir/adapter/dhis/model/ImmutableDataElement.java +++ b/dhis/src/main/java/org/dhis2/fhir/adapter/dhis/model/ImmutableDataElement.java @@ -45,7 +45,7 @@ * @author volsch */ @Scriptable -public class ImmutableDataElement implements DataElement, Serializable +public class ImmutableDataElement implements DataElement, ImmutableDhisObject, Serializable { private static final long serialVersionUID = -7040983692879273412L; diff --git a/dhis/src/main/java/org/dhis2/fhir/adapter/dhis/model/ImmutableDataValue.java b/dhis/src/main/java/org/dhis2/fhir/adapter/dhis/model/ImmutableDataValue.java index 25dfb768..b4f19108 100644 --- a/dhis/src/main/java/org/dhis2/fhir/adapter/dhis/model/ImmutableDataValue.java +++ b/dhis/src/main/java/org/dhis2/fhir/adapter/dhis/model/ImmutableDataValue.java @@ -43,7 +43,7 @@ * * @author volsch */ -public class ImmutableDataValue implements DataValue, Serializable +public class ImmutableDataValue implements DataValue, ImmutableDhisObject, Serializable { private static final long serialVersionUID = 5577400306112844124L; diff --git a/dhis/src/main/java/org/dhis2/fhir/adapter/dhis/model/ImmutableDhisObject.java b/dhis/src/main/java/org/dhis2/fhir/adapter/dhis/model/ImmutableDhisObject.java new file mode 100644 index 00000000..04be75e8 --- /dev/null +++ b/dhis/src/main/java/org/dhis2/fhir/adapter/dhis/model/ImmutableDhisObject.java @@ -0,0 +1,38 @@ +package org.dhis2.fhir.adapter.dhis.model; + +/* + * 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. + */ + +/** + * Marker interface that marks immutable DHIS objects. + * + * @author volsch + */ +public interface ImmutableDhisObject +{ +} diff --git a/dhis/src/main/java/org/dhis2/fhir/adapter/dhis/model/ImmutableOption.java b/dhis/src/main/java/org/dhis2/fhir/adapter/dhis/model/ImmutableOption.java index 59f22987..f4d1a10a 100644 --- a/dhis/src/main/java/org/dhis2/fhir/adapter/dhis/model/ImmutableOption.java +++ b/dhis/src/main/java/org/dhis2/fhir/adapter/dhis/model/ImmutableOption.java @@ -42,7 +42,7 @@ * * @author volsch */ -public class ImmutableOption implements Option, Serializable +public class ImmutableOption implements Option, ImmutableDhisObject, Serializable { private static final long serialVersionUID = -4836329163657710844L; diff --git a/dhis/src/main/java/org/dhis2/fhir/adapter/dhis/model/ImmutableOptionSet.java b/dhis/src/main/java/org/dhis2/fhir/adapter/dhis/model/ImmutableOptionSet.java index 88bdf7ff..f88fb968 100644 --- a/dhis/src/main/java/org/dhis2/fhir/adapter/dhis/model/ImmutableOptionSet.java +++ b/dhis/src/main/java/org/dhis2/fhir/adapter/dhis/model/ImmutableOptionSet.java @@ -44,7 +44,7 @@ * * @author volsch */ -public class ImmutableOptionSet implements OptionSet, Serializable +public class ImmutableOptionSet implements OptionSet, ImmutableDhisObject, Serializable { private static final long serialVersionUID = -5542523378884979052L; diff --git a/dhis/src/main/java/org/dhis2/fhir/adapter/dhis/sync/DhisResourceQueueItem.java b/dhis/src/main/java/org/dhis2/fhir/adapter/dhis/sync/DhisResourceQueueItem.java index 71a8d3e0..2a8291e4 100644 --- a/dhis/src/main/java/org/dhis2/fhir/adapter/dhis/sync/DhisResourceQueueItem.java +++ b/dhis/src/main/java/org/dhis2/fhir/adapter/dhis/sync/DhisResourceQueueItem.java @@ -28,7 +28,6 @@ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -import com.fasterxml.jackson.annotation.JsonIgnore; import org.dhis2.fhir.adapter.data.model.ProcessedItemInfo; import org.dhis2.fhir.adapter.data.model.UuidDataGroupId; import org.dhis2.fhir.adapter.data.processor.DataItemQueueItem; @@ -55,17 +54,4 @@ public DhisResourceQueueItem( @Nonnull UuidDataGroupId dataGroupId, @Nonnull Pro { super( dataGroupId, processedItemInfo ); } - - @JsonIgnore - @Override - public UuidDataGroupId getDataGroupId() - { - return super.getDataGroupId(); - } - - @Override - public void setDataGroupId( UuidDataGroupId dataGroupId ) - { - super.setDataGroupId( dataGroupId ); - } } diff --git a/dhis/src/main/java/org/dhis2/fhir/adapter/dhis/tracker/program/Enrollment.java b/dhis/src/main/java/org/dhis2/fhir/adapter/dhis/tracker/program/Enrollment.java index 8dab58ea..77bd56ee 100644 --- a/dhis/src/main/java/org/dhis2/fhir/adapter/dhis/tracker/program/Enrollment.java +++ b/dhis/src/main/java/org/dhis2/fhir/adapter/dhis/tracker/program/Enrollment.java @@ -62,7 +62,8 @@ public class Enrollment implements DhisResource, Serializable @JsonInclude( JsonInclude.Include.NON_NULL ) private String id; - @JsonProperty( access = JsonProperty.Access.READ_ONLY ) + @JsonProperty + @JsonInclude( JsonInclude.Include.NON_NULL ) private ZonedDateTime lastUpdated; @JsonProperty( "orgUnit" ) diff --git a/dhis/src/main/java/org/dhis2/fhir/adapter/dhis/tracker/program/Event.java b/dhis/src/main/java/org/dhis2/fhir/adapter/dhis/tracker/program/Event.java index 1b30068f..0d24da64 100644 --- a/dhis/src/main/java/org/dhis2/fhir/adapter/dhis/tracker/program/Event.java +++ b/dhis/src/main/java/org/dhis2/fhir/adapter/dhis/tracker/program/Event.java @@ -60,7 +60,8 @@ public class Event implements DhisResource, Serializable, Comparable @JsonInclude( JsonInclude.Include.NON_NULL ) private String id; - @JsonProperty( access = JsonProperty.Access.READ_ONLY ) + @JsonProperty + @JsonInclude( JsonInclude.Include.NON_NULL ) private ZonedDateTime lastUpdated; @JsonProperty( "orgUnit" ) diff --git a/dhis/src/main/java/org/dhis2/fhir/adapter/dhis/tracker/program/ImmutableProgram.java b/dhis/src/main/java/org/dhis2/fhir/adapter/dhis/tracker/program/ImmutableProgram.java index 7de50df5..294f92d9 100644 --- a/dhis/src/main/java/org/dhis2/fhir/adapter/dhis/tracker/program/ImmutableProgram.java +++ b/dhis/src/main/java/org/dhis2/fhir/adapter/dhis/tracker/program/ImmutableProgram.java @@ -31,6 +31,7 @@ import com.fasterxml.jackson.annotation.JsonCreator; import com.fasterxml.jackson.annotation.JsonIgnore; import com.fasterxml.jackson.annotation.JsonProperty; +import org.dhis2.fhir.adapter.dhis.model.ImmutableDhisObject; import org.dhis2.fhir.adapter.dhis.model.Reference; import javax.annotation.Nonnull; @@ -41,7 +42,7 @@ import java.util.Set; import java.util.stream.Collectors; -public class ImmutableProgram implements Program, Serializable +public class ImmutableProgram implements Program, ImmutableDhisObject, Serializable { private static final long serialVersionUID = 4157706296470781519L; 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 ad66c908..d1038add 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 @@ -31,6 +31,7 @@ import com.fasterxml.jackson.annotation.JsonCreator; import com.fasterxml.jackson.annotation.JsonIgnore; import com.fasterxml.jackson.annotation.JsonProperty; +import org.dhis2.fhir.adapter.dhis.model.ImmutableDhisObject; import org.dhis2.fhir.adapter.dhis.model.Reference; import javax.annotation.Nonnull; @@ -41,7 +42,7 @@ import java.util.Set; import java.util.stream.Collectors; -public class ImmutableProgramStage implements ProgramStage, Serializable +public class ImmutableProgramStage implements ProgramStage, ImmutableDhisObject, Serializable { private static final long serialVersionUID = 6563663856499434307L; diff --git a/dhis/src/main/java/org/dhis2/fhir/adapter/dhis/tracker/program/ImmutableProgramStageDataElement.java b/dhis/src/main/java/org/dhis2/fhir/adapter/dhis/tracker/program/ImmutableProgramStageDataElement.java index 6c3fe3f5..a60c1cdc 100644 --- a/dhis/src/main/java/org/dhis2/fhir/adapter/dhis/tracker/program/ImmutableProgramStageDataElement.java +++ b/dhis/src/main/java/org/dhis2/fhir/adapter/dhis/tracker/program/ImmutableProgramStageDataElement.java @@ -31,12 +31,13 @@ import com.fasterxml.jackson.annotation.JsonCreator; import com.fasterxml.jackson.annotation.JsonIgnore; import com.fasterxml.jackson.annotation.JsonProperty; +import org.dhis2.fhir.adapter.dhis.model.ImmutableDhisObject; import org.dhis2.fhir.adapter.dhis.model.WritableDataElement; import javax.annotation.Nonnull; import java.io.Serializable; -public class ImmutableProgramStageDataElement implements ProgramStageDataElement, Serializable +public class ImmutableProgramStageDataElement implements ProgramStageDataElement, ImmutableDhisObject, Serializable { private static final long serialVersionUID = -5845301607041878968L; diff --git a/dhis/src/main/java/org/dhis2/fhir/adapter/dhis/tracker/program/ImmutableProgramTrackedEntityAttribute.java b/dhis/src/main/java/org/dhis2/fhir/adapter/dhis/tracker/program/ImmutableProgramTrackedEntityAttribute.java index 8010df4d..e2a4ef16 100644 --- a/dhis/src/main/java/org/dhis2/fhir/adapter/dhis/tracker/program/ImmutableProgramTrackedEntityAttribute.java +++ b/dhis/src/main/java/org/dhis2/fhir/adapter/dhis/tracker/program/ImmutableProgramTrackedEntityAttribute.java @@ -31,6 +31,7 @@ import com.fasterxml.jackson.annotation.JsonCreator; import com.fasterxml.jackson.annotation.JsonIgnore; import com.fasterxml.jackson.annotation.JsonProperty; +import org.dhis2.fhir.adapter.dhis.model.ImmutableDhisObject; import org.dhis2.fhir.adapter.dhis.tracker.trackedentity.ImmutableTrackedEntityAttribute; import org.dhis2.fhir.adapter.dhis.tracker.trackedentity.TrackedEntityAttribute; import org.dhis2.fhir.adapter.model.ValueType; @@ -38,7 +39,7 @@ import javax.annotation.Nonnull; import java.io.Serializable; -public class ImmutableProgramTrackedEntityAttribute implements ProgramTrackedEntityAttribute, Serializable +public class ImmutableProgramTrackedEntityAttribute implements ProgramTrackedEntityAttribute, ImmutableDhisObject, Serializable { private static final long serialVersionUID = -6094500152005916960L; diff --git a/dhis/src/main/java/org/dhis2/fhir/adapter/dhis/tracker/program/ProgramMetadataService.java b/dhis/src/main/java/org/dhis2/fhir/adapter/dhis/tracker/program/ProgramMetadataService.java index d582a96d..29d6c62f 100644 --- a/dhis/src/main/java/org/dhis2/fhir/adapter/dhis/tracker/program/ProgramMetadataService.java +++ b/dhis/src/main/java/org/dhis2/fhir/adapter/dhis/tracker/program/ProgramMetadataService.java @@ -36,5 +36,5 @@ public interface ProgramMetadataService { @Nonnull - Optional findOneByReference( @Nonnull Reference reference ); + Optional findProgramByReference( @Nonnull Reference reference ); } 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 591cf224..658d1315 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 @@ -84,7 +84,7 @@ public ProgramMetadataServiceImpl( @Nonnull @Qualifier( "systemDhis2RestTemplate @Cacheable @Nonnull @Override - public Optional findOneByReference( @Nonnull Reference reference ) + public Optional findProgramByReference( @Nonnull Reference reference ) { final ResponseEntity result; switch ( reference.getType() ) diff --git a/dhis/src/main/java/org/dhis2/fhir/adapter/dhis/tracker/trackedentity/ImmutableTrackedEntityAttribute.java b/dhis/src/main/java/org/dhis2/fhir/adapter/dhis/tracker/trackedentity/ImmutableTrackedEntityAttribute.java index 6ccfe712..dee9828f 100644 --- a/dhis/src/main/java/org/dhis2/fhir/adapter/dhis/tracker/trackedentity/ImmutableTrackedEntityAttribute.java +++ b/dhis/src/main/java/org/dhis2/fhir/adapter/dhis/tracker/trackedentity/ImmutableTrackedEntityAttribute.java @@ -31,6 +31,7 @@ import com.fasterxml.jackson.annotation.JsonCreator; import com.fasterxml.jackson.annotation.JsonIgnore; import com.fasterxml.jackson.annotation.JsonProperty; +import org.dhis2.fhir.adapter.dhis.model.ImmutableDhisObject; import org.dhis2.fhir.adapter.dhis.model.ImmutableOptionSet; import org.dhis2.fhir.adapter.dhis.model.OptionSet; import org.dhis2.fhir.adapter.model.ValueType; @@ -38,7 +39,7 @@ import javax.annotation.Nonnull; import java.io.Serializable; -public class ImmutableTrackedEntityAttribute implements TrackedEntityAttribute, Serializable +public class ImmutableTrackedEntityAttribute implements TrackedEntityAttribute, ImmutableDhisObject, Serializable { private static final long serialVersionUID = -6135628541438060211L; diff --git a/dhis/src/main/java/org/dhis2/fhir/adapter/dhis/tracker/trackedentity/ImmutableTrackedEntityType.java b/dhis/src/main/java/org/dhis2/fhir/adapter/dhis/tracker/trackedentity/ImmutableTrackedEntityType.java index e0256641..72630440 100644 --- a/dhis/src/main/java/org/dhis2/fhir/adapter/dhis/tracker/trackedentity/ImmutableTrackedEntityType.java +++ b/dhis/src/main/java/org/dhis2/fhir/adapter/dhis/tracker/trackedentity/ImmutableTrackedEntityType.java @@ -31,6 +31,7 @@ import com.fasterxml.jackson.annotation.JsonCreator; import com.fasterxml.jackson.annotation.JsonIgnore; import com.fasterxml.jackson.annotation.JsonProperty; +import org.dhis2.fhir.adapter.dhis.model.ImmutableDhisObject; import org.dhis2.fhir.adapter.dhis.model.Reference; import org.dhis2.fhir.adapter.scriptable.Scriptable; @@ -43,7 +44,7 @@ import java.util.stream.Collectors; @Scriptable -public class ImmutableTrackedEntityType implements TrackedEntityType, Serializable +public class ImmutableTrackedEntityType implements TrackedEntityType, ImmutableDhisObject, Serializable { private static final long serialVersionUID = 797154293863611491L; diff --git a/dhis/src/main/java/org/dhis2/fhir/adapter/dhis/tracker/trackedentity/ImmutableTrackedEntityTypeAttribute.java b/dhis/src/main/java/org/dhis2/fhir/adapter/dhis/tracker/trackedentity/ImmutableTrackedEntityTypeAttribute.java index 6c400108..45d484af 100644 --- a/dhis/src/main/java/org/dhis2/fhir/adapter/dhis/tracker/trackedentity/ImmutableTrackedEntityTypeAttribute.java +++ b/dhis/src/main/java/org/dhis2/fhir/adapter/dhis/tracker/trackedentity/ImmutableTrackedEntityTypeAttribute.java @@ -31,12 +31,13 @@ import com.fasterxml.jackson.annotation.JsonCreator; import com.fasterxml.jackson.annotation.JsonIgnore; import com.fasterxml.jackson.annotation.JsonProperty; +import org.dhis2.fhir.adapter.dhis.model.ImmutableDhisObject; import org.dhis2.fhir.adapter.model.ValueType; import javax.annotation.Nonnull; import java.io.Serializable; -public class ImmutableTrackedEntityTypeAttribute implements TrackedEntityTypeAttribute, Serializable +public class ImmutableTrackedEntityTypeAttribute implements TrackedEntityTypeAttribute, ImmutableDhisObject, Serializable { private static final long serialVersionUID = -6094500152005916960L; diff --git a/dhis/src/main/java/org/dhis2/fhir/adapter/dhis/tracker/trackedentity/TrackedEntityAttributes.java b/dhis/src/main/java/org/dhis2/fhir/adapter/dhis/tracker/trackedentity/TrackedEntityAttributes.java index adbf18d8..ab3f7172 100644 --- a/dhis/src/main/java/org/dhis2/fhir/adapter/dhis/tracker/trackedentity/TrackedEntityAttributes.java +++ b/dhis/src/main/java/org/dhis2/fhir/adapter/dhis/tracker/trackedentity/TrackedEntityAttributes.java @@ -89,7 +89,8 @@ public Optional getOptionalById( @Nonnull String id ) Map attributesById = this.attributesById; if ( attributesById == null ) { - this.attributesById = attributesById = attributes.stream().collect( Collectors.toMap( TrackedEntityAttribute::getId, a -> a ) ); + this.attributesById = attributesById = attributes.stream().map( ImmutableTrackedEntityAttribute::new ) + .collect( Collectors.toMap( TrackedEntityAttribute::getId, a -> a ) ); } return Optional.ofNullable( attributesById.get( id ) ); } @@ -100,7 +101,8 @@ public Optional getOptionalByCode( @Nonnull String code Map attributesByCode = this.attributesByCode; if ( attributesByCode == null ) { - this.attributesByCode = attributesByCode = attributes.stream().filter( a -> StringUtils.isNotBlank( a.getCode() ) ).collect( Collectors.toMap( TrackedEntityAttribute::getCode, a -> a ) ); + this.attributesByCode = attributesByCode = attributes.stream().filter( a -> StringUtils.isNotBlank( a.getCode() ) ) + .map( ImmutableTrackedEntityAttribute::new ).collect( Collectors.toMap( TrackedEntityAttribute::getCode, a -> a ) ); } return Optional.ofNullable( attributesByCode.get( code ) ); } @@ -111,7 +113,8 @@ public Optional getOptionalByName( @Nonnull String name Map attributesByName = this.attributesByName; if ( attributesByName == null ) { - this.attributesByName = attributesByName = attributes.stream().collect( Collectors.toMap( TrackedEntityAttribute::getName, a -> a ) ); + this.attributesByName = attributesByName = attributes.stream().map( ImmutableTrackedEntityAttribute::new ) + .collect( Collectors.toMap( TrackedEntityAttribute::getName, a -> a ) ); } return Optional.ofNullable( attributesByName.get( name ) ); } diff --git a/dhis/src/main/java/org/dhis2/fhir/adapter/dhis/tracker/trackedentity/TrackedEntityInstance.java b/dhis/src/main/java/org/dhis2/fhir/adapter/dhis/tracker/trackedentity/TrackedEntityInstance.java index 41833737..f1ca9766 100644 --- a/dhis/src/main/java/org/dhis2/fhir/adapter/dhis/tracker/trackedentity/TrackedEntityInstance.java +++ b/dhis/src/main/java/org/dhis2/fhir/adapter/dhis/tracker/trackedentity/TrackedEntityInstance.java @@ -54,7 +54,8 @@ public class TrackedEntityInstance implements DhisResource, Serializable @JsonInclude( JsonInclude.Include.NON_NULL ) private String id; - @JsonProperty( access = JsonProperty.Access.READ_ONLY ) + @JsonProperty + @JsonInclude( JsonInclude.Include.NON_NULL ) private ZonedDateTime lastUpdated; @JsonIgnore diff --git a/dhis/src/main/java/org/dhis2/fhir/adapter/dhis/tracker/trackedentity/TrackedEntityMetadataService.java b/dhis/src/main/java/org/dhis2/fhir/adapter/dhis/tracker/trackedentity/TrackedEntityMetadataService.java index f255f1e7..7f7aa967 100644 --- a/dhis/src/main/java/org/dhis2/fhir/adapter/dhis/tracker/trackedentity/TrackedEntityMetadataService.java +++ b/dhis/src/main/java/org/dhis2/fhir/adapter/dhis/tracker/trackedentity/TrackedEntityMetadataService.java @@ -37,7 +37,7 @@ public interface TrackedEntityMetadataService { @Nonnull - Optional getType( @Nonnull Reference reference ); + Optional findTypeByReference( @Nonnull Reference reference ); @Nonnull TrackedEntityAttributes getAttributes(); diff --git a/dhis/src/main/java/org/dhis2/fhir/adapter/dhis/tracker/trackedentity/impl/TrackedEntityMetadataServiceImpl.java b/dhis/src/main/java/org/dhis2/fhir/adapter/dhis/tracker/trackedentity/impl/TrackedEntityMetadataServiceImpl.java index a3992e97..70254438 100644 --- a/dhis/src/main/java/org/dhis2/fhir/adapter/dhis/tracker/trackedentity/impl/TrackedEntityMetadataServiceImpl.java +++ b/dhis/src/main/java/org/dhis2/fhir/adapter/dhis/tracker/trackedentity/impl/TrackedEntityMetadataServiceImpl.java @@ -77,7 +77,7 @@ public TrackedEntityMetadataServiceImpl( @Nonnull @Qualifier( "systemDhis2RestTe @Cacheable( value = "trackedEntityTypes", cacheManager = "dhisCacheManager" ) @Nonnull @Override - public Optional getType( @Nonnull Reference reference ) + public Optional findTypeByReference( @Nonnull Reference reference ) { switch ( reference.getType() ) { 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 95d6ea95..2c941773 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 @@ -83,7 +83,7 @@ public void findOneByReferenceId() throws IOException "generatedByEnrollmentDate,minDaysFromStart,programStageDataElements%5Bid,compulsory,allowProvidedElsewhere,dataElement%5Bid,name,code,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.findOneByReference( new Reference( "93783", ReferenceType.ID ) ); + Optional ou = service.findProgramByReference( new Reference( "93783", ReferenceType.ID ) ); Assert.assertTrue( ou.isPresent() ); assertProgram( ou.get() ); } @@ -96,7 +96,7 @@ public void findOneByReferenceIdNotFound() "generatedByEnrollmentDate,minDaysFromStart,programStageDataElements%5Bid,compulsory,allowProvidedElsewhere,dataElement%5Bid,name,code,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 ) ); - Optional ou = service.findOneByReference( new Reference( "93783", ReferenceType.ID ) ); + Optional ou = service.findProgramByReference( new Reference( "93783", ReferenceType.ID ) ); Assert.assertFalse( ou.isPresent() ); } @@ -107,7 +107,7 @@ public void findOneByReferenceIdServerError() "trackedEntityType%5Bid%5D,programTrackedEntityAttributes%5Bid,name,valueType,mandatory,allowFutureDate,trackedEntityAttribute%5Bid,name,code,valueType,generated%5D%5D,programStages%5Bid,name,repeatable,captureCoordinates," + "generatedByEnrollmentDate,minDaysFromStart,programStageDataElements%5Bid,compulsory,allowProvidedElsewhere,dataElement%5Bid,name,code,valueType,optionSetValue,optionSet%5Bid,name,options%5Bcode,name%5D%5D%5D%5D%5D" ) ).andExpect( method( HttpMethod.GET ) ) .andRespond( MockRestResponseCreators.withServerError() ); - service.findOneByReference( new Reference( "93783", ReferenceType.ID ) ); + service.findProgramByReference( new Reference( "93783", ReferenceType.ID ) ); } @Test @@ -118,7 +118,7 @@ public void findOneByReferenceCode() throws IOException "generatedByEnrollmentDate,minDaysFromStart,programStageDataElements%5Bid,compulsory,allowProvidedElsewhere,dataElement%5Bid,name,code,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 ) ); - Optional ou = service.findOneByReference( new Reference( "OU_3783", ReferenceType.CODE ) ); + Optional ou = service.findProgramByReference( new Reference( "OU_3783", ReferenceType.CODE ) ); Assert.assertTrue( ou.isPresent() ); assertProgram( ou.get() ); } @@ -131,7 +131,7 @@ public void findOneByReferenceCodeNotFound() throws IOException "generatedByEnrollmentDate,minDaysFromStart,programStageDataElements%5Bid,compulsory,allowProvidedElsewhere,dataElement%5Bid,name,code,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 ) ); - Optional ou = service.findOneByReference( new Reference( "OU_3783", ReferenceType.CODE ) ); + Optional ou = service.findProgramByReference( new Reference( "OU_3783", ReferenceType.CODE ) ); Assert.assertFalse( ou.isPresent() ); } @@ -143,7 +143,7 @@ public void findOneByReferenceName() throws IOException "generatedByEnrollmentDate,minDaysFromStart,programStageDataElements%5Bid,compulsory,allowProvidedElsewhere,dataElement%5Bid,name,code,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 ) ); - Optional ou = service.findOneByReference( new Reference( "Freetown", ReferenceType.NAME ) ); + Optional ou = service.findProgramByReference( new Reference( "Freetown", ReferenceType.NAME ) ); Assert.assertTrue( ou.isPresent() ); assertProgram( ou.get() ); } @@ -156,7 +156,7 @@ public void findOneByReferenceNameNotFound() throws IOException "generatedByEnrollmentDate,minDaysFromStart,programStageDataElements%5Bid,compulsory,allowProvidedElsewhere,dataElement%5Bid,name,code,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 ) ); - Optional ou = service.findOneByReference( new Reference( "Freetown", ReferenceType.NAME ) ); + Optional ou = service.findProgramByReference( new Reference( "Freetown", ReferenceType.NAME ) ); Assert.assertFalse( ou.isPresent() ); } diff --git a/fhir-dstu3/src/main/java/org/dhis2/fhir/adapter/fhir/repository/impl/dstu3/Dstu3RemoteFhirResourceRepositoryImpl.java b/fhir-dstu3/src/main/java/org/dhis2/fhir/adapter/fhir/repository/impl/dstu3/Dstu3RemoteFhirResourceRepositoryImpl.java index dcd259d0..c9530c81 100644 --- a/fhir-dstu3/src/main/java/org/dhis2/fhir/adapter/fhir/repository/impl/dstu3/Dstu3RemoteFhirResourceRepositoryImpl.java +++ b/fhir-dstu3/src/main/java/org/dhis2/fhir/adapter/fhir/repository/impl/dstu3/Dstu3RemoteFhirResourceRepositoryImpl.java @@ -29,16 +29,24 @@ */ import ca.uhn.fhir.context.FhirContext; +import org.dhis2.fhir.adapter.fhir.metadata.model.FhirResourceType; import org.dhis2.fhir.adapter.fhir.metadata.model.RemoteSubscriptionResource; import org.dhis2.fhir.adapter.fhir.metadata.model.SubscriptionType; import org.dhis2.fhir.adapter.fhir.metadata.repository.RemoteSubscriptionRepository; +import org.dhis2.fhir.adapter.fhir.repository.FhirRepositoryException; import org.dhis2.fhir.adapter.fhir.repository.impl.AbstractRemoteFhirResourceRepositoryImpl; +import org.hl7.fhir.dstu3.model.Bundle; +import org.hl7.fhir.dstu3.model.ResourceFactory; import org.hl7.fhir.dstu3.model.Subscription; +import org.hl7.fhir.exceptions.FHIRException; import org.hl7.fhir.instance.model.api.IAnyResource; +import org.hl7.fhir.instance.model.api.IBaseBundle; +import org.hl7.fhir.instance.model.api.IBaseResource; import org.springframework.beans.factory.ObjectProvider; import org.springframework.stereotype.Component; import javax.annotation.Nonnull; +import javax.annotation.Nullable; import java.util.List; /** @@ -54,6 +62,20 @@ public Dstu3RemoteFhirResourceRepositoryImpl( @Nonnull RemoteSubscriptionReposit super( repository, fhirContexts ); } + @Nonnull + @Override + public IBaseResource createTransient( @Nonnull FhirResourceType fhirResourceType ) + { + try + { + return ResourceFactory.createResource( fhirResourceType.getResourceTypeName() ); + } + catch ( FHIRException e ) + { + throw new FhirRepositoryException( "Unknown FHIR resource type: " + fhirResourceType, e ); + } + } + @Nonnull @Override protected IAnyResource createFhirSubscription( @Nonnull RemoteSubscriptionResource subscriptionResource ) @@ -73,4 +95,19 @@ protected IAnyResource createFhirSubscription( @Nonnull RemoteSubscriptionResour subscription.setChannel( channelComponent ); return subscription; } + + @Nonnull + @Override + protected Class getBundleClass() + { + return Bundle.class; + } + + @Nullable + @Override + protected IBaseResource getFirstResource( @Nonnull IBaseBundle bundle ) + { + final Bundle b = (Bundle) bundle; + return (b.isEmpty() || b.getEntry().isEmpty()) ? null : b.getEntryFirstRep().getResource(); + } } 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 6b9b42b4..28cd74fd 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 @@ -66,9 +66,9 @@ @Inheritance( strategy = InheritanceType.JOINED ) @DiscriminatorColumn( name = "dhis_resource_type", discriminatorType = DiscriminatorType.STRING ) @NamedQueries( { - @NamedQuery( name = AbstractRule.FIND_RULES_BY_TYPE_NAMED_QUERY, query = "SELECT r FROM AbstractRule r " + + @NamedQuery( name = AbstractRule.FIND_RULES_BY_FHIR_TYPE_NAMED_QUERY, query = "SELECT r FROM AbstractRule r " + "WHERE r.fhirResourceType=:fhirResourceType AND r.applicableCodeSet IS NULL AND r.enabled=true AND r.inEnabled=true" ), - @NamedQuery( name = AbstractRule.FIND_RULES_BY_TYPE_CODES_NAMED_QUERY, query = + @NamedQuery( name = AbstractRule.FIND_RULES_BY_FHIR_TYPE_CODES_NAMED_QUERY, query = "SELECT r FROM AbstractRule r WHERE r.fhirResourceType=:fhirResourceType AND r.enabled=true " + "AND r.inEnabled=true AND (r.applicableCodeSet IS NULL OR (r.applicableCodeSet IS NOT NULL AND EXISTS " + "(SELECT 1 FROM CodeSetValue csv JOIN csv.code c JOIN c.systemCodes sc ON sc.systemCodeValue IN (:systemCodeValues) " + @@ -82,9 +82,9 @@ public abstract class AbstractRule extends VersionedBaseMetadata implements Seri { private static final long serialVersionUID = 3426378271314934021L; - public static final String FIND_RULES_BY_TYPE_NAMED_QUERY = "findRulesByType"; + public static final String FIND_RULES_BY_FHIR_TYPE_NAMED_QUERY = "findRulesByFhirType"; - public static final String FIND_RULES_BY_TYPE_CODES_NAMED_QUERY = "findRulesByTypeAndCodes"; + public static final String FIND_RULES_BY_FHIR_TYPE_CODES_NAMED_QUERY = "findRulesByFhirTypeAndCodes"; public static final int MAX_NAME_LENGTH = 230; @@ -106,10 +106,16 @@ public abstract class AbstractRule extends VersionedBaseMetadata implements Seri @EnumValue( FhirResourceType.class ) private FhirResourceType fhirResourceType; - private boolean inEnabled; + private boolean inEnabled = true; private boolean outEnabled; + private boolean fhirCreateEnabled = true; + + private boolean fhirUpdateEnabled; + + private boolean stop; + private ExecutableScript applicableInScript; private CodeSet applicableCodeSet; @@ -259,6 +265,42 @@ public void setOutEnabled( boolean outEnabled ) this.outEnabled = outEnabled; } + @Basic + @Column( name = "fhir_create_enabled", nullable = false, columnDefinition = "BOOLEAN DEFAULT TRUE NOT NULL" ) + public boolean isFhirCreateEnabled() + { + return fhirCreateEnabled; + } + + public void setFhirCreateEnabled( boolean fhirCreateEnabled ) + { + this.fhirCreateEnabled = fhirCreateEnabled; + } + + @Basic + @Column( name = "fhir_update_enabled", nullable = false, columnDefinition = "BOOLEAN DEFAULT FALSE NOT NULL" ) + public boolean isFhirUpdateEnabled() + { + return fhirUpdateEnabled; + } + + public void setFhirUpdateEnabled( boolean fhirUpdateEnabled ) + { + this.fhirUpdateEnabled = fhirUpdateEnabled; + } + + @Basic + @Column( name = "stop", nullable = false, columnDefinition = "BOOLEAN DEFAULT FALSE NOT NULL" ) + public boolean isStop() + { + return stop; + } + + public void setStop( boolean stop ) + { + this.stop = stop; + } + @ManyToOne @JoinColumn( name = "transform_in_script_id", referencedColumnName = "id", nullable = false ) public ExecutableScript getTransformInScript() 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 b801638d..7ef07a5e 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 @@ -44,21 +44,24 @@ * Defines the supported FHIR resource types. For every FHIR Resource type the name of * the simple name of the HAPI FHIR class that implements this FHIR Resource type can * be specified. This enabled version independent lookups. Since HAPI FHIR Resource - * class maty have been extended, also the super class hierarchy of that class is - * checked. + * class may have been extended, also the super class hierarchy of that class is + * checked.
+ * Resources may depend on each other. A lower order means that the resource does + * not depend on resources with a lower order than its order or it least can be created. + * without the need that the other resources must be present. * * @author volsch */ public enum FhirResourceType { - DIAGNOSTIC_REPORT( "DiagnosticReport", "DiagnosticReport" ), - IMMUNIZATION( "Immunization", "Immunization" ), - LOCATION( "Location", "Location" ), - MEDICATION_REQUEST( "MedicationRequest", "MedicationRequest" ), - OBSERVATION( "Observation", "Observation" ), - ORGANIZATION( "Organization", "Organization" ), - PATIENT( "Patient", "Patient" ), - RELATED_PERSON( "RelatedPerson", "RelatedPerson" ); + DIAGNOSTIC_REPORT( "DiagnosticReport", 30, "DiagnosticReport" ), + IMMUNIZATION( "Immunization", 22, "Immunization" ), + LOCATION( "Location", 2, "Location" ), + MEDICATION_REQUEST( "MedicationRequest", 21, "MedicationRequest" ), + OBSERVATION( "Observation", 20, "Observation" ), + ORGANIZATION( "Organization", 1, "Organization" ), + PATIENT( "Patient", 10, "Patient" ), + RELATED_PERSON( "RelatedPerson", 11, "RelatedPerson" ); 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 ) ); @@ -86,11 +89,14 @@ public static FhirResourceType getByResource( @Nullable IBaseResource resource ) private final String resourceTypeName; + private final int order; + private final Set simpleClassNames; - FhirResourceType( String resourceTypeName, String... simpleClassNames ) + FhirResourceType( String resourceTypeName, int order, String... simpleClassNames ) { this.resourceTypeName = resourceTypeName; + this.order = order; this.simpleClassNames = Collections.unmodifiableSet( new HashSet<>( Arrays.asList( simpleClassNames ) ) ); } @@ -100,6 +106,11 @@ public String getResourceTypeName() return resourceTypeName; } + public int getOrder() + { + return order; + } + @Nonnull public Set getSimpleClassNames() { diff --git a/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/metadata/model/MappedTrackedEntity.java b/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/metadata/model/MappedTrackedEntity.java index c22f83a2..da3a0d0d 100644 --- a/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/metadata/model/MappedTrackedEntity.java +++ b/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/metadata/model/MappedTrackedEntity.java @@ -64,6 +64,12 @@ public class MappedTrackedEntity extends VersionedBaseMetadata implements Serial private boolean enabled = true; + private boolean outEnabled; + + private boolean fhirCreateEnabled = true; + + private boolean fhirUpdateEnabled; + @NotNull @Valid private Reference trackedEntityReference; @@ -108,6 +114,42 @@ public void setEnabled( boolean enabled ) this.enabled = enabled; } + @Basic + @Column( name = "out_enabled", nullable = false, columnDefinition = "BOOLEAN DEFAULT FALSE NOT NULL" ) + public boolean isOutEnabled() + { + return outEnabled; + } + + public void setOutEnabled( boolean outEnabled ) + { + this.outEnabled = outEnabled; + } + + @Basic + @Column( name = "fhir_create_enabled", nullable = false, columnDefinition = "BOOLEAN DEFAULT TRUE NOT NULL" ) + public boolean isFhirCreateEnabled() + { + return fhirCreateEnabled; + } + + public void setFhirCreateEnabled( boolean fhirCreateEnabled ) + { + this.fhirCreateEnabled = fhirCreateEnabled; + } + + @Basic + @Column( name = "fhir_update_enabled", nullable = false, columnDefinition = "BOOLEAN DEFAULT FALSE NOT NULL" ) + public boolean isFhirUpdateEnabled() + { + return fhirUpdateEnabled; + } + + public void setFhirUpdateEnabled( boolean fhirUpdateEnabled ) + { + this.fhirUpdateEnabled = fhirUpdateEnabled; + } + @Basic @Column( name = "tracked_entity_ref", nullable = false, length = 230 ) @Convert( converter = ReferenceAttributeConverter.class ) diff --git a/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/metadata/model/RemoteSubscription.java b/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/metadata/model/RemoteSubscription.java index db0b448c..9c07981a 100644 --- a/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/metadata/model/RemoteSubscription.java +++ b/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/metadata/model/RemoteSubscription.java @@ -42,6 +42,7 @@ import javax.persistence.Entity; import javax.persistence.EnumType; import javax.persistence.Enumerated; +import javax.persistence.NamedQuery; import javax.persistence.OneToMany; import javax.persistence.OrderBy; import javax.persistence.Table; @@ -63,10 +64,13 @@ @Entity @Table( name = "fhir_remote_subscription" ) @JsonFilter( ToManyPropertyFilter.FILTER_NAME ) +@NamedQuery( name = RemoteSubscription.ALL_REMOTE_SUBSCRIPTIONS_NAMED_QUERY, query = "SELECT rs FROM RemoteSubscription rs" ) public class RemoteSubscription extends VersionedBaseMetadata implements Serializable { private static final long serialVersionUID = -2488855592701580509L; + public static final String ALL_REMOTE_SUBSCRIPTIONS_NAMED_QUERY = "allRemoteSubscriptions"; + public static final int MAX_NAME_LENGTH = 50; public static final int MAX_CODE_LENGTH = 20; @@ -83,6 +87,8 @@ public class RemoteSubscription extends VersionedBaseMetadata implements Seriali private boolean locked; + private boolean outEnabled; + private String description; @NotNull @@ -136,7 +142,7 @@ public void setCode( String code ) } @Basic - @Column( name = "enabled", nullable = false ) + @Column( name = "enabled", nullable = false, columnDefinition = "BOOLEAN DEFAULT TRUE NOT NULL" ) public boolean isEnabled() { return enabled; @@ -148,7 +154,7 @@ public void setEnabled( boolean enabled ) } @Basic - @Column( name = "locked", nullable = false ) + @Column( name = "locked", nullable = false, columnDefinition = "BOOLEAN DEFAULT FALSE NOT NULL" ) public boolean isLocked() { return locked; @@ -159,6 +165,18 @@ public void setLocked( boolean locked ) this.locked = locked; } + @Basic + @Column( name = "out_enabled", nullable = false, columnDefinition = "BOOLEAN DEFAULT FALSE NOT NULL" ) + public boolean isOutEnabled() + { + return outEnabled; + } + + public void setOutEnabled( boolean outEnabled ) + { + this.outEnabled = outEnabled; + } + @Basic @Column( name = "description", columnDefinition = "TEXT" ) public String getDescription() diff --git a/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/metadata/model/System.java b/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/metadata/model/System.java index 2da55833..6ecac3b2 100644 --- a/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/metadata/model/System.java +++ b/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/metadata/model/System.java @@ -28,16 +28,25 @@ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ +import com.fasterxml.jackson.annotation.JsonIgnore; import org.dhis2.fhir.adapter.model.VersionedBaseMetadata; +import org.springframework.data.rest.core.annotation.RestResource; import javax.persistence.Basic; import javax.persistence.Column; import javax.persistence.Entity; +import javax.persistence.OneToMany; import javax.persistence.Table; import javax.validation.constraints.NotBlank; import javax.validation.constraints.Size; import java.io.Serializable; +import java.util.Collection; +/** + * Defines coding systems. + * + * @author volsch + */ @Entity @Table( name = "fhir_system" ) public class System extends VersionedBaseMetadata implements Serializable @@ -68,6 +77,8 @@ public class System extends VersionedBaseMetadata implements Serializable private boolean descriptionProtected; + private Collection systemCodes; + @Basic @Column( name = "name", nullable = false, length = 230 ) public String getName() @@ -139,4 +150,17 @@ public void setDescriptionProtected( boolean descriptionProtected ) { this.descriptionProtected = descriptionProtected; } + + @RestResource( exported = false ) + @JsonIgnore + @OneToMany( mappedBy = "system" ) + public Collection getSystemCodes() + { + return systemCodes; + } + + public void setSystemCodes( Collection systemCodes ) + { + this.systemCodes = systemCodes; + } } 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 a149c1dd..ed11ded0 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 @@ -28,6 +28,7 @@ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ +import com.fasterxml.jackson.annotation.JsonIgnore; import org.dhis2.fhir.adapter.dhis.model.DhisResourceType; import javax.persistence.DiscriminatorValue; @@ -35,6 +36,7 @@ import javax.persistence.JoinColumn; import javax.persistence.ManyToOne; import javax.persistence.Table; +import javax.persistence.Transient; import javax.validation.constraints.NotNull; /** @@ -110,4 +112,18 @@ public void setTeiLookupScript( ExecutableScript teiLookupScript ) { this.teiLookupScript = teiLookupScript; } + + @Transient + @JsonIgnore + public boolean isEffectiveFhirCreateEnable() + { + return isOutEnabled() && isFhirCreateEnabled() && getTrackedEntity().isFhirCreateEnabled(); + } + + @Transient + @JsonIgnore + public boolean isEffectiveFhirUpdateEnable() + { + return isOutEnabled() && isFhirUpdateEnabled() && getTrackedEntity().isFhirUpdateEnabled(); + } } diff --git a/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/metadata/repository/CustomRemoteSubscriptionRepository.java b/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/metadata/repository/CustomRemoteSubscriptionRepository.java index 224320bc..44e7dba4 100644 --- a/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/metadata/repository/CustomRemoteSubscriptionRepository.java +++ b/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/metadata/repository/CustomRemoteSubscriptionRepository.java @@ -31,6 +31,7 @@ import org.dhis2.fhir.adapter.fhir.metadata.model.RemoteSubscription; import javax.annotation.Nonnull; +import java.util.Optional; /** * Custom repository for {@link RemoteSubscription}. @@ -39,6 +40,9 @@ */ public interface CustomRemoteSubscriptionRepository { + @Nonnull + Optional findOnly(); + @Nonnull S saveAndFlush( @Nonnull S entity ); diff --git a/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/metadata/repository/TrackedEntityRuleRepository.java b/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/metadata/repository/TrackedEntityRuleRepository.java index a9cac764..d7a9fc97 100644 --- a/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/metadata/repository/TrackedEntityRuleRepository.java +++ b/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/metadata/repository/TrackedEntityRuleRepository.java @@ -28,16 +28,22 @@ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ +import org.dhis2.fhir.adapter.dhis.model.Reference; import org.dhis2.fhir.adapter.fhir.metadata.model.TrackedEntityRule; import org.springframework.cache.annotation.CacheConfig; import org.springframework.cache.annotation.CacheEvict; import org.springframework.cache.annotation.CachePut; +import org.springframework.cache.annotation.Cacheable; import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.Query; import org.springframework.data.querydsl.QuerydslPredicateExecutor; +import org.springframework.data.repository.query.Param; import org.springframework.data.rest.core.annotation.RepositoryRestResource; +import org.springframework.data.rest.core.annotation.RestResource; import org.springframework.security.access.prepost.PreAuthorize; import javax.annotation.Nonnull; +import java.util.Collection; import java.util.List; import java.util.UUID; @@ -51,6 +57,13 @@ @PreAuthorize( "hasRole('DATA_MAPPING')" ) public interface TrackedEntityRuleRepository extends JpaRepository, QuerydslPredicateExecutor { + @RestResource( exported = false ) + @Nonnull + @Query( "SELECT ter FROM #{#entityName} ter JOIN ter.trackedEntity te WHERE ter.enabled=true AND ter.outEnabled=true AND (ter.fhirCreateEnabled=true OR ter.fhirUpdateEnabled=true) AND " + + "te.enabled=true AND te.outEnabled=true AND (te.fhirCreateEnabled=true OR te.fhirUpdateEnabled=true) AND te.trackedEntityReference IN (:typeReferences)" ) + @Cacheable( keyGenerator = "trackedEntityRuleFindAllByTypeKeyGenerator" ) + Collection findAllByType( @Param( "typeReferences" ) @Nonnull Collection typeReferences ); + @Override @Nonnull @CacheEvict( allEntries = true ) diff --git a/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/metadata/repository/cache/TrackedEntityRuleFindAllByTypeKeyGenerator.java b/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/metadata/repository/cache/TrackedEntityRuleFindAllByTypeKeyGenerator.java new file mode 100644 index 00000000..297fc1ec --- /dev/null +++ b/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/metadata/repository/cache/TrackedEntityRuleFindAllByTypeKeyGenerator.java @@ -0,0 +1,67 @@ +package org.dhis2.fhir.adapter.fhir.metadata.repository.cache; + +/* + * 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.dhis.model.Reference; +import org.springframework.cache.interceptor.KeyGenerator; +import org.springframework.stereotype.Component; + +import javax.annotation.Nonnull; +import java.lang.reflect.Method; +import java.util.Collection; +import java.util.SortedSet; +import java.util.TreeSet; +import java.util.stream.Collectors; + +/** + * Key generator for {@link org.dhis2.fhir.adapter.fhir.metadata.repository.TrackedEntityRuleRepository#findAllByType(Collection)} + * Since cache may be serialized to external storage, cache is automatically a string representation. + * + * @author volsch + */ +@Component +public class TrackedEntityRuleFindAllByTypeKeyGenerator implements KeyGenerator +{ + @Override + @Nonnull + public Object generate( @Nonnull Object target, @Nonnull Method method, @Nonnull Object... params ) + { + @SuppressWarnings( "unchecked" ) final Collection references = (Collection) params[0]; + final SortedSet referenceStrings = references.stream().map( Reference::toString ) + .collect( Collectors.toCollection( TreeSet::new ) ); + + final StringBuilder sb = new StringBuilder( "findAllByType" ); + // codes must have same order every time + for ( final String string : referenceStrings ) + { + sb.append( ',' ).append( string ); + } + return sb.toString(); + } +} diff --git a/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/metadata/repository/impl/CustomRemoteSubscriptionRepositoryImpl.java b/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/metadata/repository/impl/CustomRemoteSubscriptionRepositoryImpl.java index afc1bddf..ca7b891a 100644 --- a/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/metadata/repository/impl/CustomRemoteSubscriptionRepositoryImpl.java +++ b/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/metadata/repository/impl/CustomRemoteSubscriptionRepositoryImpl.java @@ -36,6 +36,7 @@ import org.dhis2.fhir.adapter.fhir.metadata.repository.event.AutoCreatedRemoteSubscriptionResourceEvent; import org.springframework.cache.annotation.CacheEvict; import org.springframework.cache.annotation.CachePut; +import org.springframework.cache.annotation.Cacheable; import org.springframework.context.ApplicationEventPublisher; import org.springframework.transaction.annotation.Transactional; @@ -45,6 +46,7 @@ import java.time.Instant; import java.util.ArrayList; import java.util.List; +import java.util.Optional; /** * Implementation of {@link CustomRemoteSubscriptionRepository}. @@ -64,6 +66,19 @@ public CustomRemoteSubscriptionRepositoryImpl( @Nonnull EntityManager entityMana this.applicationEventPublisher = applicationEventPublisher; } + @Nonnull + @Cacheable( key = "{#root.methodName}", cacheManager = "metadataCacheManager", cacheNames = "remoteSubscription" ) + public Optional findOnly() + { + final List result = entityManager.createNamedQuery( RemoteSubscription.ALL_REMOTE_SUBSCRIPTIONS_NAMED_QUERY, RemoteSubscription.class ) + .setMaxResults( 2 ).getResultList(); + if ( (result.isEmpty()) || (result.size() > 1) ) + { + return Optional.empty(); + } + return Optional.of( result.get( 0 ) ); + } + @Override @Nonnull @CachePut( key = "#a0.id", cacheManager = "metadataCacheManager", cacheNames = "remoteSubscription" ) 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 74311b79..df4513c8 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 @@ -74,14 +74,14 @@ public List findAllByInputData( @Nonnull FhirResourceTyp if ( (systemCodeValues == null) || systemCodeValues.isEmpty() ) { rules = new ArrayList<>( - entityManager.createNamedQuery( AbstractRule.FIND_RULES_BY_TYPE_NAMED_QUERY, AbstractRule.class ) + entityManager.createNamedQuery( AbstractRule.FIND_RULES_BY_FHIR_TYPE_NAMED_QUERY, AbstractRule.class ) .setParameter( "fhirResourceType", fhirResourceType ).getResultList() ); } else { final Set systemCodes = systemCodeValues.stream().map( SystemCodeValue::toString ) .collect( Collectors.toCollection( TreeSet::new ) ); - rules = new ArrayList<>( entityManager.createNamedQuery( AbstractRule.FIND_RULES_BY_TYPE_CODES_NAMED_QUERY, AbstractRule.class ) + rules = new ArrayList<>( entityManager.createNamedQuery( AbstractRule.FIND_RULES_BY_FHIR_TYPE_CODES_NAMED_QUERY, AbstractRule.class ) .setParameter( "fhirResourceType", fhirResourceType ) .setParameter( "systemCodeValues", systemCodes ).getResultList() ); } diff --git a/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/repository/FhirRepositoryException.java b/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/repository/FhirRepositoryException.java index 8220e0ec..969e1c53 100644 --- a/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/repository/FhirRepositoryException.java +++ b/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/repository/FhirRepositoryException.java @@ -41,4 +41,9 @@ public FhirRepositoryException( String message ) { super( message ); } + + public FhirRepositoryException( String message, Throwable cause ) + { + super( message, cause ); + } } diff --git a/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/repository/RemoteFhirResourceRepository.java b/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/repository/RemoteFhirResourceRepository.java index 7b3ec3c8..4141e149 100644 --- a/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/repository/RemoteFhirResourceRepository.java +++ b/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/repository/RemoteFhirResourceRepository.java @@ -29,9 +29,11 @@ */ import ca.uhn.fhir.context.FhirContext; +import org.dhis2.fhir.adapter.fhir.metadata.model.FhirResourceType; import org.dhis2.fhir.adapter.fhir.metadata.model.RemoteSubscriptionResource; import org.dhis2.fhir.adapter.fhir.metadata.model.SubscriptionFhirEndpoint; import org.dhis2.fhir.adapter.fhir.model.FhirVersion; +import org.dhis2.fhir.adapter.fhir.model.SystemCodeValue; import org.hl7.fhir.instance.model.api.IBaseResource; import javax.annotation.Nonnull; @@ -54,6 +56,15 @@ public interface RemoteFhirResourceRepository @Nonnull Optional find( @Nonnull UUID remoteSubscriptionId, @Nonnull FhirVersion fhirVersion, @Nonnull SubscriptionFhirEndpoint fhirEndpoint, @Nonnull String resourceType, @Nonnull String resourceId ); + @Nonnull + Optional findRefreshedByIdentifier( @Nonnull UUID remoteSubscriptionId, @Nonnull FhirVersion fhirVersion, @Nonnull SubscriptionFhirEndpoint fhirEndpoint, @Nonnull String resourceType, @Nonnull SystemCodeValue identifier ); + + @Nonnull + Optional findByIdentifier( @Nonnull UUID remoteSubscriptionId, @Nonnull FhirVersion fhirVersion, @Nonnull SubscriptionFhirEndpoint fhirEndpoint, @Nonnull String resourceType, @Nonnull SystemCodeValue identifier ); + + @Nonnull + IBaseResource createTransient( @Nonnull FhirResourceType fhirResourceType ); + @Nonnull IBaseResource save( @Nonnull RemoteSubscriptionResource subscriptionResource, @Nonnull IBaseResource resource ); } diff --git a/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/repository/impl/AbstractRemoteFhirResourceRepositoryImpl.java b/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/repository/impl/AbstractRemoteFhirResourceRepositoryImpl.java index c4b483a9..447708b9 100644 --- a/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/repository/impl/AbstractRemoteFhirResourceRepositoryImpl.java +++ b/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/repository/impl/AbstractRemoteFhirResourceRepositoryImpl.java @@ -32,6 +32,7 @@ import ca.uhn.fhir.rest.api.CacheControlDirective; import ca.uhn.fhir.rest.api.MethodOutcome; import ca.uhn.fhir.rest.client.api.IGenericClient; +import ca.uhn.fhir.rest.gclient.TokenClientParam; import ca.uhn.fhir.rest.server.exceptions.BaseServerResponseException; import ca.uhn.fhir.rest.server.exceptions.ResourceNotFoundException; import com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand; @@ -40,10 +41,12 @@ 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.model.SystemCodeValue; import org.dhis2.fhir.adapter.fhir.repository.FhirClientUtils; import org.dhis2.fhir.adapter.fhir.repository.RemoteFhirResourceRepository; import org.dhis2.fhir.adapter.rest.RestBadRequestException; import org.hl7.fhir.instance.model.api.IAnyResource; +import org.hl7.fhir.instance.model.api.IBaseBundle; import org.hl7.fhir.instance.model.api.IBaseResource; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -55,6 +58,7 @@ import org.springframework.transaction.event.TransactionalEventListener; import javax.annotation.Nonnull; +import javax.annotation.Nullable; import java.util.Collections; import java.util.List; import java.util.Map; @@ -125,6 +129,33 @@ public Optional find( @Nonnull UUID remoteSubscriptionId, @Nonnul return findRefreshed( remoteSubscriptionId, fhirVersion, fhirEndpoint, resourceType, resourceId ); } + @HystrixCommand + @CachePut( key = "{#root.methodName, #remoteSubscriptionId, #fhirVersion, #resourceType, #identifier}", unless = "#result==null" ) + @Nonnull + @Override + public Optional findRefreshedByIdentifier( @Nonnull UUID remoteSubscriptionId, @Nonnull FhirVersion fhirVersion, @Nonnull SubscriptionFhirEndpoint fhirEndpoint, @Nonnull String resourceType, @Nonnull SystemCodeValue identifier ) + { + final FhirContext fhirContext = fhirContexts.get( fhirVersion ); + final IGenericClient client = FhirClientUtils.createClient( fhirContext, fhirEndpoint ); + + logger.debug( "Reading {}?identifier={} from FHIR endpoints {}.", resourceType, identifier, fhirEndpoint.getBaseUrl() ); + final IBaseBundle bundle = client.search().forResource( resourceType ).returnBundle( getBundleClass() ) + .where( new TokenClientParam( "identifier" ).exactly().systemAndIdentifier( identifier.getSystem(), identifier.getCode() ) ) + .cacheControl( new CacheControlDirective().setNoCache( true ) ).execute(); + final IBaseResource resource = getFirstResource( bundle ); + logger.debug( "Read {}?identifier={} from FHIR endpoints {} (found={}).", resourceType, identifier, fhirEndpoint.getBaseUrl(), (resource != null) ); + return Optional.ofNullable( resource ); + } + + @HystrixCommand + @Cacheable( key = "{#root.methodName, #remoteSubscriptionId, #fhirVersion, #resourceType, #identifier}", unless = "#result==null" ) + @Nonnull + @Override + public Optional findByIdentifier( @Nonnull UUID remoteSubscriptionId, @Nonnull FhirVersion fhirVersion, @Nonnull SubscriptionFhirEndpoint fhirEndpoint, @Nonnull String resourceType, @Nonnull SystemCodeValue identifier ) + { + return findRefreshedByIdentifier( remoteSubscriptionId, fhirVersion, fhirEndpoint, resourceType, identifier ); + } + @HystrixCommand @Cacheable( key = "{#subscriptionResource.remoteSubscription.id, #subscriptionResource.remoteSubscription.fhirVersion, " + "T(org.dhis2.fhir.adapter.fhir.metadata.model.FhirResourceType).getByResource(#resource).getResourceTypeName(), #resource.getIdElement().getIdPart()}", unless = "#result==null" ) @@ -170,4 +201,10 @@ protected String createWebHookUrl( @Nonnull RemoteSubscriptionResource subscript url.append( subscriptionResource.getId() ); return url.toString(); } + + @Nullable + protected abstract IBaseResource getFirstResource( @Nonnull IBaseBundle bundle ); + + @Nonnull + protected abstract Class getBundleClass(); } 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 f8d81041..13a246f0 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 @@ -178,7 +178,17 @@ protected void receiveAuthenticated( @Nonnull DhisResourceQueueItem queueItem ) return; } - final Optional resource = dhisResourceRepository.findRefreshed( Objects.requireNonNull( DhisResourceId.parse( queueItem.getId() ) ) ); + final Optional resource; + authorizationContext.setAuthorization( systemDhis2Authorization ); + try + { + resource = dhisResourceRepository.findRefreshed( Objects.requireNonNull( DhisResourceId.parse( queueItem.getId() ) ) ); + } + finally + { + authorizationContext.resetAuthorization(); + } + if ( resource.isPresent() ) { final ProcessedItemInfo processedItemInfo = getProcessedItemInfo( resource.get() ); 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 new file mode 100644 index 00000000..4912dc24 --- /dev/null +++ b/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/AbstractDhisToFhirRequestResolver.java @@ -0,0 +1,70 @@ +package org.dhis2.fhir.adapter.fhir.transform.dhis.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.RemoteSubscription; +import org.dhis2.fhir.adapter.fhir.metadata.repository.RemoteSubscriptionRepository; +import org.dhis2.fhir.adapter.fhir.transform.scripted.ScriptedDhisResource; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import javax.annotation.Nonnull; +import java.util.Optional; + +/** + * Abstract implementation of {@link DhisToFhirRequestResolver} that provides + * support for resolving the remote subscription that is stored to store this + * resource. + * + * @author volsch + */ +public abstract class AbstractDhisToFhirRequestResolver implements DhisToFhirRequestResolver +{ + private final Logger logger = LoggerFactory.getLogger( getClass() ); + + private final RemoteSubscriptionRepository remoteSubscriptionRepository; + + public AbstractDhisToFhirRequestResolver( @Nonnull RemoteSubscriptionRepository remoteSubscriptionRepository ) + { + this.remoteSubscriptionRepository = remoteSubscriptionRepository; + } + + @Nonnull + public Optional resolveRemoteSubscription( @Nonnull ScriptedDhisResource scriptedDhisResource ) + { + // TODO add support for script based remote subscription resolving + + Optional remoteSubscription = remoteSubscriptionRepository.findOnly(); + if ( remoteSubscription.isPresent() && remoteSubscription.get().isEnabled() && remoteSubscription.get().isOutEnabled() ) + { + return remoteSubscription; + } + 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 new file mode 100644 index 00000000..d5a056ec --- /dev/null +++ b/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/AbstractDhisToFhirTransformer.java @@ -0,0 +1,58 @@ +package org.dhis2.fhir.adapter.fhir.transform.dhis.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.AbstractRule; +import org.dhis2.fhir.adapter.fhir.transform.TransformerException; +import org.dhis2.fhir.adapter.fhir.transform.dhis.DhisToFhirTransformOutcome; +import org.dhis2.fhir.adapter.fhir.transform.dhis.DhisToFhirTransformerContext; +import org.dhis2.fhir.adapter.fhir.transform.scripted.ScriptedDhisResource; +import org.hl7.fhir.instance.model.api.IBaseResource; + +import javax.annotation.Nonnull; +import javax.annotation.Nullable; +import java.util.Map; + +/** + * Transforms a DHIS 2 resource to a FHIR resource. + * + * @param the concrete type of the DHIS 2 resource that is processed by this transformer. + * @param the concrete type of the transformer rule that is processed by this transformer. + * @author volsch + */ +public abstract class AbstractDhisToFhirTransformer + implements DhisToFhirTransformer +{ + @Nullable + @Override + public DhisToFhirTransformOutcome transformCasted( @Nonnull DhisToFhirTransformerContext context, @Nonnull ScriptedDhisResource input, @Nonnull AbstractRule rule, @Nonnull Map scriptVariables ) throws TransformerException + { + return transform( context, getDhisResourceClass().cast( input ), getRuleClass().cast( rule ), scriptVariables ); + } +} diff --git a/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/DhisToFhirRuleResolver.java b/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/DhisToFhirRequestResolver.java similarity index 86% rename from fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/DhisToFhirRuleResolver.java rename to fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/DhisToFhirRequestResolver.java index 2368579f..5056cc54 100644 --- a/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/DhisToFhirRuleResolver.java +++ b/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/DhisToFhirRequestResolver.java @@ -35,9 +35,8 @@ import org.dhis2.fhir.adapter.fhir.transform.scripted.ScriptedDhisResource; import javax.annotation.Nonnull; -import javax.annotation.Nullable; -import java.util.Collection; import java.util.List; +import java.util.Optional; /** * Resolves the rules for transformations from DHIS 2 resources to FHIR resources @@ -45,18 +44,17 @@ * * @author volsch */ -public interface DhisToFhirRuleResolver +public interface DhisToFhirRequestResolver { @Nonnull DhisResourceType getDhisResourceType(); @Nonnull - List resolveRule( @Nonnull DhisResource dhisResource ); + ScriptedDhisResource convert( @Nonnull DhisResource dhisResource ); - @Nullable - RemoteSubscription resolveRemoteSubscription( @Nonnull DhisResource dhisResource, - Collection possibleRules ); + @Nonnull + List resolveRules( @Nonnull ScriptedDhisResource dhisResource ); @Nonnull - ScriptedDhisResource convert( @Nonnull DhisResource dhisResource ); + Optional resolveRemoteSubscription( @Nonnull ScriptedDhisResource scriptedDhisResource ); } 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 new file mode 100644 index 00000000..a1560f2a --- /dev/null +++ b/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/DhisToFhirRuleComparator.java @@ -0,0 +1,60 @@ +package org.dhis2.fhir.adapter.fhir.transform.dhis.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.TrackedEntityRule; + +import java.util.Comparator; + +/** + * Comparator for rules when transforming from DHIS to FHIR. The dependencies of the + * FHIR resources are taken into account as well. + * + * @author volsch + */ +public class DhisToFhirRuleComparator implements Comparator +{ + public static final DhisToFhirRuleComparator INSTANCE = new DhisToFhirRuleComparator(); + + protected DhisToFhirRuleComparator() + { + super(); + } + + @Override + public int compare( TrackedEntityRule o1, TrackedEntityRule o2 ) + { + int value = o1.getFhirResourceType().getOrder() - o2.getFhirResourceType().getOrder(); + if ( value != 0 ) + { + return value; + } + return o1.compareTo( o2 ); + } +} 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 b5559389..08cf124a 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 @@ -52,18 +52,18 @@ public class DhisToFhirTransformerRequestImpl implements DhisToFhirTransformerRe private final ScriptedDhisResource input; - private final Map transformerUtils; - private final List rules; + private final Map transformerUtils; + private int ruleIndex; - public DhisToFhirTransformerRequestImpl( @Nonnull DhisToFhirTransformerContext context, @Nonnull ScriptedDhisResource input, @Nonnull Map transformerUtils, @Nonnull List rules ) + public DhisToFhirTransformerRequestImpl( @Nonnull DhisToFhirTransformerContext context, @Nonnull ScriptedDhisResource input, @Nonnull List rules, @Nonnull Map transformerUtils ) { this.context = context; this.input = input; - this.transformerUtils = transformerUtils; this.rules = rules; + this.transformerUtils = transformerUtils; } @Nonnull @@ -81,15 +81,15 @@ public ScriptedDhisResource getInput() } @Nonnull - public Map getTransformerUtils() + public List getRules() { - return transformerUtils; + return rules; } @Nonnull - public List getRules() + public Map getTransformerUtils() { - return rules; + return transformerUtils; } public boolean isFirstRule() 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 17fa8cfb..c1bc5cc4 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 @@ -45,6 +45,7 @@ import org.dhis2.fhir.adapter.fhir.transform.dhis.model.DhisRequest; import org.dhis2.fhir.adapter.fhir.transform.dhis.util.DhisToFhirTransformerUtils; import org.dhis2.fhir.adapter.fhir.transform.scripted.ScriptedDhisResource; +import org.dhis2.fhir.adapter.fhir.transform.util.DhisBeanTransformerUtils; import org.dhis2.fhir.adapter.lock.LockContext; import org.dhis2.fhir.adapter.lock.LockManager; import org.hl7.fhir.instance.model.api.IBaseResource; @@ -72,7 +73,7 @@ public class DhisToFhirTransformerServiceImpl implements DhisToFhirTransformerSe private final LockManager lockManager; - private final Map ruleResolvers = new HashMap<>(); + private final Map requestResolvers = new HashMap<>(); private final Map, DhisToFhirTransformer> transformers = new HashMap<>(); @@ -81,7 +82,7 @@ public class DhisToFhirTransformerServiceImpl implements DhisToFhirTransformerSe private final ScriptExecutor scriptExecutor; public DhisToFhirTransformerServiceImpl( @Nonnull LockManager lockManager, - @Nonnull ObjectProvider> ruleResolvers, + @Nonnull ObjectProvider> requestResolvers, @Nonnull ObjectProvider>> transformersProvider, @Nonnull ObjectProvider> transformUtilsProvider, @Nonnull ScriptExecutor scriptExecutor ) @@ -89,8 +90,8 @@ public DhisToFhirTransformerServiceImpl( @Nonnull LockManager lockManager, this.lockManager = lockManager; this.scriptExecutor = scriptExecutor; - ruleResolvers.ifAvailable( resolvers -> - resolvers.forEach( r -> this.ruleResolvers.put( r.getDhisResourceType(), r ) ) ); + requestResolvers.ifAvailable( resolvers -> + resolvers.forEach( r -> this.requestResolvers.put( r.getDhisResourceType(), r ) ) ); transformersProvider.ifAvailable( transformers -> { for ( final DhisToFhirTransformer transformer : transformers ) @@ -119,19 +120,21 @@ public DhisToFhirTransformerRequest createTransformerRequest( @Nonnull DhisReque { final DhisResource input = Objects.requireNonNull( DhisBeanTransformerUtils.clone( originalInput ) ); - final DhisToFhirRuleResolver ruleResolver = ruleResolvers.get( dhisRequest.getResourceType() ); - if ( ruleResolver == null ) + final DhisToFhirRequestResolver requestResolver = requestResolvers.get( dhisRequest.getResourceType() ); + if ( requestResolver == null ) { - throw new TransformerMappingException( "No rule resolver can be found for DHIS resource type " + dhisRequest.getResourceType() ); + throw new TransformerMappingException( "No request resolver can be found for DHIS resource type " + dhisRequest.getResourceType() ); } - final List rules = ruleResolver.resolveRule( input ); + + final ScriptedDhisResource scriptedInput = requestResolver.convert( input ); + final List rules = requestResolver.resolveRules( scriptedInput ); if ( rules.isEmpty() ) { logger.info( "Could not find any rule to process DHIS resource." ); return null; } - final RemoteSubscription remoteSubscription = ruleResolver.resolveRemoteSubscription( input, rules ); + final RemoteSubscription remoteSubscription = requestResolver.resolveRemoteSubscription( scriptedInput ).orElse( null ); if ( remoteSubscription == null ) { logger.info( "Could not determine remote subscription to process DHIS resource." ); @@ -145,7 +148,7 @@ public DhisToFhirTransformerRequest createTransformerRequest( @Nonnull DhisReque } return new DhisToFhirTransformerRequestImpl( new DhisToFhirTransformerContextImpl( dhisRequest, remoteSubscription ), - ruleResolver.convert( input ), transformerUtils, rules ); + scriptedInput, rules, transformerUtils ); } @Nullable @@ -176,9 +179,9 @@ public DhisToFhirTransformOutcome transform( @Nonnull D transformerRequestImpl.getContext(), transformerRequestImpl.getInput(), rule, scriptVariables ); if ( outcome != null ) { - logger.info( "Rule {} used successfully for transformation of {}.", - rule, transformerRequestImpl.getInput().getResourceId() ); - return new DhisToFhirTransformOutcome<>( outcome, transformerRequestImpl.isLastRule() ? null : transformerRequestImpl ); + logger.info( "Rule {} used successfully for transformation of {} (stop={}).", + rule, transformerRequestImpl.getInput().getResourceId(), rule.isStop() ); + return new DhisToFhirTransformOutcome<>( outcome, (rule.isStop() || transformerRequestImpl.isLastRule()) ? null : transformerRequestImpl ); } // if the previous transformation caused a lock of any resource this must be released since the transformation has been rolled back lockManager.getCurrentLockContext().ifPresent( LockContext::unlockAll ); diff --git a/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/trackedentity/DhisToFhirTrackedEntityRequestResolver.java b/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/trackedentity/DhisToFhirTrackedEntityRequestResolver.java new file mode 100644 index 00000000..ef2112e4 --- /dev/null +++ b/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/trackedentity/DhisToFhirTrackedEntityRequestResolver.java @@ -0,0 +1,111 @@ +package org.dhis2.fhir.adapter.fhir.transform.dhis.impl.trackedentity; + +/* + * 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.dhis.converter.ValueConverter; +import org.dhis2.fhir.adapter.dhis.model.DhisResource; +import org.dhis2.fhir.adapter.dhis.model.DhisResourceType; +import org.dhis2.fhir.adapter.dhis.model.Reference; +import org.dhis2.fhir.adapter.dhis.model.ReferenceType; +import org.dhis2.fhir.adapter.dhis.tracker.trackedentity.TrackedEntityAttributes; +import org.dhis2.fhir.adapter.dhis.tracker.trackedentity.TrackedEntityInstance; +import org.dhis2.fhir.adapter.dhis.tracker.trackedentity.TrackedEntityMetadataService; +import org.dhis2.fhir.adapter.dhis.tracker.trackedentity.TrackedEntityType; +import org.dhis2.fhir.adapter.fhir.metadata.model.AbstractRule; +import org.dhis2.fhir.adapter.fhir.metadata.repository.RemoteSubscriptionRepository; +import org.dhis2.fhir.adapter.fhir.metadata.repository.TrackedEntityRuleRepository; +import org.dhis2.fhir.adapter.fhir.transform.TransformerDataException; +import org.dhis2.fhir.adapter.fhir.transform.dhis.impl.AbstractDhisToFhirRequestResolver; +import org.dhis2.fhir.adapter.fhir.transform.dhis.impl.DhisToFhirRequestResolver; +import org.dhis2.fhir.adapter.fhir.transform.dhis.impl.DhisToFhirRuleComparator; +import org.dhis2.fhir.adapter.fhir.transform.scripted.ImmutableScriptedTrackedEntityInstance; +import org.dhis2.fhir.adapter.fhir.transform.scripted.ScriptedDhisResource; +import org.dhis2.fhir.adapter.fhir.transform.scripted.ScriptedTrackedEntityInstance; +import org.dhis2.fhir.adapter.fhir.transform.scripted.WritableScriptedTrackedEntityInstance; +import org.springframework.stereotype.Component; + +import javax.annotation.Nonnull; +import java.util.List; +import java.util.stream.Collectors; + +/** + * Implementation of {@link DhisToFhirRequestResolver} for tracked entities. + * + * @author volsch + */ +@Component +public class DhisToFhirTrackedEntityRequestResolver extends AbstractDhisToFhirRequestResolver +{ + private final TrackedEntityMetadataService trackedEntityMetadataService; + + private final TrackedEntityRuleRepository ruleRepository; + + private final ValueConverter valueConverter; + + public DhisToFhirTrackedEntityRequestResolver( + @Nonnull RemoteSubscriptionRepository remoteSubscriptionRepository, + @Nonnull TrackedEntityMetadataService trackedEntityMetadataService, + @Nonnull TrackedEntityRuleRepository ruleRepository, + @Nonnull ValueConverter valueConverter ) + { + super( remoteSubscriptionRepository ); + this.trackedEntityMetadataService = trackedEntityMetadataService; + this.ruleRepository = ruleRepository; + this.valueConverter = valueConverter; + } + + @Nonnull + @Override + public DhisResourceType getDhisResourceType() + { + return DhisResourceType.TRACKED_ENTITY; + } + + @Nonnull + @Override + public List resolveRules( @Nonnull ScriptedDhisResource dhisResource ) + { + final ScriptedTrackedEntityInstance tei = (ScriptedTrackedEntityInstance) dhisResource; + return ruleRepository.findAllByType( tei.getType().getAllReferences() ).stream() + .sorted( DhisToFhirRuleComparator.INSTANCE ).collect( Collectors.toList() ); + } + + @Nonnull + @Override + public ScriptedDhisResource convert( @Nonnull DhisResource dhisResource ) + { + final TrackedEntityInstance tei = (TrackedEntityInstance) dhisResource; + final TrackedEntityType trackedEntityType = trackedEntityMetadataService.findTypeByReference( new Reference( tei.getTypeId(), ReferenceType.ID ) ) + .orElseThrow( () -> new TransformerDataException( "Tracked entity type " + tei.getTypeId() + " of tracked entity instance " + + tei.getId() + " could not be found." ) ); + final TrackedEntityAttributes trackedEntityAttributes = trackedEntityMetadataService.getAttributes(); + return new ImmutableScriptedTrackedEntityInstance( new WritableScriptedTrackedEntityInstance( + trackedEntityAttributes, trackedEntityType, tei, valueConverter ) ); + } +} diff --git a/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/trackedentity/TrackedEntityToFhirTransformer.java b/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/trackedentity/TrackedEntityToFhirTransformer.java new file mode 100644 index 00000000..266c4429 --- /dev/null +++ b/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/trackedentity/TrackedEntityToFhirTransformer.java @@ -0,0 +1,91 @@ +package org.dhis2.fhir.adapter.fhir.transform.dhis.impl.trackedentity; + +/* + * 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.dhis.model.DhisResourceType; +import org.dhis2.fhir.adapter.fhir.metadata.model.TrackedEntityRule; +import org.dhis2.fhir.adapter.fhir.model.FhirVersion; +import org.dhis2.fhir.adapter.fhir.transform.TransformerException; +import org.dhis2.fhir.adapter.fhir.transform.dhis.DhisToFhirTransformOutcome; +import org.dhis2.fhir.adapter.fhir.transform.dhis.DhisToFhirTransformerContext; +import org.dhis2.fhir.adapter.fhir.transform.dhis.impl.AbstractDhisToFhirTransformer; +import org.dhis2.fhir.adapter.fhir.transform.dhis.impl.DhisToFhirTransformer; +import org.dhis2.fhir.adapter.fhir.transform.scripted.ScriptedTrackedEntityInstance; +import org.hl7.fhir.instance.model.api.IBaseResource; +import org.springframework.stereotype.Component; + +import javax.annotation.Nonnull; +import javax.annotation.Nullable; +import java.util.Map; +import java.util.Set; + +/** + * Implementation of {@link DhisToFhirTransformer} for transforming DHIS 2 tracked + * entity instances to FHIR resources. + * + * @author volsch + */ +@Component +public class TrackedEntityToFhirTransformer extends AbstractDhisToFhirTransformer +{ + @Nonnull + @Override + public DhisResourceType getDhisResourceType() + { + return DhisResourceType.TRACKED_ENTITY; + } + + @Nonnull + @Override + public Class getDhisResourceClass() + { + return ScriptedTrackedEntityInstance.class; + } + + @Nonnull + @Override + public Class getRuleClass() + { + return TrackedEntityRule.class; + } + + @Nonnull + @Override + public Set getFhirVersions() + { + return FhirVersion.ALL; + } + + @Nullable + @Override + public DhisToFhirTransformOutcome transform( @Nonnull DhisToFhirTransformerContext context, @Nonnull ScriptedTrackedEntityInstance input, @Nonnull TrackedEntityRule rule, @Nonnull Map scriptVariables ) throws TransformerException + { + return null; + } +} diff --git a/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/fhir/impl/FhirToDhisTransformerServiceImpl.java b/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/fhir/impl/FhirToDhisTransformerServiceImpl.java index 8861a102..fbcacd64 100644 --- a/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/fhir/impl/FhirToDhisTransformerServiceImpl.java +++ b/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/fhir/impl/FhirToDhisTransformerServiceImpl.java @@ -46,9 +46,9 @@ import org.dhis2.fhir.adapter.fhir.transform.fhir.FhirToDhisTransformerRequest; import org.dhis2.fhir.adapter.fhir.transform.fhir.FhirToDhisTransformerService; import org.dhis2.fhir.adapter.fhir.transform.fhir.impl.util.AbstractCodeFhirToDhisTransformerUtils; -import org.dhis2.fhir.adapter.fhir.transform.fhir.impl.util.FhirBeanTransformerUtils; import org.dhis2.fhir.adapter.fhir.transform.fhir.impl.util.FhirToDhisTransformerUtils; import org.dhis2.fhir.adapter.fhir.transform.fhir.model.FhirRequest; +import org.dhis2.fhir.adapter.fhir.transform.util.FhirBeanTransformerUtils; import org.dhis2.fhir.adapter.lock.LockContext; import org.dhis2.fhir.adapter.lock.LockManager; import org.hl7.fhir.instance.model.api.IBaseResource; @@ -139,7 +139,7 @@ public FhirToDhisTransformerRequest createTransformerRequest( @Nonnull FhirReque .orElseThrow( () -> new FatalTransformerException( "FHIR context for FHIR version " + fhirRequest.getVersion() + " is not available." ) ); final IBaseResource input = Objects.requireNonNull( FhirBeanTransformerUtils.clone( fhirContext, originalInput ) ); final List rules = ruleRepository.findAllByInputData( fhirRequest.getResourceType(), codeTransformerUtils.getResourceCodes( input ) ) - .stream().filter( r -> !contained || r.isContainedAllowed() ).collect( Collectors.toList() ); + .stream().filter( r -> !contained || r.isContainedAllowed() ).sorted().collect( Collectors.toList() ); return new FhirToDhisTransformerRequestImpl( new FhirToDhisTransformerContextImpl( fhirRequest, false ), input, transformerUtils, rules ); } @@ -180,8 +180,9 @@ public FhirToDhisTransformOutcome transform( @Nonnull Fh transformerRequestImpl.getContext(), transformerRequestImpl.getInput(), rule, scriptVariables ); if ( outcome != null ) { - logger.info( "Rule {} used successfully for transformation of {}.", rule, transformerRequestImpl.getInput().getIdElement() ); - return new FhirToDhisTransformOutcome<>( outcome, transformerRequestImpl.isLastRule() ? null : transformerRequestImpl ); + logger.info( "Rule {} used successfully for transformation of {} (stop={}).", + rule, transformerRequestImpl.getInput().getIdElement(), rule.isStop() ); + return new FhirToDhisTransformOutcome<>( outcome, (rule.isStop() || transformerRequestImpl.isLastRule()) ? null : transformerRequestImpl ); } // if the previous transformation caused a lock of any resource this must be released since the transformation has been rolled back lockManager.getCurrentLockContext().ifPresent( LockContext::unlockAll ); 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 12da2f07..46298eb6 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 @@ -63,6 +63,7 @@ import org.dhis2.fhir.adapter.fhir.transform.fhir.FhirToDhisTransformerContext; import org.dhis2.fhir.adapter.fhir.transform.fhir.impl.AbstractFhirToDhisTransformer; import org.dhis2.fhir.adapter.fhir.transform.scripted.ImmutableScriptedEnrollment; +import org.dhis2.fhir.adapter.fhir.transform.scripted.ScriptedEnrollment; import org.dhis2.fhir.adapter.fhir.transform.scripted.ScriptedTrackedEntityInstance; import org.dhis2.fhir.adapter.fhir.transform.scripted.WritableScriptedEnrollment; import org.dhis2.fhir.adapter.fhir.transform.scripted.WritableScriptedEvent; @@ -179,12 +180,13 @@ public FhirToDhisTransformOutcome transform( @Nonnull FhirToDhisTransform } rule.getEventStatusUpdate().update( event ); + final ScriptedTrackedEntityInstance scriptedTrackedEntityInstance = getScriptVariable( variables, ScriptVariable.TRACKED_ENTITY_INSTANCE, ScriptedTrackedEntityInstance.class ); final ProgramStage programStage = getScriptVariable( variables, ScriptVariable.PROGRAM_STAGE, ProgramStage.class ); - final List scriptedProgramStageEvents = createScriptedProgramStageEvents( context, programStage, event.getEnrollment().getEvents() ); + final List scriptedProgramStageEvents = createScriptedProgramStageEvents( context, programStage, event.getEnrollment().getEvents(), scriptedTrackedEntityInstance ); variables.put( ScriptVariable.PROGRAM_STAGE_EVENTS.getVariableName(), scriptedProgramStageEvents ); final Program program = getScriptVariable( variables, ScriptVariable.PROGRAM, Program.class ); - final WritableScriptedEnrollment scriptedEnrollment = new WritableScriptedEnrollment( program, event.getEnrollment(), valueConverter ); + final WritableScriptedEnrollment scriptedEnrollment = new WritableScriptedEnrollment( program, event.getEnrollment(), scriptedTrackedEntityInstance, valueConverter ); variables.put( ScriptVariable.ENROLLMENT.getVariableName(), scriptedEnrollment ); if ( !event.isNewResource() && !beforeEnrollmentEvent( context, rule, programStage, event.getEnrollment(), scriptVariables ) ) { @@ -200,7 +202,7 @@ public FhirToDhisTransformOutcome transform( @Nonnull FhirToDhisTransform return null; } - final WritableScriptedEvent scriptedEvent = new WritableScriptedEvent( context, programStage, event, valueConverter ); + final WritableScriptedEvent scriptedEvent = new WritableScriptedEvent( context, programStage, event, scriptedTrackedEntityInstance, valueConverter ); variables.put( ScriptVariable.OUTPUT.getVariableName(), scriptedEvent ); updateEventDate( context, rule, resourceMapping, event.getEnrollment(), programStage, event, variables ); @@ -233,14 +235,14 @@ public FhirToDhisTransformOutcome transform( @Nonnull FhirToDhisTransform protected void addBasicScriptVariables( @Nonnull Map variables, @Nonnull ProgramStageRule rule ) throws TransformerException { - final Program program = programMetadataService.findOneByReference( rule.getProgramStage().getProgram().getProgramReference() ) + final Program program = programMetadataService.findProgramByReference( rule.getProgramStage().getProgram().getProgramReference() ) .orElseThrow( () -> new TransformerMappingException( "Mapping " + rule + " requires program \"" + rule.getProgramStage().getProgram().getProgramReference() + "\" that does not exist." ) ); variables.put( ScriptVariable.PROGRAM.getVariableName(), program ); final TrackedEntityAttributes attributes = trackedEntityMetadataService.getAttributes(); final TrackedEntityType trackedEntityType = trackedEntityMetadataService - .getType( new Reference( program.getTrackedEntityTypeId(), ReferenceType.ID ) ) + .findTypeByReference( new Reference( program.getTrackedEntityTypeId(), ReferenceType.ID ) ) .orElseThrow( () -> new TransformerMappingException( "Program \"" + program.getName() + "\" references tracked entity type " + program.getTrackedEntityTypeId() + " that does not exist." ) ); variables.put( ScriptVariable.TRACKED_ENTITY_ATTRIBUTES.getVariableName(), attributes ); @@ -254,7 +256,7 @@ protected void addScriptVariables( @Nonnull FhirToDhisTransformerContext context { throw new FatalTransformerException( "Identifier of tracked entity instance has not yet been set." ); } - variables.put( ScriptVariable.TRACKED_ENTITY_INSTANCE.getVariableName(), new WritableScriptedTrackedEntityInstance( context, + variables.put( ScriptVariable.TRACKED_ENTITY_INSTANCE.getVariableName(), new WritableScriptedTrackedEntityInstance( getScriptVariable( variables, ScriptVariable.TRACKED_ENTITY_ATTRIBUTES, TrackedEntityAttributes.class ), getScriptVariable( variables, ScriptVariable.TRACKED_ENTITY_TYPE, TrackedEntityType.class ), trackedEntityInstance, valueConverter ) ); @@ -311,13 +313,8 @@ protected Optional getActiveResource( @Nonnull FhirToDhisTransformerConte final Enrollment enrollment = eventInfo.getEnrollment().orElseThrow( () -> new TransformerMappingException( "Enrolled events do not have an enrollment." ) ); - final Map variables = new HashMap<>( scriptVariables ); - final Program program = eventInfo.getProgram(); - final ProgramStage programStage = eventInfo.getProgramStage(); - final WritableScriptedEnrollment scriptedEnrollment = new WritableScriptedEnrollment( program, enrollment, valueConverter ); - final List scriptedProgramStageEvents = createScriptedProgramStageEvents( context, programStage, enrollment.getEvents() ); - variables.put( ScriptVariable.ENROLLMENT.getVariableName(), scriptedEnrollment ); - variables.put( ScriptVariable.PROGRAM_STAGE_EVENTS.getVariableName(), scriptedProgramStageEvents ); + final ScriptedTrackedEntityInstance scriptedTrackedEntityInstance = getScriptVariable( scriptVariables, ScriptVariable.TRACKED_ENTITY_INSTANCE, ScriptedTrackedEntityInstance.class ); + final Map variables = createResourceVariables( context, scriptVariables, eventInfo, enrollment ); final FhirResourceMapping resourceMapping = getResourceMapping( rule ); final Optional orgUnit = getEventOrgUnit( context, resourceMapping, enrollment, variables ); @@ -328,10 +325,10 @@ protected Optional getActiveResource( @Nonnull FhirToDhisTransformerConte if ( (eventDecisionType == null) || (eventDecisionType == EventDecisionType.BREAK) ) { logger.info( "Processing of event for program stage \"{}\" has been cancelled by event script after execution. " + - "It will be tried to create a new one.", programStage.getName() ); + "It will be tried to create a new one.", eventInfo.getProgramStage().getName() ); return Optional.empty(); } - if ( (eventDecisionType == EventDecisionType.NEW_EVENT) && programStage.isRepeatable() ) + if ( (eventDecisionType == EventDecisionType.NEW_EVENT) && eventInfo.getProgramStage().isRepeatable() ) { // it will be tried to create a new one return Optional.empty(); @@ -339,7 +336,7 @@ protected Optional getActiveResource( @Nonnull FhirToDhisTransformerConte if ( enrollment.isModified() ) { - scriptedEnrollment.validate(); + getScriptVariable( scriptVariables, ScriptVariable.ENROLLMENT, ScriptedEnrollment.class ).validate(); } } return Optional.of( event ); @@ -378,14 +375,8 @@ protected Event createResource( @Nonnull FhirToDhisTransformerContext context, @ return null; } - final Map variables = new HashMap<>( scriptVariables ); - final Program program = eventInfo.getProgram(); - final ProgramStage programStage = eventInfo.getProgramStage(); - final WritableScriptedEnrollment scriptedEnrollment = new WritableScriptedEnrollment( program, enrollment, valueConverter ); - final List scriptedProgramStageEvents = createScriptedProgramStageEvents( context, programStage, enrollment.getEvents() ); - variables.put( ScriptVariable.ENROLLMENT.getVariableName(), scriptedEnrollment ); - variables.put( ScriptVariable.PROGRAM_STAGE_EVENTS.getVariableName(), scriptedProgramStageEvents ); - if ( !beforeEnrollmentEvent( context, rule, programStage, enrollment, scriptVariables ) ) + final Map variables = createResourceVariables( context, scriptVariables, eventInfo, enrollment ); + if ( !beforeEnrollmentEvent( context, rule, eventInfo.getProgramStage(), enrollment, scriptVariables ) ) { return null; } @@ -398,7 +389,8 @@ protected Event createResource( @Nonnull FhirToDhisTransformerContext context, @ event.setProgramStageId( eventInfo.getProgramStage().getId() ); event.setTrackedEntityInstanceId( eventInfo.getTrackedEntityInstance().getId() ); - final WritableScriptedEvent scriptedEvent = new WritableScriptedEvent( context, eventInfo.getProgramStage(), event, valueConverter ); + final ScriptedTrackedEntityInstance scriptedTrackedEntityInstance = getScriptVariable( scriptVariables, ScriptVariable.TRACKED_ENTITY_INSTANCE, ScriptedTrackedEntityInstance.class ); + final WritableScriptedEvent scriptedEvent = new WritableScriptedEvent( context, eventInfo.getProgramStage(), event, scriptedTrackedEntityInstance, valueConverter ); variables.put( ScriptVariable.EVENT.getVariableName(), scriptedEvent ); final ZonedDateTime eventDate; @@ -436,12 +428,26 @@ protected Event createResource( @Nonnull FhirToDhisTransformerContext context, @ if ( enrollment.isModified() ) { - scriptedEnrollment.validate(); + getScriptVariable( scriptVariables, ScriptVariable.ENROLLMENT, ScriptedEnrollment.class ).validate(); } scriptedEvent.validate(); return event; } + @Nonnull + private Map createResourceVariables( @Nonnull FhirToDhisTransformerContext context, @Nonnull Map scriptVariables, @Nonnull EventInfo eventInfo, @Nonnull Enrollment enrollment ) + { + final ScriptedTrackedEntityInstance scriptedTrackedEntityInstance = getScriptVariable( scriptVariables, ScriptVariable.TRACKED_ENTITY_INSTANCE, ScriptedTrackedEntityInstance.class ); + final Map variables = new HashMap<>( scriptVariables ); + final Program program = eventInfo.getProgram(); + final ProgramStage programStage = eventInfo.getProgramStage(); + final WritableScriptedEnrollment scriptedEnrollment = new WritableScriptedEnrollment( program, enrollment, scriptedTrackedEntityInstance, valueConverter ); + final List scriptedProgramStageEvents = createScriptedProgramStageEvents( context, programStage, enrollment.getEvents(), scriptedTrackedEntityInstance ); + variables.put( ScriptVariable.ENROLLMENT.getVariableName(), scriptedEnrollment ); + variables.put( ScriptVariable.PROGRAM_STAGE_EVENTS.getVariableName(), scriptedProgramStageEvents ); + return variables; + } + protected void updateEventDate( @Nonnull FhirToDhisTransformerContext context, @Nonnull ProgramStageRule rule, @Nonnull FhirResourceMapping resourceMapping, @Nonnull Enrollment enrollment, @Nonnull ProgramStage programStage, @Nonnull Event event, @Nonnull Map variables ) { @@ -534,13 +540,14 @@ protected Enrollment createEnrollment( @Nonnull FhirToDhisTransformerContext con return null; } + final ScriptedTrackedEntityInstance scriptedTrackedEntityInstance = getScriptVariable( variables, ScriptVariable.TRACKED_ENTITY_INSTANCE, ScriptedTrackedEntityInstance.class ); final Enrollment enrollment = new Enrollment( true ); enrollment.setProgramId( program.getId() ); - enrollment.setTrackedEntityInstanceId( getScriptVariable( variables, ScriptVariable.TRACKED_ENTITY_INSTANCE, ScriptedTrackedEntityInstance.class ).getId() ); + enrollment.setTrackedEntityInstanceId( scriptedTrackedEntityInstance.getId() ); enrollment.setOrgUnitId( organisationUnit.get().getId() ); enrollment.setEnrollmentDate( enrollmentDate ); - final WritableScriptedEnrollment scriptedEnrollment = new WritableScriptedEnrollment( program, enrollment, valueConverter ); + final WritableScriptedEnrollment scriptedEnrollment = new WritableScriptedEnrollment( program, enrollment, scriptedTrackedEntityInstance, valueConverter ); variables.put( ScriptVariable.ENROLLMENT.getVariableName(), scriptedEnrollment ); if ( (mappedProgram.getCreationScript() != null) && !Boolean.TRUE.equals( getScriptExecutor().execute( @@ -611,10 +618,11 @@ protected boolean afterEnrollmentEvent( @Nonnull FhirToDhisTransformerContext co } @Nonnull - protected List createScriptedProgramStageEvents( @Nonnull FhirToDhisTransformerContext transformerContext, @Nonnull ProgramStage programStage, @Nonnull List events ) + protected List createScriptedProgramStageEvents( @Nonnull FhirToDhisTransformerContext transformerContext, @Nonnull ProgramStage programStage, @Nonnull List events, + @Nonnull ScriptedTrackedEntityInstance scriptedTrackedEntityInstance ) { return events.stream().filter( e -> programStage.getId().equals( e.getProgramStageId() ) ).sorted( new EventComparator() ) - .map( e -> new WritableScriptedEvent( transformerContext, programStage, e, valueConverter ) ).collect( Collectors.toList() ); + .map( e -> new WritableScriptedEvent( transformerContext, programStage, e, scriptedTrackedEntityInstance, valueConverter ) ).collect( Collectors.toList() ); } protected boolean initAndValidateTrackedEntity( @Nonnull Program program, @Nonnull Map variables ) @@ -701,9 +709,9 @@ protected Optional getEventOrgUnit( @Nonnull FhirToDhisTransfo @Nonnull Enrollment enrollment, @Nonnull Map scriptVariables ) throws TransformerException { final Program program = getScriptVariable( scriptVariables, ScriptVariable.PROGRAM, Program.class ); + final ScriptedTrackedEntityInstance scriptedTrackedEntityInstance = getScriptVariable( scriptVariables, ScriptVariable.TRACKED_ENTITY_INSTANCE, ScriptedTrackedEntityInstance.class ); final Map variables = new HashMap<>( scriptVariables ); - variables.put( ScriptVariable.ENROLLMENT.getVariableName(), - new ImmutableScriptedEnrollment( new WritableScriptedEnrollment( program, enrollment, valueConverter ) ) ); + variables.put( ScriptVariable.ENROLLMENT.getVariableName(), new ImmutableScriptedEnrollment( new WritableScriptedEnrollment( program, enrollment, scriptedTrackedEntityInstance, valueConverter ) ) ); return getOrgUnit( context, resourceMapping.getEventOrgLookupScript(), variables ); } diff --git a/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/fhir/impl/trackedentity/FhirToTrackedEntityTransformer.java b/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/fhir/impl/trackedentity/FhirToTrackedEntityTransformer.java index a115d3aa..4e9799dd 100644 --- a/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/fhir/impl/trackedentity/FhirToTrackedEntityTransformer.java +++ b/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/fhir/impl/trackedentity/FhirToTrackedEntityTransformer.java @@ -128,7 +128,7 @@ public FhirToDhisTransformOutcome transform( @Nonnull Fhi } final WritableScriptedTrackedEntityInstance scriptedTrackedEntityInstance = new WritableScriptedTrackedEntityInstance( - context, trackedEntityAttributes, trackedEntityType, trackedEntityInstance, valueConverter ); + trackedEntityAttributes, trackedEntityType, trackedEntityInstance, valueConverter ); variables.put( ScriptVariable.OUTPUT.getVariableName(), scriptedTrackedEntityInstance ); final Optional organisationUnit; @@ -171,7 +171,7 @@ protected boolean addScriptVariables( @Nonnull Map arguments, @N { final TrackedEntityAttributes trackedEntityAttributes = trackedEntityMetadataService.getAttributes(); arguments.put( ScriptVariable.TRACKED_ENTITY_ATTRIBUTES.getVariableName(), trackedEntityAttributes ); - final TrackedEntityType trackedEntityType = trackedEntityMetadataService.getType( + final TrackedEntityType trackedEntityType = trackedEntityMetadataService.findTypeByReference( rule.getTrackedEntity().getTrackedEntityReference() ) .orElseThrow( () -> new TransformerMappingException( "Tracked entity type in rule " + rule + " could not be found: " + rule.getTrackedEntity().getTrackedEntityReference() ) ); @@ -199,15 +199,7 @@ protected Optional getResourceById( @Nullable String id ) protected Optional getActiveResource( @Nonnull FhirToDhisTransformerContext context, @Nonnull TrackedEntityRule rule, @Nonnull Map scriptVariables, boolean sync ) throws TransformerException { - final IBaseResource baseResource; - if ( rule.getTeiLookupScript() == null ) - { - baseResource = getScriptVariable( scriptVariables, ScriptVariable.INPUT, IBaseResource.class ); - } - else - { - baseResource = getScriptExecutor().execute( rule.getTeiLookupScript(), context.getFhirRequest().getVersion(), scriptVariables, IBaseResource.class ); - } + final IBaseResource baseResource = getTeiResource( context, rule, scriptVariables ); if ( baseResource == null ) { return Optional.empty(); @@ -268,6 +260,17 @@ protected TrackedEntityInstance createResource( @Nonnull FhirToDhisTransformerCo @Nullable protected String getIdentifier( @Nonnull FhirToDhisTransformerContext context, @Nonnull TrackedEntityRule rule, @Nonnull Map scriptVariables ) + { + final IBaseResource baseResource = getTeiResource( context, rule, scriptVariables ); + if ( baseResource == null ) + { + return null; + } + return getIdentifier( context, baseResource, scriptVariables ); + } + + @Nullable + private IBaseResource getTeiResource( @Nonnull FhirToDhisTransformerContext context, @Nonnull TrackedEntityRule rule, @Nonnull Map scriptVariables ) { final IBaseResource baseResource; if ( rule.getTeiLookupScript() == null ) @@ -278,10 +281,6 @@ protected String getIdentifier( @Nonnull FhirToDhisTransformerContext context, @ { baseResource = getScriptExecutor().execute( rule.getTeiLookupScript(), context.getFhirRequest().getVersion(), scriptVariables, IBaseResource.class ); } - if ( baseResource == null ) - { - return null; - } - return getIdentifier( context, baseResource, scriptVariables ); + return baseResource; } } diff --git a/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/fhir/impl/util/AbstractOrganizationFhirToDhisTransformerUtils.java b/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/fhir/impl/util/AbstractOrganizationFhirToDhisTransformerUtils.java index 222a1ef9..eb55ae2c 100644 --- a/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/fhir/impl/util/AbstractOrganizationFhirToDhisTransformerUtils.java +++ b/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/fhir/impl/util/AbstractOrganizationFhirToDhisTransformerUtils.java @@ -47,6 +47,7 @@ import org.dhis2.fhir.adapter.fhir.transform.TransformerMappingException; import org.dhis2.fhir.adapter.fhir.transform.fhir.FhirToDhisTransformerContext; import org.dhis2.fhir.adapter.fhir.transform.fhir.model.ResourceSystem; +import org.dhis2.fhir.adapter.fhir.transform.util.FhirBeanTransformerUtils; import org.dhis2.fhir.adapter.scriptable.ScriptMethod; import org.dhis2.fhir.adapter.scriptable.ScriptMethodArg; import org.dhis2.fhir.adapter.scriptable.ScriptType; diff --git a/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/fhir/impl/util/ReferenceFhirToDhisTransformerUtils.java b/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/fhir/impl/util/ReferenceFhirToDhisTransformerUtils.java index 11003f78..dbcc5cc8 100644 --- a/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/fhir/impl/util/ReferenceFhirToDhisTransformerUtils.java +++ b/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/fhir/impl/util/ReferenceFhirToDhisTransformerUtils.java @@ -42,6 +42,7 @@ import org.dhis2.fhir.adapter.fhir.transform.TransformerMappingException; import org.dhis2.fhir.adapter.fhir.transform.fhir.FhirToDhisTransformerContext; import org.dhis2.fhir.adapter.fhir.transform.scripted.TransformerScriptException; +import org.dhis2.fhir.adapter.fhir.transform.util.FhirBeanTransformerUtils; import org.dhis2.fhir.adapter.scriptable.ScriptMethod; import org.dhis2.fhir.adapter.scriptable.ScriptMethodArg; import org.dhis2.fhir.adapter.scriptable.ScriptType; diff --git a/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/scripted/ImmutableScriptedEnrollment.java b/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/scripted/ImmutableScriptedEnrollment.java index 2d7fd63d..f3e55bb4 100644 --- a/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/scripted/ImmutableScriptedEnrollment.java +++ b/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/scripted/ImmutableScriptedEnrollment.java @@ -29,6 +29,7 @@ */ import org.dhis2.fhir.adapter.dhis.model.DhisResourceId; +import org.dhis2.fhir.adapter.dhis.model.ImmutableDhisObject; import org.dhis2.fhir.adapter.fhir.transform.TransformerException; import org.dhis2.fhir.adapter.geo.Location; import org.dhis2.fhir.adapter.scriptable.Scriptable; @@ -37,6 +38,7 @@ import javax.annotation.Nullable; import java.io.Serializable; import java.time.ZonedDateTime; +import java.util.Objects; /** * Immutable scripted enrollment. @@ -44,7 +46,7 @@ * @author volsch */ @Scriptable -public class ImmutableScriptedEnrollment implements ScriptedEnrollment, Serializable +public class ImmutableScriptedEnrollment implements ScriptedEnrollment, ImmutableDhisObject, Serializable { private static final long serialVersionUID = 3106142635120155470L; @@ -89,6 +91,17 @@ public String getOrganizationUnitId() return delegate.getOrganizationUnitId(); } + @Nullable + @Override + public ScriptedTrackedEntityInstance getTrackedEntityInstance() + { + if ( delegate.getTrackedEntityInstance() instanceof ImmutableDhisObject ) + { + return delegate.getTrackedEntityInstance(); + } + return new ImmutableScriptedTrackedEntityInstance( Objects.requireNonNull( delegate.getTrackedEntityInstance() ) ); + } + @Override @Nullable public ZonedDateTime getEnrollmentDate() diff --git a/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/scripted/ImmutableScriptedEvent.java b/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/scripted/ImmutableScriptedEvent.java index b8304ad5..e832474f 100644 --- a/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/scripted/ImmutableScriptedEvent.java +++ b/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/scripted/ImmutableScriptedEvent.java @@ -30,6 +30,7 @@ import org.dhis2.fhir.adapter.dhis.model.DhisResourceId; +import org.dhis2.fhir.adapter.dhis.model.ImmutableDhisObject; import org.dhis2.fhir.adapter.dhis.tracker.program.EventStatus; import org.dhis2.fhir.adapter.fhir.transform.TransformerException; import org.dhis2.fhir.adapter.geo.Location; @@ -39,6 +40,7 @@ import javax.annotation.Nullable; import java.io.Serializable; import java.time.ZonedDateTime; +import java.util.Objects; /** * Immutable scripted event. @@ -46,7 +48,7 @@ * @author volsch */ @Scriptable -public class ImmutableScriptedEvent implements ScriptedEvent, Serializable +public class ImmutableScriptedEvent implements ScriptedEvent, ImmutableDhisObject, Serializable { private static final long serialVersionUID = -3248712035742910069L; @@ -91,6 +93,17 @@ public String getOrganizationUnitId() return delegate.getOrganizationUnitId(); } + @Nullable + @Override + public ScriptedTrackedEntityInstance getTrackedEntityInstance() + { + if ( delegate.getTrackedEntityInstance() instanceof ImmutableDhisObject ) + { + return delegate.getTrackedEntityInstance(); + } + return new ImmutableScriptedTrackedEntityInstance( Objects.requireNonNull( delegate.getTrackedEntityInstance() ) ); + } + @Nullable @Override public ZonedDateTime getEventDate() diff --git a/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/scripted/ImmutableScriptedTrackedEntityInstance.java b/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/scripted/ImmutableScriptedTrackedEntityInstance.java index 3252b1ce..231002e6 100644 --- a/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/scripted/ImmutableScriptedTrackedEntityInstance.java +++ b/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/scripted/ImmutableScriptedTrackedEntityInstance.java @@ -29,7 +29,10 @@ */ import org.dhis2.fhir.adapter.dhis.model.DhisResourceId; +import org.dhis2.fhir.adapter.dhis.model.ImmutableDhisObject; import org.dhis2.fhir.adapter.dhis.model.Reference; +import org.dhis2.fhir.adapter.dhis.tracker.trackedentity.TrackedEntityAttributes; +import org.dhis2.fhir.adapter.dhis.tracker.trackedentity.TrackedEntityType; import org.dhis2.fhir.adapter.fhir.transform.TransformerException; import javax.annotation.Nonnull; @@ -41,7 +44,7 @@ * * @author volsch */ -public class ImmutableScriptedTrackedEntityInstance implements ScriptedTrackedEntityInstance +public class ImmutableScriptedTrackedEntityInstance implements ScriptedTrackedEntityInstance, ImmutableDhisObject { private final ScriptedTrackedEntityInstance delegate; @@ -98,6 +101,31 @@ public ZonedDateTime getLastUpdated() return delegate.getLastUpdated(); } + @Nullable + @Override + public ScriptedTrackedEntityInstance getTrackedEntityInstance() + { + if ( delegate instanceof ImmutableDhisObject ) + { + return delegate; + } + return new ImmutableScriptedTrackedEntityInstance( delegate ); + } + + @Nonnull + @Override + public TrackedEntityAttributes getTrackedEntityAttributes() + { + return delegate.getTrackedEntityAttributes(); + } + + @Nonnull + @Override + public TrackedEntityType getType() + { + return delegate.getType(); + } + @Override @Nullable public String getOrganizationUnitId() diff --git a/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/scripted/ScriptedDhisResource.java b/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/scripted/ScriptedDhisResource.java index 6fc6330b..2c48ab81 100644 --- a/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/scripted/ScriptedDhisResource.java +++ b/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/scripted/ScriptedDhisResource.java @@ -57,5 +57,13 @@ public interface ScriptedDhisResource @Nullable String getOrganizationUnitId(); + /** + * @return the tracked entity instance that is associated with this resource + * (may be this resource itself if it is the tracked entity) or null + * if resource is not associated with a tracked entity. + */ + @Nullable + ScriptedTrackedEntityInstance getTrackedEntityInstance(); + void validate() throws TransformerException; } diff --git a/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/scripted/ScriptedTrackedEntityInstance.java b/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/scripted/ScriptedTrackedEntityInstance.java index 970f8576..e73792e2 100644 --- a/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/scripted/ScriptedTrackedEntityInstance.java +++ b/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/scripted/ScriptedTrackedEntityInstance.java @@ -29,6 +29,8 @@ */ import org.dhis2.fhir.adapter.dhis.model.Reference; +import org.dhis2.fhir.adapter.dhis.tracker.trackedentity.TrackedEntityAttributes; +import org.dhis2.fhir.adapter.dhis.tracker.trackedentity.TrackedEntityType; import org.dhis2.fhir.adapter.scriptable.Scriptable; import javax.annotation.Nonnull; @@ -50,4 +52,10 @@ public interface ScriptedTrackedEntityInstance extends ScriptedDhisResource @Nullable Object getValue( @Nonnull Reference attributeReference ); + + @Nonnull + TrackedEntityAttributes getTrackedEntityAttributes(); + + @Nonnull + TrackedEntityType getType(); } diff --git a/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/scripted/WritableScriptedEnrollment.java b/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/scripted/WritableScriptedEnrollment.java index f43cbcf9..32d2355f 100644 --- a/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/scripted/WritableScriptedEnrollment.java +++ b/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/scripted/WritableScriptedEnrollment.java @@ -61,12 +61,15 @@ public class WritableScriptedEnrollment implements ScriptedEnrollment, Serializa private final Enrollment enrollment; + private final ScriptedTrackedEntityInstance trackedEntityInstance; + private final ValueConverter valueConverter; - public WritableScriptedEnrollment( @Nonnull Program program, @Nonnull Enrollment enrollment, @Nonnull ValueConverter valueConverter ) + public WritableScriptedEnrollment( @Nonnull Program program, @Nonnull Enrollment enrollment, @Nonnull ScriptedTrackedEntityInstance trackedEntityInstance, @Nonnull ValueConverter valueConverter ) { this.program = program; this.enrollment = enrollment; + this.trackedEntityInstance = trackedEntityInstance; this.valueConverter = valueConverter; } @@ -105,6 +108,13 @@ public String getOrganizationUnitId() return enrollment.getOrgUnitId(); } + @Nullable + @Override + public ScriptedTrackedEntityInstance getTrackedEntityInstance() + { + return trackedEntityInstance; + } + @Nullable @Override public ZonedDateTime getEnrollmentDate() diff --git a/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/scripted/WritableScriptedEvent.java b/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/scripted/WritableScriptedEvent.java index 09c311be..ecbabd17 100644 --- a/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/scripted/WritableScriptedEvent.java +++ b/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/scripted/WritableScriptedEvent.java @@ -72,13 +72,16 @@ public class WritableScriptedEvent implements ScriptedEvent, Serializable private final Event event; + private final ScriptedTrackedEntityInstance trackedEntityInstance; + private final ValueConverter valueConverter; - public WritableScriptedEvent( @Nonnull FhirToDhisTransformerContext transformerContext, @Nonnull ProgramStage programStage, @Nonnull Event event, @Nonnull ValueConverter valueConverter ) + public WritableScriptedEvent( @Nonnull FhirToDhisTransformerContext transformerContext, @Nonnull ProgramStage programStage, @Nonnull Event event, @Nullable ScriptedTrackedEntityInstance trackedEntityInstance, @Nonnull ValueConverter valueConverter ) { this.transformerContext = transformerContext; this.programStage = programStage; this.event = event; + this.trackedEntityInstance = trackedEntityInstance; this.valueConverter = valueConverter; } @@ -117,6 +120,13 @@ public String getOrganizationUnitId() return event.getOrgUnitId(); } + @Nullable + @Override + public ScriptedTrackedEntityInstance getTrackedEntityInstance() + { + return trackedEntityInstance; + } + @Nullable @Override public ZonedDateTime getEventDate() diff --git a/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/scripted/WritableScriptedTrackedEntityInstance.java b/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/scripted/WritableScriptedTrackedEntityInstance.java index 95771656..7772386b 100644 --- a/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/scripted/WritableScriptedTrackedEntityInstance.java +++ b/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/scripted/WritableScriptedTrackedEntityInstance.java @@ -41,7 +41,6 @@ import org.dhis2.fhir.adapter.dhis.tracker.trackedentity.TrackedEntityTypeAttribute; import org.dhis2.fhir.adapter.fhir.transform.TransformerException; import org.dhis2.fhir.adapter.fhir.transform.TransformerMappingException; -import org.dhis2.fhir.adapter.fhir.transform.fhir.FhirToDhisTransformerContext; import org.dhis2.fhir.adapter.fhir.transform.fhir.impl.util.ScriptedDateTimeUtils; import org.dhis2.fhir.adapter.model.ValueType; import org.dhis2.fhir.adapter.scriptable.ScriptMethod; @@ -75,7 +74,7 @@ public class WritableScriptedTrackedEntityInstance implements ScriptedTrackedEnt private final ValueConverter valueConverter; - public WritableScriptedTrackedEntityInstance( @Nonnull FhirToDhisTransformerContext transformerContext, + public WritableScriptedTrackedEntityInstance( @Nonnull TrackedEntityAttributes trackedEntityAttributes, @Nonnull TrackedEntityType trackedEntityType, @Nonnull TrackedEntityInstance trackedEntityInstance, @Nonnull ValueConverter valueConverter ) { @@ -123,6 +122,27 @@ public String getTypeId() return trackedEntityType.getId(); } + @Nullable + @Override + public ScriptedTrackedEntityInstance getTrackedEntityInstance() + { + return this; + } + + @Nonnull + @Override + public TrackedEntityAttributes getTrackedEntityAttributes() + { + return trackedEntityAttributes; + } + + @Nonnull + @Override + public TrackedEntityType getType() + { + return trackedEntityType; + } + @Nonnull @ScriptMethod( description = "Returns the national identifier of the tracked entity instance." ) public String getIdentifier() diff --git a/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/DhisBeanTransformerUtils.java b/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/util/DhisBeanTransformerUtils.java similarity index 97% rename from fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/DhisBeanTransformerUtils.java rename to fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/util/DhisBeanTransformerUtils.java index c7e0e645..74a7387a 100644 --- a/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/DhisBeanTransformerUtils.java +++ b/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/util/DhisBeanTransformerUtils.java @@ -1,4 +1,4 @@ -package org.dhis2.fhir.adapter.fhir.transform.dhis.impl; +package org.dhis2.fhir.adapter.fhir.transform.util; /* * Copyright (c) 2004-2018, University of Oslo diff --git a/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/fhir/impl/util/FhirBeanTransformerUtils.java b/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/util/FhirBeanTransformerUtils.java similarity index 97% rename from fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/fhir/impl/util/FhirBeanTransformerUtils.java rename to fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/util/FhirBeanTransformerUtils.java index 80dcb66d..cd911740 100644 --- a/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/fhir/impl/util/FhirBeanTransformerUtils.java +++ b/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/util/FhirBeanTransformerUtils.java @@ -1,4 +1,4 @@ -package org.dhis2.fhir.adapter.fhir.transform.fhir.impl.util; +package org.dhis2.fhir.adapter.fhir.transform.util; /* * Copyright (c) 2004-2018, University of Oslo diff --git a/fhir/src/main/resources/db/migration/production/V1.1.0.0_0_0__TEI_Export.sql b/fhir/src/main/resources/db/migration/production/V1.1.0.0_0_0__TEI_Export.sql index 8c44561f..aca75e40 100644 --- a/fhir/src/main/resources/db/migration/production/V1.1.0.0_0_0__TEI_Export.sql +++ b/fhir/src/main/resources/db/migration/production/V1.1.0.0_0_0__TEI_Export.sql @@ -131,10 +131,14 @@ COMMENT ON COLUMN fhir_queued_dhis_resource.dhis_resource_id IS 'The ID (with re COMMENT ON COLUMN fhir_queued_dhis_resource.queued_at IS 'The timestamp when the data has been queued last time.'; ALTER TABLE fhir_rule - ADD in_enabled BOOLEAN DEFAULT TRUE NOT NULL, - ADD out_enabled BOOLEAN DEFAULT FALSE NOT NULL; + ADD COLUMN in_enabled BOOLEAN DEFAULT TRUE NOT NULL, + ADD COLUMN out_enabled BOOLEAN DEFAULT FALSE NOT NULL, + ADD COLUMN fhir_create_enabled BOOLEAN DEFAULT TRUE NOT NULL, + ADD COLUMN fhir_update_enabled BOOLEAN DEFAULT FALSE NOT NULL; COMMENT ON COLUMN fhir_rule.in_enabled IS 'Specifies if transformation from FHIR to DHIS resource is enabled.'; COMMENT ON COLUMN fhir_rule.out_enabled IS 'Specifies if transformation from DHIS to FHIR resource is enabled.'; +COMMENT ON COLUMN fhir_rule.fhir_create_enabled IS 'Specifies if the creation of a FHIR resource is enabled for output transformations from DHIS to FHIR for this rule.'; +COMMENT ON COLUMN fhir_rule.fhir_update_enabled IS 'Specifies if the update of a FHIR resource is enabled for output transformations from DHIS to FHIR for this rule.'; ALTER TABLE fhir_rule ADD COLUMN applicable_out_script_id UUID, @@ -143,12 +147,28 @@ CREATE INDEX fhir_rule_i4 ON fhir_rule (applicable_out_script_id); COMMENT ON COLUMN fhir_rule.applicable_out_script_id IS 'References the evaluation script that is used to evaluate if the DHIS 2 resource is applicable to be processed by this rule. If no script has been specified, the rule is applicable for further processing.'; +ALTER TABLE fhir_rule + ADD COLUMN stop BOOLEAN DEFAULT FALSE NOT NULL; +COMMENT ON COLUMN fhir_rule.stop IS 'Specifies if this rule is the last applied rule. When the transformation should not stop further rules are applied as well.'; + ALTER TABLE fhir_rule ADD COLUMN transform_out_script_id UUID, - ADD CONSTRAINT fhir_rule_fk6 FOREIGN KEY (transform_out_script_id) REFERENCES fhir_executable_script (id); + ADD CONSTRAINT fhir_rule_fk7 FOREIGN KEY (transform_out_script_id) REFERENCES fhir_executable_script (id); CREATE INDEX fhir_rule_i5 ON fhir_rule (transform_out_script_id); COMMENT ON COLUMN fhir_rule.transform_out_script_id IS 'References the transformation script that is used to transform the DHIS 2 resource to the FHIR resource.'; ALTER TABLE fhir_rule ALTER COLUMN transform_in_script_id DROP NOT NULL; + +ALTER TABLE fhir_tracked_entity + ADD COLUMN out_enabled BOOLEAN DEFAULT FALSE NOT NULL, + ADD COLUMN fhir_create_enabled BOOLEAN DEFAULT TRUE NOT NULL, + ADD COLUMN fhir_update_enabled BOOLEAN DEFAULT FALSE NOT NULL; +COMMENT ON COLUMN fhir_tracked_entity.out_enabled IS 'Specifies if output transformation from DHIS to FHIR for this tracked entity type is enabled.'; +COMMENT ON COLUMN fhir_tracked_entity.fhir_create_enabled IS 'Specifies if the creation of a FHIR resource is enabled for output transformations from DHIS to FHIR for this tracked entity type.'; +COMMENT ON COLUMN fhir_tracked_entity.fhir_update_enabled IS 'Specifies if the update of a FHIR resource is enabled for output transformations from DHIS to FHIR for this tracked entity type.'; + +ALTER TABLE fhir_remote_subscription + ADD COLUMN out_enabled BOOLEAN DEFAULT FALSE NOT NULL; +COMMENT ON COLUMN fhir_remote_subscription.out_enabled IS 'Specifies if output transformation from DHIS to FHIR for this remote subscription is enabled.'; diff --git a/fhir/src/main/resources/db/migration/sample/V1.1.0.0_10_0__TEI_Export_Sample_Data.sql b/fhir/src/main/resources/db/migration/sample/V1.1.0.0_10_0__TEI_Export_Sample_Data.sql new file mode 100644 index 00000000..f091fff6 --- /dev/null +++ b/fhir/src/main/resources/db/migration/sample/V1.1.0.0_10_0__TEI_Export_Sample_Data.sql @@ -0,0 +1,34 @@ +/* + * 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. + */ + +UPDATE fhir_rule +SET out_enabled = true; +UPDATE fhir_tracked_entity +SET out_enabled = true; +UPDATE fhir_remote_subscription +SET out_enabled = true; diff --git a/fhir/src/test/java/org/dhis2/fhir/adapter/fhir/metadata/repository/RemoteSubscriptionRepositoryRestDocsTest.java b/fhir/src/test/java/org/dhis2/fhir/adapter/fhir/metadata/repository/RemoteSubscriptionRepositoryRestDocsTest.java index 39e21188..28028b58 100644 --- a/fhir/src/test/java/org/dhis2/fhir/adapter/fhir/metadata/repository/RemoteSubscriptionRepositoryRestDocsTest.java +++ b/fhir/src/test/java/org/dhis2/fhir/adapter/fhir/metadata/repository/RemoteSubscriptionRepositoryRestDocsTest.java @@ -81,6 +81,7 @@ public void createRemoteSubscription() throws Exception "If the remote subscription has not been enabled, no subscription notifications are processed from the corresponding FHIR service." ).type( JsonFieldType.BOOLEAN ), fields.withPath( "locked" ).description( "Specifies if this remote subscription has been locked. " + "If the remote subscription has been locked (i.e. by automatic processes), no subscription notifications are processed from the corresponding FHIR service." ).type( JsonFieldType.BOOLEAN ), + fields.withPath( "outEnabled" ).description( "Specifies if output transformation from DHIS to FHIR for this remote subscription is enabled (by default false)." ).type( JsonFieldType.BOOLEAN ).optional(), fields.withPath( "fhirVersion" ).description( "The FHIR version that should be used when communicating with the remote FHIR service." ).type( JsonFieldType.STRING ), fields.withPath( "toleranceMillis" ).description( "The number of milli-seconds to subtract from the last updated timestamp when searching for created and updated resources." ).type( JsonFieldType.NUMBER ), fields.withPath( "autoCreatedSubscriptionResources" ).description( "Subscription resources for which the subscriptions should be created automatically when creating the subscription resource. This value will not be returned and can only " + @@ -117,6 +118,7 @@ public void createRemoteSubscription() throws Exception .andExpect( jsonPath( "description", is( "Main FHIR service on which the adapter has subscriptions." ) ) ) .andExpect( jsonPath( "enabled", is( true ) ) ) .andExpect( jsonPath( "locked", is( false ) ) ) + .andExpect( jsonPath( "outEnabled", is( false ) ) ) .andExpect( jsonPath( "autoCreatedSubscriptionResources" ).doesNotExist() ) .andExpect( jsonPath( "adapterEndpoint.baseUrl", is( "http://localhist:8081" ) ) ) .andExpect( jsonPath( "adapterEndpoint.subscriptionType", is( "REST_HOOK" ) ) ) @@ -154,6 +156,7 @@ public void readRemoteSubscription() throws Exception "If the remote subscription has not been enabled, no subscription notifications are processed from the corresponding FHIR service." ).type( JsonFieldType.BOOLEAN ), fields.withPath( "locked" ).description( "Specifies if this remote subscription has been locked. " + "If the remote subscription has been locked (i.e. by automatic processes), no subscription notifications are processed from the corresponding FHIR service." ).type( JsonFieldType.BOOLEAN ), + fields.withPath( "outEnabled" ).description( "Specifies if output transformation from DHIS to FHIR for this remote subscription is enabled." ).type( JsonFieldType.BOOLEAN ), fields.withPath( "fhirVersion" ).description( "The FHIR version that should be used when communicating with the remote FHIR service." ).type( JsonFieldType.STRING ), fields.withPath( "toleranceMillis" ).description( "The number of milli-seconds to subtract from the last updated timestamp when searching for created and updated resources." ).type( JsonFieldType.NUMBER ), fields.withPath( "autoCreatedSubscriptionResources" ).description( "Subscription resources for which the subscriptions should be created automatically when creating the subscription resource. This value will not be returned and can only " + diff --git a/fhir/src/test/java/org/dhis2/fhir/adapter/fhir/metadata/repository/RuleRepositoryRestDocsTest.java b/fhir/src/test/java/org/dhis2/fhir/adapter/fhir/metadata/repository/RuleRepositoryRestDocsTest.java index 9d44543f..cfebfca3 100644 --- a/fhir/src/test/java/org/dhis2/fhir/adapter/fhir/metadata/repository/RuleRepositoryRestDocsTest.java +++ b/fhir/src/test/java/org/dhis2/fhir/adapter/fhir/metadata/repository/RuleRepositoryRestDocsTest.java @@ -95,6 +95,9 @@ public void createTrackedEntityRule() throws Exception fields.withPath( "enabled" ).description( "Specifies if this rule is enabled." ).type( JsonFieldType.BOOLEAN ), fields.withPath( "inEnabled" ).description( "Specifies if transformation of a FHIR to a DHIS2 resource has been enabled (by default true)." ).type( JsonFieldType.BOOLEAN ).optional(), fields.withPath( "outEnabled" ).description( "Specifies if transformation of a DHIS2 to a FHIR resource has been enabled (by default false)." ).type( JsonFieldType.BOOLEAN ).optional(), + fields.withPath( "fhirCreateEnabled" ).description( "Specifies if the creation of a FHIR resource is enabled for output transformations from DHIS to FHIR for this rule (by default true)." ).type( JsonFieldType.BOOLEAN ).optional(), + fields.withPath( "fhirUpdateEnabled" ).description( "Specifies if the update of a FHIR resource is enabled for output transformations from DHIS to FHIR for this rule (by default false)." ).type( JsonFieldType.BOOLEAN ).optional(), + fields.withPath( "stop" ).description( "'Specifies if this rule is the last applied rule. When the transformation should not stop further rules are applied as well (by default false)." ).type( JsonFieldType.BOOLEAN ).optional(), fields.withPath( "evaluationOrder" ).description( "Specifies the precedence of this rule when several rules match. Higher values define a higher precedence." ).type( JsonFieldType.NUMBER ), fields.withPath( "applicableCodeSet" ).description( "Link to the code set reference that is used to check if the incoming request is applicable for this rule." ).type( JsonFieldType.STRING ).optional(), fields.withPath( "applicableInScript" ).description( "Link to the executable script reference that is used to check if the incoming request is applicable for this rule when transforming from a FHIR to a DHIS2 resource. " + @@ -119,6 +122,11 @@ public void createTrackedEntityRule() throws Exception .andExpect( jsonPath( "lastUpdatedBy", is( "2h2maqu827d" ) ) ) .andExpect( jsonPath( "name", is( "FHIR Patient to Person (disabled)" ) ) ) .andExpect( jsonPath( "enabled", is( false ) ) ) + .andExpect( jsonPath( "inEnabled", is( true ) ) ) + .andExpect( jsonPath( "outEnabled", is( false ) ) ) + .andExpect( jsonPath( "fhirCreateEnabled", is( true ) ) ) + .andExpect( jsonPath( "fhirUpdateEnabled", is( false ) ) ) + .andExpect( jsonPath( "stop", is( false ) ) ) .andExpect( jsonPath( "evaluationOrder", is( 0 ) ) ) .andExpect( jsonPath( "dhisResourceType", is( "TRACKED_ENTITY" ) ) ) .andExpect( jsonPath( "fhirResourceType", is( "PATIENT" ) ) ) @@ -157,6 +165,9 @@ public void readTrackedEntityRule() throws Exception fields.withPath( "enabled" ).description( "Specifies if this rule is enabled." ).type( JsonFieldType.BOOLEAN ), fields.withPath( "inEnabled" ).description( "Specifies if transformation of a FHIR to a DHIS2 resource has been enabled." ).type( JsonFieldType.BOOLEAN ), fields.withPath( "outEnabled" ).description( "Specifies if transformation of a DHIS2 to a FHIR resource has been enabled." ).type( JsonFieldType.BOOLEAN ), + fields.withPath( "fhirCreateEnabled" ).description( "Specifies if the creation of a FHIR resource is enabled for output transformations from DHIS to FHIR for this rule (by default true)." ).type( JsonFieldType.BOOLEAN ).optional(), + fields.withPath( "fhirUpdateEnabled" ).description( "Specifies if the update of a FHIR resource is enabled for output transformations from DHIS to FHIR for this rule (by default false)." ).type( JsonFieldType.BOOLEAN ).optional(), + fields.withPath( "stop" ).description( "'Specifies if this rule is the last applied rule. When the transformation should not stop further rules are applied as well." ).type( JsonFieldType.BOOLEAN ), fields.withPath( "evaluationOrder" ).description( "Specifies the precedence of this rule when several rules match. Higher values define a higher precedence." ).type( JsonFieldType.NUMBER ), fields.withPath( "containedAllowed" ).description( "Specified if this rule can process contained resources." ).type( JsonFieldType.BOOLEAN ).optional(), subsectionWithPath( "_links" ).description( "Links to other resources" ) @@ -177,7 +188,8 @@ protected AbstractRule loadTrackedEntityRule( @Nonnull String name ) { final AbstractRule example = new TrackedEntityRule(); example.setName( name ); - return ruleRepository.findOne( Example.of( example, ExampleMatcher.matching().withIgnorePaths( "enabled", "evaluationOrder", "inEnabled", "outEnabled" ) ) ) + return ruleRepository.findOne( Example.of( example, ExampleMatcher.matching().withIgnorePaths( "enabled", "evaluationOrder", + "inEnabled", "outEnabled", "fhirCreateEnabled", "fhirUpdateEnabled" ) ) ) .orElseThrow( () -> new AssertionError( "Rule does not exist: " + name ) ); } diff --git a/fhir/src/test/java/org/dhis2/fhir/adapter/fhir/metadata/repository/TrackedEntityRepositoryRestDocsTest.java b/fhir/src/test/java/org/dhis2/fhir/adapter/fhir/metadata/repository/TrackedEntityRepositoryRestDocsTest.java index 35386fbf..1bc69ef5 100644 --- a/fhir/src/test/java/org/dhis2/fhir/adapter/fhir/metadata/repository/TrackedEntityRepositoryRestDocsTest.java +++ b/fhir/src/test/java/org/dhis2/fhir/adapter/fhir/metadata/repository/TrackedEntityRepositoryRestDocsTest.java @@ -78,6 +78,11 @@ public void createTrackedEntity() throws Exception fields.withPath( "name" ).description( "The unique name of the rule." ).type( JsonFieldType.STRING ), fields.withPath( "description" ).description( "The detailed description that describes for which purpose the rule is used." ).type( JsonFieldType.STRING ).optional(), fields.withPath( "enabled" ).description( "Specifies if this rule is enabled." ).type( JsonFieldType.BOOLEAN ), + fields.withPath( "outEnabled" ).description( "Specifies if output transformation from DHIS to FHIR for this tracked entity type is enabled." ).type( JsonFieldType.BOOLEAN ).optional(), + fields.withPath( "fhirCreateEnabled" ).description( "Specifies if the creation of a FHIR resource is enabled for output transformations from DHIS to FHIR for this tracked entity type (by default true)." ) + .type( JsonFieldType.BOOLEAN ).optional(), + fields.withPath( "fhirUpdateEnabled" ).description( "Specifies if the update of a FHIR resource is enabled for output transformations from DHIS to FHIR for this tracked entity type. (by default false)." ) + .type( JsonFieldType.BOOLEAN ).optional(), fields.withPath( "trackedEntityReference" ).description( "The reference to the DHIS2 Tracked Entity Type." ).type( JsonFieldType.OBJECT ), fields.withPath( "trackedEntityReference.value" ).description( "The unique ID/code/name of the Tracked Entity Type." ).type( JsonFieldType.STRING ), fields.withPath( "trackedEntityReference.type" ).description( "The type of reference value of the Tracked Entity Type." ).type( JsonFieldType.STRING ), @@ -92,6 +97,9 @@ public void createTrackedEntity() throws Exception .andExpect( jsonPath( "lastUpdatedBy", is( "2h2maqu827d" ) ) ) .andExpect( jsonPath( "name", is( "Person Tracked Entity (disabled)" ) ) ) .andExpect( jsonPath( "enabled", is( false ) ) ) + .andExpect( jsonPath( "outEnabled", is( false ) ) ) + .andExpect( jsonPath( "fhirCreateEnabled", is( true ) ) ) + .andExpect( jsonPath( "fhirUpdateEnabled", is( false ) ) ) .andExpect( jsonPath( "trackedEntityReference.value", is( "Obsolete Person" ) ) ) .andExpect( jsonPath( "trackedEntityReference.type", is( "NAME" ) ) ) .andExpect( jsonPath( "trackedEntityIdentifierReference.value", is( "National identifier" ) ) ) @@ -117,6 +125,11 @@ public void readTrackedEntity() throws Exception fields.withPath( "name" ).description( "The unique name of the rule." ).type( JsonFieldType.STRING ), fields.withPath( "description" ).description( "The detailed description that describes for which purpose the rule is used." ).type( JsonFieldType.STRING ).optional(), fields.withPath( "enabled" ).description( "Specifies if this rule is enabled." ).type( JsonFieldType.BOOLEAN ), + fields.withPath( "outEnabled" ).description( "Specifies if output transformation from DHIS to FHIR for this tracked entity type is enabled." ).type( JsonFieldType.BOOLEAN ).optional(), + fields.withPath( "fhirCreateEnabled" ).description( "Specifies if the creation of a FHIR resource is enabled for output transformations from DHIS to FHIR for this tracked entity type (by default true)." ) + .type( JsonFieldType.BOOLEAN ).optional(), + fields.withPath( "fhirUpdateEnabled" ).description( "Specifies if the update of a FHIR resource is enabled for output transformations from DHIS to FHIR for this tracked entity type. (by default false)." ) + .type( JsonFieldType.BOOLEAN ).optional(), fields.withPath( "trackedEntityReference" ).description( "The reference to the DHIS2 Tracked Entity Type." ).type( JsonFieldType.OBJECT ), fields.withPath( "trackedEntityReference.value" ).description( "The unique ID/code/name of the Tracked Entity Type." ).type( JsonFieldType.STRING ), fields.withPath( "trackedEntityReference.type" ).description( "The type of reference value of the Tracked Entity Type." ).type( JsonFieldType.STRING ), diff --git a/pom.xml b/pom.xml index 1f5c994b..9993c1e8 100644 --- a/pom.xml +++ b/pom.xml @@ -38,7 +38,7 @@ org.springframework.boot spring-boot-starter-parent - 2.0.6.RELEASE + 2.0.7.RELEASE dhis2-fhir-adapter