diff --git a/app/src/test/java/org/dhis2/fhir/adapter/fhir/server/r4/R4ProgramMetadataFhirRestAppTest.java b/app/src/test/java/org/dhis2/fhir/adapter/fhir/server/r4/R4ProgramMetadataFhirRestAppTest.java index e828ae5b..f20ce7fa 100644 --- a/app/src/test/java/org/dhis2/fhir/adapter/fhir/server/r4/R4ProgramMetadataFhirRestAppTest.java +++ b/app/src/test/java/org/dhis2/fhir/adapter/fhir/server/r4/R4ProgramMetadataFhirRestAppTest.java @@ -116,7 +116,8 @@ private void getPlanDefinition() throws Exception .andRespond( withSuccess( IOUtils.resourceToString( "/org/dhis2/fhir/adapter/dhis/test/single-program.json", StandardCharsets.UTF_8 ), MediaType.APPLICATION_JSON ) ); userDhis2Server.expect( ExpectedCount.between( 0, 1 ), method( HttpMethod.GET ) ).andExpect( header( "Authorization", "Basic Zmhpcl9jbGllbnQ6Zmhpcl9jbGllbnRfMQ==" ) ) .andExpect( requestTo( dhis2BaseUrl + "/api/" + dhis2ApiVersion + "/programs.json?paging=false&fields=id,name,code,description,lastUpdated,selectIncidentDatesInFuture,selectEnrollmentDatesInFuture,displayIncidentDate,registration," + - "withoutRegistration,captureCoordinates,trackedEntityType%5Bid%5D,programTrackedEntityAttributes%5Bid,name,valueType,mandatory,allowFutureDate,trackedEntityAttribute%5Bid,name,code,valueType,generated%5D%5D,programStages%5Bid,program%5Bid%5D,lastUpdated,name," + + "withoutRegistration,captureCoordinates,trackedEntityType%5Bid%5D,programTrackedEntityAttributes%5Bid,name,valueType,mandatory,allowFutureDate,trackedEntityAttribute%5Bid,name,code,valueType,generated%5D%5D,programStages%5Bid," + + "program%5Bid%5D,lastUpdated,name," + "description,repeatable,captureCoordinates,generatedByEnrollmentDate,minDaysFromStart,programStageDataElements%5Bid,compulsory,allowProvidedElsewhere,dataElement%5Bid,name,code,formName,valueType,optionSetValue,optionSet%5Bid,name," + "options%5Bcode,name%5D%5D%5D%5D%5D" ) ) .andRespond( withSuccess( IOUtils.resourceToString( "/org/dhis2/fhir/adapter/dhis/test/all-programs.json", StandardCharsets.UTF_8 ), MediaType.APPLICATION_JSON ) ); @@ -153,7 +154,8 @@ private void getPlanDefinitionNotExists() userDhis2Server.expect( ExpectedCount.once(), method( HttpMethod.GET ) ).andExpect( header( "Authorization", "Basic Zmhpcl9jbGllbnQ6Zmhpcl9jbGllbnRfMQ==" ) ) .andExpect( requestTo( dhis2BaseUrl + "/api/" + dhis2ApiVersion + "/programs/0dXIdLNUNEn.json?fields=id,name,code,description,lastUpdated,selectIncidentDatesInFuture,selectEnrollmentDatesInFuture,displayIncidentDate,registration," + - "withoutRegistration,captureCoordinates,trackedEntityType%5Bid%5D,programTrackedEntityAttributes%5Bid,name,valueType,mandatory,allowFutureDate,trackedEntityAttribute%5Bid,name,code,valueType,generated%5D%5D,programStages%5Bid,program%5Bid%5D,lastUpdated,name," + + "withoutRegistration,captureCoordinates,trackedEntityType%5Bid%5D,programTrackedEntityAttributes%5Bid,name,valueType,mandatory,allowFutureDate,trackedEntityAttribute%5Bid,name,code,valueType,generated%5D%5D,programStages%5Bid," + + "program%5Bid%5D,lastUpdated,name," + "description,repeatable,captureCoordinates,generatedByEnrollmentDate,minDaysFromStart,programStageDataElements%5Bid,compulsory,allowProvidedElsewhere,dataElement%5Bid,name,code,formName,valueType,optionSetValue,optionSet%5Bid,name," + "options%5Bcode,name%5D%5D%5D%5D%5D" ) ) .andRespond( withStatus( HttpStatus.NOT_FOUND ).contentType( MediaType.APPLICATION_JSON ).body( "{}" ) ); @@ -218,7 +220,8 @@ public void getPlanDefinitionByIdentifierInvalidAuthorization() userDhis2Server.expect( ExpectedCount.once(), method( HttpMethod.GET ) ).andExpect( header( "Authorization", "Basic Zmhpcl9jbGllbnQ6aW52YWxpZF8x" ) ) .andExpect( requestTo( dhis2BaseUrl + "/api/" + dhis2ApiVersion + "/programs.json?paging=false&fields=id,name,code,description,lastUpdated,selectIncidentDatesInFuture,selectEnrollmentDatesInFuture,displayIncidentDate,registration,withoutRegistration," + - "captureCoordinates,trackedEntityType%5Bid%5D,programTrackedEntityAttributes%5Bid,name,valueType,mandatory,allowFutureDate,trackedEntityAttribute%5Bid,name,code,valueType,generated%5D%5D,programStages%5Bid,program%5Bid%5D,lastUpdated,name,description,repeatable," + + "captureCoordinates,trackedEntityType%5Bid%5D,programTrackedEntityAttributes%5Bid,name,valueType,mandatory,allowFutureDate,trackedEntityAttribute%5Bid,name,code,valueType,generated%5D%5D,programStages%5Bid,program%5Bid%5D,lastUpdated,name," + + "description,repeatable," + "captureCoordinates,generatedByEnrollmentDate,minDaysFromStart,programStageDataElements%5Bid,compulsory,allowProvidedElsewhere,dataElement%5Bid,name,code,formName,valueType,optionSetValue,optionSet%5Bid,name,options%5Bcode," + "name%5D%5D%5D%5D%5D&filter=code:eq:OU_1234" ) ) .andRespond( withStatus( HttpStatus.UNAUTHORIZED ) ); @@ -247,7 +250,8 @@ private void getPlanDefinitionByIdentifier() throws Exception userDhis2Server.expect( ExpectedCount.between( 1, 2 ), method( HttpMethod.GET ) ).andExpect( header( "Authorization", "Basic Zmhpcl9jbGllbnQ6Zmhpcl9jbGllbnRfMQ==" ) ) .andExpect( requestTo( dhis2BaseUrl + "/api/" + dhis2ApiVersion + "/programs.json?paging=false&fields=id,name,code,description,lastUpdated,selectIncidentDatesInFuture,selectEnrollmentDatesInFuture,displayIncidentDate,registration," + "withoutRegistration," + - "captureCoordinates,trackedEntityType%5Bid%5D,programTrackedEntityAttributes%5Bid,name,valueType,mandatory,allowFutureDate,trackedEntityAttribute%5Bid,name,code,valueType,generated%5D%5D,programStages%5Bid,program%5Bid%5D,lastUpdated,name,description,repeatable," + + "captureCoordinates,trackedEntityType%5Bid%5D,programTrackedEntityAttributes%5Bid,name,valueType,mandatory,allowFutureDate,trackedEntityAttribute%5Bid,name,code,valueType,generated%5D%5D,programStages%5Bid,program%5Bid%5D,lastUpdated,name," + + "description,repeatable," + "captureCoordinates,generatedByEnrollmentDate,minDaysFromStart,programStageDataElements%5Bid,compulsory,allowProvidedElsewhere,dataElement%5Bid,name,code,formName,valueType,optionSetValue,optionSet%5Bid,name,options%5Bcode," + "name%5D%5D%5D%5D%5D&filter=code:eq:PD_1234" ) ) .andRespond( withSuccess( IOUtils.resourceToString( "/org/dhis2/fhir/adapter/dhis/test/default-program.json", StandardCharsets.UTF_8 ), MediaType.APPLICATION_JSON ) ); @@ -275,7 +279,8 @@ public void searchPlanDefinitionWithoutAuthorization() public void searchPlanDefinitionInvalidAuthorization() { userDhis2Server.expect( ExpectedCount.once(), method( HttpMethod.GET ) ).andExpect( header( "Authorization", "Basic Zmhpcl9jbGllbnQ6aW52YWxpZF8x" ) ) - .andExpect( requestTo( dhis2BaseUrl + "/api/" + dhis2ApiVersion + "/programs.json?filter=name:$ilike:Test&paging=true&page=1&pageSize=10&order=id&fields=id,name,code,description,lastUpdated,selectIncidentDatesInFuture,selectEnrollmentDatesInFuture," + + .andExpect( requestTo( dhis2BaseUrl + "/api/" + dhis2ApiVersion + "/programs.json?filter=name:$ilike:Test&paging=true&page=1&pageSize=10&order=id&fields=id,name,code,description,lastUpdated,selectIncidentDatesInFuture," + + "selectEnrollmentDatesInFuture," + "displayIncidentDate,registration,withoutRegistration,captureCoordinates,trackedEntityType%5Bid%5D,programTrackedEntityAttributes%5Bid,name,valueType,mandatory,allowFutureDate,trackedEntityAttribute%5Bid,name,code,valueType,generated%5D%5D," + "programStages%5Bid,program%5Bid%5D,lastUpdated,name,description,repeatable,captureCoordinates,generatedByEnrollmentDate,minDaysFromStart,programStageDataElements%5Bid,compulsory,allowProvidedElsewhere,dataElement%5Bid,name,code,formName," + "valueType,optionSetValue," + @@ -297,7 +302,8 @@ public void searchPlanDefinition() throws Exception userDhis2Server.expect( ExpectedCount.once(), method( HttpMethod.GET ) ).andExpect( header( "Authorization", "Basic Zmhpcl9jbGllbnQ6Zmhpcl9jbGllbnRfMQ==" ) ) .andExpect( requestTo( dhis2BaseUrl + "/api/" + dhis2ApiVersion + "/programs.json?filter=name:$ilike:Child%20Programme&paging=true&page=1&pageSize=10&order=id&fields=id,name,code,description,lastUpdated,selectIncidentDatesInFuture," + "selectEnrollmentDatesInFuture,displayIncidentDate,registration,withoutRegistration,captureCoordinates,trackedEntityType%5Bid%5D,programTrackedEntityAttributes%5Bid,name,valueType,mandatory,allowFutureDate,trackedEntityAttribute%5Bid,name," + - "code,valueType,generated%5D%5D,programStages%5Bid,program%5Bid%5D,lastUpdated,name,description,repeatable,captureCoordinates,generatedByEnrollmentDate,minDaysFromStart,programStageDataElements%5Bid,compulsory,allowProvidedElsewhere,dataElement%5Bid,name,code,formName," + + "code,valueType,generated%5D%5D,programStages%5Bid,program%5Bid%5D,lastUpdated,name,description,repeatable,captureCoordinates,generatedByEnrollmentDate,minDaysFromStart,programStageDataElements%5Bid,compulsory,allowProvidedElsewhere," + + "dataElement%5Bid,name,code,formName," + "valueType,optionSetValue,optionSet%5Bid,name,options%5Bcode,name%5D%5D%5D%5D%5D" ) ) .andRespond( withSuccess( IOUtils.resourceToString( "/org/dhis2/fhir/adapter/dhis/test/default-program.json", StandardCharsets.UTF_8 ), MediaType.APPLICATION_JSON ) ); diff --git a/common/src/main/java/org/dhis2/fhir/adapter/spring/StaticObjectProvider.java b/common/src/main/java/org/dhis2/fhir/adapter/spring/StaticObjectProvider.java index fedc50ea..2db32e3e 100644 --- a/common/src/main/java/org/dhis2/fhir/adapter/spring/StaticObjectProvider.java +++ b/common/src/main/java/org/dhis2/fhir/adapter/spring/StaticObjectProvider.java @@ -1,7 +1,7 @@ package org.dhis2.fhir.adapter.spring; /* - * Copyright (c) 2004-2018, University of Oslo + * Copyright (c) 2004-2019, University of Oslo * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -29,9 +29,11 @@ */ import org.springframework.beans.BeansException; +import org.springframework.beans.factory.BeanCreationException; import org.springframework.beans.factory.ObjectProvider; import javax.annotation.Nonnull; +import javax.annotation.Nullable; /** * Object provider for a static non-null object. @@ -42,7 +44,7 @@ public class StaticObjectProvider implements ObjectProvider { private final T object; - public StaticObjectProvider( @Nonnull T object ) + public StaticObjectProvider( @Nullable T object ) { this.object = object; } @@ -51,6 +53,11 @@ public StaticObjectProvider( @Nonnull T object ) @Nonnull public T getObject( @Nonnull Object... args ) throws BeansException { + if ( object == null ) + { + throw new BeanCreationException( "Bean has not been provided." ); + } + return object; } @@ -70,6 +77,11 @@ public T getIfUnique() throws BeansException @Nonnull public T getObject() throws BeansException { + if ( object == null ) + { + throw new BeanCreationException( "Bean has not been provided." ); + } + return object; } } diff --git a/dhis/src/main/java/org/dhis2/fhir/adapter/dhis/aggregate/DataValueSet.java b/dhis/src/main/java/org/dhis2/fhir/adapter/dhis/aggregate/DataValueSet.java new file mode 100644 index 00000000..5d22a0e7 --- /dev/null +++ b/dhis/src/main/java/org/dhis2/fhir/adapter/dhis/aggregate/DataValueSet.java @@ -0,0 +1,283 @@ +package org.dhis2.fhir.adapter.dhis.aggregate; + +/* + * Copyright (c) 2004-2019, University of Oslo + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * Neither the name of the HISP project nor the names of its contributors may + * be used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +import com.fasterxml.jackson.annotation.JsonIgnore; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import org.dhis2.fhir.adapter.dhis.model.DataValue; +import org.dhis2.fhir.adapter.dhis.model.DhisResource; +import org.dhis2.fhir.adapter.dhis.model.DhisResourceId; +import org.dhis2.fhir.adapter.dhis.model.DhisResourceType; +import org.dhis2.fhir.adapter.dhis.model.WritableDataValue; + +import javax.annotation.Nonnull; +import java.io.Serializable; +import java.time.ZonedDateTime; +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; + +/** + * @author David Katuscak + */ +public class DataValueSet implements DhisResource, Serializable +{ +// reporter > reference -> Organisation unit +// period > start/end -> Period +// group > code > text -> Data element +// group > measurescore -> Data value +// +// orgUnit +// period +// dataElement +// dataValue + + private static final long serialVersionUID = 6347075531883647618L; + + @JsonProperty( access = JsonProperty.Access.WRITE_ONLY ) + private boolean deleted; + + @JsonIgnore + private boolean modified; + + @JsonIgnore + private boolean newResource; + + @JsonIgnore + private boolean local; + + @JsonProperty + @JsonInclude( JsonInclude.Include.NON_NULL ) + private ZonedDateTime lastUpdated; + + @JsonProperty( "dataValueSet" ) + @JsonInclude( JsonInclude.Include.NON_NULL ) + private String id; + + @JsonProperty( "dataSet" ) + private String dataSetId; + + @JsonProperty( "orgUnit" ) + private String orgUnitId; + + @JsonProperty + private String period; + + private List dataValues; + + public DataValueSet() + { + super(); + } + + public DataValueSet( @Nonnull String id ) + { + this.id = id; + } + + public DataValueSet( boolean newResource ) + { + this.newResource = newResource; + this.modified = newResource; + this.local = newResource; + this.dataValues = new ArrayList<>(); + } + + @Override + @JsonIgnore + public String getId() + { + return id; + } + + @Override + public void setId( String id ) + { + this.id = id; + } + + @Override + public DhisResourceId getResourceId() + { + return (getId() == null) ? null : new DhisResourceId( DhisResourceType.DATA_VALUE_SET, getId() ); + } + + @Override + public String getOrgUnitId() + { + return orgUnitId; + } + + public void setOrgUnitId( String orgUnitId ) + { + this.orgUnitId = orgUnitId; + } + + public String getPeriod() + { + return period; + } + + public void setPeriod( String period ) + { + this.period = period; + } + + public String getDataSetId() + { + return dataSetId; + } + + public void setDataSetId( String dataSetId ) + { + this.dataSetId = dataSetId; + } + + @JsonIgnore + @Nonnull + @Override + public DhisResourceType getResourceType() + { + return DhisResourceType.DATA_VALUE_SET; + } + + @Override + public boolean isNewResource() + { + return newResource; + } + + @Override + public void resetNewResource() + { + this.newResource = false; + this.modified = false; + + if ( lastUpdated == null ) + { + lastUpdated = ZonedDateTime.now(); + } + } + + @Override + public boolean isLocal() + { + return local; + } + + public void setLocal( boolean local ) + { + this.local = local; + } + + public boolean isModified() + { + return modified; + } + + public void setModified( boolean modified ) + { + this.modified = modified; + } + + @Override + public boolean isDeleted() + { + return deleted; + } + + @Override + public ZonedDateTime getLastUpdated() + { + return lastUpdated; + } + + public void setLastUpdated( ZonedDateTime lastUpdated ) + { + this.lastUpdated = lastUpdated; + } + + public List getDataValues() + { + return dataValues; + } + + public void setDataValues( List dataValues ) + { + this.dataValues = dataValues; + } + + public boolean containsDataValue( @Nonnull String dataElementId ) + { + return getDataValues().stream().anyMatch( dv -> Objects.equals( dataElementId, dv.getDataElementId() ) ); + } + + public boolean containsDataValue( @Nonnull String dataElementId, @Nonnull String value ) + { + return getDataValues().stream() + .anyMatch( dv -> Objects.equals( dataElementId, dv.getDataElementId() ) + && dv.getValue() != null + && Objects.equals( value, String.valueOf( dv.getValue() ) ) ); + } + + public boolean containsDataValueWithValue( @Nonnull String dataElementId ) + { + return getDataValues().stream() + .filter( dv -> ( dv.getValue() != null ) ) + .anyMatch( dv -> Objects.equals( dataElementId, dv.getDataElementId() ) ); + } + + @Nonnull + public WritableDataValue getDataValue( @Nonnull String dataElementId ) + { + if ( getDataValues() == null ) + { + setDataValues( new ArrayList<>() ); + } + + WritableDataValue dataValue = getDataValues().stream() + .filter( dv -> Objects.equals( dataElementId, dv.getDataElementId() ) ) + .findFirst() + .orElse( null ); + + if ( dataValue == null ) + { + dataValue = new WritableDataValue( dataElementId, true ); + getDataValues().add( dataValue ); + } + return dataValue; + } + + @JsonIgnore + public boolean isAnyDataValueModified() + { + return (getDataValues() != null) && getDataValues().stream().anyMatch( DataValue::isModified ); + } +} diff --git a/dhis/src/main/java/org/dhis2/fhir/adapter/dhis/aggregate/DataValueSetService.java b/dhis/src/main/java/org/dhis2/fhir/adapter/dhis/aggregate/DataValueSetService.java new file mode 100644 index 00000000..30c32d3b --- /dev/null +++ b/dhis/src/main/java/org/dhis2/fhir/adapter/dhis/aggregate/DataValueSetService.java @@ -0,0 +1,40 @@ +package org.dhis2.fhir.adapter.dhis.aggregate; + +/* + * Copyright (c) 2004-2019, University of Oslo + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * Neither the name of the HISP project nor the names of its contributors may + * be used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +import javax.annotation.Nonnull; + +/** + * @author David Katuscak + */ +public interface DataValueSetService +{ + @Nonnull + DataValueSet createOrUpdate( @Nonnull DataValueSet enrollment ); +} diff --git a/dhis/src/main/java/org/dhis2/fhir/adapter/dhis/aggregate/impl/DataValueSetServiceImpl.java b/dhis/src/main/java/org/dhis2/fhir/adapter/dhis/aggregate/impl/DataValueSetServiceImpl.java new file mode 100644 index 00000000..f7c3d02b --- /dev/null +++ b/dhis/src/main/java/org/dhis2/fhir/adapter/dhis/aggregate/impl/DataValueSetServiceImpl.java @@ -0,0 +1,91 @@ +package org.dhis2.fhir.adapter.dhis.aggregate.impl; + +/* + * Copyright (c) 2004-2019, University of Oslo + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * Neither the name of the HISP project nor the names of its contributors may + * be used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +import org.dhis2.fhir.adapter.dhis.DhisConflictException; +import org.dhis2.fhir.adapter.dhis.DhisImportUnsuccessfulException; +import org.dhis2.fhir.adapter.dhis.aggregate.DataValueSet; +import org.dhis2.fhir.adapter.dhis.aggregate.DataValueSetService; +import org.dhis2.fhir.adapter.dhis.model.ImportSummaryWebMessage; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Qualifier; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.stereotype.Service; +import org.springframework.web.client.HttpClientErrorException; +import org.springframework.web.client.RestTemplate; + +import javax.annotation.Nonnull; +import java.util.Objects; + +/** + * @author David Katuscak + */ +@Service +public class DataValueSetServiceImpl implements DataValueSetService +{ + private static final String DATA_VALUE_SETS_URI = "/dataValueSets.json"; + + private final RestTemplate restTemplate; + + @Autowired + public DataValueSetServiceImpl( @Nonnull @Qualifier( "userDhis2RestTemplate" ) RestTemplate restTemplate ) + { + this.restTemplate = restTemplate; + } + + @Nonnull + @Override + public DataValueSet createOrUpdate( @Nonnull final DataValueSet dataValueSet ) + { + final ResponseEntity response; + + try + { + response = restTemplate.postForEntity( DATA_VALUE_SETS_URI, dataValueSet, ImportSummaryWebMessage.class ); + } + catch ( HttpClientErrorException e ) + { + if ( HttpStatus.CONFLICT.equals( e.getStatusCode() ) ) + { + throw new DhisConflictException( "DataValueSet could not be created: " + e.getResponseBodyAsString(), e ); + } + throw e; + } + + final ImportSummaryWebMessage result = Objects.requireNonNull( response.getBody() ); + + if ( result.isNotSuccessful() ) + { + throw new DhisImportUnsuccessfulException( "Response indicates an unsuccessful DataValueSet import." ); + } + + return dataValueSet; + } +} diff --git a/dhis/src/main/java/org/dhis2/fhir/adapter/dhis/model/DataElements.java b/dhis/src/main/java/org/dhis2/fhir/adapter/dhis/model/DataElements.java new file mode 100644 index 00000000..0366f269 --- /dev/null +++ b/dhis/src/main/java/org/dhis2/fhir/adapter/dhis/model/DataElements.java @@ -0,0 +1,127 @@ +package org.dhis2.fhir.adapter.dhis.model; + +/* + * Copyright (c) 2004-2019, University of Oslo + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * Neither the name of the HISP project nor the names of its contributors may + * be used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +import com.fasterxml.jackson.annotation.JsonIgnore; +import com.fasterxml.jackson.annotation.JsonProperty; +import org.apache.commons.lang3.StringUtils; + +import javax.annotation.Nonnull; +import java.io.Serializable; +import java.util.Collection; +import java.util.Collections; +import java.util.Map; +import java.util.Optional; +import java.util.stream.Collectors; + +/** + * @author David Katuscak + */ +public class DataElements implements Serializable +{ + private static final long serialVersionUID = -4563360527610569923L; + + @JsonProperty( "dataElements" ) + private Collection dataElements; + + @JsonIgnore + private transient volatile Map dataElementsById; + + @JsonIgnore + private transient volatile Map dataElementsByName; + + @JsonIgnore + private transient volatile Map dataElementsByCode; + + public DataElements() + { + this.dataElements = Collections.emptyList(); + } + + public DataElements( @Nonnull Collection dataElements ) + { + this.dataElements = dataElements; + } + + @Nonnull + public Optional getOptional( @Nonnull Reference reference ) + { + switch ( reference.getType() ) + { + case CODE: + return getOptionalByCode( reference.getValue() ); + case NAME: + return getOptionalByName( reference.getValue() ); + case ID: + return getOptionalById( reference.getValue() ); + default: + throw new AssertionError( "Unhandled reference type: " + reference.getType() ); + } + } + + @Nonnull + public Optional getOptionalById( @Nonnull String id ) + { + Map tempDataElementsById = dataElementsById; + if ( tempDataElementsById == null ) + { + dataElementsById = tempDataElementsById = dataElements.stream() + .map( ImmutableDataElement::new ) + .collect( Collectors.toMap( DataElement::getId, de -> de ) ); + } + return Optional.ofNullable( tempDataElementsById.get( id ) ); + } + + @Nonnull + public Optional getOptionalByCode( @Nonnull String code ) + { + Map tempDataElementsByCode = dataElementsByCode; + if ( tempDataElementsByCode == null ) + { + dataElementsByCode = tempDataElementsByCode = dataElements.stream() + .filter( de -> StringUtils.isNotBlank( de.getCode() ) ) + .map( ImmutableDataElement::new ) + .collect( Collectors.toMap( DataElement::getCode, de -> de ) ); + } + return Optional.ofNullable( tempDataElementsByCode.get( code ) ); + } + + @Nonnull + public Optional getOptionalByName( @Nonnull String name ) + { + Map tempDataElementsByName = dataElementsByName; + if ( tempDataElementsByName == null ) + { + dataElementsByName = tempDataElementsByName = dataElements.stream() + .map( ImmutableDataElement::new ) + .collect( Collectors.toMap( DataElement::getName, de -> de ) ); + } + return Optional.ofNullable( tempDataElementsByName.get( name ) ); + } +} diff --git a/dhis/src/main/java/org/dhis2/fhir/adapter/dhis/model/DhisResourceType.java b/dhis/src/main/java/org/dhis2/fhir/adapter/dhis/model/DhisResourceType.java index 8b6ca64a..c41e4eb9 100644 --- a/dhis/src/main/java/org/dhis2/fhir/adapter/dhis/model/DhisResourceType.java +++ b/dhis/src/main/java/org/dhis2/fhir/adapter/dhis/model/DhisResourceType.java @@ -47,6 +47,11 @@ public enum DhisResourceType */ ORGANIZATION_UNIT( "organisationUnits", "ou", "OrganizationUnitRule" ), + /** + * Resource is a Data Value Set. + */ + DATA_VALUE_SET( "dataValueSets", "dvs", "DataValueSetRule" ), + /** * The program stage metadata. */ diff --git a/dhis/src/main/java/org/dhis2/fhir/adapter/dhis/model/ImportSummariesWebMessage.java b/dhis/src/main/java/org/dhis2/fhir/adapter/dhis/model/ImportSummariesWebMessage.java new file mode 100644 index 00000000..c678ead1 --- /dev/null +++ b/dhis/src/main/java/org/dhis2/fhir/adapter/dhis/model/ImportSummariesWebMessage.java @@ -0,0 +1,60 @@ +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. + */ + +/** + * The web message that contains (1 to many) import summaries for creating and updating + * DHIS2 resources. + * + * @author David Katuscak + */ +public class ImportSummariesWebMessage extends WebMessage +{ + private static final long serialVersionUID = -7713823944527785249L; + + private ImportSummaries response; + + public ImportSummaries getResponse() + { + return response; + } + + public void setResponse( ImportSummaries response ) + { + this.response = response; + } + + public boolean isNotSuccessful() + { + return (getStatus() != Status.OK) || + (getResponse().getImportSummaries().size() != 1) || + (getResponse().getImportSummaries().get( 0 ).getStatus() != ImportStatus.SUCCESS) || + (getResponse().getImportSummaries().get( 0 ).getReference() == null); + } +} diff --git a/dhis/src/main/java/org/dhis2/fhir/adapter/dhis/model/ImportSummaryWebMessage.java b/dhis/src/main/java/org/dhis2/fhir/adapter/dhis/model/ImportSummaryWebMessage.java index 834e09c0..69d18af9 100644 --- a/dhis/src/main/java/org/dhis2/fhir/adapter/dhis/model/ImportSummaryWebMessage.java +++ b/dhis/src/main/java/org/dhis2/fhir/adapter/dhis/model/ImportSummaryWebMessage.java @@ -1,7 +1,7 @@ package org.dhis2.fhir.adapter.dhis.model; /* - * Copyright (c) 2004-2018, University of Oslo + * Copyright (c) 2004-2019, University of Oslo * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -29,32 +29,29 @@ */ /** - * The web message that contains import summaries for creating and updating + * The web message that contains (1) import summary for creating and updating * DHIS2 resources. * * @author volsch */ -public class ImportSummaryWebMessage extends WebMessage +public class ImportSummaryWebMessage extends WebMessage { private static final long serialVersionUID = -7713823944527785249L; - private ImportSummaries response; + private ImportSummary response; - public ImportSummaries getResponse() + public ImportSummary getResponse() { return response; } - public void setResponse( ImportSummaries response ) + public void setResponse( ImportSummary response ) { this.response = response; } public boolean isNotSuccessful() { - return (getStatus() != Status.OK) || - (getResponse().getImportSummaries().size() != 1) || - (getResponse().getImportSummaries().get( 0 ).getStatus() != ImportStatus.SUCCESS) || - (getResponse().getImportSummaries().get( 0 ).getReference() == null); + return getStatus() != Status.OK; } } diff --git a/dhis/src/main/java/org/dhis2/fhir/adapter/dhis/sync/impl/DhisResourceRepositoryImpl.java b/dhis/src/main/java/org/dhis2/fhir/adapter/dhis/sync/impl/DhisResourceRepositoryImpl.java index 5d44677c..24c14251 100644 --- a/dhis/src/main/java/org/dhis2/fhir/adapter/dhis/sync/impl/DhisResourceRepositoryImpl.java +++ b/dhis/src/main/java/org/dhis2/fhir/adapter/dhis/sync/impl/DhisResourceRepositoryImpl.java @@ -28,6 +28,8 @@ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ +import org.dhis2.fhir.adapter.dhis.aggregate.DataValueSet; +import org.dhis2.fhir.adapter.dhis.aggregate.DataValueSetService; import org.dhis2.fhir.adapter.dhis.model.DhisResource; import org.dhis2.fhir.adapter.dhis.model.DhisResourceId; import org.dhis2.fhir.adapter.dhis.model.Reference; @@ -56,6 +58,7 @@ * * @author volsch * @author Charles Chigoriwa (ITINORDIC) + * @author David Katuscak */ @Component public class DhisResourceRepositoryImpl implements DhisResourceRepository @@ -74,9 +77,11 @@ public class DhisResourceRepositoryImpl implements DhisResourceRepository private final EventService eventService; + private final DataValueSetService dataValueSetService; + public DhisResourceRepositoryImpl( @Nonnull OrganizationUnitService organizationUnitService, ProgramMetadataService programMetadataService, @Nonnull ProgramStageMetadataService programStageMetadataService, @Nonnull TrackedEntityService trackedEntityService, - @Nonnull EnrollmentService enrollmentService, @Nonnull EventService eventService ) + @Nonnull EnrollmentService enrollmentService, @Nonnull EventService eventService, @Nonnull DataValueSetService dataValueSetService ) { this.organizationUnitService = organizationUnitService; this.programMetadataService = programMetadataService; @@ -84,6 +89,7 @@ public DhisResourceRepositoryImpl( @Nonnull OrganizationUnitService organization this.trackedEntityService = trackedEntityService; this.enrollmentService = enrollmentService; this.eventService = eventService; + this.dataValueSetService = dataValueSetService; } @Nonnull @@ -104,8 +110,10 @@ public Optional findRefreshed( @Nonnull DhisResourceId d return eventService.findOneById( dhisResourceId.getId() ); case ENROLLMENT: return enrollmentService.findOneById( dhisResourceId.getId() ); + case DATA_VALUE_SET: + throw new UnsupportedOperationException( "Finding DHIS2 DataValueSet resources is not supported." ); default: - throw new AssertionError( "Unhandled DHIS resource type: " + dhisResourceId.getType() ); + throw new AssertionError( "Unhandled DHIS2 resource type: " + dhisResourceId.getType() ); } } @@ -122,9 +130,10 @@ public Optional findRefreshedDeleted( @Nonnull DhisResou case PROGRAM_STAGE_METADATA: case TRACKED_ENTITY: case ENROLLMENT: - throw new UnsupportedOperationException( "Retrieving deleted " + dhisResourceId.getType() + " DHIS resource items is not supported." ); + case DATA_VALUE_SET: + throw new UnsupportedOperationException( "Retrieving deleted " + dhisResourceId.getType() + " DHIS2 resource items is not supported." ); default: - throw new AssertionError( "Unhandled DHIS resource type: " + dhisResourceId.getType() ); + throw new AssertionError( "Unhandled DHIS2 resource type: " + dhisResourceId.getType() ); } } @@ -151,8 +160,11 @@ public DhisResource save( @Nonnull DhisResource resource ) case PROGRAM_STAGE_EVENT: saveEvent( (Event) resource ); break; + case DATA_VALUE_SET: + saveDataValueSet( (DataValueSet) resource ); + break; default: - throw new AssertionError( "Unhandled DHIS resource type: " + resource.getResourceType() ); + throw new AssertionError( "Unhandled DHIS2 resource type: " + resource.getResourceType() ); } return resource; } @@ -168,8 +180,10 @@ public boolean delete( @Nonnull DhisResource resource ) return enrollmentService.delete( resource.getId() ); case PROGRAM_STAGE_EVENT: return eventService.delete( resource.getId() ); + case DATA_VALUE_SET: + throw new UnsupportedOperationException( "Deleting DHIS2 DataValueSet resources is nut supported." ); default: - throw new AssertionError( "Unhandled DHIS resource type: " + resource.getResourceType() ); + throw new AssertionError( "Unhandled DHIS2 resource type: " + resource.getResourceType() ); } } @@ -263,4 +277,42 @@ else if ( enrollment.isModified() ) return updated; } + + private boolean saveDataValueSet( @Nonnull DataValueSet dataValueSet ) + { + if ( validateDataValueSet( dataValueSet ) ) + { + if ( dataValueSet.isNewResource() ) + { + logger.info( "Creating new DataValueSet." ); + dataValueSetService.createOrUpdate( dataValueSet ); + logger.info( "Created new DataValueSet for dataSetId: {}, orgUnit: {}, period: {}.", + dataValueSet.getDataSetId(), dataValueSet.getOrgUnitId(), dataValueSet.getPeriod() ); + return true; + } + else if ( dataValueSet.isModified() ) + { + logger.info( "Updating existing DataValueSet." ); + dataValueSetService.createOrUpdate( dataValueSet ); + logger.info( "Created new DataValueSet for dataSetId: {}, orgUnit: {}, period: {}.", + dataValueSet.getDataSetId(), dataValueSet.getOrgUnitId(), dataValueSet.getPeriod() ); + return true; + } + } + + return false; + } + + private boolean validateDataValueSet( @Nonnull DataValueSet dataValueSet ) + { + if ( dataValueSet.getDataSetId() != null && !dataValueSet.getDataSetId().isEmpty() && + dataValueSet.getOrgUnitId() != null && !dataValueSet.getOrgUnitId().isEmpty() && + dataValueSet.getPeriod() != null && !dataValueSet.getPeriod().isEmpty() && + dataValueSet.getDataValues() != null && !dataValueSet.getDataValues().isEmpty() ) + { + return true; + } + + return false; + } } diff --git a/dhis/src/main/java/org/dhis2/fhir/adapter/dhis/tracker/program/impl/EnrollmentServiceImpl.java b/dhis/src/main/java/org/dhis2/fhir/adapter/dhis/tracker/program/impl/EnrollmentServiceImpl.java index b5067391..ba8b1e07 100644 --- a/dhis/src/main/java/org/dhis2/fhir/adapter/dhis/tracker/program/impl/EnrollmentServiceImpl.java +++ b/dhis/src/main/java/org/dhis2/fhir/adapter/dhis/tracker/program/impl/EnrollmentServiceImpl.java @@ -42,8 +42,8 @@ import org.dhis2.fhir.adapter.dhis.model.DhisResourceType; import org.dhis2.fhir.adapter.dhis.model.ImportStatus; import org.dhis2.fhir.adapter.dhis.model.ImportSummaries; +import org.dhis2.fhir.adapter.dhis.model.ImportSummariesWebMessage; import org.dhis2.fhir.adapter.dhis.model.ImportSummary; -import org.dhis2.fhir.adapter.dhis.model.ImportSummaryWebMessage; import org.dhis2.fhir.adapter.dhis.model.Status; import org.dhis2.fhir.adapter.dhis.tracker.program.Enrollment; import org.dhis2.fhir.adapter.dhis.tracker.program.EnrollmentService; @@ -215,7 +215,7 @@ public Enrollment createOrUpdate( @Nonnull Enrollment enrollment ) @Nonnull protected Enrollment _create( @Nonnull Enrollment enrollment ) { - final ResponseEntity response; + final ResponseEntity response; if ( enrollment.getId() == null ) { @@ -237,7 +237,7 @@ protected Enrollment _create( @Nonnull Enrollment enrollment ) try { - response = restTemplate.exchange( ENROLLMENT_CREATE_URI, HttpMethod.POST, new HttpEntity<>( enrollment ), ImportSummaryWebMessage.class ); + response = restTemplate.exchange( ENROLLMENT_CREATE_URI, HttpMethod.POST, new HttpEntity<>( enrollment ), ImportSummariesWebMessage.class ); } catch ( HttpClientErrorException e ) { @@ -249,7 +249,7 @@ protected Enrollment _create( @Nonnull Enrollment enrollment ) throw e; } - final ImportSummaryWebMessage result = Objects.requireNonNull( response.getBody() ); + final ImportSummariesWebMessage result = Objects.requireNonNull( response.getBody() ); if ( result.isNotSuccessful() ) { @@ -290,11 +290,11 @@ protected Enrollment _update( @Nonnull Enrollment enrollment ) // update of included events is not supported enrollment.setEvents( Collections.emptyList() ); - final ResponseEntity response; + final ResponseEntity response; try { response = restTemplate.exchange( ENROLLMENT_UPDATE_URI, HttpMethod.PUT, new HttpEntity<>( enrollment ), - ImportSummaryWebMessage.class, enrollment.getId() ); + ImportSummariesWebMessage.class, enrollment.getId() ); } catch ( HttpClientErrorException e ) { @@ -306,7 +306,7 @@ protected Enrollment _update( @Nonnull Enrollment enrollment ) throw e; } - final ImportSummaryWebMessage result = Objects.requireNonNull( response.getBody() ); + final ImportSummariesWebMessage result = Objects.requireNonNull( response.getBody() ); if ( result.getStatus() != Status.OK ) { @@ -334,10 +334,10 @@ public void persistSave( @Nonnull Collection resources, boolean crea final List enrollments = resources.stream().sorted( DhisResourceComparator.INSTANCE ).collect( Collectors.toList() ); enrollments.forEach( e -> e.setEvents( Collections.emptyList() ) ); - final ResponseEntity response = - restTemplate.postForEntity( create ? ENROLLMENT_CREATES_URI : ENROLLMENT_UPDATES_URI, new DhisEnrollments( enrollments ), ImportSummaryWebMessage.class ); + final ResponseEntity response = + restTemplate.postForEntity( create ? ENROLLMENT_CREATES_URI : ENROLLMENT_UPDATES_URI, new DhisEnrollments( enrollments ), ImportSummariesWebMessage.class ); - final ImportSummaryWebMessage result = Objects.requireNonNull( response.getBody() ); + final ImportSummariesWebMessage result = Objects.requireNonNull( response.getBody() ); final int size = enrollments.size(); if ( result.getStatus() != Status.OK || result.getResponse() == null || result.getResponse().getImportSummaries().size() != size ) @@ -413,10 +413,10 @@ public void persistDeleteById( @Nonnull Collection ids, @Nullable Consum } final List Enrollments = ids.stream().map( Enrollment::new ).sorted( DhisResourceComparator.INSTANCE ).collect( Collectors.toList() ); - final ResponseEntity response = - restTemplate.postForEntity( ENROLLMENT_DELETES_URI, new DhisEnrollments( Enrollments ), ImportSummaryWebMessage.class ); + final ResponseEntity response = + restTemplate.postForEntity( ENROLLMENT_DELETES_URI, new DhisEnrollments( Enrollments ), ImportSummariesWebMessage.class ); - final ImportSummaryWebMessage result = Objects.requireNonNull( response.getBody() ); + final ImportSummariesWebMessage result = Objects.requireNonNull( response.getBody() ); final int size = Enrollments.size(); if ( result.getStatus() != Status.OK || result.getResponse() == null || result.getResponse().getImportSummaries().size() != size ) diff --git a/dhis/src/main/java/org/dhis2/fhir/adapter/dhis/tracker/program/impl/EventServiceImpl.java b/dhis/src/main/java/org/dhis2/fhir/adapter/dhis/tracker/program/impl/EventServiceImpl.java index 5799e5b4..4cf20c3a 100644 --- a/dhis/src/main/java/org/dhis2/fhir/adapter/dhis/tracker/program/impl/EventServiceImpl.java +++ b/dhis/src/main/java/org/dhis2/fhir/adapter/dhis/tracker/program/impl/EventServiceImpl.java @@ -47,8 +47,8 @@ import org.dhis2.fhir.adapter.dhis.model.DhisResourceResult; import org.dhis2.fhir.adapter.dhis.model.DhisResourceType; import org.dhis2.fhir.adapter.dhis.model.ImportStatus; +import org.dhis2.fhir.adapter.dhis.model.ImportSummariesWebMessage; import org.dhis2.fhir.adapter.dhis.model.ImportSummary; -import org.dhis2.fhir.adapter.dhis.model.ImportSummaryWebMessage; import org.dhis2.fhir.adapter.dhis.model.Status; import org.dhis2.fhir.adapter.dhis.model.UriFilterApplier; import org.dhis2.fhir.adapter.dhis.tracker.program.Event; @@ -181,10 +181,10 @@ public void persistDeleteById( @Nonnull Collection ids, @Nullable Consum } final List Events = ids.stream().map( Event::new ).sorted( DhisResourceComparator.INSTANCE ).collect( Collectors.toList() ); - final ResponseEntity response = - restTemplate.postForEntity( DELETES_URI, new DhisEvents( Events ), ImportSummaryWebMessage.class ); + final ResponseEntity response = + restTemplate.postForEntity( DELETES_URI, new DhisEvents( Events ), ImportSummariesWebMessage.class ); - final ImportSummaryWebMessage result = Objects.requireNonNull( response.getBody() ); + final ImportSummariesWebMessage result = Objects.requireNonNull( response.getBody() ); final int size = Events.size(); if ( result.getStatus() != Status.OK || result.getResponse() == null || result.getResponse().getImportSummaries().size() != size ) @@ -249,10 +249,10 @@ public void persistSave( @Nonnull Collection resources, boolean create, @ } final List events = resources.stream().sorted( DhisResourceComparator.INSTANCE ).collect( Collectors.toList() ); - final ResponseEntity response = - restTemplate.postForEntity( create ? CREATES_URI : UPDATES_URI, new DhisEvents( events ), ImportSummaryWebMessage.class ); + final ResponseEntity response = + restTemplate.postForEntity( create ? CREATES_URI : UPDATES_URI, new DhisEvents( events ), ImportSummariesWebMessage.class ); - final ImportSummaryWebMessage result = Objects.requireNonNull( response.getBody() ); + final ImportSummariesWebMessage result = Objects.requireNonNull( response.getBody() ); final int size = events.size(); if ( result.getStatus() != Status.OK || result.getResponse() == null || result.getResponse().getImportSummaries().size() != size ) @@ -381,7 +381,7 @@ public Instant poll( @Nonnull DhisSyncGroup group, @Nonnull Instant lastUpdated, @Nonnull protected Event create( @Nonnull Event event ) { - final ResponseEntity response; + final ResponseEntity response; if ( event.getId() == null ) { @@ -390,7 +390,7 @@ protected Event create( @Nonnull Event event ) try { - response = restTemplate.exchange( CREATE_URI, HttpMethod.POST, new HttpEntity<>( event ), ImportSummaryWebMessage.class ); + response = restTemplate.exchange( CREATE_URI, HttpMethod.POST, new HttpEntity<>( event ), ImportSummariesWebMessage.class ); } catch ( HttpClientErrorException e ) { @@ -402,7 +402,7 @@ protected Event create( @Nonnull Event event ) throw e; } - final ImportSummaryWebMessage result = Objects.requireNonNull( response.getBody() ); + final ImportSummariesWebMessage result = Objects.requireNonNull( response.getBody() ); if ( result.isNotSuccessful() ) { @@ -419,12 +419,12 @@ protected Event create( @Nonnull Event event ) @Nonnull protected Event update( @Nonnull Event event ) { - final ResponseEntity response; + final ResponseEntity response; try { response = restTemplate.exchange( UPDATE_URI, HttpMethod.PUT, new HttpEntity<>( event ), - ImportSummaryWebMessage.class, event.getId() ); + ImportSummariesWebMessage.class, event.getId() ); } catch ( HttpClientErrorException e ) { @@ -436,7 +436,7 @@ protected Event update( @Nonnull Event event ) throw e; } - final ImportSummaryWebMessage result = Objects.requireNonNull( response.getBody() ); + final ImportSummariesWebMessage result = Objects.requireNonNull( response.getBody() ); if ( result.getStatus() != Status.OK ) { @@ -481,11 +481,11 @@ public DhisResourceResult find( @Nonnull String programId, @Nonnull Strin protected void update( @Nonnull MinimalEvent event ) { - final ResponseEntity response; + final ResponseEntity response; try { response = restTemplate.exchange( UPDATE_DATA_VALUE_URI, HttpMethod.PUT, new HttpEntity<>( event ), - ImportSummaryWebMessage.class, event.getId(), event.getDataElementId() ); + ImportSummariesWebMessage.class, event.getId(), event.getDataElementId() ); } catch ( HttpClientErrorException e ) { @@ -495,7 +495,7 @@ protected void update( @Nonnull MinimalEvent event ) } throw e; } - final ImportSummaryWebMessage result = Objects.requireNonNull( response.getBody() ); + final ImportSummariesWebMessage result = Objects.requireNonNull( response.getBody() ); if ( result.getStatus() != Status.OK ) { diff --git a/dhis/src/main/java/org/dhis2/fhir/adapter/dhis/tracker/trackedentity/impl/TrackedEntityServiceImpl.java b/dhis/src/main/java/org/dhis2/fhir/adapter/dhis/tracker/trackedentity/impl/TrackedEntityServiceImpl.java index 7c8102f0..a955f56c 100644 --- a/dhis/src/main/java/org/dhis2/fhir/adapter/dhis/tracker/trackedentity/impl/TrackedEntityServiceImpl.java +++ b/dhis/src/main/java/org/dhis2/fhir/adapter/dhis/tracker/trackedentity/impl/TrackedEntityServiceImpl.java @@ -47,8 +47,8 @@ import org.dhis2.fhir.adapter.dhis.model.DhisResourceResult; import org.dhis2.fhir.adapter.dhis.model.DhisResourceType; import org.dhis2.fhir.adapter.dhis.model.ImportStatus; +import org.dhis2.fhir.adapter.dhis.model.ImportSummariesWebMessage; import org.dhis2.fhir.adapter.dhis.model.ImportSummary; -import org.dhis2.fhir.adapter.dhis.model.ImportSummaryWebMessage; import org.dhis2.fhir.adapter.dhis.model.Status; import org.dhis2.fhir.adapter.dhis.model.UriFilterApplier; import org.dhis2.fhir.adapter.dhis.sync.DhisLastUpdated; @@ -306,10 +306,10 @@ public void persistSave( @Nonnull Collection resources, b trackedEntityInstances.forEach( this::clear ); - final ResponseEntity response = - restTemplate.postForEntity( create ? CREATES_URI : UPDATES_URI, new TrackedEntityInstances( trackedEntityInstances ), ImportSummaryWebMessage.class ); + final ResponseEntity response = + restTemplate.postForEntity( create ? CREATES_URI : UPDATES_URI, new TrackedEntityInstances( trackedEntityInstances ), ImportSummariesWebMessage.class ); - final ImportSummaryWebMessage result = Objects.requireNonNull( response.getBody() ); + final ImportSummariesWebMessage result = Objects.requireNonNull( response.getBody() ); final int size = trackedEntityInstances.size(); if ( result.getStatus() != Status.OK || result.getResponse() == null || result.getResponse().getImportSummaries().size() != size ) @@ -388,10 +388,10 @@ public void persistDeleteById( @Nonnull Collection ids, @Nullable Consum } final List trackedEntityInstances = ids.stream().map( TrackedEntityInstance::new ).sorted( DhisResourceComparator.INSTANCE ).collect( Collectors.toList() ); - final ResponseEntity response = - restTemplate.postForEntity( DELETES_URI, new TrackedEntityInstances( trackedEntityInstances ), ImportSummaryWebMessage.class ); + final ResponseEntity response = + restTemplate.postForEntity( DELETES_URI, new TrackedEntityInstances( trackedEntityInstances ), ImportSummariesWebMessage.class ); - final ImportSummaryWebMessage result = Objects.requireNonNull( response.getBody() ); + final ImportSummariesWebMessage result = Objects.requireNonNull( response.getBody() ); final int size = trackedEntityInstances.size(); if ( result.getStatus() != Status.OK || result.getResponse() == null || result.getResponse().getImportSummaries().size() != size ) @@ -470,7 +470,7 @@ protected TrackedEntityInstance create( @Nonnull TrackedEntityInstance trackedEn { final DhisSyncGroup syncGroup = storedItemService.findSyncGroupById( DhisSyncGroup.DEFAULT_ID ) .orElseThrow( () -> new DhisResourceException( "Could not load default DHIS2 sync group." ) ); - final ResponseEntity response; + final ResponseEntity response; if ( trackedEntityInstance.getId() == null ) { @@ -480,7 +480,7 @@ protected TrackedEntityInstance create( @Nonnull TrackedEntityInstance trackedEn try { clear( trackedEntityInstance ); - response = restTemplate.exchange( CREATE_URI, HttpMethod.POST, new HttpEntity<>( trackedEntityInstance ), ImportSummaryWebMessage.class ); + response = restTemplate.exchange( CREATE_URI, HttpMethod.POST, new HttpEntity<>( trackedEntityInstance ), ImportSummariesWebMessage.class ); } catch ( HttpClientErrorException e ) { @@ -492,7 +492,7 @@ protected TrackedEntityInstance create( @Nonnull TrackedEntityInstance trackedEn throw e; } - final ImportSummaryWebMessage result = Objects.requireNonNull( response.getBody() ); + final ImportSummariesWebMessage result = Objects.requireNonNull( response.getBody() ); if ( result.isNotSuccessful() ) { @@ -513,12 +513,12 @@ protected TrackedEntityInstance update( @Nonnull TrackedEntityInstance trackedEn final DhisSyncGroup syncGroup = storedItemService.findSyncGroupById( DhisSyncGroup.DEFAULT_ID ) .orElseThrow( () -> new DhisResourceException( "Could not load default DHIS2 sync group." ) ); - final ResponseEntity response; + final ResponseEntity response; try { clear( trackedEntityInstance ); response = restTemplate.exchange( UPDATE_URI, HttpMethod.PUT, new HttpEntity<>( trackedEntityInstance ), - ImportSummaryWebMessage.class, trackedEntityInstance.getId() ); + ImportSummariesWebMessage.class, trackedEntityInstance.getId() ); } catch ( HttpClientErrorException e ) { @@ -528,7 +528,7 @@ protected TrackedEntityInstance update( @Nonnull TrackedEntityInstance trackedEn } throw e; } - final ImportSummaryWebMessage result = Objects.requireNonNull( response.getBody() ); + final ImportSummariesWebMessage result = Objects.requireNonNull( response.getBody() ); if ( result.getStatus() != Status.OK ) { throw new DhisImportUnsuccessfulException( "Response indicates an unsuccessful import of tracked entity instance: " + result.getStatus() ); 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 c12402da..8c56e419 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 @@ -98,8 +98,9 @@ public void findMetadataByReferenceIdNotFound() { mockServer.expect( requestTo( "http://localhost:8080/api/programs/93783.json?fields=id,name,code,description,lastUpdated,selectIncidentDatesInFuture,selectEnrollmentDatesInFuture,displayIncidentDate,registration,withoutRegistration,captureCoordinates," + - "trackedEntityType%5Bid%5D,programTrackedEntityAttributes%5Bid,name,valueType,mandatory,allowFutureDate,trackedEntityAttribute%5Bid,name,code,valueType,generated%5D%5D,programStages%5Bid,program%5Bid%5D,lastUpdated,name,description,repeatable," + - "captureCoordinates," + + "trackedEntityType%5Bid%5D,programTrackedEntityAttributes%5Bid,name,valueType,mandatory,allowFutureDate,trackedEntityAttribute%5Bid,name,code,valueType,generated%5D%5D,programStages%5Bid,program%5Bid%5D,lastUpdated,name,description," + + "repeatable," + + "captureCoordinates," + "generatedByEnrollmentDate,minDaysFromStart,programStageDataElements%5Bid,compulsory,allowProvidedElsewhere,dataElement%5Bid,name,code,formName,valueType,optionSetValue,optionSet%5Bid,name,options%5Bcode,name%5D%5D%5D%5D%5D" ) ).andExpect( method( HttpMethod.GET ) ) .andRespond( MockRestResponseCreators.withStatus( HttpStatus.NOT_FOUND ).body( "{}" ).contentType( MediaType.APPLICATION_JSON ) ); @@ -112,7 +113,8 @@ public void findMetadataByReferenceIdServerError() { mockServer.expect( requestTo( "http://localhost:8080/api/programs/93783.json?fields=id,name,code,description,lastUpdated,selectIncidentDatesInFuture,selectEnrollmentDatesInFuture,displayIncidentDate,registration,withoutRegistration,captureCoordinates," + - "trackedEntityType%5Bid%5D,programTrackedEntityAttributes%5Bid,name,valueType,mandatory,allowFutureDate,trackedEntityAttribute%5Bid,name,code,valueType,generated%5D%5D,programStages%5Bid,program%5Bid%5D,lastUpdated,name,description,repeatable,captureCoordinates," + + "trackedEntityType%5Bid%5D,programTrackedEntityAttributes%5Bid,name,valueType,mandatory,allowFutureDate,trackedEntityAttribute%5Bid,name,code,valueType,generated%5D%5D,programStages%5Bid,program%5Bid%5D,lastUpdated,name,description," + + "repeatable,captureCoordinates," + "generatedByEnrollmentDate,minDaysFromStart,programStageDataElements%5Bid,compulsory,allowProvidedElsewhere,dataElement%5Bid,name,code,formName,valueType,optionSetValue,optionSet%5Bid,name,options%5Bcode,name%5D%5D%5D%5D%5D" ) ).andExpect( method( HttpMethod.GET ) ) .andRespond( MockRestResponseCreators.withServerError() ); service.findMetadataByReference( new Reference( "93783", ReferenceType.ID ) ); diff --git a/dhis/src/test/java/org/dhis2/fhir/adapter/dhis/tracker/program/impl/ProgramStageMetadataServiceImplTest.java b/dhis/src/test/java/org/dhis2/fhir/adapter/dhis/tracker/program/impl/ProgramStageMetadataServiceImplTest.java index eba3e0b4..0afc8706 100644 --- a/dhis/src/test/java/org/dhis2/fhir/adapter/dhis/tracker/program/impl/ProgramStageMetadataServiceImplTest.java +++ b/dhis/src/test/java/org/dhis2/fhir/adapter/dhis/tracker/program/impl/ProgramStageMetadataServiceImplTest.java @@ -94,7 +94,8 @@ public void findMetadataByReferenceId() throws IOException @Test public void findMetadataByReferenceIdNotFound() { - mockServer.expect( requestTo( "http://localhost:8080/api/programStages/93783.json?fields=id,program%5Bid%5D,lastUpdated,name,description,repeatable,captureCoordinates,generatedByEnrollmentDate,minDaysFromStart,programStageDataElements%5Bid,compulsory," + + mockServer.expect( requestTo( "http://localhost:8080/api/programStages/93783.json?fields=id,program%5Bid%5D,lastUpdated,name,description,repeatable,captureCoordinates,generatedByEnrollmentDate,minDaysFromStart,programStageDataElements%5Bid," + + "compulsory," + "allowProvidedElsewhere,dataElement%5Bid,name,code,formName,valueType,optionSetValue,optionSet%5Bid,name,options%5Bcode,name%5D%5D%5D%5D" ) ).andExpect( method( HttpMethod.GET ) ) .andRespond( MockRestResponseCreators.withStatus( HttpStatus.NOT_FOUND ).body( "{}" ).contentType( MediaType.APPLICATION_JSON ) ); @@ -105,7 +106,8 @@ public void findMetadataByReferenceIdNotFound() @Test( expected = HttpServerErrorException.class ) public void findMetadataByReferenceIdServerError() { - mockServer.expect( requestTo( "http://localhost:8080/api/programStages/93783.json?fields=id,program%5Bid%5D,lastUpdated,name,description,repeatable,captureCoordinates,generatedByEnrollmentDate,minDaysFromStart,programStageDataElements%5Bid,compulsory," + + mockServer.expect( requestTo( "http://localhost:8080/api/programStages/93783.json?fields=id,program%5Bid%5D,lastUpdated,name,description,repeatable,captureCoordinates,generatedByEnrollmentDate,minDaysFromStart,programStageDataElements%5Bid," + + "compulsory," + "allowProvidedElsewhere,dataElement%5Bid,name,code,formName,valueType,optionSetValue,optionSet%5Bid,name,options%5Bcode,name%5D%5D%5D%5D" ) ).andExpect( method( HttpMethod.GET ) ) .andRespond( MockRestResponseCreators.withServerError() ); service.findMetadataByReference( new Reference( "93783", ReferenceType.ID ) ); @@ -114,7 +116,8 @@ public void findMetadataByReferenceIdServerError() @Test public void findMetadataByReferenceName() throws IOException { - mockServer.expect( requestTo( "http://localhost:8080/api/programStages.json?paging=false&fields=id,program%5Bid%5D,lastUpdated,name,description,repeatable,captureCoordinates,generatedByEnrollmentDate,minDaysFromStart,programStageDataElements%5Bid,compulsory," + + mockServer.expect( requestTo( "http://localhost:8080/api/programStages.json?paging=false&fields=id,program%5Bid%5D,lastUpdated,name,description,repeatable,captureCoordinates,generatedByEnrollmentDate,minDaysFromStart,programStageDataElements%5Bid," + + "compulsory," + "allowProvidedElsewhere,dataElement%5Bid,name,code,formName,valueType,optionSetValue,optionSet%5Bid,name,options%5Bcode,name%5D%5D%5D%5D&filter=name:eq:Birth" ) ).andExpect( method( HttpMethod.GET ) ) .andRespond( withSuccess( IOUtils.resourceToByteArray( "/org/dhis2/fhir/adapter/dhis/tracker/program/impl/programStages.json" ), MediaType.APPLICATION_JSON ) ); @@ -126,7 +129,8 @@ public void findMetadataByReferenceName() throws IOException @Test public void findMetadataByReferenceNameNotFound() throws IOException { - mockServer.expect( requestTo( "http://localhost:8080/api/programStages.json?paging=false&fields=id,program%5Bid%5D,lastUpdated,name,description,repeatable,captureCoordinates,generatedByEnrollmentDate,minDaysFromStart,programStageDataElements%5Bid,compulsory," + + mockServer.expect( requestTo( "http://localhost:8080/api/programStages.json?paging=false&fields=id,program%5Bid%5D,lastUpdated,name,description,repeatable,captureCoordinates,generatedByEnrollmentDate,minDaysFromStart,programStageDataElements%5Bid," + + "compulsory," + "allowProvidedElsewhere,dataElement%5Bid,name,code,formName,valueType,optionSetValue,optionSet%5Bid,name,options%5Bcode,name%5D%5D%5D%5D&filter=name:eq:xBirth" ) ).andExpect( method( HttpMethod.GET ) ) .andRespond( withSuccess( IOUtils.resourceToByteArray( "/org/dhis2/fhir/adapter/dhis/tracker/program/impl/emptyPrograms.json" ), MediaType.APPLICATION_JSON ) ); diff --git a/fhir-dstu3/src/main/java/org/dhis2/fhir/adapter/fhir/server/provider/dstu3/Dstu3MeasureReportResourceProvider.java b/fhir-dstu3/src/main/java/org/dhis2/fhir/adapter/fhir/server/provider/dstu3/Dstu3MeasureReportResourceProvider.java new file mode 100644 index 00000000..7ee3bc84 --- /dev/null +++ b/fhir-dstu3/src/main/java/org/dhis2/fhir/adapter/fhir/server/provider/dstu3/Dstu3MeasureReportResourceProvider.java @@ -0,0 +1,80 @@ +package org.dhis2.fhir.adapter.fhir.server.provider.dstu3; + +/* + * Copyright (c) 2004-2019, University of Oslo + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * Neither the name of the HISP project nor the names of its contributors may + * be used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +import ca.uhn.fhir.rest.annotation.Count; +import ca.uhn.fhir.rest.annotation.OptionalParam; +import ca.uhn.fhir.rest.annotation.RawParam; +import ca.uhn.fhir.rest.annotation.Search; +import ca.uhn.fhir.rest.api.server.IBundleProvider; +import ca.uhn.fhir.rest.api.server.RequestDetails; +import ca.uhn.fhir.rest.param.DateRangeParam; +import org.dhis2.fhir.adapter.fhir.metadata.repository.FhirClientResourceRepository; +import org.dhis2.fhir.adapter.fhir.metadata.repository.FhirClientSystemRepository; +import org.dhis2.fhir.adapter.fhir.model.FhirVersion; +import org.dhis2.fhir.adapter.fhir.repository.DhisRepository; +import org.dhis2.fhir.adapter.fhir.repository.FhirRepository; +import org.dhis2.fhir.adapter.fhir.server.provider.AbstractReadWriteResourceProvider; +import org.hl7.fhir.dstu3.model.MeasureReport; +import org.springframework.stereotype.Component; + +import javax.annotation.Nonnull; +import javax.annotation.Nullable; +import java.util.List; +import java.util.Map; + +/** + * DSTU3 resource provider + * + * @author David Katuscak + */ +@Component +public class Dstu3MeasureReportResourceProvider extends AbstractReadWriteResourceProvider +{ + public Dstu3MeasureReportResourceProvider( @Nonnull FhirClientResourceRepository fhirClientResourceRepository, + @Nonnull FhirClientSystemRepository fhirClientSystemRepository, + @Nonnull FhirRepository fhirRepository, @Nonnull DhisRepository dhisRepository ) + { + super( MeasureReport.class, fhirClientResourceRepository, fhirClientSystemRepository, fhirRepository, dhisRepository ); + } + + @Nonnull + @Override + public FhirVersion getFhirVersion() + { + return FhirVersion.DSTU3; + } + + @Search( allowUnknownParams = true ) + @Nonnull + public IBundleProvider search( @Nonnull RequestDetails requestDetails, @Nullable @Count Integer count, @Nullable @OptionalParam( name = SP_LAST_UPDATED ) DateRangeParam lastUpdatedDateRange, @Nullable @RawParam Map> filter ) + { + return search( requestDetails, count, null, lastUpdatedDateRange, filter ); + } +} diff --git a/fhir-r4/src/main/java/org/dhis2/fhir/adapter/fhir/server/provider/r4/R4MeasureReportResourceProvider.java b/fhir-r4/src/main/java/org/dhis2/fhir/adapter/fhir/server/provider/r4/R4MeasureReportResourceProvider.java new file mode 100644 index 00000000..d34c8f0a --- /dev/null +++ b/fhir-r4/src/main/java/org/dhis2/fhir/adapter/fhir/server/provider/r4/R4MeasureReportResourceProvider.java @@ -0,0 +1,80 @@ +package org.dhis2.fhir.adapter.fhir.server.provider.r4; + +/* + * Copyright (c) 2004-2019, University of Oslo + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * Neither the name of the HISP project nor the names of its contributors may + * be used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +import ca.uhn.fhir.rest.annotation.Count; +import ca.uhn.fhir.rest.annotation.OptionalParam; +import ca.uhn.fhir.rest.annotation.RawParam; +import ca.uhn.fhir.rest.annotation.Search; +import ca.uhn.fhir.rest.api.server.IBundleProvider; +import ca.uhn.fhir.rest.api.server.RequestDetails; +import ca.uhn.fhir.rest.param.DateRangeParam; +import org.dhis2.fhir.adapter.fhir.metadata.repository.FhirClientResourceRepository; +import org.dhis2.fhir.adapter.fhir.metadata.repository.FhirClientSystemRepository; +import org.dhis2.fhir.adapter.fhir.model.FhirVersion; +import org.dhis2.fhir.adapter.fhir.repository.DhisRepository; +import org.dhis2.fhir.adapter.fhir.repository.FhirRepository; +import org.dhis2.fhir.adapter.fhir.server.provider.AbstractReadWriteResourceProvider; +import org.hl7.fhir.r4.model.MeasureReport; +import org.springframework.stereotype.Component; + +import javax.annotation.Nonnull; +import javax.annotation.Nullable; +import java.util.List; +import java.util.Map; + +/** + * r4 resource provider + * + * @author David Katuscak + */ +@Component +public class R4MeasureReportResourceProvider extends AbstractReadWriteResourceProvider +{ + public R4MeasureReportResourceProvider( @Nonnull FhirClientResourceRepository fhirClientResourceRepository, + @Nonnull FhirClientSystemRepository fhirClientSystemRepository, + @Nonnull FhirRepository fhirRepository, @Nonnull DhisRepository dhisRepository ) + { + super( MeasureReport.class, fhirClientResourceRepository, fhirClientSystemRepository, fhirRepository, dhisRepository ); + } + + @Nonnull + @Override + public FhirVersion getFhirVersion() + { + return FhirVersion.R4; + } + + @Search( allowUnknownParams = true ) + @Nonnull + public IBundleProvider search( @Nonnull RequestDetails requestDetails, @Nullable @Count Integer count, @Nullable @OptionalParam( name = SP_LAST_UPDATED ) DateRangeParam lastUpdatedDateRange, @Nullable @RawParam Map> filter ) + { + return search( requestDetails, count, null, lastUpdatedDateRange, filter ); + } +} 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 14e64cc8..f3c88e5b 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 @@ -107,7 +107,8 @@ @JsonSubTypes.Type( value = OrganizationUnitRule.class, name = "ORGANIZATION_UNIT" ), @JsonSubTypes.Type( value = EnrollmentRule.class, name = "ENROLLMENT" ), @JsonSubTypes.Type( value = ProgramMetadataRule.class, name = "PROGRAM_METADATA" ), - @JsonSubTypes.Type( value = ProgramStageMetadataRule.class, name = "PROGRAM_STAGE_METADATA" ) + @JsonSubTypes.Type( value = ProgramStageMetadataRule.class, name = "PROGRAM_STAGE_METADATA" ), + @JsonSubTypes.Type( value = DataValueSetRule.class, name = "DATA_VALUE_SET" ) } ) @JsonFilter( value = AdapterBeanPropertyFilter.FILTER_NAME ) public abstract class AbstractRule extends VersionedBaseMetadata implements Serializable, Comparable, NamedMetadata diff --git a/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/metadata/model/DataValueSetRule.java b/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/metadata/model/DataValueSetRule.java new file mode 100644 index 00000000..8289d5a1 --- /dev/null +++ b/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/metadata/model/DataValueSetRule.java @@ -0,0 +1,123 @@ +package org.dhis2.fhir.adapter.fhir.metadata.model; + +/* + * Copyright (c) 2004-2019, University of Oslo + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * Neither the name of the HISP project nor the names of its contributors may + * be used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +import com.fasterxml.jackson.annotation.JsonFilter; +import com.fasterxml.jackson.annotation.JsonIgnore; +import org.dhis2.fhir.adapter.dhis.model.DhisResourceType; +import org.dhis2.fhir.adapter.jackson.AdapterBeanPropertyFilter; +import org.dhis2.fhir.adapter.jackson.JsonCacheId; + +import javax.annotation.Nonnull; +import javax.persistence.DiscriminatorValue; +import javax.persistence.Entity; +import javax.persistence.JoinColumn; +import javax.persistence.ManyToOne; +import javax.persistence.Table; +import javax.persistence.Transient; + +/** + * Rule for data value sets. + * + * @author David Katuscak + */ +@Entity +@Table( name = "fhir_data_value_set_rule" ) +@DiscriminatorValue( "DATA_VALUE_SET" ) +@JsonFilter( value = AdapterBeanPropertyFilter.FILTER_NAME ) +public class DataValueSetRule extends AbstractRule +{ + private static final long serialVersionUID = 5463810804987445631L; + + //TODO: Not sure about scripts yet + private ExecutableScript orgUnitLookupScript; + + private ExecutableScript locationLookupScript; + + public DataValueSetRule() + { + super( DhisResourceType.DATA_VALUE_SET ); + } + + //TODO: Not sure about scripts yet - BEGIN + + @JsonCacheId + @ManyToOne + @JoinColumn( name = "org_lookup_script_id" ) + public ExecutableScript getOrgUnitLookupScript() + { + return orgUnitLookupScript; + } + + public void setOrgUnitLookupScript( ExecutableScript orgUnitLookupScript ) + { + this.orgUnitLookupScript = orgUnitLookupScript; + } + + @JsonCacheId + @ManyToOne + @JoinColumn( name = "loc_lookup_script_id" ) + public ExecutableScript getLocationLookupScript() + { + return locationLookupScript; + } + + public void setLocationLookupScript( ExecutableScript locationLookupScript ) + { + this.locationLookupScript = locationLookupScript; + } + + //Not sure about scripts yet - END + + @Override + @Transient + @JsonIgnore + public boolean isEffectiveFhirCreateEnable() { + return isExpEnabled() && isFhirCreateEnabled(); + } + + @Override + @Transient + @JsonIgnore + public boolean isEffectiveFhirUpdateEnable() { + return isExpEnabled() && isFhirUpdateEnabled(); + } + + @Override + @Transient + @JsonIgnore + public boolean isEffectiveFhirDeleteEnable() { + return isExpEnabled() && isFhirDeleteEnabled(); + } + + @Override public boolean coversExecutedRule( @Nonnull AbstractRule executedRule ) + { + return false; + } +} diff --git a/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/metadata/model/TransformDataType.java b/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/metadata/model/TransformDataType.java index 0ae3ab47..460f6195 100644 --- a/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/metadata/model/TransformDataType.java +++ b/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/metadata/model/TransformDataType.java @@ -42,6 +42,7 @@ public enum TransformDataType DHIS_TRACKED_ENTITY_INSTANCE( null ), DHIS_ENROLLMENT( null ), DHIS_EVENT( null ), + DHIS_DATA_VALUE_SET( null ), FHIR_ENCOUNTER( FhirResourceType.ENCOUNTER ), FHIR_LOCATION( FhirResourceType.LOCATION ), FHIR_ORGANIZATION( FhirResourceType.ORGANIZATION ), @@ -56,7 +57,8 @@ public enum TransformDataType FHIR_PLAN_DEFINITION( FhirResourceType.PLAN_DEFINITION ), FHIR_QUESTIONNAIRE( FhirResourceType.QUESTIONNAIRE ), FHIR_CARE_PLAN( FhirResourceType.CARE_PLAN ), - FHIR_QUESTIONNAIRE_RESPONSE( FhirResourceType.QUESTIONNAIRE_RESPONSE ); + FHIR_QUESTIONNAIRE_RESPONSE( FhirResourceType.QUESTIONNAIRE_RESPONSE ), + FHIR_MEASURE_REPORT( FhirResourceType.MEASURE_REPORT ); private final FhirResourceType fhirResourceType; diff --git a/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/metadata/repository/DataValueSetRuleRepository.java b/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/metadata/repository/DataValueSetRuleRepository.java new file mode 100644 index 00000000..ff16350b --- /dev/null +++ b/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/metadata/repository/DataValueSetRuleRepository.java @@ -0,0 +1,103 @@ +package org.dhis2.fhir.adapter.fhir.metadata.repository; + +/* + * Copyright (c) 2004-2019, University of Oslo + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * Neither the name of the HISP project nor the names of its contributors may + * be used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +import org.dhis2.fhir.adapter.fhir.metadata.model.DataValueSetRule; +import org.springframework.cache.annotation.CacheConfig; +import org.springframework.cache.annotation.CacheEvict; +import org.springframework.cache.annotation.CachePut; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.querydsl.QuerydslPredicateExecutor; +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.List; +import java.util.UUID; + +/** + * @author David Katuscak + */ +@CacheConfig( cacheManager = "metadataCacheManager", cacheNames = "rule" ) +@RepositoryRestResource +@PreAuthorize( "hasRole('DATA_MAPPING')" ) +public interface DataValueSetRuleRepository extends JpaRepository, + QuerydslPredicateExecutor, MetadataRepository +{ + @Nonnull + @Override + @RestResource( exported = false ) + @PreAuthorize( "true" ) + default Class getEntityType() + { + return DataValueSetRule.class; + } + + @Override + @Nonnull + @CacheEvict( allEntries = true ) + List saveAll( @Nonnull Iterable entities ); + + @Override + @Nonnull + @CachePut( key = "#a0.id" ) + @CacheEvict( allEntries = true ) + S saveAndFlush( @Nonnull S entity ); + + @Override + @Nonnull + @CachePut( key = "#a0.id" ) + @CacheEvict( allEntries = true ) + S save( @Nonnull S entity ); + + @Override + @CacheEvict( allEntries = true ) + void deleteInBatch( @Nonnull Iterable entities ); + + @Override + @CacheEvict( allEntries = true ) + void deleteAllInBatch(); + + @Override + @CacheEvict( key = "#a0" ) + void deleteById( @Nonnull UUID id ); + + @Override + @CacheEvict( key = "#a0.id" ) + void delete( @Nonnull DataValueSetRule entity ); + + @Override + @CacheEvict( allEntries = true ) + void deleteAll( @Nonnull Iterable entities ); + + @Override + @CacheEvict( allEntries = true ) + void deleteAll(); +} diff --git a/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/metadata/repository/listener/DataValueSetRuleEventListener.java b/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/metadata/repository/listener/DataValueSetRuleEventListener.java new file mode 100644 index 00000000..3e7e600d --- /dev/null +++ b/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/metadata/repository/listener/DataValueSetRuleEventListener.java @@ -0,0 +1,42 @@ +package org.dhis2.fhir.adapter.fhir.metadata.repository.listener; + +/* + * Copyright (c) 2004-2019, University of Oslo + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * Neither the name of the HISP project nor the names of its contributors may + * be used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +import org.dhis2.fhir.adapter.fhir.metadata.model.DataValueSetRule; +import org.springframework.stereotype.Component; + +/** + * Event listener that prepares {@link DataValueSetRule} class before saving. + * + * @author David Katuscak + */ +@Component +public class DataValueSetRuleEventListener extends AbstractRuleEventListener +{ +} diff --git a/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/metadata/repository/validator/BeforeCreateSaveDataValueSetRuleValidator.java b/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/metadata/repository/validator/BeforeCreateSaveDataValueSetRuleValidator.java new file mode 100644 index 00000000..4b9c5dc5 --- /dev/null +++ b/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/metadata/repository/validator/BeforeCreateSaveDataValueSetRuleValidator.java @@ -0,0 +1,61 @@ +package org.dhis2.fhir.adapter.fhir.metadata.repository.validator; + +/* + * Copyright (c) 2004-2019, University of Oslo + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * Neither the name of the HISP project nor the names of its contributors may + * be used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +import org.dhis2.fhir.adapter.fhir.metadata.model.DataValueSetRule; +import org.dhis2.fhir.adapter.fhir.metadata.model.TransformDataType; +import org.springframework.stereotype.Component; +import org.springframework.validation.Errors; + +import javax.annotation.Nonnull; +import javax.persistence.EntityManager; + +/** + * Spring Data REST validator for {@link DataValueSetRule}. + * + * @author David Katuscak + */ +@Component +public class BeforeCreateSaveDataValueSetRuleValidator extends AbstractBeforeCreateSaveRuleValidator + implements MetadataValidator +{ + public BeforeCreateSaveDataValueSetRuleValidator( @Nonnull EntityManager entityManager ) + { + super( DataValueSetRule.class, entityManager ); + } + + @Override protected void doValidate( @Nonnull DataValueSetRule rule, @Nonnull Errors errors ) + { + validate( rule, TransformDataType.DHIS_DATA_VALUE_SET, errors ); + + //TODO: Do I need to validate some script? E.g. as in Enrollment validator? + BeforeCreateSaveFhirResourceMappingValidator.checkValidOrgLookupScript( errors, "DataValueSetRule.", "orgUnitLookupScript", rule.getFhirResourceType(), rule.getOrgUnitLookupScript() ); + BeforeCreateSaveFhirResourceMappingValidator.checkValidLocationLookupScript( errors, "DataValueSetRule.", "locationLookupScript", rule.getFhirResourceType(), rule.getLocationLookupScript() ); + } +} diff --git a/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/fhir/impl/aggregate/FhirToDataValueSetTransformer.java b/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/fhir/impl/aggregate/FhirToDataValueSetTransformer.java new file mode 100644 index 00000000..64f2bb43 --- /dev/null +++ b/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/fhir/impl/aggregate/FhirToDataValueSetTransformer.java @@ -0,0 +1,183 @@ +package org.dhis2.fhir.adapter.fhir.transform.fhir.impl.aggregate; + +/* + * Copyright (c) 2004-2019, University of Oslo + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * Neither the name of the HISP project nor the names of its contributors may + * be used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +import org.dhis2.fhir.adapter.dhis.aggregate.DataValueSet; +import org.dhis2.fhir.adapter.dhis.aggregate.DataValueSetService; +import org.dhis2.fhir.adapter.dhis.converter.ValueConverter; +import org.dhis2.fhir.adapter.dhis.model.DhisResourceType; +import org.dhis2.fhir.adapter.dhis.orgunit.OrganizationUnitService; +import org.dhis2.fhir.adapter.fhir.data.repository.FhirDhisAssignmentRepository; +import org.dhis2.fhir.adapter.fhir.metadata.model.DataValueSetRule; +import org.dhis2.fhir.adapter.fhir.metadata.model.FhirClientResource; +import org.dhis2.fhir.adapter.fhir.metadata.model.RuleInfo; +import org.dhis2.fhir.adapter.fhir.model.FhirVersion; +import org.dhis2.fhir.adapter.fhir.repository.DhisFhirResourceId; +import org.dhis2.fhir.adapter.fhir.script.ScriptExecutionContext; +import org.dhis2.fhir.adapter.fhir.script.ScriptExecutor; +import org.dhis2.fhir.adapter.fhir.transform.TransformerException; +import org.dhis2.fhir.adapter.fhir.transform.fhir.FhirToDhisDeleteTransformOutcome; +import org.dhis2.fhir.adapter.fhir.transform.fhir.FhirToDhisTransformOutcome; +import org.dhis2.fhir.adapter.fhir.transform.fhir.FhirToDhisTransformerContext; +import org.dhis2.fhir.adapter.fhir.transform.fhir.impl.AbstractFhirToDhisTransformer; +import org.dhis2.fhir.adapter.spring.StaticObjectProvider; +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.Optional; +import java.util.Set; + +/** + * @author David Katuscak + */ +@Component +public class FhirToDataValueSetTransformer extends AbstractFhirToDhisTransformer +{ + private final DataValueSetService dataValueSetService; + + public FhirToDataValueSetTransformer( + @Nonnull ScriptExecutor scriptExecutor, + @Nonnull OrganizationUnitService organizationUnitService, + @Nonnull FhirDhisAssignmentRepository fhirDhisAssignmentRepository, + @Nonnull DataValueSetService dataValueSetService, + @Nonnull ScriptExecutionContext scriptExecutionContext, @Nonnull ValueConverter valueConverter ) + { + super( scriptExecutor, organizationUnitService, new StaticObjectProvider<>( null ), new StaticObjectProvider<>( null ), + fhirDhisAssignmentRepository, scriptExecutionContext, valueConverter ); + + this.dataValueSetService = dataValueSetService; + } + + @Nonnull + @Override + public DhisResourceType getDhisResourceType() + { + return DhisResourceType.DATA_VALUE_SET; + } + + @Nonnull + @Override + public Class getDhisResourceClass() + { + return DataValueSet.class; + } + + @Nonnull + @Override + public Class getRuleClass() + { + return DataValueSetRule.class; + } + + @Nonnull + @Override + public Set getFhirVersions() + { + return FhirVersion.ALL; + } + + @Nonnull + @Override + protected Optional getResourceById( @Nullable final String id ) + throws TransformerException + { + return Optional.empty(); + } + + @Nonnull + @Override + protected Optional getActiveResource( @Nonnull final FhirToDhisTransformerContext context, + @Nonnull final RuleInfo ruleInfo, @Nonnull final Map scriptVariables, + final boolean sync, final boolean refreshed ) throws TransformerException + { + return Optional.empty(); + } + + @Nonnull + @Override + protected Optional findResourceById( @Nonnull final FhirToDhisTransformerContext context, + @Nonnull final RuleInfo ruleInfo, @Nonnull final String id, + @Nonnull final Map scriptVariables ) + { + return Optional.empty(); + } + + @Override + protected boolean isAlwaysActiveResource( @Nonnull final RuleInfo ruleInfo ) + { + return false; + } + + @Nullable + @Override + protected DataValueSet createResource( @Nonnull final FhirToDhisTransformerContext context, + @Nonnull final RuleInfo ruleInfo, @Nullable final String id, + @Nonnull final Map scriptVariables, final boolean sync, final boolean refreshed ) + throws TransformerException + { + if ( context.isCreationDisabled() ) + { + return null; + } + + //TODO: + return null; + } + + @Nullable + @Override + public FhirToDhisTransformOutcome transform( @Nonnull final FhirClientResource fhirClientResource, + @Nonnull final FhirToDhisTransformerContext context, @Nonnull final IBaseResource input, + @Nonnull final RuleInfo ruleInfo, @Nonnull final Map scriptVariables ) + throws TransformerException + { + //TODO: + return null; + } + + @Nullable + @Override + public FhirToDhisDeleteTransformOutcome transformDeletion( + @Nonnull final FhirClientResource fhirClientResource, @Nonnull final RuleInfo ruleInfo, + @Nonnull final DhisFhirResourceId dhisFhirResourceId ) throws TransformerException + { + return null; + } + + @Override + protected boolean isSyncRequired( @Nonnull final FhirToDhisTransformerContext context, + @Nonnull final RuleInfo ruleInfo, @Nonnull final Map scriptVariables ) + throws TransformerException + { + return context.getFhirRequest().isSync(); + } +} diff --git a/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/scripted/ScriptedDataValueSet.java b/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/scripted/ScriptedDataValueSet.java new file mode 100644 index 00000000..799cb863 --- /dev/null +++ b/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/scripted/ScriptedDataValueSet.java @@ -0,0 +1,53 @@ +package org.dhis2.fhir.adapter.fhir.transform.scripted; + +/* + * Copyright (c) 2004-2019, University of Oslo + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * Neither the name of the HISP project nor the names of its contributors may + * be used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +import org.dhis2.fhir.adapter.dhis.model.WritableDataValue; +import org.dhis2.fhir.adapter.scriptable.Scriptable; + +import javax.annotation.Nonnull; +import java.util.List; + +/** + * Mutable or immutable data value set resource that can be used by scripts safely. + * + * @author David Katuscak + */ +@Scriptable +public interface ScriptedDataValueSet extends ScriptedDhisResource +{ + @Nonnull + String getDataSetId(); + + @Nonnull + String getPeriod(); + + @Nonnull + List getDataValues(); +} diff --git a/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/scripted/WritableScriptedDataValueSet.java b/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/scripted/WritableScriptedDataValueSet.java new file mode 100644 index 00000000..42ed9346 --- /dev/null +++ b/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/scripted/WritableScriptedDataValueSet.java @@ -0,0 +1,319 @@ +package org.dhis2.fhir.adapter.fhir.transform.scripted; + +/* + * Copyright (c) 2004-2019, University of Oslo + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * Neither the name of the HISP project nor the names of its contributors may + * be used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +import org.dhis2.fhir.adapter.converter.ConversionException; +import org.dhis2.fhir.adapter.dhis.aggregate.DataValueSet; +import org.dhis2.fhir.adapter.dhis.converter.ValueConverter; +import org.dhis2.fhir.adapter.dhis.model.DataElement; +import org.dhis2.fhir.adapter.dhis.model.DataElements; +import org.dhis2.fhir.adapter.dhis.model.DataValue; +import org.dhis2.fhir.adapter.dhis.model.DhisResourceId; +import org.dhis2.fhir.adapter.dhis.model.DhisResourceType; +import org.dhis2.fhir.adapter.dhis.model.Reference; +import org.dhis2.fhir.adapter.dhis.model.WritableDataValue; +import org.dhis2.fhir.adapter.dhis.orgunit.OrganizationUnitService; +import org.dhis2.fhir.adapter.dhis.tracker.trackedentity.TrackedEntityAttribute; +import org.dhis2.fhir.adapter.dhis.tracker.trackedentity.TrackedEntityAttributeValue; +import org.dhis2.fhir.adapter.fhir.transform.TransformerException; +import org.dhis2.fhir.adapter.fhir.transform.TransformerMappingException; +import org.dhis2.fhir.adapter.fhir.transform.fhir.impl.util.ScriptedDateTimeUtils; +import org.dhis2.fhir.adapter.scriptable.ScriptMethod; +import org.dhis2.fhir.adapter.scriptable.ScriptMethodArg; +import org.dhis2.fhir.adapter.scriptable.ScriptType; +import org.dhis2.fhir.adapter.scriptable.Scriptable; + +import javax.annotation.Nonnull; +import javax.annotation.Nullable; +import java.io.Serializable; +import java.time.ZonedDateTime; +import java.util.Collection; +import java.util.List; +import java.util.Objects; + +/** + * Mutable data value set resource that can be used by scripts safely. + * + * @author David Katuscak + */ +@Scriptable +@ScriptType( value = "DataValueSet", var = "dataValueSet", transformDataType = "DHIS_DATA_VALUE_SET", + description = "Data Value Set. If Data Value Set is not new and will be modified, it will be persisted." ) +public class WritableScriptedDataValueSet implements ScriptedDataValueSet, Serializable +{ + private final DataElements dataElements; + + private final DataValueSet dataValueSet; + + private final OrganizationUnitService organizationUnitService; + + private final ValueConverter valueConverter; + + public WritableScriptedDataValueSet ( @Nonnull DataElements dataElements, @Nonnull DataValueSet dataValueSet, + @Nonnull OrganizationUnitService organizationUnitService, @Nonnull ValueConverter valueConverter ) + { + this.dataElements = dataElements; + this.dataValueSet = dataValueSet; + this.organizationUnitService = organizationUnitService; + this.valueConverter = valueConverter; + } + + @Nullable + @Override + @ScriptMethod( description = "Returns the ID of the DataValueSet on DHIS2. Return null if the instance is new." ) + public String getId() + { + return dataValueSet.getId(); + } + + @Nonnull + @Override + public DhisResourceType getResourceType() + { + return dataValueSet.getResourceType(); + } + + @Nullable + @Override + public DhisResourceId getResourceId() + { + return dataValueSet.getResourceId(); + } + + @Override + @ScriptMethod( description = "Returns if the data value set is new ans has not yet been saved on DHIS2." ) + public boolean isNewResource() + { + return dataValueSet.isNewResource(); + } + + @Override + public boolean isLocal() + { + return dataValueSet.isLocal(); + } + + @Override public boolean isDeleted() + { + return false; + } + + @Nullable + @Override + @ScriptMethod( description = "Returns the date and time when the resource has been updated the last time or null if this is a new resource." ) + public ZonedDateTime getLastUpdated() + { + return dataValueSet.getLastUpdated(); + } + + @Nullable + @Override + @ScriptMethod( description = "Returns the ID of the organisation unit on DHIS2 this data value set holds data for." ) + public String getOrganizationUnitId() + { + return dataValueSet.getOrgUnitId(); + } + + @ScriptMethod( description = "Sets the organization unit ID of the organization unit to which this data value set belongs to on DHIS2 (must not be null).", + args = @ScriptMethodArg( value = "orgUnitId", description = "The organization unit ID to which the data value set belongs to" ), + returnDescription = "Returns true each time." ) + public boolean setOrganizationUnitId( @Nullable String orgUnitId ) + { + if ( orgUnitId == null ) + { + throw new TransformerMappingException( "Organization unit ID of data value set must not be null." ); + } + + //I expect that all orgUnits are in place. In the future, if a dynamic OrgUnit resolution feature should be + // in place, then the logic can be changed to use that feature + Reference orgUnitReference = Reference.createIdReference( orgUnitId ); + if ( organizationUnitService.findOneByReference( orgUnitReference ).isPresent() ) + { + if ( !Objects.equals( dataValueSet.getOrgUnitId(), orgUnitId ) ) + { + dataValueSet.setModified( true ); + } + + dataValueSet.setOrgUnitId( orgUnitId ); + } + else + { + throw new TransformerMappingException( "Organization unit with provided Organization unit ID (" + orgUnitId + ") does not exist." ); + } + + return true; + } + + @Nullable + @Override + public ScriptedTrackedEntityInstance getTrackedEntityInstance() + { + return null; + } + + @Nonnull + @Override + @ScriptMethod( description = "Returns the data set ID on DHIS2 this data value set holds data for." ) + public String getDataSetId() + { + return dataValueSet.getDataSetId(); + } + + @ScriptMethod( description = "Sets the data set ID of the data set to which this data value set belongs to on DHIS2 (must not be null).", + args = @ScriptMethodArg( value = "dataSetId", description = "The data set ID to which the data value set belongs to" ), + returnDescription = "Returns true each time." ) + public boolean setDataSetId( @Nullable String dataSetId ) + { + //TODO: Consider fetching all available dataSets from https://play.dhis2.org/dev/api/dataSets and validate + + if ( dataSetId == null ) + { + throw new TransformerMappingException( "Data set ID of data value set must not be null." ); + } + if ( !Objects.equals( dataValueSet.getDataSetId(), dataSetId ) ) + { + dataValueSet.setModified( true ); + } + dataValueSet.setDataSetId( dataSetId ); + + return true; + } + + @Nonnull + @Override + @ScriptMethod( description = "Returns the period on DHIS2 this data value set holds data for." ) + public String getPeriod() + { + return dataValueSet.getPeriod(); + } + + @ScriptMethod( description = "Sets the period to which this data value set belongs to on DHIS2 (must not be null).", + args = @ScriptMethodArg( value = "period", description = "The period to which the data value set belongs to" ), + returnDescription = "Returns true each time." ) + public boolean setPeriod( @Nullable String period ) + { + + //TODO: Consider to create a PeriodUtils class and add some more comprehensive validation of period + if ( period == null ) + { + throw new TransformerMappingException( "Period of data value set must not be null." ); + } + if ( !Objects.equals( dataValueSet.getPeriod(), period ) ) + { + dataValueSet.setModified( true ); + } + dataValueSet.setPeriod( period ); + + return true; + } + + @Nonnull + @Override + public List getDataValues() + { + return dataValueSet.getDataValues(); + } + + @ScriptMethod( description = "Sets the data value of a data value set (must not be null). " + + "This will be skipped when the specified last updated date is before the current last updated data and the user which has last modified the data value set was not the adapter itself.", + returnDescription = "Returns only true if updating the data value has not been skipped since the specified last updated date is before the current last updated date.", + args = { + @ScriptMethodArg( value = "dataElementReference", description = "The reference object to the data element." ), + @ScriptMethodArg( value = "value", description = "The value that should be set for given data element. The value will be converted to the required type automatically (if possible)." ), + @ScriptMethodArg( value = "lastUpdated", description = "The last updated timestamp of the data value that should be assigned to the data value set. This value can be null if check should not be made." ) + } ) + public boolean setDataValue( @Nonnull Reference dataElementReference, @Nullable Object value, + @Nullable Object lastUpdated ) throws TransformerException + { + + final DataElement dataElement = dataElements.getOptional( dataElementReference ).orElseThrow( () -> + new TransformerScriptException( "Data element \"" + dataElementReference + "\" does not exist." ) ); + ZonedDateTime convertedLastUpdated = ScriptedDateTimeUtils.toZonedDateTime( lastUpdated, valueConverter ); + + return setDataValue( dataElement, value, convertedLastUpdated ); + } + + private boolean setDataValue( @Nonnull DataElement dataElement, @Nullable Object value, + @Nullable ZonedDateTime lastUpdated ) throws TransformerException + { + if ( value == null ) + { + throw new TransformerMappingException( "Data value of data value set must not be null." ); + } + + Object convertedValue; + try + { + convertedValue = valueConverter.convert( value, dataElement.getValueType(), String.class ); + } + catch ( ConversionException e ) + { + throw new TransformerMappingException( "Data value of data element \"" + dataElement.getName() + "\" could not be converted: " + e.getMessage(), e ); + } + + final WritableDataValue dataValue = dataValueSet.getDataValue( dataElement.getId() ); + if ( (lastUpdated != null) && (dataValue.getLastUpdated() != null) && dataValue.getLastUpdated().isAfter( lastUpdated ) ) + { + return false; + } + + if ( !Objects.equals( dataValue.getValue(), convertedValue ) ) + { + dataValueSet.setModified( true ); + } + dataValue.setValue( convertedValue ); + + return true; + } + + @Override + @ScriptMethod( description = "Validates the content of the data value set and throws an exception if the content is invalid." ) + public void validate() throws TransformerException + { + if ( dataValueSet.getOrgUnitId() == null ) + { + throw new TransformerMappingException( "Organization unit ID of data value set has not been specified." ); + } + + if ( dataValueSet.getDataSetId() == null ) + { + throw new TransformerMappingException( "Data set ID of data value set has not been specified." ); + } + + if ( dataValueSet.getPeriod() == null ) + { + throw new TransformerMappingException( "Period of data value set has not been specified." ); + } + + if ( dataValueSet.getDataValues() == null || dataValueSet.getDataValues().isEmpty() ) + { + throw new TransformerMappingException( "No data value for data value set has been specified." ); + } + } +} diff --git a/fhir/src/main/resources/db/migration/production/V1.1.0.42_0_0__Add_New_Resource_Type_Into_Enum.sql b/fhir/src/main/resources/db/migration/production/V1.1.0.42_0_0__Add_New_Resource_Type_Into_Enum.sql new file mode 100644 index 00000000..067659e3 --- /dev/null +++ b/fhir/src/main/resources/db/migration/production/V1.1.0.42_0_0__Add_New_Resource_Type_Into_Enum.sql @@ -0,0 +1,30 @@ +/* + * Copyright (c) 2004-2019, University of Oslo + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * Neither the name of the HISP project nor the names of its contributors may + * be used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO PROGRAM_STAGE_EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +-- @formatter:off +INSERT INTO fhir_resource_type_enum VALUES ('MEASURE_REPORT'); \ No newline at end of file