diff --git a/app/pom.xml b/app/pom.xml
index 1d220104..686c577e 100644
--- a/app/pom.xml
+++ b/app/pom.xml
@@ -1,6 +1,6 @@
+
+
+ 4.0.0
+
+ dhis2-fhir-adapter-fhir-r4
+ jar
+
+
+ org.dhis2.fhir.adapter
+ dhis2-fhir-adapter
+ 1.1.0-SNAPSHOT
+ ..
+
+
+ dhis2-fhir-adapter-fhir-r4
+
+ The FHIR version R4 specific implementation of classes. This module has
+ (unlike other modules) access to R4 dependencies of HAPI FHIR. Classes
+ of this module may be duplicated (with FHIR version specific code changes)
+ for other FHIR versions in other modules
+
+
+
+
+ org.dhis2.fhir.adapter
+ dhis2-fhir-adapter-fhir
+ ${project.version}
+
+
+
+ ca.uhn.hapi.fhir
+ hapi-fhir-structures-r4
+
+
+ ca.uhn.hapi.fhir
+ hapi-fhir-validation-resources-r4
+
+
+
+ org.mockito
+ mockito-core
+ test
+
+
+
diff --git a/fhir-r4/src/main/java/org/dhis2/fhir/adapter/fhir/config/r4/R4FhirConfig.java b/fhir-r4/src/main/java/org/dhis2/fhir/adapter/fhir/config/r4/R4FhirConfig.java
new file mode 100644
index 00000000..b5b350ab
--- /dev/null
+++ b/fhir-r4/src/main/java/org/dhis2/fhir/adapter/fhir/config/r4/R4FhirConfig.java
@@ -0,0 +1,43 @@
+package org.dhis2.fhir.adapter.fhir.config.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.context.FhirContext;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+
+@Configuration
+public class R4FhirConfig
+{
+ @Bean
+ protected FhirContext fhirContextR4()
+ {
+ return FhirContext.forR4();
+ }
+}
diff --git a/fhir-r4/src/main/java/org/dhis2/fhir/adapter/fhir/converter/r4/R4AdministrativeGenderToStringConverter.java b/fhir-r4/src/main/java/org/dhis2/fhir/adapter/fhir/converter/r4/R4AdministrativeGenderToStringConverter.java
new file mode 100644
index 00000000..f4d7d4df
--- /dev/null
+++ b/fhir-r4/src/main/java/org/dhis2/fhir/adapter/fhir/converter/r4/R4AdministrativeGenderToStringConverter.java
@@ -0,0 +1,53 @@
+package org.dhis2.fhir.adapter.fhir.converter.r4;
+
+/*
+ * Copyright (c) 2004-2019, University of Oslo
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * Neither the name of the HISP project nor the names of its contributors may
+ * be used to endorse or promote products derived from this software without
+ * specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
+ * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+import org.dhis2.fhir.adapter.converter.ConvertedValueTypes;
+import org.dhis2.fhir.adapter.fhir.converter.AbstractAdministrativeGenderToStringConverter;
+import org.dhis2.fhir.adapter.fhir.metadata.model.ConstantResolver;
+import org.dhis2.fhir.adapter.model.ValueType;
+import org.hl7.fhir.r4.model.Enumerations.AdministrativeGender;
+import org.springframework.stereotype.Component;
+
+import javax.annotation.Nonnull;
+
+/**
+ * FHIR version R4 implementation of {@link AbstractAdministrativeGenderToStringConverter}.
+ *
+ * @author volsch
+ */
+@Component
+@ConvertedValueTypes( types = ValueType.TEXT )
+public class R4AdministrativeGenderToStringConverter extends AbstractAdministrativeGenderToStringConverter
+{
+ public R4AdministrativeGenderToStringConverter( @Nonnull ConstantResolver constantResolver )
+ {
+ super( AdministrativeGender.class, constantResolver );
+ }
+}
diff --git a/fhir-r4/src/main/java/org/dhis2/fhir/adapter/fhir/converter/r4/R4BaseDateTimeTypeToDateStringConverter.java b/fhir-r4/src/main/java/org/dhis2/fhir/adapter/fhir/converter/r4/R4BaseDateTimeTypeToDateStringConverter.java
new file mode 100644
index 00000000..17c3097c
--- /dev/null
+++ b/fhir-r4/src/main/java/org/dhis2/fhir/adapter/fhir/converter/r4/R4BaseDateTimeTypeToDateStringConverter.java
@@ -0,0 +1,50 @@
+package org.dhis2.fhir.adapter.fhir.converter.r4;
+
+/*
+ * Copyright (c) 2004-2019, University of Oslo
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * Neither the name of the HISP project nor the names of its contributors may
+ * be used to endorse or promote products derived from this software without
+ * specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
+ * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+import org.dhis2.fhir.adapter.converter.ConvertedValueTypes;
+import org.dhis2.fhir.adapter.fhir.converter.AbstractPrimitiveTypeDateToDateStringConverter;
+import org.dhis2.fhir.adapter.model.ValueType;
+import org.hl7.fhir.r4.model.BaseDateTimeType;
+import org.springframework.stereotype.Component;
+
+/**
+ * FHIR version R4 implementation of {@link AbstractPrimitiveTypeDateToDateStringConverter}.
+ *
+ * @author volsch
+ */
+@Component
+@ConvertedValueTypes( types = ValueType.DATE )
+public class R4BaseDateTimeTypeToDateStringConverter extends AbstractPrimitiveTypeDateToDateStringConverter
+{
+ public R4BaseDateTimeTypeToDateStringConverter()
+ {
+ super( BaseDateTimeType.class );
+ }
+}
diff --git a/fhir-r4/src/main/java/org/dhis2/fhir/adapter/fhir/converter/r4/R4BaseDateTimeTypeToZonedDateTimeConverter.java b/fhir-r4/src/main/java/org/dhis2/fhir/adapter/fhir/converter/r4/R4BaseDateTimeTypeToZonedDateTimeConverter.java
new file mode 100644
index 00000000..88c17915
--- /dev/null
+++ b/fhir-r4/src/main/java/org/dhis2/fhir/adapter/fhir/converter/r4/R4BaseDateTimeTypeToZonedDateTimeConverter.java
@@ -0,0 +1,50 @@
+package org.dhis2.fhir.adapter.fhir.converter.r4;
+
+/*
+ * Copyright (c) 2004-2019, University of Oslo
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * Neither the name of the HISP project nor the names of its contributors may
+ * be used to endorse or promote products derived from this software without
+ * specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
+ * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+import org.dhis2.fhir.adapter.converter.ConvertedValueTypes;
+import org.dhis2.fhir.adapter.fhir.converter.AbstractPrimitiveTypeDateToZonedDateTimeConverter;
+import org.dhis2.fhir.adapter.model.ValueType;
+import org.hl7.fhir.r4.model.BaseDateTimeType;
+import org.springframework.stereotype.Component;
+
+/**
+ * FHIR version R4 implementation of {@link AbstractPrimitiveTypeDateToZonedDateTimeConverter}.
+ *
+ * @author volsch
+ */
+@Component
+@ConvertedValueTypes( types = ValueType.DATETIME )
+public class R4BaseDateTimeTypeToZonedDateTimeConverter extends AbstractPrimitiveTypeDateToZonedDateTimeConverter
+{
+ public R4BaseDateTimeTypeToZonedDateTimeConverter()
+ {
+ super( BaseDateTimeType.class );
+ }
+}
diff --git a/fhir-r4/src/main/java/org/dhis2/fhir/adapter/fhir/converter/r4/R4QuantityToStringConverter.java b/fhir-r4/src/main/java/org/dhis2/fhir/adapter/fhir/converter/r4/R4QuantityToStringConverter.java
new file mode 100644
index 00000000..16ead739
--- /dev/null
+++ b/fhir-r4/src/main/java/org/dhis2/fhir/adapter/fhir/converter/r4/R4QuantityToStringConverter.java
@@ -0,0 +1,62 @@
+package org.dhis2.fhir.adapter.fhir.converter.r4;
+
+/*
+ * Copyright (c) 2004-2019, University of Oslo
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * Neither the name of the HISP project nor the names of its contributors may
+ * be used to endorse or promote products derived from this software without
+ * specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
+ * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+import org.dhis2.fhir.adapter.converter.ConversionException;
+import org.dhis2.fhir.adapter.converter.ConvertedValueTypes;
+import org.dhis2.fhir.adapter.converter.TypedConverter;
+import org.dhis2.fhir.adapter.model.ValueType;
+import org.hl7.fhir.r4.model.Quantity;
+import org.springframework.stereotype.Component;
+
+import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
+
+/**
+ * FHIR version R4 implementation of a FHIR quantity converter.
+ *
+ * @author volsch
+ */
+@Component
+@ConvertedValueTypes( types = ValueType.TEXT )
+public class R4QuantityToStringConverter extends TypedConverter
+{
+ public R4QuantityToStringConverter()
+ {
+ super( Quantity.class, String.class );
+ }
+
+ @Nullable
+ @Override
+ public String doConvert( @Nonnull Quantity source ) throws ConversionException
+ {
+ final Number number = source.getValueElement().getValueAsNumber();
+ return (number == null) ? null : number.toString();
+ }
+}
diff --git a/fhir-r4/src/main/java/org/dhis2/fhir/adapter/fhir/converter/r4/R4StringToAdministrativeGenderConverter.java b/fhir-r4/src/main/java/org/dhis2/fhir/adapter/fhir/converter/r4/R4StringToAdministrativeGenderConverter.java
new file mode 100644
index 00000000..2dd347c7
--- /dev/null
+++ b/fhir-r4/src/main/java/org/dhis2/fhir/adapter/fhir/converter/r4/R4StringToAdministrativeGenderConverter.java
@@ -0,0 +1,73 @@
+package org.dhis2.fhir.adapter.fhir.converter.r4;
+
+/*
+ * Copyright (c) 2004-2019, University of Oslo
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * Neither the name of the HISP project nor the names of its contributors may
+ * be used to endorse or promote products derived from this software without
+ * specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
+ * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+import org.dhis2.fhir.adapter.converter.ConvertedValueTypes;
+import org.dhis2.fhir.adapter.fhir.converter.AbstractStringToAdministrativeGenderConverter;
+import org.dhis2.fhir.adapter.fhir.metadata.model.ConstantResolver;
+import org.dhis2.fhir.adapter.model.ValueType;
+import org.hl7.fhir.r4.model.Enumerations.AdministrativeGender;
+import org.springframework.stereotype.Component;
+
+import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
+
+/**
+ * FHIR version R4 implementation of {@link AbstractStringToAdministrativeGenderConverter}.
+ *
+ * @author volsch
+ */
+@Component
+@ConvertedValueTypes( types = ValueType.TEXT )
+public class R4StringToAdministrativeGenderConverter extends AbstractStringToAdministrativeGenderConverter
+{
+ public R4StringToAdministrativeGenderConverter( @Nonnull ConstantResolver constantResolver )
+ {
+ super( AdministrativeGender.class, constantResolver );
+ }
+
+ @Nullable
+ @Override
+ protected AdministrativeGender getAdministrativeGender( @Nullable Gender gender )
+ {
+ if ( gender == null )
+ {
+ return AdministrativeGender.NULL;
+ }
+ switch ( gender )
+ {
+ case FEMALE:
+ return AdministrativeGender.FEMALE;
+ case MALE:
+ return AdministrativeGender.MALE;
+ default:
+ throw new AssertionError( "Unhandled gender: " + gender );
+ }
+ }
+}
diff --git a/fhir-r4/src/main/java/org/dhis2/fhir/adapter/fhir/repository/impl/r4/R4FhirRepositoryResourceUtils.java b/fhir-r4/src/main/java/org/dhis2/fhir/adapter/fhir/repository/impl/r4/R4FhirRepositoryResourceUtils.java
new file mode 100644
index 00000000..108b2862
--- /dev/null
+++ b/fhir-r4/src/main/java/org/dhis2/fhir/adapter/fhir/repository/impl/r4/R4FhirRepositoryResourceUtils.java
@@ -0,0 +1,82 @@
+package org.dhis2.fhir.adapter.fhir.repository.impl.r4;
+
+/*
+ * Copyright (c) 2004-2019, University of Oslo
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * Neither the name of the HISP project nor the names of its contributors may
+ * be used to endorse or promote products derived from this software without
+ * specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
+ * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+import org.dhis2.fhir.adapter.fhir.metadata.repository.FhirServerSystemRepository;
+import org.dhis2.fhir.adapter.fhir.repository.FhirResourceTransformationException;
+import org.dhis2.fhir.adapter.fhir.repository.impl.AbstractFhirRepositoryResourceUtils;
+import org.dhis2.fhir.adapter.util.NameUtils;
+import org.hl7.fhir.exceptions.FHIRException;
+import org.hl7.fhir.instance.model.api.IBaseResource;
+import org.hl7.fhir.instance.model.api.ICompositeType;
+import org.hl7.fhir.r4.model.Identifier;
+import org.hl7.fhir.r4.model.ResourceFactory;
+
+import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
+import java.util.Collection;
+import java.util.UUID;
+
+/**
+ * R4 specific implementation of {@link AbstractFhirRepositoryResourceUtils}.
+ *
+ * @author volsch
+ */
+public class R4FhirRepositoryResourceUtils extends AbstractFhirRepositoryResourceUtils
+{
+ public R4FhirRepositoryResourceUtils( @Nonnull UUID fhirServerId, @Nonnull FhirServerSystemRepository fhirServerSystemRepository )
+ {
+ super( fhirServerId, fhirServerSystemRepository );
+ }
+
+ @Nonnull
+ @Override
+ public IBaseResource createResource( @Nonnull Object resourceType )
+ {
+ try
+ {
+ return ResourceFactory.createResource( NameUtils.toClassName( resourceType ) );
+ }
+ catch ( FHIRException e )
+ {
+ throw new FhirResourceTransformationException( "Unknown FHIR resource type: " + resourceType, e );
+ }
+ }
+
+ @Nullable
+ @Override
+ public String getIdentifierValue( @Nullable Collection extends ICompositeType> identifiers, @Nullable String system )
+ {
+ if ( (identifiers == null) || (system == null) )
+ {
+ return null;
+ }
+ return identifiers.stream().map( i -> (Identifier) i ).filter( i -> system.equals( i.getSystem() ) ).findFirst().map( Identifier::getValue ).orElse( null );
+ }
+}
diff --git a/fhir-r4/src/main/java/org/dhis2/fhir/adapter/fhir/repository/impl/r4/R4FhirResourceRepositorySupport.java b/fhir-r4/src/main/java/org/dhis2/fhir/adapter/fhir/repository/impl/r4/R4FhirResourceRepositorySupport.java
new file mode 100644
index 00000000..4e24533c
--- /dev/null
+++ b/fhir-r4/src/main/java/org/dhis2/fhir/adapter/fhir/repository/impl/r4/R4FhirResourceRepositorySupport.java
@@ -0,0 +1,127 @@
+package org.dhis2.fhir.adapter.fhir.repository.impl.r4;
+
+/*
+ * Copyright (c) 2004-2019, University of Oslo
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * Neither the name of the HISP project nor the names of its contributors may
+ * be used to endorse or promote products derived from this software without
+ * specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
+ * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+import org.dhis2.fhir.adapter.fhir.metadata.model.FhirServerResource;
+import org.dhis2.fhir.adapter.fhir.metadata.model.SubscriptionType;
+import org.dhis2.fhir.adapter.fhir.metadata.repository.FhirServerSystemRepository;
+import org.dhis2.fhir.adapter.fhir.model.FhirVersion;
+import org.dhis2.fhir.adapter.fhir.repository.impl.AbstractFhirRepositoryResourceUtils;
+import org.dhis2.fhir.adapter.fhir.repository.impl.AbstractFhirResourceRepositorySupport;
+import org.hl7.fhir.instance.model.api.IAnyResource;
+import org.hl7.fhir.instance.model.api.IBaseBundle;
+import org.hl7.fhir.instance.model.api.IBaseResource;
+import org.hl7.fhir.r4.model.Bundle;
+import org.hl7.fhir.r4.model.Resource;
+import org.hl7.fhir.r4.model.Subscription;
+import org.springframework.stereotype.Component;
+
+import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
+import java.util.List;
+import java.util.Set;
+import java.util.UUID;
+
+/**
+ * Implementation of {@link AbstractFhirResourceRepositorySupport} for R4.
+ *
+ * @author volsch
+ */
+@Component
+public class R4FhirResourceRepositorySupport extends AbstractFhirResourceRepositorySupport
+{
+ private final FhirServerSystemRepository fhirServerSystemRepository;
+
+ public R4FhirResourceRepositorySupport( @Nonnull FhirServerSystemRepository fhirServerSystemRepository )
+ {
+ this.fhirServerSystemRepository = fhirServerSystemRepository;
+ }
+
+ @Nonnull
+ @Override
+ public Set getFhirVersions()
+ {
+ return FhirVersion.R4_ONLY;
+ }
+
+ @Nonnull
+ @Override
+ protected AbstractFhirRepositoryResourceUtils createFhirRepositoryResourceUtils( @Nonnull UUID fhirServerId )
+ {
+ return new R4FhirRepositoryResourceUtils( fhirServerId, fhirServerSystemRepository );
+ }
+
+ @Nonnull
+ @Override
+ protected IAnyResource createFhirSubscription( @Nonnull FhirServerResource fhirServerResource )
+ {
+ final Subscription.SubscriptionChannelComponent channelComponent = new Subscription.SubscriptionChannelComponent();
+ channelComponent.setType( Subscription.SubscriptionChannelType.RESTHOOK );
+ channelComponent.setEndpoint( createWebHookUrl( fhirServerResource ) );
+ channelComponent.addHeader( "Authorization: " + fhirServerResource.getFhirServer().getAdapterEndpoint().getAuthorizationHeader() );
+ if ( fhirServerResource.getFhirServer().getAdapterEndpoint().getSubscriptionType() == SubscriptionType.REST_HOOK_WITH_JSON_PAYLOAD )
+ {
+ channelComponent.setPayload( "application/fhir+json" );
+ }
+
+ final Subscription subscription = new Subscription();
+ subscription.setStatus( Subscription.SubscriptionStatus.REQUESTED );
+ subscription.setCriteria( fhirServerResource.getFhirResourceType().getResourceTypeName() + "?" );
+ subscription.setChannel( channelComponent );
+ return subscription;
+ }
+
+ @Nonnull
+ @Override
+ protected Class extends IBaseBundle> getBundleClass()
+ {
+ return Bundle.class;
+ }
+
+ @Nullable
+ @Override
+ protected IBaseResource getFirstResource( @Nonnull IBaseBundle bundle )
+ {
+ final Bundle b = (Bundle) bundle;
+ return (b.isEmpty() || b.getEntry().isEmpty()) ? null : b.getEntryFirstRep().getResource();
+ }
+
+ @Nonnull
+ @Override
+ protected IBaseBundle createBundle( @Nonnull List extends IBaseResource> resources )
+ {
+ final Bundle bundle = new Bundle();
+ resources.stream().map( r -> {
+ final Bundle.BundleEntryComponent component = new Bundle.BundleEntryComponent();
+ component.setResource( (Resource) r );
+ return component;
+ } ).forEach( bundle::addEntry );
+ return bundle;
+ }
+}
diff --git a/fhir-r4/src/main/java/org/dhis2/fhir/adapter/fhir/server/impl/r4/R4SubscriptionResourceItemRetrieverImpl.java b/fhir-r4/src/main/java/org/dhis2/fhir/adapter/fhir/server/impl/r4/R4SubscriptionResourceItemRetrieverImpl.java
new file mode 100644
index 00000000..9998c16e
--- /dev/null
+++ b/fhir-r4/src/main/java/org/dhis2/fhir/adapter/fhir/server/impl/r4/R4SubscriptionResourceItemRetrieverImpl.java
@@ -0,0 +1,114 @@
+package org.dhis2.fhir.adapter.fhir.server.impl.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.context.FhirContext;
+import ca.uhn.fhir.rest.client.api.IGenericClient;
+import org.dhis2.fhir.adapter.fhir.model.FhirVersion;
+import org.dhis2.fhir.adapter.fhir.server.impl.AbstractSubscriptionResourceItemRetriever;
+import org.hl7.fhir.instance.model.api.IAnyResource;
+import org.hl7.fhir.instance.model.api.IBaseBundle;
+import org.hl7.fhir.r4.model.Bundle;
+import org.springframework.beans.factory.annotation.Qualifier;
+import org.springframework.stereotype.Component;
+
+import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
+import java.util.List;
+import java.util.Set;
+import java.util.stream.Collectors;
+
+/**
+ * Implementation of {@link AbstractSubscriptionResourceItemRetriever} for R4.
+ *
+ * @author volsch
+ */
+@Component
+public class R4SubscriptionResourceItemRetrieverImpl extends AbstractSubscriptionResourceItemRetriever
+{
+ public R4SubscriptionResourceItemRetrieverImpl( @Nonnull @Qualifier( "fhirContextR4" ) FhirContext fhirContext )
+ {
+ super( fhirContext );
+ }
+
+ @Nonnull
+ @Override
+ public Set getFhirVersions()
+ {
+ return FhirVersion.R4_ONLY;
+ }
+
+ @Nonnull
+ @Override
+ protected Class extends IBaseBundle> getBundleClass()
+ {
+ return Bundle.class;
+ }
+
+ @Nonnull
+ @Override
+ protected List extends IAnyResource> getResourceEntries( @Nonnull IBaseBundle bundle )
+ {
+ final Bundle b = (Bundle) bundle;
+ return b.getEntry().stream().map( Bundle.BundleEntryComponent::getResource ).collect( Collectors.toList() );
+ }
+
+ @Nullable
+ @Override
+ protected Long getBundleTotalCount( @Nonnull IBaseBundle bundle )
+ {
+ final Bundle b = (Bundle) bundle;
+ return b.hasTotal() ? (long) b.getTotal() : null;
+ }
+
+ @Nullable
+ @Override
+ protected IBaseBundle loadNextPage( @Nonnull IGenericClient client, @Nonnull IBaseBundle bundle )
+ {
+ final Bundle b = (Bundle) bundle;
+ final Bundle.BundleLinkComponent link = b.getLink( Bundle.LINK_NEXT );
+ return ((link != null) && !link.isEmpty()) ? client.loadPage().next( bundle ).execute() : null;
+ }
+
+ @Nullable
+ @Override
+ protected IBaseBundle loadPreviousPage( @Nonnull IGenericClient client, @Nonnull IBaseBundle bundle )
+ {
+ final Bundle b = (Bundle) bundle;
+ final Bundle.BundleLinkComponent link = b.getLink( Bundle.LINK_PREV );
+ return ((link != null) && !link.isEmpty()) ? client.loadPage().previous( bundle ).execute() : null;
+ }
+
+ @Override
+ protected boolean isEmpty( @Nullable IBaseBundle bundle )
+ {
+ final Bundle b = (Bundle) bundle;
+ return (b == null) || b.getEntry().isEmpty();
+ }
+}
diff --git a/fhir-r4/src/main/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/util/r4/R4AdministrativeGenderDhisToFhirTransformerUtils.java b/fhir-r4/src/main/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/util/r4/R4AdministrativeGenderDhisToFhirTransformerUtils.java
new file mode 100644
index 00000000..d7a67fcc
--- /dev/null
+++ b/fhir-r4/src/main/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/util/r4/R4AdministrativeGenderDhisToFhirTransformerUtils.java
@@ -0,0 +1,69 @@
+package org.dhis2.fhir.adapter.fhir.transform.dhis.impl.util.r4;
+
+/*
+ * Copyright (c) 2004-2019, University of Oslo
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * Neither the name of the HISP project nor the names of its contributors may
+ * be used to endorse or promote products derived from this software without
+ * specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
+ * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+import org.dhis2.fhir.adapter.dhis.converter.ValueConverter;
+import org.dhis2.fhir.adapter.fhir.model.FhirVersion;
+import org.dhis2.fhir.adapter.fhir.script.ScriptExecutionContext;
+import org.dhis2.fhir.adapter.fhir.transform.dhis.impl.util.AbstractAdministrativeGenderDhisToFhirTransformerUtils;
+import org.dhis2.fhir.adapter.scriptable.Scriptable;
+import org.hl7.fhir.r4.model.Enumerations;
+import org.springframework.stereotype.Component;
+
+import javax.annotation.Nonnull;
+import java.util.Set;
+
+/**
+ * DTSU3 specific implementation of {@link AbstractAdministrativeGenderDhisToFhirTransformerUtils}.
+ *
+ * @author volsch
+ */
+@Scriptable
+@Component
+public class R4AdministrativeGenderDhisToFhirTransformerUtils extends AbstractAdministrativeGenderDhisToFhirTransformerUtils
+{
+ public R4AdministrativeGenderDhisToFhirTransformerUtils( @Nonnull ScriptExecutionContext scriptExecutionContext, @Nonnull ValueConverter valueConverter )
+ {
+ super( scriptExecutionContext, valueConverter );
+ }
+
+ @Nonnull
+ @Override
+ public Set getFhirVersions()
+ {
+ return FhirVersion.R4_ONLY;
+ }
+
+ @Nonnull
+ @Override
+ protected Class getAdministrativeGenderClass()
+ {
+ return Enumerations.AdministrativeGender.class;
+ }
+}
diff --git a/fhir-r4/src/main/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/util/r4/R4CodeDhisToFhirTransformerUtils.java b/fhir-r4/src/main/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/util/r4/R4CodeDhisToFhirTransformerUtils.java
new file mode 100644
index 00000000..ccd4fc26
--- /dev/null
+++ b/fhir-r4/src/main/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/util/r4/R4CodeDhisToFhirTransformerUtils.java
@@ -0,0 +1,79 @@
+package org.dhis2.fhir.adapter.fhir.transform.dhis.impl.util.r4;
+
+/*
+ * Copyright (c) 2004-2019, University of Oslo
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * Neither the name of the HISP project nor the names of its contributors may
+ * be used to endorse or promote products derived from this software without
+ * specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
+ * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+import org.dhis2.fhir.adapter.fhir.metadata.repository.SystemCodeRepository;
+import org.dhis2.fhir.adapter.fhir.model.FhirVersion;
+import org.dhis2.fhir.adapter.fhir.model.SystemCodeValue;
+import org.dhis2.fhir.adapter.fhir.model.SystemCodeValues;
+import org.dhis2.fhir.adapter.fhir.script.ScriptExecutionContext;
+import org.dhis2.fhir.adapter.fhir.transform.dhis.impl.util.AbstractCodeDhisToFhirTransformerUtils;
+import org.dhis2.fhir.adapter.scriptable.Scriptable;
+import org.hl7.fhir.instance.model.api.ICompositeType;
+import org.hl7.fhir.r4.model.CodeableConcept;
+import org.springframework.stereotype.Component;
+
+import javax.annotation.Nonnull;
+import java.util.Set;
+
+/**
+ * R4 specific implementation of {@link AbstractCodeDhisToFhirTransformerUtils}.
+ *
+ * @author volsch
+ */
+@Scriptable
+@Component
+public class R4CodeDhisToFhirTransformerUtils extends AbstractCodeDhisToFhirTransformerUtils
+{
+ public R4CodeDhisToFhirTransformerUtils( @Nonnull ScriptExecutionContext scriptExecutionContext, @Nonnull SystemCodeRepository systemCodeRepository )
+ {
+ super( scriptExecutionContext, systemCodeRepository );
+ }
+
+ @Nonnull
+ @Override
+ public Set getFhirVersions()
+ {
+ return FhirVersion.R4_ONLY;
+ }
+
+ @Nonnull
+ @Override
+ protected ICompositeType createCodeableConcept( @Nonnull SystemCodeValues systemCodeValues )
+ {
+ final CodeableConcept codeableConcept = new CodeableConcept();
+ codeableConcept.setText( systemCodeValues.getText() );
+ for ( final SystemCodeValue scv : systemCodeValues.getSystemCodeValues() )
+ {
+ codeableConcept.addCoding().setSystem( scv.getSystem() ).setCode( scv.getCode() )
+ .setDisplay( scv.getDisplayName() );
+ }
+ return codeableConcept;
+ }
+}
diff --git a/fhir-r4/src/main/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/util/r4/R4DateTimeDhisToFhirTransformerUtils.java b/fhir-r4/src/main/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/util/r4/R4DateTimeDhisToFhirTransformerUtils.java
new file mode 100644
index 00000000..656c5c5d
--- /dev/null
+++ b/fhir-r4/src/main/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/util/r4/R4DateTimeDhisToFhirTransformerUtils.java
@@ -0,0 +1,136 @@
+package org.dhis2.fhir.adapter.fhir.transform.dhis.impl.util.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.model.api.TemporalPrecisionEnum;
+import org.dhis2.fhir.adapter.fhir.model.FhirVersion;
+import org.dhis2.fhir.adapter.fhir.script.ScriptExecutionContext;
+import org.dhis2.fhir.adapter.fhir.transform.dhis.impl.util.AbstractDateTimeDhisToFhirTransformerUtils;
+import org.dhis2.fhir.adapter.scriptable.Scriptable;
+import org.dhis2.fhir.adapter.util.CastUtils;
+import org.hl7.fhir.instance.model.api.IPrimitiveType;
+import org.hl7.fhir.r4.model.BaseDateTimeType;
+import org.hl7.fhir.r4.model.DateTimeType;
+import org.hl7.fhir.r4.model.DateType;
+import org.springframework.stereotype.Component;
+
+import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
+import java.time.LocalDate;
+import java.time.LocalDateTime;
+import java.time.ZoneId;
+import java.time.temporal.Temporal;
+import java.util.Date;
+import java.util.Set;
+
+/**
+ * FHIR version R4 implementation of {@link AbstractDateTimeDhisToFhirTransformerUtils}.
+ *
+ * @author volsch
+ */
+@Component
+@Scriptable
+public class R4DateTimeDhisToFhirTransformerUtils extends AbstractDateTimeDhisToFhirTransformerUtils
+{
+ protected final ZoneId zoneId = ZoneId.systemDefault();
+
+ public R4DateTimeDhisToFhirTransformerUtils( @Nonnull ScriptExecutionContext scriptExecutionContext )
+ {
+ super( scriptExecutionContext );
+ }
+
+ @Nonnull
+ @Override
+ public Set getFhirVersions()
+ {
+ return FhirVersion.R4_ONLY;
+ }
+
+ @Nullable
+ @Override
+ public IPrimitiveType getPreciseDateElement( @Nullable Object dateTime )
+ {
+ final LocalDate date = castDate( dateTime );
+ if ( date == null )
+ {
+ return null;
+ }
+ return new DateType( Date.from( date.atStartOfDay( zoneId ).toInstant() ), TemporalPrecisionEnum.DAY );
+ }
+
+ @Nullable
+ @Override
+ public IPrimitiveType getDateTimeElement( @Nullable Object dateTime )
+ {
+ final LocalDateTime date = castDateTime( dateTime );
+ if ( date == null )
+ {
+ return null;
+ }
+ return new DateTimeType( Date.from( date.atZone( zoneId ).toInstant() ), TemporalPrecisionEnum.MILLI );
+ }
+
+ @Nullable
+ @Override
+ public IPrimitiveType getDayDateTimeElement( @Nullable Object dateTime )
+ {
+ final LocalDateTime date = castDateTime( dateTime );
+ if ( date == null )
+ {
+ return null;
+ }
+ return new DateTimeType( Date.from( date.atZone( zoneId ).toInstant() ), TemporalPrecisionEnum.DAY );
+ }
+
+ @Nullable
+ protected LocalDate castDate( @Nullable Object date )
+ {
+ return CastUtils.cast( date,
+ BaseDateTimeType.class, d -> {
+ final Date result = hasDayPrecision( d ) ? d.getValue() : null;
+ return (result == null) ? null : LocalDate.from( result.toInstant().atZone( zoneId ) );
+ },
+ Date.class, d -> LocalDate.from( d.toInstant().atZone( zoneId ) ),
+ Temporal.class, LocalDate::from );
+ }
+
+ @Nullable
+ protected LocalDateTime castDateTime( @Nullable Object date )
+ {
+ return CastUtils.cast( date,
+ BaseDateTimeType.class, d -> LocalDateTime.ofInstant( d.getValue().toInstant(), zoneId ),
+ Date.class, d -> LocalDateTime.from( d.toInstant().atZone( zoneId ) ),
+ Temporal.class, LocalDateTime::from );
+ }
+
+ protected boolean hasDayPrecision( @Nonnull IPrimitiveType dateTime )
+ {
+ return (((BaseDateTimeType) dateTime).getPrecision().ordinal() >= TemporalPrecisionEnum.DAY.ordinal());
+ }
+}
diff --git a/fhir-r4/src/main/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/util/r4/R4EncounterDhisToFhirTransformerUtils.java b/fhir-r4/src/main/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/util/r4/R4EncounterDhisToFhirTransformerUtils.java
new file mode 100644
index 00000000..a1c34da0
--- /dev/null
+++ b/fhir-r4/src/main/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/util/r4/R4EncounterDhisToFhirTransformerUtils.java
@@ -0,0 +1,79 @@
+package org.dhis2.fhir.adapter.fhir.transform.dhis.impl.util.r4;
+
+/*
+ * Copyright (c) 2004-2019, University of Oslo
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * Neither the name of the HISP project nor the names of its contributors may
+ * be used to endorse or promote products derived from this software without
+ * specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
+ * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+import org.dhis2.fhir.adapter.fhir.model.FhirVersion;
+import org.dhis2.fhir.adapter.fhir.script.ScriptExecutionContext;
+import org.dhis2.fhir.adapter.fhir.transform.TransformerException;
+import org.dhis2.fhir.adapter.fhir.transform.dhis.impl.util.AbstractEncounterDhisToFhirTransformerUtils;
+import org.dhis2.fhir.adapter.fhir.transform.scripted.TransformerScriptException;
+import org.dhis2.fhir.adapter.scriptable.Scriptable;
+import org.hl7.fhir.exceptions.FHIRException;
+import org.hl7.fhir.r4.model.Encounter;
+import org.springframework.stereotype.Component;
+
+import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
+import java.util.Set;
+
+/**
+ * R4 specific implementation of {@link AbstractEncounterDhisToFhirTransformerUtils}.
+ *
+ * @author volsch
+ */
+@Scriptable
+@Component
+public class R4EncounterDhisToFhirTransformerUtils extends AbstractEncounterDhisToFhirTransformerUtils
+{
+ public R4EncounterDhisToFhirTransformerUtils( @Nonnull ScriptExecutionContext scriptExecutionContext )
+ {
+ super( scriptExecutionContext );
+ }
+
+ @Nonnull
+ @Override
+ public Set getFhirVersions()
+ {
+ return FhirVersion.R4_ONLY;
+ }
+
+ @Nullable
+ @Override
+ public Enum> getEncounterStatus( @Nonnull String code ) throws TransformerException
+ {
+ try
+ {
+ return Encounter.EncounterStatus.fromCode( code );
+ }
+ catch ( FHIRException e )
+ {
+ throw new TransformerScriptException( "Unknown encounter status code: " + code, e );
+ }
+ }
+}
diff --git a/fhir-dstu3/src/main/java/org/dhis2/fhir/adapter/fhir/server/impl/dstu3/Dstu3HierarchicallyFhirResourceRepositoryImpl.java b/fhir-r4/src/main/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/util/r4/R4FhirClientDhisToFhirTransformerUtils.java
similarity index 58%
rename from fhir-dstu3/src/main/java/org/dhis2/fhir/adapter/fhir/server/impl/dstu3/Dstu3HierarchicallyFhirResourceRepositoryImpl.java
rename to fhir-r4/src/main/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/util/r4/R4FhirClientDhisToFhirTransformerUtils.java
index 83ed2ecb..b11dae2e 100644
--- a/fhir-dstu3/src/main/java/org/dhis2/fhir/adapter/fhir/server/impl/dstu3/Dstu3HierarchicallyFhirResourceRepositoryImpl.java
+++ b/fhir-r4/src/main/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/util/r4/R4FhirClientDhisToFhirTransformerUtils.java
@@ -1,7 +1,7 @@
-package org.dhis2.fhir.adapter.fhir.server.impl.dstu3;
+package org.dhis2.fhir.adapter.fhir.transform.dhis.impl.util.r4;
/*
- * 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
@@ -28,40 +28,38 @@
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
+import ca.uhn.fhir.context.FhirContext;
+import org.dhis2.fhir.adapter.fhir.metadata.repository.FhirServerRepository;
+import org.dhis2.fhir.adapter.fhir.model.FhirVersion;
import org.dhis2.fhir.adapter.fhir.repository.FhirResourceRepository;
-import org.dhis2.fhir.adapter.fhir.repository.impl.AbstractHierarchicallyFhirResourceRepositoryImpl;
-import org.hl7.fhir.dstu3.model.Bundle;
-import org.hl7.fhir.dstu3.model.Resource;
-import org.hl7.fhir.instance.model.api.IBaseBundle;
-import org.hl7.fhir.instance.model.api.IBaseResource;
+import org.dhis2.fhir.adapter.fhir.script.ScriptExecutionContext;
+import org.dhis2.fhir.adapter.fhir.transform.dhis.impl.util.AbstractFhirClientDhisToFhirTransformerUtils;
+import org.dhis2.fhir.adapter.scriptable.Scriptable;
+import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Component;
import javax.annotation.Nonnull;
-import java.util.List;
+import java.util.Set;
/**
- * Implementation of {@link AbstractHierarchicallyFhirResourceRepositoryImpl} for DSTU3.
+ * FHIR version R4 implementation of {@link AbstractFhirClientDhisToFhirTransformerUtils}.
*
* @author volsch
*/
@Component
-public class Dstu3HierarchicallyFhirResourceRepositoryImpl extends AbstractHierarchicallyFhirResourceRepositoryImpl
+@Scriptable
+public class R4FhirClientDhisToFhirTransformerUtils extends AbstractFhirClientDhisToFhirTransformerUtils
{
- public Dstu3HierarchicallyFhirResourceRepositoryImpl( @Nonnull FhirResourceRepository fhirResourceRepository )
+ public R4FhirClientDhisToFhirTransformerUtils( @Nonnull ScriptExecutionContext scriptExecutionContext, @Nonnull @Qualifier( "fhirContextR4" ) FhirContext fhirContext,
+ @Nonnull FhirServerRepository subscriptionRepository, @Nonnull FhirResourceRepository fhirResourceRepository )
{
- super( fhirResourceRepository );
+ super( scriptExecutionContext, fhirContext, subscriptionRepository, fhirResourceRepository );
}
@Nonnull
@Override
- protected IBaseBundle createBundle( @Nonnull List extends IBaseResource> resources )
+ public Set getFhirVersions()
{
- final Bundle bundle = new Bundle();
- resources.stream().map( r -> {
- final Bundle.BundleEntryComponent component = new Bundle.BundleEntryComponent();
- component.setResource( (Resource) r );
- return component;
- } ).forEach( bundle::addEntry );
- return bundle;
+ return FhirVersion.R4_ONLY;
}
}
diff --git a/fhir-r4/src/main/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/util/r4/R4FhirResourceDhisToFhirTransformerUtils.java b/fhir-r4/src/main/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/util/r4/R4FhirResourceDhisToFhirTransformerUtils.java
new file mode 100644
index 00000000..bec9e366
--- /dev/null
+++ b/fhir-r4/src/main/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/util/r4/R4FhirResourceDhisToFhirTransformerUtils.java
@@ -0,0 +1,139 @@
+package org.dhis2.fhir.adapter.fhir.transform.dhis.impl.util.r4;
+
+/*
+ * Copyright (c) 2004-2019, University of Oslo
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * Neither the name of the HISP project nor the names of its contributors may
+ * be used to endorse or promote products derived from this software without
+ * specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
+ * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+import org.dhis2.fhir.adapter.fhir.model.FhirVersion;
+import org.dhis2.fhir.adapter.fhir.repository.FhirRepositoryException;
+import org.dhis2.fhir.adapter.fhir.script.ScriptExecutionContext;
+import org.dhis2.fhir.adapter.fhir.transform.dhis.impl.util.AbstractFhirResourceDhisToFhirTransformerUtils;
+import org.dhis2.fhir.adapter.scriptable.Scriptable;
+import org.dhis2.fhir.adapter.util.NameUtils;
+import org.hl7.fhir.exceptions.FHIRException;
+import org.hl7.fhir.instance.model.api.IBase;
+import org.hl7.fhir.instance.model.api.IBaseElement;
+import org.hl7.fhir.instance.model.api.IBaseReference;
+import org.hl7.fhir.instance.model.api.IBaseResource;
+import org.hl7.fhir.instance.model.api.ICompositeType;
+import org.hl7.fhir.instance.model.api.IPrimitiveType;
+import org.hl7.fhir.r4.model.Base;
+import org.hl7.fhir.r4.model.CodeableConcept;
+import org.hl7.fhir.r4.model.Reference;
+import org.hl7.fhir.r4.model.Resource;
+import org.hl7.fhir.r4.model.ResourceFactory;
+import org.hl7.fhir.r4.model.StringType;
+import org.springframework.stereotype.Component;
+
+import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
+import java.util.List;
+import java.util.Set;
+
+/**
+ * R4 specific implementation of {@link AbstractFhirResourceDhisToFhirTransformerUtils}.
+ *
+ * @author volsch
+ */
+@Scriptable
+@Component
+public class R4FhirResourceDhisToFhirTransformerUtils extends AbstractFhirResourceDhisToFhirTransformerUtils
+{
+ public R4FhirResourceDhisToFhirTransformerUtils( @Nonnull ScriptExecutionContext scriptExecutionContext )
+ {
+ super( scriptExecutionContext );
+ }
+
+ @Nonnull
+ @Override
+ public Set getFhirVersions()
+ {
+ return FhirVersion.R4_ONLY;
+ }
+
+ @Nonnull
+ @Override
+ public IBaseResource createResource( @Nonnull String resourceType )
+ {
+ try
+ {
+ return ResourceFactory.createResource( NameUtils.toClassName( resourceType ) );
+ }
+ catch ( FHIRException e )
+ {
+ throw new FhirRepositoryException( "Unknown FHIR resource type: " + resourceType, e );
+ }
+ }
+
+ @Nonnull
+ @Override
+ public ICompositeType createCodeableConcept()
+ {
+ return new CodeableConcept();
+ }
+
+ @Nonnull
+ @Override
+ public IBaseReference createReference( @Nonnull IBaseResource resource )
+ {
+ if ( resource.getIdElement().isEmpty() || resource.getIdElement().isLocal() )
+ {
+ return new Reference( (Resource) resource );
+ }
+ return new Reference( resource.getIdElement().toUnqualifiedVersionless() );
+ }
+
+ @Nonnull
+ @Override
+ public IBaseElement createType( @Nonnull String fhirType )
+ {
+ try
+ {
+ return ResourceFactory.createType( fhirType );
+ }
+ catch ( FHIRException e )
+ {
+ throw new FhirRepositoryException( "Unknown FHIR type: " + fhirType, e );
+ }
+ }
+
+ @Override
+ public boolean containsString( @Nonnull List extends IPrimitiveType> stringList, @Nullable String value )
+ {
+ if ( value == null )
+ {
+ return false;
+ }
+ return stringList.contains( new StringType( value ) );
+ }
+
+ @Override
+ public boolean equalsDeep( @Nonnull IBase base1, @Nonnull IBase base2 )
+ {
+ return ((Base) base1).equalsDeep( (Base) base2 );
+ }
+}
diff --git a/fhir-r4/src/main/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/util/r4/R4GeoDhisToFhirTransformerUtils.java b/fhir-r4/src/main/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/util/r4/R4GeoDhisToFhirTransformerUtils.java
new file mode 100644
index 00000000..4b80511f
--- /dev/null
+++ b/fhir-r4/src/main/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/util/r4/R4GeoDhisToFhirTransformerUtils.java
@@ -0,0 +1,99 @@
+package org.dhis2.fhir.adapter.fhir.transform.dhis.impl.util.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.model.api.IElement;
+import org.dhis2.fhir.adapter.dhis.converter.ValueConverter;
+import org.dhis2.fhir.adapter.fhir.model.FhirVersion;
+import org.dhis2.fhir.adapter.fhir.script.ScriptExecutionContext;
+import org.dhis2.fhir.adapter.fhir.transform.TransformerException;
+import org.dhis2.fhir.adapter.fhir.transform.dhis.impl.util.AbstractGeoDhisToFhirTransformerUtils;
+import org.dhis2.fhir.adapter.geo.Location;
+import org.dhis2.fhir.adapter.scriptable.Scriptable;
+import org.hl7.fhir.instance.model.api.IBaseBackboneElement;
+import org.hl7.fhir.r4.model.DecimalType;
+import org.hl7.fhir.r4.model.Element;
+import org.hl7.fhir.r4.model.Extension;
+import org.hl7.fhir.r4.model.Location.LocationPositionComponent;
+import org.springframework.stereotype.Component;
+
+import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
+import java.util.Set;
+
+/**
+ * FHIR version R4 implementation of {@link AbstractGeoDhisToFhirTransformerUtils}.
+ *
+ * @author volsch
+ */
+@Component
+@Scriptable
+public class R4GeoDhisToFhirTransformerUtils extends AbstractGeoDhisToFhirTransformerUtils
+{
+ public R4GeoDhisToFhirTransformerUtils( @Nonnull ScriptExecutionContext scriptExecutionContext, @Nonnull ValueConverter valueConverter )
+ {
+ super( scriptExecutionContext );
+ }
+
+ @Nonnull
+ @Override
+ public Set getFhirVersions()
+ {
+ return FhirVersion.R4_ONLY;
+ }
+
+ @Override
+ public void updateAddressLocation( @Nonnull IElement element, @Nullable Location location ) throws TransformerException
+ {
+ final Element e = (Element) element;
+ e.getExtension().removeIf( i -> i.hasExtension( GEO_LOCATION_URI ) );
+ if ( location != null )
+ {
+ e.addExtension().setUrl( GEO_LOCATION_URI )
+ .addExtension( new Extension().setUrl( LATITUDE_URL )
+ .setValue( new DecimalType( location.getLatitude() ) ) )
+ .addExtension( new Extension().setUrl( LONGITUDE_URL )
+ .setValue( new DecimalType( location.getLongitude() ) ) );
+ }
+ }
+
+ @Nullable
+ @Override
+ public IBaseBackboneElement createPosition( @Nullable Location location )
+ {
+ if ( location == null )
+ {
+ return null;
+ }
+ final LocationPositionComponent lpc = new LocationPositionComponent();
+ lpc.setLatitude( location.getLatitude() );
+ lpc.setLongitude( location.getLongitude() );
+ return lpc;
+ }
+}
diff --git a/fhir-r4/src/main/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/util/r4/R4HumanNameDhisToFhirTransformerUtils.java b/fhir-r4/src/main/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/util/r4/R4HumanNameDhisToFhirTransformerUtils.java
new file mode 100644
index 00000000..745bafd1
--- /dev/null
+++ b/fhir-r4/src/main/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/util/r4/R4HumanNameDhisToFhirTransformerUtils.java
@@ -0,0 +1,80 @@
+package org.dhis2.fhir.adapter.fhir.transform.dhis.impl.util.r4;
+
+/*
+ * Copyright (c) 2004-2019, University of Oslo
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * Neither the name of the HISP project nor the names of its contributors may
+ * be used to endorse or promote products derived from this software without
+ * specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
+ * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+import org.apache.commons.lang3.StringUtils;
+import org.dhis2.fhir.adapter.fhir.model.FhirVersion;
+import org.dhis2.fhir.adapter.fhir.script.ScriptExecutionContext;
+import org.dhis2.fhir.adapter.fhir.transform.dhis.impl.util.AbstractHumanNameDhisToFhirTransformerUtils;
+import org.dhis2.fhir.adapter.scriptable.Scriptable;
+import org.hl7.fhir.instance.model.api.ICompositeType;
+import org.hl7.fhir.r4.model.HumanName;
+import org.hl7.fhir.r4.model.StringType;
+import org.springframework.stereotype.Component;
+
+import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
+import java.util.Arrays;
+import java.util.Set;
+import java.util.stream.Collectors;
+
+/**
+ * R4 specific implementation of {@link AbstractHumanNameDhisToFhirTransformerUtils}.
+ *
+ * @author volsch
+ */
+@Scriptable
+@Component
+public class R4HumanNameDhisToFhirTransformerUtils extends AbstractHumanNameDhisToFhirTransformerUtils
+{
+ public R4HumanNameDhisToFhirTransformerUtils( @Nonnull ScriptExecutionContext scriptExecutionContext )
+ {
+ super( scriptExecutionContext );
+ }
+
+ @Nonnull
+ @Override
+ public Set getFhirVersions()
+ {
+ return FhirVersion.R4_ONLY;
+ }
+
+ @Override
+ public void updateGiven( @Nonnull ICompositeType humanName, @Nullable String firstName )
+ {
+ if ( StringUtils.isBlank( firstName ) )
+ {
+ ((HumanName) humanName).setGiven( null );
+ }
+ else
+ {
+ ((HumanName) humanName).setGiven( Arrays.stream( firstName.split( " " ) ).map( StringType::new ).collect( Collectors.toList() ) );
+ }
+ }
+}
diff --git a/fhir-r4/src/main/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/util/r4/R4IdentifierDhisToFhirTransformerUtils.java b/fhir-r4/src/main/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/util/r4/R4IdentifierDhisToFhirTransformerUtils.java
new file mode 100644
index 00000000..9262e84b
--- /dev/null
+++ b/fhir-r4/src/main/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/util/r4/R4IdentifierDhisToFhirTransformerUtils.java
@@ -0,0 +1,100 @@
+package org.dhis2.fhir.adapter.fhir.transform.dhis.impl.util.r4;
+
+/*
+ * Copyright (c) 2004-2019, University of Oslo
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * Neither the name of the HISP project nor the names of its contributors may
+ * be used to endorse or promote products derived from this software without
+ * specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
+ * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+import org.apache.commons.lang3.StringUtils;
+import org.dhis2.fhir.adapter.fhir.model.FhirVersion;
+import org.dhis2.fhir.adapter.fhir.script.ScriptExecutionContext;
+import org.dhis2.fhir.adapter.fhir.transform.FatalTransformerException;
+import org.dhis2.fhir.adapter.fhir.transform.dhis.impl.util.AbstractIdentifierDhisToFhirTransformerUtils;
+import org.dhis2.fhir.adapter.fhir.transform.util.FhirIdentifierUtils;
+import org.dhis2.fhir.adapter.scriptable.Scriptable;
+import org.hl7.fhir.instance.model.api.IBaseResource;
+import org.hl7.fhir.r4.model.Identifier;
+import org.springframework.stereotype.Component;
+import org.springframework.util.ReflectionUtils;
+
+import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
+import java.lang.reflect.Method;
+import java.util.List;
+import java.util.Objects;
+import java.util.Optional;
+import java.util.Set;
+
+/**
+ * R4 specific implementation of {@link AbstractIdentifierDhisToFhirTransformerUtils}.
+ *
+ * @author volsch
+ */
+@Scriptable
+@Component
+public class R4IdentifierDhisToFhirTransformerUtils extends AbstractIdentifierDhisToFhirTransformerUtils
+{
+ public R4IdentifierDhisToFhirTransformerUtils( @Nonnull ScriptExecutionContext scriptExecutionContext, @Nonnull FhirIdentifierUtils fhirIdentifierUtils )
+ {
+ super( scriptExecutionContext, fhirIdentifierUtils );
+ }
+
+ @Nonnull
+ @Override
+ public Set getFhirVersions()
+ {
+ return FhirVersion.R4_ONLY;
+ }
+
+ @Override
+ protected void addOrUpdateIdentifier( @Nonnull IBaseResource resource, @Nonnull Method identifierMethod, @Nullable String system, @Nonnull String value, @Nullable String text, boolean secondary )
+ {
+ @SuppressWarnings( "unchecked" ) final List identifiers = (List) ReflectionUtils.invokeMethod( identifierMethod, resource );
+ if ( identifiers == null )
+ {
+ throw new FatalTransformerException( "FHIR resource " + resource.getClass().getSimpleName() + " returned null for identifiers." );
+ }
+
+ final Optional identifier = identifiers.stream().filter( i -> Objects.equals( system, i.getSystem() ) ).findFirst();
+ if ( identifier.isPresent() )
+ {
+ identifier.get().setValue( value );
+ }
+ else
+ {
+ final Identifier i = new Identifier().setSystem( system ).setValue( value );
+ if ( StringUtils.isNotBlank( text ) )
+ {
+ i.getType().setText( text );
+ }
+ if ( secondary )
+ {
+ i.setUse( Identifier.IdentifierUse.SECONDARY );
+ }
+ identifiers.add( i );
+ }
+ }
+}
diff --git a/fhir-r4/src/main/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/util/r4/R4LocationDhisToFhirTransformerUtils.java b/fhir-r4/src/main/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/util/r4/R4LocationDhisToFhirTransformerUtils.java
new file mode 100644
index 00000000..c1d05cbb
--- /dev/null
+++ b/fhir-r4/src/main/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/util/r4/R4LocationDhisToFhirTransformerUtils.java
@@ -0,0 +1,79 @@
+package org.dhis2.fhir.adapter.fhir.transform.dhis.impl.util.r4;
+
+/*
+ * Copyright (c) 2004-2019, University of Oslo
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * Neither the name of the HISP project nor the names of its contributors may
+ * be used to endorse or promote products derived from this software without
+ * specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
+ * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+import org.dhis2.fhir.adapter.fhir.model.FhirVersion;
+import org.dhis2.fhir.adapter.fhir.script.ScriptExecutionContext;
+import org.dhis2.fhir.adapter.fhir.transform.TransformerException;
+import org.dhis2.fhir.adapter.fhir.transform.dhis.impl.util.AbstractLocationDhisToFhirTransformerUtils;
+import org.dhis2.fhir.adapter.fhir.transform.scripted.TransformerScriptException;
+import org.dhis2.fhir.adapter.scriptable.Scriptable;
+import org.hl7.fhir.exceptions.FHIRException;
+import org.hl7.fhir.r4.model.Location;
+import org.springframework.stereotype.Component;
+
+import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
+import java.util.Set;
+
+/**
+ * R4 specific implementation of {@link AbstractLocationDhisToFhirTransformerUtils}.
+ *
+ * @author volsch
+ */
+@Scriptable
+@Component
+public class R4LocationDhisToFhirTransformerUtils extends AbstractLocationDhisToFhirTransformerUtils
+{
+ public R4LocationDhisToFhirTransformerUtils( @Nonnull ScriptExecutionContext scriptExecutionContext )
+ {
+ super( scriptExecutionContext );
+ }
+
+ @Nonnull
+ @Override
+ public Set getFhirVersions()
+ {
+ return FhirVersion.R4_ONLY;
+ }
+
+ @Nullable
+ @Override
+ public Enum> getLocationStatus( @Nonnull String code ) throws TransformerException
+ {
+ try
+ {
+ return Location.LocationStatus.fromCode( code );
+ }
+ catch ( FHIRException e )
+ {
+ throw new TransformerScriptException( "Unknown location status code: " + code, e );
+ }
+ }
+}
diff --git a/fhir-r4/src/main/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/util/r4/R4ObservationDhisToFhirTransformerUtils.java b/fhir-r4/src/main/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/util/r4/R4ObservationDhisToFhirTransformerUtils.java
new file mode 100644
index 00000000..cd4decad
--- /dev/null
+++ b/fhir-r4/src/main/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/util/r4/R4ObservationDhisToFhirTransformerUtils.java
@@ -0,0 +1,79 @@
+package org.dhis2.fhir.adapter.fhir.transform.dhis.impl.util.r4;
+
+/*
+ * Copyright (c) 2004-2019, University of Oslo
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * Neither the name of the HISP project nor the names of its contributors may
+ * be used to endorse or promote products derived from this software without
+ * specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
+ * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+import org.dhis2.fhir.adapter.fhir.model.FhirVersion;
+import org.dhis2.fhir.adapter.fhir.script.ScriptExecutionContext;
+import org.dhis2.fhir.adapter.fhir.transform.TransformerException;
+import org.dhis2.fhir.adapter.fhir.transform.dhis.impl.util.AbstractObservationDhisToFhirTransformerUtils;
+import org.dhis2.fhir.adapter.fhir.transform.scripted.TransformerScriptException;
+import org.dhis2.fhir.adapter.scriptable.Scriptable;
+import org.hl7.fhir.exceptions.FHIRException;
+import org.hl7.fhir.r4.model.Observation;
+import org.springframework.stereotype.Component;
+
+import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
+import java.util.Set;
+
+/**
+ * R4 specific implementation of {@link AbstractObservationDhisToFhirTransformerUtils}.
+ *
+ * @author volsch
+ */
+@Scriptable
+@Component
+public class R4ObservationDhisToFhirTransformerUtils extends AbstractObservationDhisToFhirTransformerUtils
+{
+ public R4ObservationDhisToFhirTransformerUtils( @Nonnull ScriptExecutionContext scriptExecutionContext )
+ {
+ super( scriptExecutionContext );
+ }
+
+ @Nonnull
+ @Override
+ public Set getFhirVersions()
+ {
+ return FhirVersion.R4_ONLY;
+ }
+
+ @Nullable
+ @Override
+ public Enum> getObservationStatus( @Nonnull String code ) throws TransformerException
+ {
+ try
+ {
+ return Observation.ObservationStatus.fromCode( code );
+ }
+ catch ( FHIRException e )
+ {
+ throw new TransformerScriptException( "Unknown observation status code: " + code, e );
+ }
+ }
+}
diff --git a/fhir-r4/src/main/java/org/dhis2/fhir/adapter/fhir/transform/fhir/impl/util/r4/R4AddressFhirToDhisTransformerUtils.java b/fhir-r4/src/main/java/org/dhis2/fhir/adapter/fhir/transform/fhir/impl/util/r4/R4AddressFhirToDhisTransformerUtils.java
new file mode 100644
index 00000000..22452282
--- /dev/null
+++ b/fhir-r4/src/main/java/org/dhis2/fhir/adapter/fhir/transform/fhir/impl/util/r4/R4AddressFhirToDhisTransformerUtils.java
@@ -0,0 +1,206 @@
+package org.dhis2.fhir.adapter.fhir.transform.fhir.impl.util.r4;
+
+/*
+ * Copyright (c) 2004-2019, University of Oslo
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * Neither the name of the HISP project nor the names of its contributors may
+ * be used to endorse or promote products derived from this software without
+ * specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
+ * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+import org.apache.commons.lang3.StringUtils;
+import org.dhis2.fhir.adapter.fhir.model.FhirVersion;
+import org.dhis2.fhir.adapter.fhir.script.ScriptExecutionContext;
+import org.dhis2.fhir.adapter.fhir.transform.fhir.impl.util.AbstractAddressFhirToDhisTransformerUtils;
+import org.dhis2.fhir.adapter.fhir.transform.fhir.impl.util.TransformerComparatorUtils;
+import org.dhis2.fhir.adapter.scriptable.Scriptable;
+import org.hl7.fhir.instance.model.api.ICompositeType;
+import org.hl7.fhir.r4.model.Address;
+import org.hl7.fhir.r4.model.PrimitiveType;
+import org.springframework.stereotype.Component;
+
+import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
+import java.util.Comparator;
+import java.util.List;
+import java.util.Optional;
+import java.util.Set;
+import java.util.stream.Collectors;
+
+/**
+ * FHIR version R4 implementation of {@link AbstractAddressFhirToDhisTransformerUtils}.
+ *
+ * @author volsch
+ */
+@Component
+@Scriptable
+public class R4AddressFhirToDhisTransformerUtils extends AbstractAddressFhirToDhisTransformerUtils
+{
+ private final R4DateTimeFhirToDhisTransformerUtils dateTimeTransformerUtils;
+
+ public R4AddressFhirToDhisTransformerUtils( @Nonnull ScriptExecutionContext scriptExecutionContext, @Nonnull R4DateTimeFhirToDhisTransformerUtils dateTimeTransformerUtils )
+ {
+ super( scriptExecutionContext );
+ this.dateTimeTransformerUtils = dateTimeTransformerUtils;
+ }
+
+ @Nonnull
+ @Override
+ public Set getFhirVersions()
+ {
+ return FhirVersion.R4_ONLY;
+ }
+
+ @Nullable
+ @Override
+ public ICompositeType getPrimaryAddress( @Nonnull List extends ICompositeType> addresses )
+ {
+ return getOptionalPrimaryAddress( addresses ).orElse( new Address() );
+ }
+
+ @Nullable
+ @Override
+ public String getSingleLine( @Nullable ICompositeType address, @Nonnull String delimiter )
+ {
+ final Address convertedAddress = (Address) address;
+ if ( (address == null) || convertedAddress.getLine().isEmpty() )
+ {
+ return null;
+ }
+ return convertedAddress.getLine().stream().map( PrimitiveType::getValue ).collect( Collectors.joining( delimiter ) );
+ }
+
+ @Nullable
+ @Override
+ public String getConstructedText( @Nullable ICompositeType address, @Nonnull String delimiter )
+ {
+ final Address convertedAddress = (Address) address;
+ if ( address == null )
+ {
+ return null;
+ }
+
+ final StringBuilder sb = new StringBuilder();
+ convertedAddress.getLine().stream().filter( l -> (l != null) && StringUtils.isNotBlank( l.getValue() ) )
+ .forEach( l -> sb.append( delimiter ).append( l.getValue() ) );
+ if ( StringUtils.isNotBlank( convertedAddress.getPostalCode() ) && StringUtils.isNotBlank( convertedAddress.getCity() ) )
+ {
+ sb.append( delimiter ).append( convertedAddress.getPostalCode() ).append( ' ' ).append( convertedAddress.getCity() );
+ }
+ else if ( StringUtils.isNotBlank( convertedAddress.getPostalCode() ) )
+ {
+ sb.append( delimiter ).append( convertedAddress.getPostalCode() );
+ }
+ else if ( StringUtils.isNotBlank( convertedAddress.getCity() ) )
+ {
+ sb.append( delimiter ).append( convertedAddress.getCity() );
+ }
+ if ( StringUtils.isNotBlank( convertedAddress.getState() ) )
+ {
+ sb.append( delimiter ).append( convertedAddress.getState() );
+ }
+ if ( sb.length() == 0 )
+ {
+ return convertedAddress.getText();
+ }
+ return sb.substring( delimiter.length() );
+ }
+
+ @Nullable
+ @Override
+ public String getText( @Nullable ICompositeType address )
+ {
+ final Address convertedAddress = (Address) address;
+ return (convertedAddress == null) ? null : convertedAddress.getText();
+ }
+
+ @Nonnull
+ protected Optional getOptionalPrimaryAddress( @Nullable List extends ICompositeType> addresses )
+ {
+ if ( (addresses == null) || addresses.isEmpty() )
+ {
+ return Optional.empty();
+ }
+ if ( addresses.size() == 1 )
+ {
+ return Optional.of( (Address) addresses.get( 0 ) );
+ }
+ return addresses.stream().map( Address.class::cast )
+ .filter( a -> dateTimeTransformerUtils.isValidNow( a.getPeriod() ) ).min( new AddressComparator() );
+ }
+
+ protected static class AddressComparator implements Comparator
+ {
+ @Override
+ public int compare( Address o1, Address o2 )
+ {
+ int value = getAddressUseValue( o1.getUse() ) - getAddressUseValue( o2.getUse() );
+ if ( value != 0 )
+ {
+ return value;
+ }
+ value = getAddressTypeValue( o1.getType() ) - getAddressTypeValue( o2.getType() );
+ if ( value != 0 )
+ {
+ return value;
+ }
+ return comparatorValue( o1 ).compareTo( comparatorValue( o2 ) );
+ }
+
+ private int getAddressUseValue( @Nullable Address.AddressUse au )
+ {
+ if ( au == Address.AddressUse.OLD )
+ {
+ return 8;
+ }
+ else if ( au == Address.AddressUse.WORK )
+ {
+ return 6;
+ }
+ else if ( au == Address.AddressUse.TEMP )
+ {
+ return 5;
+ }
+ return 0;
+ }
+
+ private int getAddressTypeValue( @Nullable Address.AddressType at )
+ {
+ if ( at == Address.AddressType.POSTAL )
+ {
+ return 8;
+ }
+ if ( at == Address.AddressType.BOTH )
+ {
+ return 2;
+ }
+ return 0;
+ }
+
+ @Nonnull
+ private String comparatorValue( @Nonnull Address a )
+ {
+ return TransformerComparatorUtils.comparatorValue( a.getText(), a.getLine(), a.getPostalCode(), a.getCity(), a.getState(), a.getCountry() );
+ }
+ }
+}
diff --git a/fhir-r4/src/main/java/org/dhis2/fhir/adapter/fhir/transform/fhir/impl/util/r4/R4CodeFhirToDhisTransformerUtils.java b/fhir-r4/src/main/java/org/dhis2/fhir/adapter/fhir/transform/fhir/impl/util/r4/R4CodeFhirToDhisTransformerUtils.java
new file mode 100644
index 00000000..0ab65746
--- /dev/null
+++ b/fhir-r4/src/main/java/org/dhis2/fhir/adapter/fhir/transform/fhir/impl/util/r4/R4CodeFhirToDhisTransformerUtils.java
@@ -0,0 +1,183 @@
+package org.dhis2.fhir.adapter.fhir.transform.fhir.impl.util.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 com.google.common.collect.Sets;
+import org.dhis2.fhir.adapter.fhir.metadata.model.ScriptArgUtils;
+import org.dhis2.fhir.adapter.fhir.metadata.model.SystemCode;
+import org.dhis2.fhir.adapter.fhir.metadata.repository.CodeRepository;
+import org.dhis2.fhir.adapter.fhir.metadata.repository.SystemCodeRepository;
+import org.dhis2.fhir.adapter.fhir.model.FhirVersion;
+import org.dhis2.fhir.adapter.fhir.model.SystemCodeValue;
+import org.dhis2.fhir.adapter.fhir.script.ScriptExecutionContext;
+import org.dhis2.fhir.adapter.fhir.transform.fhir.impl.util.AbstractCodeFhirToDhisTransformerUtils;
+import org.dhis2.fhir.adapter.scriptable.Scriptable;
+import org.hl7.fhir.instance.model.api.ICompositeType;
+import org.hl7.fhir.instance.model.api.IDomainResource;
+import org.hl7.fhir.r4.model.CodeableConcept;
+import org.hl7.fhir.r4.model.Coding;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.stereotype.Component;
+import org.springframework.util.CollectionUtils;
+import org.springframework.util.ReflectionUtils;
+
+import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
+import java.lang.reflect.Method;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Objects;
+import java.util.Set;
+
+/**
+ * FHIR version R4 implementation of {@link AbstractCodeFhirToDhisTransformerUtils}.
+ *
+ * @author volsch
+ */
+@Component
+@Scriptable
+public class R4CodeFhirToDhisTransformerUtils extends AbstractCodeFhirToDhisTransformerUtils
+{
+ private final Logger logger = LoggerFactory.getLogger( getClass() );
+
+ public R4CodeFhirToDhisTransformerUtils( @Nonnull ScriptExecutionContext scriptExecutionContext,
+ @Nonnull CodeRepository codeRepository,
+ @Nonnull SystemCodeRepository systemCodeRepository )
+ {
+ super( scriptExecutionContext, codeRepository, systemCodeRepository );
+ }
+
+ @Nonnull
+ @Override
+ public Set getFhirVersions()
+ {
+ return FhirVersion.R4_ONLY;
+ }
+
+ @Nonnull
+ @Override
+ public List getSystemCodeValues( @Nullable ICompositeType codeableConcept )
+ {
+ if ( codeableConcept == null )
+ {
+ return Collections.emptyList();
+ }
+ final List result = new ArrayList<>();
+ for ( final Coding coding : ((CodeableConcept) codeableConcept).getCoding() )
+ {
+ result.add( new SystemCodeValue( coding.getSystem(), coding.getCode() ) );
+ }
+ return result;
+ }
+
+ @Override
+ @Nullable
+ public String getCode( @Nullable ICompositeType codeableConcept, @Nullable String system )
+ {
+ if ( system == null )
+ {
+ throw new IllegalArgumentException( "System must be specified." );
+ }
+
+ if ( codeableConcept == null )
+ {
+ return null;
+ }
+ for ( final Coding coding : ((CodeableConcept) codeableConcept).getCoding() )
+ {
+ if ( system.equals( coding.getSystem() ) )
+ {
+ return coding.getCode();
+ }
+ }
+ return null;
+ }
+
+ @Override
+ public boolean containsMappingCode( @Nullable ICompositeType codeableConcept, @Nullable Object mappingCodes )
+ {
+ if ( mappingCodes == null )
+ {
+ throw new IllegalArgumentException( "Codes must be specified." );
+ }
+
+ if ( (codeableConcept == null) || codeableConcept.isEmpty() )
+ {
+ return false;
+ }
+ final List convertedCodes = ScriptArgUtils.extractStringArray( mappingCodes );
+ if ( CollectionUtils.isEmpty( convertedCodes ) )
+ {
+ return false;
+ }
+
+ final Set checkedCodes = new HashSet<>();
+ final Collection systemCodes = getSystemCodeRepository().findAllByCodes( convertedCodes );
+ for ( final SystemCode systemCode : systemCodes )
+ {
+ if ( containsCode( codeableConcept, systemCode.getSystem().getSystemUri(), systemCode.getSystemCode() ) )
+ {
+ return true;
+ }
+ checkedCodes.add( systemCode.getCode().getCode() );
+ }
+ if ( logger.isDebugEnabled() && (checkedCodes.size() < convertedCodes.size()) )
+ {
+ logger.info( "Codes have not been defined: " + Sets.difference( new HashSet<>( convertedCodes ), checkedCodes ) );
+ }
+ return false;
+ }
+
+ @Override
+ public boolean containsCode( @Nullable ICompositeType codeableConcept, @Nullable String system, @Nonnull String code )
+ {
+ if ( codeableConcept == null )
+ {
+ return false;
+ }
+ return ((CodeableConcept) codeableConcept).getCoding().stream().anyMatch(
+ coding -> Objects.equals( system, coding.getSystem() ) && code.equals( coding.getCode() ) );
+ }
+
+ @Nullable
+ @Override
+ protected List getSystemCodeValues( @Nonnull IDomainResource domainResource, @Nonnull Method identifierMethod )
+ {
+ final ICompositeType codeableConcept = (ICompositeType) ReflectionUtils.invokeMethod( identifierMethod, domainResource );
+ if ( codeableConcept != null )
+ {
+ return getSystemCodeValues( codeableConcept );
+ }
+ return null;
+ }
+}
diff --git a/fhir-r4/src/main/java/org/dhis2/fhir/adapter/fhir/transform/fhir/impl/util/r4/R4ContactPointFhirToDhisTransformerUtils.java b/fhir-r4/src/main/java/org/dhis2/fhir/adapter/fhir/transform/fhir/impl/util/r4/R4ContactPointFhirToDhisTransformerUtils.java
new file mode 100644
index 00000000..f9d64000
--- /dev/null
+++ b/fhir-r4/src/main/java/org/dhis2/fhir/adapter/fhir/transform/fhir/impl/util/r4/R4ContactPointFhirToDhisTransformerUtils.java
@@ -0,0 +1,121 @@
+package org.dhis2.fhir.adapter.fhir.transform.fhir.impl.util.r4;
+
+/*
+ * Copyright (c) 2004-2019, University of Oslo
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * Neither the name of the HISP project nor the names of its contributors may
+ * be used to endorse or promote products derived from this software without
+ * specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
+ * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+import org.dhis2.fhir.adapter.fhir.model.FhirVersion;
+import org.dhis2.fhir.adapter.fhir.script.ScriptExecutionContext;
+import org.dhis2.fhir.adapter.fhir.transform.fhir.impl.util.AbstractContactPointFhirToDhisTransformerUtils;
+import org.dhis2.fhir.adapter.fhir.transform.fhir.impl.util.TransformerComparatorUtils;
+import org.dhis2.fhir.adapter.scriptable.Scriptable;
+import org.hl7.fhir.instance.model.api.ICompositeType;
+import org.hl7.fhir.r4.model.ContactPoint;
+import org.springframework.stereotype.Component;
+
+import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
+import java.util.Comparator;
+import java.util.List;
+import java.util.Set;
+
+/**
+ * FHIR version R4 implementation of {@link AbstractContactPointFhirToDhisTransformerUtils}.
+ *
+ * @author volsch
+ */
+@Component
+@Scriptable
+public class R4ContactPointFhirToDhisTransformerUtils extends AbstractContactPointFhirToDhisTransformerUtils
+{
+ private final R4DateTimeFhirToDhisTransformerUtils dateTimeTransformerUtils;
+
+ public R4ContactPointFhirToDhisTransformerUtils( @Nonnull ScriptExecutionContext scriptExecutionContext, @Nonnull R4DateTimeFhirToDhisTransformerUtils dateTimeTransformerUtils )
+ {
+ super( scriptExecutionContext );
+ this.dateTimeTransformerUtils = dateTimeTransformerUtils;
+ }
+
+ @Nonnull
+ @Override
+ public Set getFhirVersions()
+ {
+ return FhirVersion.R4_ONLY;
+ }
+
+ @Nullable
+ @Override
+ protected String getContactPointValue( @Nullable List extends ICompositeType> contactPoints, @Nullable String code )
+ {
+ if ( (contactPoints == null) || (code == null) )
+ {
+ return null;
+ }
+ return contactPoints.stream().map( cp -> (ContactPoint) cp )
+ .filter( cp -> cp.hasSystem() && code.equalsIgnoreCase( cp.getSystem().toCode() )
+ && dateTimeTransformerUtils.isValidNow( cp.getPeriod() ) && cp.hasValue() )
+ .min( new ContactPointComparator() ).map( ContactPoint::getValue ).orElse( null );
+ }
+
+ protected static class ContactPointComparator implements Comparator
+ {
+ @Override
+ public int compare( ContactPoint o1, ContactPoint o2 )
+ {
+ int value = getContactPointUseValue( o1.getUse() ) - getContactPointUseValue( o2.getUse() );
+ if ( value != 0 )
+ {
+ return value;
+ }
+ value = o1.getRank() - o2.getRank();
+ if ( value != 0 )
+ {
+ return value;
+ }
+ return comparatorValue( o1 ).compareTo( comparatorValue( o2 ) );
+ }
+
+ private int getContactPointUseValue( @Nullable ContactPoint.ContactPointUse cpu )
+ {
+ if ( cpu == ContactPoint.ContactPointUse.OLD )
+ {
+ return 8;
+ }
+ else if ( cpu == ContactPoint.ContactPointUse.TEMP )
+ {
+ return 5;
+ }
+ return 0;
+ }
+
+ @Nonnull
+ private String comparatorValue( @Nonnull ContactPoint cp )
+ {
+ return TransformerComparatorUtils.comparatorValue( cp.getValue() );
+ }
+ }
+}
diff --git a/fhir-r4/src/main/java/org/dhis2/fhir/adapter/fhir/transform/fhir/impl/util/r4/R4DateTimeFhirToDhisTransformerUtils.java b/fhir-r4/src/main/java/org/dhis2/fhir/adapter/fhir/transform/fhir/impl/util/r4/R4DateTimeFhirToDhisTransformerUtils.java
new file mode 100644
index 00000000..ec5bb2c0
--- /dev/null
+++ b/fhir-r4/src/main/java/org/dhis2/fhir/adapter/fhir/transform/fhir/impl/util/r4/R4DateTimeFhirToDhisTransformerUtils.java
@@ -0,0 +1,111 @@
+package org.dhis2.fhir.adapter.fhir.transform.fhir.impl.util.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.model.api.TemporalPrecisionEnum;
+import org.dhis2.fhir.adapter.fhir.model.FhirVersion;
+import org.dhis2.fhir.adapter.fhir.script.ScriptExecutionContext;
+import org.dhis2.fhir.adapter.fhir.transform.fhir.impl.util.AbstractDateTimeFhirToDhisTransformerUtils;
+import org.dhis2.fhir.adapter.scriptable.Scriptable;
+import org.dhis2.fhir.adapter.util.CastUtils;
+import org.hl7.fhir.instance.model.api.ICompositeType;
+import org.hl7.fhir.instance.model.api.IPrimitiveType;
+import org.hl7.fhir.r4.model.BaseDateTimeType;
+import org.hl7.fhir.r4.model.Period;
+import org.springframework.stereotype.Component;
+
+import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
+import java.time.LocalDate;
+import java.time.ZoneId;
+import java.time.temporal.Temporal;
+import java.util.Date;
+import java.util.Set;
+
+/**
+ * FHIR version R4 implementation of {@link AbstractDateTimeFhirToDhisTransformerUtils}.
+ *
+ * @author volsch
+ */
+@Component
+@Scriptable
+public class R4DateTimeFhirToDhisTransformerUtils extends AbstractDateTimeFhirToDhisTransformerUtils
+{
+ protected final ZoneId zoneId = ZoneId.systemDefault();
+
+ public R4DateTimeFhirToDhisTransformerUtils( @Nonnull ScriptExecutionContext scriptExecutionContext )
+ {
+ super( scriptExecutionContext );
+ }
+
+ @Nonnull
+ @Override
+ public Set getFhirVersions()
+ {
+ return FhirVersion.R4_ONLY;
+ }
+
+ @Override
+ public boolean hasDayPrecision( @Nullable IPrimitiveType dateTime )
+ {
+ if ( dateTime == null )
+ {
+ // an unspecified date has at least day precision
+ return true;
+ }
+ return (((BaseDateTimeType) dateTime).getPrecision().ordinal() >= TemporalPrecisionEnum.DAY.ordinal());
+ }
+
+ @Override
+ public boolean isValidNow( @Nullable ICompositeType period )
+ {
+ if ( period == null )
+ {
+ return true;
+ }
+
+ final Period p = (Period) period;
+ final Date now = new Date();
+ // start will be ignored since there may be no further notification about that event
+ return (p.getEnd() == null) || !now.after( p.getEnd() );
+ }
+
+ @Override
+ @Nullable
+ protected LocalDate castDate( @Nonnull Object date )
+ {
+ return CastUtils.cast( date,
+ BaseDateTimeType.class, d -> {
+ final Date result = getPreciseDate( d );
+ return (result == null) ? null : LocalDate.from( result.toInstant().atZone( zoneId ) );
+ },
+ Date.class, d -> LocalDate.from( d.toInstant().atZone( zoneId ) ),
+ Temporal.class, LocalDate::from );
+ }
+}
diff --git a/fhir-r4/src/main/java/org/dhis2/fhir/adapter/fhir/transform/fhir/impl/util/r4/R4FhirClientFhirToDhisTransformerUtils.java b/fhir-r4/src/main/java/org/dhis2/fhir/adapter/fhir/transform/fhir/impl/util/r4/R4FhirClientFhirToDhisTransformerUtils.java
new file mode 100644
index 00000000..892f7a09
--- /dev/null
+++ b/fhir-r4/src/main/java/org/dhis2/fhir/adapter/fhir/transform/fhir/impl/util/r4/R4FhirClientFhirToDhisTransformerUtils.java
@@ -0,0 +1,104 @@
+package org.dhis2.fhir.adapter.fhir.transform.fhir.impl.util.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.context.FhirContext;
+import org.dhis2.fhir.adapter.fhir.metadata.repository.FhirServerResourceRepository;
+import org.dhis2.fhir.adapter.fhir.metadata.repository.SystemCodeRepository;
+import org.dhis2.fhir.adapter.fhir.model.FhirVersion;
+import org.dhis2.fhir.adapter.fhir.script.ScriptExecutionContext;
+import org.dhis2.fhir.adapter.fhir.transform.fhir.impl.util.AbstractFhirClientFhirToDhisTransformerUtils;
+import org.dhis2.fhir.adapter.scriptable.Scriptable;
+import org.hl7.fhir.instance.model.api.IBaseBundle;
+import org.hl7.fhir.instance.model.api.IBaseResource;
+import org.hl7.fhir.r4.model.Bundle;
+import org.hl7.fhir.r4.model.Bundle.BundleEntryComponent;
+import org.hl7.fhir.r4.model.Bundle.BundleLinkComponent;
+import org.hl7.fhir.r4.model.Resource;
+import org.springframework.beans.factory.annotation.Qualifier;
+import org.springframework.stereotype.Component;
+
+import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Set;
+
+@Component
+@Scriptable
+public class R4FhirClientFhirToDhisTransformerUtils extends AbstractFhirClientFhirToDhisTransformerUtils
+{
+ public R4FhirClientFhirToDhisTransformerUtils( @Nonnull ScriptExecutionContext scriptExecutionContext, @Nonnull @Qualifier( "fhirContextR4" ) FhirContext fhirContext,
+ @Nonnull FhirServerResourceRepository fhirServerResourceRepository, @Nonnull SystemCodeRepository systemCodeRepository )
+ {
+ super( scriptExecutionContext, fhirContext, fhirServerResourceRepository, systemCodeRepository );
+ }
+
+ @Nonnull
+ @Override
+ public Set getFhirVersions()
+ {
+ return FhirVersion.R4_ONLY;
+ }
+
+ @Nonnull
+ @Override
+ protected Class extends IBaseBundle> getBundleClass()
+ {
+ return Bundle.class;
+ }
+
+ @Override
+ protected boolean hasNextLink( @Nonnull IBaseBundle bundle )
+ {
+ final BundleLinkComponent link = ((Bundle) bundle).getLink( Bundle.LINK_NEXT );
+ return (link != null) && !link.isEmpty();
+ }
+
+ @Nullable
+ @Override
+ protected IBaseResource getFirstRep( @Nonnull IBaseBundle bundle )
+ {
+ final Bundle b = (Bundle) bundle;
+ return (b.getEntry().isEmpty() ? null : b.getEntryFirstRep().getResource());
+ }
+
+ @Override
+ @Nonnull
+ protected List extends IBaseResource> getEntries( @Nonnull IBaseBundle bundle )
+ {
+ final Bundle b = (Bundle) bundle;
+ final List resources = new ArrayList<>();
+ for ( final BundleEntryComponent entry : b.getEntry() )
+ {
+ resources.add( entry.getResource() );
+ }
+ return resources;
+ }
+}
diff --git a/fhir-r4/src/main/java/org/dhis2/fhir/adapter/fhir/transform/fhir/impl/util/r4/R4GeoFhirToDhisTransformerUtils.java b/fhir-r4/src/main/java/org/dhis2/fhir/adapter/fhir/transform/fhir/impl/util/r4/R4GeoFhirToDhisTransformerUtils.java
new file mode 100644
index 00000000..35298292
--- /dev/null
+++ b/fhir-r4/src/main/java/org/dhis2/fhir/adapter/fhir/transform/fhir/impl/util/r4/R4GeoFhirToDhisTransformerUtils.java
@@ -0,0 +1,106 @@
+package org.dhis2.fhir.adapter.fhir.transform.fhir.impl.util.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.model.api.IElement;
+import org.dhis2.fhir.adapter.fhir.model.FhirVersion;
+import org.dhis2.fhir.adapter.fhir.script.ScriptExecutionContext;
+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.AbstractGeoFhirToDhisTransformerUtils;
+import org.dhis2.fhir.adapter.geo.Location;
+import org.dhis2.fhir.adapter.scriptable.Scriptable;
+import org.hl7.fhir.r4.model.DecimalType;
+import org.hl7.fhir.r4.model.Element;
+import org.hl7.fhir.r4.model.Extension;
+import org.springframework.stereotype.Component;
+
+import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
+import java.util.List;
+import java.util.Set;
+
+/**
+ * FHIR version R4 implementation of {@link AbstractGeoFhirToDhisTransformerUtils}.
+ *
+ * @author volsch
+ */
+@Component
+@Scriptable
+public class R4GeoFhirToDhisTransformerUtils extends AbstractGeoFhirToDhisTransformerUtils
+{
+ public R4GeoFhirToDhisTransformerUtils( @Nonnull ScriptExecutionContext scriptExecutionContext )
+ {
+ super( scriptExecutionContext );
+ }
+
+ @Nonnull
+ @Override
+ public Set getFhirVersions()
+ {
+ return FhirVersion.R4_ONLY;
+ }
+
+ @Override
+ @Nullable
+ public Location getLocation( @Nonnull IElement element ) throws TransformerException
+ {
+ final Element e = (Element) element;
+ final List locationExtensions = e.getExtensionsByUrl( GEO_LOCATION_URI );
+ if ( locationExtensions.isEmpty() )
+ {
+ return null;
+ }
+ if ( locationExtensions.size() > 1 )
+ {
+ throw new TransformerMappingException( "Element " + e.fhirType() + " contains " + locationExtensions.size() + " GEO locations." );
+ }
+ final Extension locationExtension = locationExtensions.get( 0 );
+
+ return new Location( getLocationComponentValue( e, locationExtension, LONGITUDE_URL ),
+ getLocationComponentValue( e, locationExtension, LATITUDE_URL ) );
+ }
+
+ private double getLocationComponentValue( @Nonnull Element element, @Nonnull Extension locationExtension, @Nonnull String componentUrl )
+ {
+ final List extensions = locationExtension.getExtensionsByUrl( componentUrl );
+ if ( extensions.size() != 1 )
+ {
+ throw new TransformerMappingException( "GEO location of element " + element.fhirType() + " does not include a valid " + componentUrl + " extension." );
+ }
+
+ final Extension valueExtension = extensions.get( 0 );
+ if ( !(valueExtension.getValue() instanceof DecimalType) )
+ {
+ throw new TransformerMappingException( "GEO location of element " + element.fhirType() + " does not include a valid " + componentUrl + " extension value." );
+ }
+
+ return ((DecimalType) valueExtension.getValue()).getValueAsNumber().doubleValue();
+ }
+}
diff --git a/fhir-r4/src/main/java/org/dhis2/fhir/adapter/fhir/transform/fhir/impl/util/r4/R4HumanNameFhirToDhisTransformerUtils.java b/fhir-r4/src/main/java/org/dhis2/fhir/adapter/fhir/transform/fhir/impl/util/r4/R4HumanNameFhirToDhisTransformerUtils.java
new file mode 100644
index 00000000..3e112dc6
--- /dev/null
+++ b/fhir-r4/src/main/java/org/dhis2/fhir/adapter/fhir/transform/fhir/impl/util/r4/R4HumanNameFhirToDhisTransformerUtils.java
@@ -0,0 +1,155 @@
+package org.dhis2.fhir.adapter.fhir.transform.fhir.impl.util.r4;
+
+/*
+ * Copyright (c) 2004-2019, University of Oslo
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * Neither the name of the HISP project nor the names of its contributors may
+ * be used to endorse or promote products derived from this software without
+ * specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
+ * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+import org.dhis2.fhir.adapter.fhir.model.FhirVersion;
+import org.dhis2.fhir.adapter.fhir.script.ScriptExecutionContext;
+import org.dhis2.fhir.adapter.fhir.transform.fhir.impl.util.AbstractHumanNameFhirToDhisTransformerUtils;
+import org.dhis2.fhir.adapter.fhir.transform.fhir.impl.util.TransformerComparatorUtils;
+import org.dhis2.fhir.adapter.scriptable.Scriptable;
+import org.hl7.fhir.instance.model.api.ICompositeType;
+import org.hl7.fhir.r4.model.HumanName;
+import org.hl7.fhir.r4.model.PrimitiveType;
+import org.springframework.stereotype.Component;
+
+import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
+import java.util.Comparator;
+import java.util.List;
+import java.util.Optional;
+import java.util.Set;
+import java.util.stream.Collectors;
+
+/**
+ * FHIR version R4 implementation of {@link AbstractHumanNameFhirToDhisTransformerUtils}.
+ *
+ * @author volsch
+ */
+@Component
+@Scriptable
+public class R4HumanNameFhirToDhisTransformerUtils extends AbstractHumanNameFhirToDhisTransformerUtils
+{
+ private final R4DateTimeFhirToDhisTransformerUtils dateTimeTransformerUtils;
+
+ public R4HumanNameFhirToDhisTransformerUtils( @Nonnull ScriptExecutionContext scriptExecutionContext, @Nonnull R4DateTimeFhirToDhisTransformerUtils dateTimeTransformerUtils )
+ {
+ super( scriptExecutionContext );
+ this.dateTimeTransformerUtils = dateTimeTransformerUtils;
+ }
+
+ @Nonnull
+ @Override
+ public Set getFhirVersions()
+ {
+ return FhirVersion.R4_ONLY;
+ }
+
+ @Override
+ @Nullable
+ public String getSingleGiven( @Nullable ICompositeType humanName )
+ {
+ if ( (humanName == null) || ((HumanName) humanName).getGiven().isEmpty() )
+ {
+ return null;
+ }
+ return ((HumanName) humanName).getGiven().stream().map( PrimitiveType::getValue ).collect( Collectors.joining( DEFAULT_GIVEN_DELIMITER ) );
+ }
+
+ @Override
+ @Nullable
+ public HumanName getPrimaryName( @Nonnull List extends ICompositeType> names )
+ {
+ return getOptionalPrimaryName( names ).orElse( new HumanName() );
+ }
+
+ @Nonnull
+ protected Optional getOptionalPrimaryName( @Nullable List extends ICompositeType> names )
+ {
+ if ( (names == null) || names.isEmpty() )
+ {
+ return Optional.empty();
+ }
+ if ( names.size() == 1 )
+ {
+ return Optional.of( (HumanName) names.get( 0 ) );
+ }
+ return names.stream().map( HumanName.class::cast )
+ .filter( hn -> dateTimeTransformerUtils.isValidNow( hn.getPeriod() ) )
+ .min( new HumanNameComparator() );
+ }
+
+ protected static class HumanNameComparator implements Comparator
+ {
+ @Override
+ public int compare( HumanName o1, HumanName o2 )
+ {
+ int value = getHumanNameUseValue( o1.getUse() ) - getHumanNameUseValue( o2.getUse() );
+ if ( value != 0 )
+ {
+ return value;
+ }
+ return comparatorValue( o1 ).compareTo( comparatorValue( o2 ) );
+ }
+
+ private int getHumanNameUseValue( @Nullable HumanName.NameUse nu )
+ {
+ if ( nu == HumanName.NameUse.OLD )
+ {
+ return 9;
+ }
+ else if ( nu == HumanName.NameUse.MAIDEN )
+ {
+ return 8;
+ }
+ else if ( nu == HumanName.NameUse.TEMP )
+ {
+ return 7;
+ }
+ else if ( nu == HumanName.NameUse.NICKNAME )
+ {
+ return 6;
+ }
+ else if ( nu == HumanName.NameUse.ANONYMOUS )
+ {
+ return 5;
+ }
+ else if ( nu == HumanName.NameUse.USUAL )
+ {
+ return 4;
+ }
+ return 0;
+ }
+
+ @Nonnull
+ private String comparatorValue( @Nonnull HumanName hu )
+ {
+ return TransformerComparatorUtils.comparatorValue( hu.getText(), hu.getGiven(), hu.getFamily(), hu.getPrefix(), hu.getSuffix() );
+ }
+ }
+}
diff --git a/fhir-r4/src/main/java/org/dhis2/fhir/adapter/fhir/transform/fhir/impl/util/r4/R4IdentifierFhirToDhisTransformerUtils.java b/fhir-r4/src/main/java/org/dhis2/fhir/adapter/fhir/transform/fhir/impl/util/r4/R4IdentifierFhirToDhisTransformerUtils.java
new file mode 100644
index 00000000..98a0861a
--- /dev/null
+++ b/fhir-r4/src/main/java/org/dhis2/fhir/adapter/fhir/transform/fhir/impl/util/r4/R4IdentifierFhirToDhisTransformerUtils.java
@@ -0,0 +1,81 @@
+package org.dhis2.fhir.adapter.fhir.transform.fhir.impl.util.r4;
+
+/*
+ * Copyright (c) 2004-2019, University of Oslo
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * Neither the name of the HISP project nor the names of its contributors may
+ * be used to endorse or promote products derived from this software without
+ * specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
+ * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+import org.dhis2.fhir.adapter.fhir.model.FhirVersion;
+import org.dhis2.fhir.adapter.fhir.script.ScriptExecutionContext;
+import org.dhis2.fhir.adapter.fhir.transform.fhir.impl.util.AbstractIdentifierFhirToDhisTransformerUtils;
+import org.dhis2.fhir.adapter.fhir.transform.fhir.impl.util.ReferenceFhirToDhisTransformerUtils;
+import org.dhis2.fhir.adapter.fhir.transform.util.FhirIdentifierUtils;
+import org.dhis2.fhir.adapter.scriptable.Scriptable;
+import org.hl7.fhir.instance.model.api.IDomainResource;
+import org.hl7.fhir.r4.model.Identifier;
+import org.springframework.stereotype.Component;
+import org.springframework.util.ReflectionUtils;
+
+import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
+import java.lang.reflect.Method;
+import java.util.List;
+import java.util.Objects;
+import java.util.Set;
+
+/**
+ * FHIR version R4 implementation of {@link AbstractIdentifierFhirToDhisTransformerUtils}.
+ *
+ * @author volsch
+ */
+@Component
+@Scriptable
+public class R4IdentifierFhirToDhisTransformerUtils extends AbstractIdentifierFhirToDhisTransformerUtils
+{
+ public R4IdentifierFhirToDhisTransformerUtils( @Nonnull ScriptExecutionContext scriptExecutionContext, @Nonnull FhirIdentifierUtils fhirIdentifierUtils, @Nonnull ReferenceFhirToDhisTransformerUtils referenceFhirToDhisTransformerUtils )
+ {
+ super( scriptExecutionContext, fhirIdentifierUtils, referenceFhirToDhisTransformerUtils );
+ }
+
+ @Nonnull
+ @Override
+ public Set getFhirVersions()
+ {
+ return FhirVersion.R4_ONLY;
+ }
+
+ @Nullable
+ @Override
+ protected String getIdentifierValue( @Nonnull IDomainResource domainResource, @Nonnull Method identifierMethod, @Nullable String system )
+ {
+ @SuppressWarnings( "unchecked" ) final List identifiers = (List) ReflectionUtils.invokeMethod( identifierMethod, domainResource );
+ if ( identifiers != null )
+ {
+ return identifiers.stream().filter( i -> Objects.equals( system, i.getSystem() ) ).findFirst().orElse( new Identifier() ).getValue();
+ }
+ return null;
+ }
+}
diff --git a/fhir-r4/src/main/java/org/dhis2/fhir/adapter/fhir/transform/fhir/impl/util/r4/R4ImmunizationFhirToDhisTransformerUtils.java b/fhir-r4/src/main/java/org/dhis2/fhir/adapter/fhir/transform/fhir/impl/util/r4/R4ImmunizationFhirToDhisTransformerUtils.java
new file mode 100644
index 00000000..05175ff0
--- /dev/null
+++ b/fhir-r4/src/main/java/org/dhis2/fhir/adapter/fhir/transform/fhir/impl/util/r4/R4ImmunizationFhirToDhisTransformerUtils.java
@@ -0,0 +1,72 @@
+package org.dhis2.fhir.adapter.fhir.transform.fhir.impl.util.r4;
+
+/*
+ * Copyright (c) 2004-2019, University of Oslo
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * Neither the name of the HISP project nor the names of its contributors may
+ * be used to endorse or promote products derived from this software without
+ * specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
+ * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+import org.dhis2.fhir.adapter.fhir.model.FhirVersion;
+import org.dhis2.fhir.adapter.fhir.script.ScriptExecutionContext;
+import org.dhis2.fhir.adapter.fhir.transform.TransformerException;
+import org.dhis2.fhir.adapter.fhir.transform.fhir.impl.util.AbstractImmunizationFhirToDhisTransformerUtils;
+import org.dhis2.fhir.adapter.scriptable.Scriptable;
+import org.hl7.fhir.instance.model.api.IDomainResource;
+import org.hl7.fhir.r4.model.Immunization;
+import org.hl7.fhir.r4.model.PrimitiveType;
+import org.springframework.stereotype.Component;
+
+import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
+import java.util.Comparator;
+import java.util.Set;
+
+@Component
+@Scriptable
+public class R4ImmunizationFhirToDhisTransformerUtils extends AbstractImmunizationFhirToDhisTransformerUtils
+{
+ public R4ImmunizationFhirToDhisTransformerUtils( @Nonnull ScriptExecutionContext scriptExecutionContext )
+ {
+ super( scriptExecutionContext );
+ }
+
+ @Nonnull
+ @Override
+ public Set getFhirVersions()
+ {
+ return FhirVersion.R4_ONLY;
+ }
+
+ @Override
+ public int getMaxDoseSequence( @Nullable IDomainResource immunization ) throws TransformerException
+ {
+ if ( immunization == null )
+ {
+ return 0;
+ }
+ return ((Immunization) immunization).getProtocolApplied().stream().map( Immunization.ImmunizationProtocolAppliedComponent::getDoseNumberPositiveIntType )
+ .map( PrimitiveType::getValue ).max( Comparator.naturalOrder() ).orElse( 0 );
+ }
+}
diff --git a/fhir-r4/src/main/java/org/dhis2/fhir/adapter/fhir/transform/fhir/impl/util/r4/R4LocationFhirToDhisTransformerUtils.java b/fhir-r4/src/main/java/org/dhis2/fhir/adapter/fhir/transform/fhir/impl/util/r4/R4LocationFhirToDhisTransformerUtils.java
new file mode 100644
index 00000000..fd108506
--- /dev/null
+++ b/fhir-r4/src/main/java/org/dhis2/fhir/adapter/fhir/transform/fhir/impl/util/r4/R4LocationFhirToDhisTransformerUtils.java
@@ -0,0 +1,106 @@
+package org.dhis2.fhir.adapter.fhir.transform.fhir.impl.util.r4;
+
+/*
+ * Copyright (c) 2004-2019, University of Oslo
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * Neither the name of the HISP project nor the names of its contributors may
+ * be used to endorse or promote products derived from this software without
+ * specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
+ * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+import org.dhis2.fhir.adapter.dhis.orgunit.OrganizationUnitService;
+import org.dhis2.fhir.adapter.fhir.metadata.repository.FhirServerResourceRepository;
+import org.dhis2.fhir.adapter.fhir.model.FhirVersion;
+import org.dhis2.fhir.adapter.fhir.repository.FhirResourceRepository;
+import org.dhis2.fhir.adapter.fhir.repository.HierarchicallyFhirResourceRepository;
+import org.dhis2.fhir.adapter.fhir.script.ScriptExecutionContext;
+import org.dhis2.fhir.adapter.fhir.transform.fhir.impl.util.AbstractLocationFhirToDhisTransformerUtils;
+import org.dhis2.fhir.adapter.scriptable.Scriptable;
+import org.hl7.fhir.instance.model.api.IBaseBundle;
+import org.hl7.fhir.instance.model.api.IBaseReference;
+import org.hl7.fhir.instance.model.api.IBaseResource;
+import org.hl7.fhir.r4.model.Bundle;
+import org.hl7.fhir.r4.model.Location;
+import org.springframework.stereotype.Component;
+
+import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
+import java.util.Collections;
+import java.util.List;
+import java.util.Set;
+import java.util.stream.Collectors;
+
+/**
+ * FHIR version R4 implementation of {@link AbstractLocationFhirToDhisTransformerUtils}.
+ *
+ * @author volsch
+ */
+@Component
+@Scriptable
+public class R4LocationFhirToDhisTransformerUtils extends AbstractLocationFhirToDhisTransformerUtils
+{
+ public R4LocationFhirToDhisTransformerUtils( @Nonnull ScriptExecutionContext scriptExecutionContext,
+ @Nonnull OrganizationUnitService organizationUnitService,
+ @Nonnull FhirServerResourceRepository fhirServerResourceRepository,
+ @Nonnull FhirResourceRepository fhirResourceRepository,
+ @Nonnull HierarchicallyFhirResourceRepository hierarchicallyFhirResourceRepository )
+ {
+ super( scriptExecutionContext, organizationUnitService, fhirServerResourceRepository, fhirResourceRepository, hierarchicallyFhirResourceRepository );
+ }
+
+ @Nonnull
+ @Override
+ public Set getFhirVersions()
+ {
+ return FhirVersion.R4_ONLY;
+ }
+
+ @Nullable
+ @Override
+ protected IBaseReference getParentReference( @Nullable IBaseResource resource )
+ {
+ if ( resource == null )
+ {
+ return null;
+ }
+ return ((Location) resource).getPartOf();
+ }
+
+ @Nonnull
+ @Override
+ protected List extractResources( @Nullable IBaseBundle bundle )
+ {
+ if ( bundle == null )
+ {
+ return Collections.emptyList();
+ }
+
+ final Bundle b = (Bundle) bundle;
+ if ( b.getEntry() == null )
+ {
+ return Collections.emptyList();
+ }
+
+ return b.getEntry().stream().map( Bundle.BundleEntryComponent::getResource ).collect( Collectors.toList() );
+ }
+}
diff --git a/fhir-r4/src/main/java/org/dhis2/fhir/adapter/fhir/transform/fhir/impl/util/r4/R4ObservationFhirToDhisTransformerUtils.java b/fhir-r4/src/main/java/org/dhis2/fhir/adapter/fhir/transform/fhir/impl/util/r4/R4ObservationFhirToDhisTransformerUtils.java
new file mode 100644
index 00000000..9a3b5eaa
--- /dev/null
+++ b/fhir-r4/src/main/java/org/dhis2/fhir/adapter/fhir/transform/fhir/impl/util/r4/R4ObservationFhirToDhisTransformerUtils.java
@@ -0,0 +1,116 @@
+package org.dhis2.fhir.adapter.fhir.transform.fhir.impl.util.r4;
+
+/*
+ * Copyright (c) 2004-2019, University of Oslo
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * Neither the name of the HISP project nor the names of its contributors may
+ * be used to endorse or promote products derived from this software without
+ * specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
+ * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+import org.dhis2.fhir.adapter.fhir.model.FhirVersion;
+import org.dhis2.fhir.adapter.fhir.script.ScriptExecutionContext;
+import org.dhis2.fhir.adapter.fhir.transform.fhir.impl.util.AbstractObservationFhirToDhisTransformerUtils;
+import org.dhis2.fhir.adapter.scriptable.Scriptable;
+import org.hl7.fhir.instance.model.api.IBaseBackboneElement;
+import org.hl7.fhir.instance.model.api.IBaseResource;
+import org.hl7.fhir.instance.model.api.ICompositeType;
+import org.hl7.fhir.r4.model.Observation;
+import org.hl7.fhir.r4.model.Observation.ObservationComponentComponent;
+import org.hl7.fhir.r4.model.ResourceType;
+import org.springframework.stereotype.Component;
+
+import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
+import java.util.List;
+import java.util.Set;
+
+@Component
+@Scriptable
+public class R4ObservationFhirToDhisTransformerUtils extends AbstractObservationFhirToDhisTransformerUtils
+{
+ public R4ObservationFhirToDhisTransformerUtils( @Nonnull ScriptExecutionContext scriptExecutionContext, @Nonnull R4FhirClientFhirToDhisTransformerUtils clientTransformUtils,
+ @Nonnull R4CodeFhirToDhisTransformerUtils codeTransformerUtils )
+ {
+ super( scriptExecutionContext, clientTransformUtils, codeTransformerUtils );
+ }
+
+ @Nonnull
+ @Override
+ public Set getFhirVersions()
+ {
+ return FhirVersion.R4_ONLY;
+ }
+
+ @Nonnull
+ @Override
+ public String getResourceName()
+ {
+ return ResourceType.Observation.name();
+ }
+
+ @Nullable
+ @Override
+ public ICompositeType getCodes( @Nullable IBaseResource resource )
+ {
+ return (resource == null) ? null : ((Observation) resource).getCode();
+ }
+
+ @Nullable
+ @Override
+ public String getComponentText( @Nullable IBaseResource resource )
+ {
+ final Observation observation = (Observation) resource;
+ if ( (observation == null) || !observation.hasComponent() )
+ {
+ return null;
+ }
+
+ final StringBuilder text = new StringBuilder();
+ for ( final ObservationComponentComponent component : observation.getComponent() )
+ {
+ if ( component.hasValueStringType() )
+ {
+ if ( text.length() > 0 )
+ {
+ text.append( COMPONENT_SEPARATOR );
+ }
+ text.append( component.getValue().toString() );
+ }
+ }
+ return (text.length() == 0) ? null : text.toString();
+ }
+
+ @Override
+ @Nullable
+ public IBaseBackboneElement getBackboneElement( @Nullable List extends IBaseBackboneElement> backboneElements, @Nonnull String system, @Nonnull String code )
+ {
+ if ( backboneElements == null )
+ {
+ return null;
+ }
+ return backboneElements.stream().map( ObservationComponentComponent.class::cast )
+ .filter( c -> getCodeTransformerUtils().containsCode( c.getCode(), system, code ) )
+ .findFirst().orElse( new ObservationComponentComponent() );
+ }
+}
diff --git a/fhir-r4/src/main/java/org/dhis2/fhir/adapter/fhir/transform/fhir/impl/util/r4/R4OrganizationFhirToDhisTransformerUtils.java b/fhir-r4/src/main/java/org/dhis2/fhir/adapter/fhir/transform/fhir/impl/util/r4/R4OrganizationFhirToDhisTransformerUtils.java
new file mode 100644
index 00000000..4ae3a90c
--- /dev/null
+++ b/fhir-r4/src/main/java/org/dhis2/fhir/adapter/fhir/transform/fhir/impl/util/r4/R4OrganizationFhirToDhisTransformerUtils.java
@@ -0,0 +1,106 @@
+package org.dhis2.fhir.adapter.fhir.transform.fhir.impl.util.r4;
+
+/*
+ * Copyright (c) 2004-2019, University of Oslo
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * Neither the name of the HISP project nor the names of its contributors may
+ * be used to endorse or promote products derived from this software without
+ * specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
+ * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+import org.dhis2.fhir.adapter.dhis.orgunit.OrganizationUnitService;
+import org.dhis2.fhir.adapter.fhir.metadata.repository.FhirServerResourceRepository;
+import org.dhis2.fhir.adapter.fhir.model.FhirVersion;
+import org.dhis2.fhir.adapter.fhir.repository.FhirResourceRepository;
+import org.dhis2.fhir.adapter.fhir.repository.HierarchicallyFhirResourceRepository;
+import org.dhis2.fhir.adapter.fhir.script.ScriptExecutionContext;
+import org.dhis2.fhir.adapter.fhir.transform.fhir.impl.util.AbstractOrganizationFhirToDhisTransformerUtils;
+import org.dhis2.fhir.adapter.scriptable.Scriptable;
+import org.hl7.fhir.instance.model.api.IBaseBundle;
+import org.hl7.fhir.instance.model.api.IBaseReference;
+import org.hl7.fhir.instance.model.api.IBaseResource;
+import org.hl7.fhir.r4.model.Bundle;
+import org.hl7.fhir.r4.model.Organization;
+import org.springframework.stereotype.Component;
+
+import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
+import java.util.Collections;
+import java.util.List;
+import java.util.Set;
+import java.util.stream.Collectors;
+
+/**
+ * FHIR version R4 implementation of {@link AbstractOrganizationFhirToDhisTransformerUtils}.
+ *
+ * @author volsch
+ */
+@Component
+@Scriptable
+public class R4OrganizationFhirToDhisTransformerUtils extends AbstractOrganizationFhirToDhisTransformerUtils
+{
+ public R4OrganizationFhirToDhisTransformerUtils( @Nonnull ScriptExecutionContext scriptExecutionContext,
+ @Nonnull OrganizationUnitService organizationUnitService,
+ @Nonnull FhirServerResourceRepository fhirServerResourceRepository,
+ @Nonnull FhirResourceRepository fhirResourceRepository,
+ @Nonnull HierarchicallyFhirResourceRepository hierarchicallyFhirResourceRepository )
+ {
+ super( scriptExecutionContext, organizationUnitService, fhirServerResourceRepository, fhirResourceRepository, hierarchicallyFhirResourceRepository );
+ }
+
+ @Nonnull
+ @Override
+ public Set getFhirVersions()
+ {
+ return FhirVersion.R4_ONLY;
+ }
+
+ @Nullable
+ @Override
+ protected IBaseReference getParentReference( @Nullable IBaseResource resource )
+ {
+ if ( resource == null )
+ {
+ return null;
+ }
+ return ((Organization) resource).getPartOf();
+ }
+
+ @Nonnull
+ @Override
+ protected List extractResources( @Nullable IBaseBundle bundle )
+ {
+ if ( bundle == null )
+ {
+ return Collections.emptyList();
+ }
+
+ final Bundle b = (Bundle) bundle;
+ if ( b.getEntry() == null )
+ {
+ return Collections.emptyList();
+ }
+
+ return b.getEntry().stream().map( Bundle.BundleEntryComponent::getResource ).collect( Collectors.toList() );
+ }
+}
diff --git a/fhir-r4/src/main/java/org/dhis2/fhir/adapter/fhir/transform/fhir/impl/util/r4/R4PatientFhirToDhisTransformerUtils.java b/fhir-r4/src/main/java/org/dhis2/fhir/adapter/fhir/transform/fhir/impl/util/r4/R4PatientFhirToDhisTransformerUtils.java
new file mode 100644
index 00000000..446739c1
--- /dev/null
+++ b/fhir-r4/src/main/java/org/dhis2/fhir/adapter/fhir/transform/fhir/impl/util/r4/R4PatientFhirToDhisTransformerUtils.java
@@ -0,0 +1,102 @@
+package org.dhis2.fhir.adapter.fhir.transform.fhir.impl.util.r4;
+
+/*
+ * Copyright (c) 2004-2019, University of Oslo
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * Neither the name of the HISP project nor the names of its contributors may
+ * be used to endorse or promote products derived from this software without
+ * specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
+ * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+import org.dhis2.fhir.adapter.fhir.model.FhirVersion;
+import org.dhis2.fhir.adapter.fhir.script.ScriptExecutionContext;
+import org.dhis2.fhir.adapter.fhir.transform.TransformerMappingException;
+import org.dhis2.fhir.adapter.fhir.transform.fhir.impl.util.AbstractPatientFhirToDhisTransformerUtils;
+import org.dhis2.fhir.adapter.scriptable.Scriptable;
+import org.hl7.fhir.instance.model.api.ICompositeType;
+import org.hl7.fhir.instance.model.api.IDomainResource;
+import org.hl7.fhir.r4.model.Address;
+import org.hl7.fhir.r4.model.Extension;
+import org.hl7.fhir.r4.model.Patient;
+import org.hl7.fhir.r4.model.Type;
+import org.springframework.stereotype.Component;
+
+import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
+import java.util.List;
+import java.util.Set;
+
+/**
+ * FHIR version R4 implementation of {@link AbstractPatientFhirToDhisTransformerUtils}.
+ *
+ * @author volsch
+ */
+@Component
+@Scriptable
+public class R4PatientFhirToDhisTransformerUtils extends AbstractPatientFhirToDhisTransformerUtils
+{
+ public R4PatientFhirToDhisTransformerUtils( @Nonnull ScriptExecutionContext scriptExecutionContext )
+ {
+ super( scriptExecutionContext );
+ }
+
+ @Nonnull
+ @Override
+ public Set getFhirVersions()
+ {
+ return FhirVersion.R4_ONLY;
+ }
+
+ @Nullable
+ @Override
+ public ICompositeType getBirthPlaceAddress( @Nullable IDomainResource patient )
+ {
+ if ( patient == null )
+ {
+ return null;
+ }
+
+ final Patient p = (Patient) patient;
+ final List birthPlaceExtensions = p.getExtensionsByUrl( BIRTH_PLACE_URI );
+ if ( birthPlaceExtensions.isEmpty() )
+ {
+ return null;
+ }
+ if ( birthPlaceExtensions.size() > 1 )
+ {
+ throw new TransformerMappingException( "Patient contains " + birthPlaceExtensions.size() + " birth places." );
+ }
+
+ final Type value = birthPlaceExtensions.get( 0 ).getValue();
+ if ( (value == null) || value.isEmpty() )
+ {
+ return null;
+ }
+
+ if ( !(value instanceof Address) )
+ {
+ throw new TransformerMappingException( "Patient contains birth place that is no address: " + value.fhirType() );
+ }
+ return (Address) value;
+ }
+}
diff --git a/fhir-r4/src/main/java/org/dhis2/fhir/adapter/fhir/transform/fhir/impl/util/r4/R4ProgramStageFhirToDhisTransformerUtils.java b/fhir-r4/src/main/java/org/dhis2/fhir/adapter/fhir/transform/fhir/impl/util/r4/R4ProgramStageFhirToDhisTransformerUtils.java
new file mode 100644
index 00000000..8a985632
--- /dev/null
+++ b/fhir-r4/src/main/java/org/dhis2/fhir/adapter/fhir/transform/fhir/impl/util/r4/R4ProgramStageFhirToDhisTransformerUtils.java
@@ -0,0 +1,86 @@
+package org.dhis2.fhir.adapter.fhir.transform.fhir.impl.util.r4;
+
+/*
+ * Copyright (c) 2004-2019, University of Oslo
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * Neither the name of the HISP project nor the names of its contributors may
+ * be used to endorse or promote products derived from this software without
+ * specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
+ * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+import org.dhis2.fhir.adapter.fhir.model.FhirVersion;
+import org.dhis2.fhir.adapter.fhir.script.ScriptExecutionContext;
+import org.dhis2.fhir.adapter.fhir.transform.fhir.impl.util.AbstractProgramStageFhirToDhisTransformerUtils;
+import org.dhis2.fhir.adapter.scriptable.Scriptable;
+import org.dhis2.fhir.adapter.util.CastUtils;
+import org.hl7.fhir.r4.model.BaseDateTimeType;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.stereotype.Component;
+
+import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
+import java.time.LocalDate;
+import java.time.ZoneId;
+import java.time.temporal.Temporal;
+import java.util.Date;
+import java.util.Set;
+
+/**
+ * FHIR version R4 implementation of {@link AbstractProgramStageFhirToDhisTransformerUtils}.
+ *
+ * @author volsch
+ */
+@Component
+@Scriptable
+public class R4ProgramStageFhirToDhisTransformerUtils extends AbstractProgramStageFhirToDhisTransformerUtils
+{
+ private final Logger logger = LoggerFactory.getLogger( getClass() );
+
+ private final ZoneId zoneId = ZoneId.systemDefault();
+
+ public R4ProgramStageFhirToDhisTransformerUtils( @Nonnull ScriptExecutionContext scriptExecutionContext )
+ {
+ super( scriptExecutionContext );
+ }
+
+ @Nonnull
+ @Override
+ public Set getFhirVersions()
+ {
+ return FhirVersion.R4_ONLY;
+ }
+
+ @Override
+ @Nullable
+ protected LocalDate castDate( @Nonnull Object date )
+ {
+ return CastUtils.cast( date,
+ BaseDateTimeType.class, d -> {
+ final Date result = d.getValue();
+ return LocalDate.from( result.toInstant().atZone( zoneId ) );
+ },
+ Date.class, d -> LocalDate.from( d.toInstant().atZone( zoneId ) ),
+ Temporal.class, LocalDate::from );
+ }
+}
diff --git a/fhir-r4/src/main/java/org/dhis2/fhir/adapter/fhir/transform/fhir/impl/util/r4/R4VitalSignFhirToDhisTransformerUtils.java b/fhir-r4/src/main/java/org/dhis2/fhir/adapter/fhir/transform/fhir/impl/util/r4/R4VitalSignFhirToDhisTransformerUtils.java
new file mode 100644
index 00000000..1a30cf04
--- /dev/null
+++ b/fhir-r4/src/main/java/org/dhis2/fhir/adapter/fhir/transform/fhir/impl/util/r4/R4VitalSignFhirToDhisTransformerUtils.java
@@ -0,0 +1,162 @@
+package org.dhis2.fhir.adapter.fhir.transform.fhir.impl.util.r4;
+
+/*
+ * Copyright (c) 2004-2019, University of Oslo
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * Neither the name of the HISP project nor the names of its contributors may
+ * be used to endorse or promote products derived from this software without
+ * specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
+ * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+import org.dhis2.fhir.adapter.fhir.model.FhirVersion;
+import org.dhis2.fhir.adapter.fhir.script.ScriptExecutionContext;
+import org.dhis2.fhir.adapter.fhir.transform.TransformerDataException;
+import org.dhis2.fhir.adapter.fhir.transform.TransformerMappingException;
+import org.dhis2.fhir.adapter.fhir.transform.fhir.impl.util.AbstractVitalSignFhirToDhisTransformerUtils;
+import org.dhis2.fhir.adapter.model.HeightUnit;
+import org.dhis2.fhir.adapter.model.WeightUnit;
+import org.dhis2.fhir.adapter.scriptable.Scriptable;
+import org.dhis2.fhir.adapter.util.NameUtils;
+import org.hl7.fhir.instance.model.api.ICompositeType;
+import org.hl7.fhir.r4.model.Quantity;
+import org.springframework.stereotype.Component;
+
+import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
+import java.math.BigDecimal;
+import java.util.Set;
+
+@Component
+@Scriptable
+public class R4VitalSignFhirToDhisTransformerUtils extends AbstractVitalSignFhirToDhisTransformerUtils
+{
+ public static final String UNIT_SYSTEM = "http://unitsofmeasure.org";
+
+ public R4VitalSignFhirToDhisTransformerUtils( @Nonnull ScriptExecutionContext scriptExecutionContext )
+ {
+ super( scriptExecutionContext );
+ }
+
+ @Nonnull
+ @Override
+ public Set getFhirVersions()
+ {
+ return FhirVersion.R4_ONLY;
+ }
+
+ @Nullable
+ @Override
+ public Double getWeight( @Nullable ICompositeType value, @Nullable Object weightUnit, boolean round ) throws TransformerDataException
+ {
+ if ( value == null )
+ {
+ return null;
+ }
+
+ if ( weightUnit == null )
+ {
+ throw new TransformerMappingException( "Weight unit has not been specified." );
+ }
+ final WeightUnit resultingWeightUnit;
+ try
+ {
+ resultingWeightUnit = NameUtils.toEnumValue( WeightUnit.class, weightUnit );
+ }
+ catch ( IllegalArgumentException e )
+ {
+ throw new TransformerMappingException( "Specified weight unit is invalid: " + weightUnit );
+ }
+
+ if ( !(value instanceof Quantity) )
+ {
+ throw new TransformerDataException( "Weight must be included as quantity, but element is " + value.getClass().getSimpleName() + "." );
+ }
+ final Quantity quantity = (Quantity) value;
+
+ if ( !UNIT_SYSTEM.equals( quantity.getSystem() ) )
+ {
+ throw new TransformerDataException( UNIT_SYSTEM + " is expected as unit system: " + quantity.getSystem() );
+ }
+ final WeightUnit actualWeightUnit = WeightUnit.getByUcumCode( quantity.getCode() );
+ if ( actualWeightUnit == null )
+ {
+ throw new TransformerDataException( "Unknown UCUM weight unit code: " + quantity.getCode() );
+ }
+
+ final BigDecimal actualValue = quantity.getValue();
+ if ( actualValue == null )
+ {
+ return null;
+ }
+ final double convertedValue = actualWeightUnit.convertTo( actualValue.doubleValue(), resultingWeightUnit );
+ return round ? Math.round( convertedValue ) : convertedValue;
+ }
+
+ @Nullable
+ @Override
+ public Double getHeight( @Nullable ICompositeType value, @Nullable Object heightUnit, boolean round ) throws TransformerDataException
+ {
+ if ( value == null )
+ {
+ return null;
+ }
+
+ if ( heightUnit == null )
+ {
+ throw new TransformerMappingException( "Height unit has not been specified." );
+ }
+ final HeightUnit resultingHeightUnit;
+ try
+ {
+ resultingHeightUnit = NameUtils.toEnumValue( HeightUnit.class, heightUnit );
+ }
+ catch ( IllegalArgumentException e )
+ {
+ throw new TransformerMappingException( "Specified height unit is invalid: " + heightUnit );
+ }
+
+ if ( !(value instanceof Quantity) )
+ {
+ throw new TransformerDataException( "Height must be included as quantity, but element is " + value.getClass().getSimpleName() + "." );
+ }
+ final Quantity quantity = (Quantity) value;
+
+ if ( !UNIT_SYSTEM.equals( quantity.getSystem() ) )
+ {
+ throw new TransformerDataException( UNIT_SYSTEM + " is expected as unit system: " + quantity.getSystem() );
+ }
+ final HeightUnit actualHeightUnit = HeightUnit.getByUcumCode( quantity.getCode() );
+ if ( actualHeightUnit == null )
+ {
+ throw new TransformerDataException( "Unknown UCUM height unit code: " + quantity.getCode() );
+ }
+
+ final BigDecimal actualValue = quantity.getValue();
+ if ( actualValue == null )
+ {
+ return null;
+ }
+ final double convertedValue = actualHeightUnit.convertTo( actualValue.doubleValue(), resultingHeightUnit );
+ return round ? Math.round( convertedValue ) : convertedValue;
+ }
+}
diff --git a/fhir-r4/src/test/java/org/dhis2/fhir/adapter/fhir/converter/r4/R4AdministrativeGenderToStringConverterTest.java b/fhir-r4/src/test/java/org/dhis2/fhir/adapter/fhir/converter/r4/R4AdministrativeGenderToStringConverterTest.java
new file mode 100644
index 00000000..72eed11b
--- /dev/null
+++ b/fhir-r4/src/test/java/org/dhis2/fhir/adapter/fhir/converter/r4/R4AdministrativeGenderToStringConverterTest.java
@@ -0,0 +1,102 @@
+package org.dhis2.fhir.adapter.fhir.converter.r4;
+
+/*
+ * Copyright (c) 2004-2019, University of Oslo
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * Neither the name of the HISP project nor the names of its contributors may
+ * be used to endorse or promote products derived from this software without
+ * specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
+ * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+import org.dhis2.fhir.adapter.fhir.metadata.model.Constant;
+import org.dhis2.fhir.adapter.fhir.metadata.model.ConstantResolver;
+import org.hl7.fhir.r4.model.Enumerations;
+import org.junit.Assert;
+import org.junit.Rule;
+import org.junit.Test;
+import org.mockito.InjectMocks;
+import org.mockito.Mock;
+import org.mockito.Mockito;
+import org.mockito.junit.MockitoJUnit;
+import org.mockito.junit.MockitoRule;
+
+import java.util.Optional;
+
+/**
+ * Unit tests for {@link R4AdministrativeGenderToStringConverter}.
+ *
+ * @author volsch
+ */
+public class R4AdministrativeGenderToStringConverterTest
+{
+ @Mock
+ private ConstantResolver constantResolver;
+
+ @InjectMocks
+ private R4AdministrativeGenderToStringConverter converter;
+
+ @Rule
+ public MockitoRule rule = MockitoJUnit.rule();
+
+ @Test
+ public void testMale()
+ {
+ final Constant constant = new Constant();
+ constant.setValue( "MaLe" );
+ Mockito.doReturn( Optional.of( constant ) ).when( constantResolver ).findOneByCode( Mockito.eq( "GENDER_MALE" ) );
+ Assert.assertEquals( "MaLe", converter.convert( Enumerations.AdministrativeGender.MALE ) );
+ }
+
+ @Test
+ public void testFemale()
+ {
+ final Constant constant = new Constant();
+ constant.setValue( "FeMaLe" );
+ Mockito.doReturn( Optional.of( constant ) ).when( constantResolver ).findOneByCode( Mockito.eq( "GENDER_FEMALE" ) );
+ Assert.assertEquals( "FeMaLe", converter.convert( Enumerations.AdministrativeGender.FEMALE ) );
+ }
+
+ @Test
+ public void testNull()
+ {
+ Assert.assertNull( converter.convert( Enumerations.AdministrativeGender.NULL ) );
+ }
+
+ @Test
+ public void testUnknown()
+ {
+ Assert.assertNull( converter.convert( Enumerations.AdministrativeGender.UNKNOWN ) );
+ }
+
+ @Test
+ public void testOther()
+ {
+ Assert.assertNull( converter.convert( Enumerations.AdministrativeGender.OTHER ) );
+ }
+
+ @Test
+ public void testNullValue()
+ {
+ Assert.assertNull( converter.convert( null ) );
+ }
+}
\ No newline at end of file
diff --git a/fhir-r4/src/test/java/org/dhis2/fhir/adapter/fhir/converter/r4/R4BaseDateTimeTypeToDateStringConverterTest.java b/fhir-r4/src/test/java/org/dhis2/fhir/adapter/fhir/converter/r4/R4BaseDateTimeTypeToDateStringConverterTest.java
new file mode 100644
index 00000000..b42358f0
--- /dev/null
+++ b/fhir-r4/src/test/java/org/dhis2/fhir/adapter/fhir/converter/r4/R4BaseDateTimeTypeToDateStringConverterTest.java
@@ -0,0 +1,63 @@
+package org.dhis2.fhir.adapter.fhir.converter.r4;
+
+/*
+ * Copyright (c) 2004-2019, University of Oslo
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * Neither the name of the HISP project nor the names of its contributors may
+ * be used to endorse or promote products derived from this software without
+ * specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
+ * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+import org.hl7.fhir.r4.model.DateTimeType;
+import org.junit.Assert;
+import org.junit.Rule;
+import org.junit.Test;
+import org.mockito.InjectMocks;
+import org.mockito.junit.MockitoJUnit;
+import org.mockito.junit.MockitoRule;
+
+import java.sql.Date;
+import java.time.ZoneId;
+import java.time.ZonedDateTime;
+
+/**
+ * Unit tests for {@link R4BaseDateTimeTypeToDateStringConverter}.
+ *
+ * @author volsch
+ */
+public class R4BaseDateTimeTypeToDateStringConverterTest
+{
+ @InjectMocks
+ private R4BaseDateTimeTypeToDateStringConverter converter;
+
+ @Rule
+ public MockitoRule rule = MockitoJUnit.rule();
+
+ @Test
+ public void fullTimestamp()
+ {
+ final ZonedDateTime zonedDateTime = ZonedDateTime.of( 2018, 7, 27, 15, 2, 43, 123456000, ZoneId.of( "CET" ) );
+ Assert.assertEquals( "2018-07-27",
+ converter.doConvert( new DateTimeType( Date.from( zonedDateTime.toInstant() ) ) ) );
+ }
+}
\ No newline at end of file
diff --git a/fhir-r4/src/test/java/org/dhis2/fhir/adapter/fhir/converter/r4/R4BaseDateTimeTypeToZonedDateTimeConverterTest.java b/fhir-r4/src/test/java/org/dhis2/fhir/adapter/fhir/converter/r4/R4BaseDateTimeTypeToZonedDateTimeConverterTest.java
new file mode 100644
index 00000000..3621855d
--- /dev/null
+++ b/fhir-r4/src/test/java/org/dhis2/fhir/adapter/fhir/converter/r4/R4BaseDateTimeTypeToZonedDateTimeConverterTest.java
@@ -0,0 +1,64 @@
+package org.dhis2.fhir.adapter.fhir.converter.r4;
+
+/*
+ * Copyright (c) 2004-2019, University of Oslo
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * Neither the name of the HISP project nor the names of its contributors may
+ * be used to endorse or promote products derived from this software without
+ * specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
+ * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+import org.hl7.fhir.r4.model.DateTimeType;
+import org.junit.Assert;
+import org.junit.Rule;
+import org.junit.Test;
+import org.mockito.InjectMocks;
+import org.mockito.junit.MockitoJUnit;
+import org.mockito.junit.MockitoRule;
+
+import java.sql.Date;
+import java.time.ZoneId;
+import java.time.ZonedDateTime;
+
+/**
+ * Unit tests for {@link R4BaseDateTimeTypeToZonedDateTimeConverter}.
+ *
+ * @author volsch
+ */
+public class R4BaseDateTimeTypeToZonedDateTimeConverterTest
+{
+ @InjectMocks
+ private R4BaseDateTimeTypeToZonedDateTimeConverter converter;
+
+ @Rule
+ public MockitoRule rule = MockitoJUnit.rule();
+
+ @Test
+ public void fullTimestamp()
+ {
+ final ZonedDateTime zonedDateTime = ZonedDateTime.of( 2018, 7, 27, 15, 2, 43, 123000000, ZoneId.of( "CET" ) );
+ final ZonedDateTime result = converter.doConvert( new DateTimeType( Date.from( zonedDateTime.toInstant() ) ) );
+ Assert.assertNotNull( result );
+ Assert.assertEquals( zonedDateTime.toLocalDateTime(), result.toLocalDateTime() );
+ }
+}
\ No newline at end of file
diff --git a/fhir-r4/src/test/java/org/dhis2/fhir/adapter/fhir/converter/r4/R4QuantityToStringConverterTest.java b/fhir-r4/src/test/java/org/dhis2/fhir/adapter/fhir/converter/r4/R4QuantityToStringConverterTest.java
new file mode 100644
index 00000000..778bba12
--- /dev/null
+++ b/fhir-r4/src/test/java/org/dhis2/fhir/adapter/fhir/converter/r4/R4QuantityToStringConverterTest.java
@@ -0,0 +1,58 @@
+package org.dhis2.fhir.adapter.fhir.converter.r4;
+
+/*
+ * Copyright (c) 2004-2019, University of Oslo
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * Neither the name of the HISP project nor the names of its contributors may
+ * be used to endorse or promote products derived from this software without
+ * specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
+ * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+import org.hl7.fhir.r4.model.Quantity;
+import org.junit.Assert;
+import org.junit.Rule;
+import org.junit.Test;
+import org.mockito.InjectMocks;
+import org.mockito.junit.MockitoJUnit;
+import org.mockito.junit.MockitoRule;
+
+/**
+ * Unit tests for {@link R4QuantityToStringConverter}.
+ *
+ * @author volsch
+ */
+public class R4QuantityToStringConverterTest
+{
+ @InjectMocks
+ private R4QuantityToStringConverter converter;
+
+ @Rule
+ public MockitoRule rule = MockitoJUnit.rule();
+
+ @Test
+ public void doubleQuantity()
+ {
+ final Quantity quantity = new Quantity( 27.3 );
+ Assert.assertEquals( "27.3", converter.convert( quantity ) );
+ }
+}
\ No newline at end of file
diff --git a/fhir-r4/src/test/java/org/dhis2/fhir/adapter/fhir/transform/fhir/impl/util/ReferenceFhirToDhisTransformerUtilsTest.java b/fhir-r4/src/test/java/org/dhis2/fhir/adapter/fhir/transform/fhir/impl/util/ReferenceFhirToDhisTransformerUtilsTest.java
new file mode 100644
index 00000000..2af3ac12
--- /dev/null
+++ b/fhir-r4/src/test/java/org/dhis2/fhir/adapter/fhir/transform/fhir/impl/util/ReferenceFhirToDhisTransformerUtilsTest.java
@@ -0,0 +1,261 @@
+package org.dhis2.fhir.adapter.fhir.transform.fhir.impl.util;
+
+/*
+ * Copyright (c) 2004-2019, University of Oslo
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * Neither the name of the HISP project nor the names of its contributors may
+ * be used to endorse or promote products derived from this software without
+ * specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
+ * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+import ca.uhn.fhir.context.FhirContext;
+import org.dhis2.fhir.adapter.fhir.metadata.model.FhirResourceType;
+import org.dhis2.fhir.adapter.fhir.metadata.model.FhirServer;
+import org.dhis2.fhir.adapter.fhir.metadata.model.FhirServerResource;
+import org.dhis2.fhir.adapter.fhir.metadata.model.SubscriptionFhirEndpoint;
+import org.dhis2.fhir.adapter.fhir.metadata.repository.FhirServerResourceRepository;
+import org.dhis2.fhir.adapter.fhir.model.FhirVersion;
+import org.dhis2.fhir.adapter.fhir.repository.FhirResourceRepository;
+import org.dhis2.fhir.adapter.fhir.script.ScriptExecution;
+import org.dhis2.fhir.adapter.fhir.script.ScriptExecutionContext;
+import org.dhis2.fhir.adapter.fhir.transform.fhir.FhirToDhisTransformerContext;
+import org.dhis2.fhir.adapter.fhir.transform.fhir.model.FhirRequest;
+import org.dhis2.fhir.adapter.fhir.transform.fhir.model.ResourceSystem;
+import org.hl7.fhir.instance.model.api.IBaseReference;
+import org.hl7.fhir.instance.model.api.IIdType;
+import org.hl7.fhir.r4.model.IdType;
+import org.hl7.fhir.r4.model.Patient;
+import org.hl7.fhir.r4.model.Reference;
+import org.hl7.fhir.r4.model.Resource;
+import org.junit.Assert;
+import org.junit.Rule;
+import org.junit.Test;
+import org.mockito.InjectMocks;
+import org.mockito.Mock;
+import org.mockito.Mockito;
+import org.mockito.junit.MockitoJUnit;
+import org.mockito.junit.MockitoRule;
+
+import java.util.Map;
+import java.util.Optional;
+import java.util.UUID;
+
+/**
+ * Unit tests for {@link ReferenceFhirToDhisTransformerUtils}.
+ *
+ * @author volsch
+ */
+public class ReferenceFhirToDhisTransformerUtilsTest
+{
+ @Mock
+ private ScriptExecutionContext scriptExecutionContext;
+
+ @Mock
+ private FhirServerResourceRepository fhirServerResourceRepository;
+
+ @Mock
+ private FhirResourceRepository fhirResourceRepository;
+
+ @Mock
+ private FhirToDhisTransformerContext context;
+
+ @Mock
+ private FhirRequest request;
+
+ @Mock
+ private ScriptExecution scriptExecution;
+
+ @Mock
+ private Map variables;
+
+ @Mock
+ private IBaseReference reference;
+
+ @InjectMocks
+ private ReferenceFhirToDhisTransformerUtils utils;
+
+ @Rule
+ public MockitoRule rule = MockitoJUnit.rule();
+
+ @Test
+ public void getResourceNull()
+ {
+ Assert.assertNull( utils.getResource( null, "PATIENT", false ) );
+ }
+
+ @Test
+ public void getIncludedResource()
+ {
+ final Patient resource = new Patient();
+ final IIdType id = new IdType( "Patient", "123" );
+ Mockito.doReturn( resource ).when( reference ).getResource();
+ Assert.assertSame( resource, utils.getResource( reference, "PATIENT", false ) );
+ }
+
+ @Test
+ public void getIncludedResourceNoType()
+ {
+ final Patient resource = new Patient();
+ final IIdType id = new IdType( null, "123" );
+ Mockito.doReturn( resource ).when( reference ).getResource();
+ Assert.assertSame( resource, utils.getResource( reference, "PATIENT", false ) );
+ }
+
+ @Test
+ public void getIncludedResourceNoRequestType()
+ {
+ final Patient resource = new Patient();
+ final IIdType id = new IdType( "Patient", "123" );
+ Mockito.doReturn( resource ).when( reference ).getResource();
+ Assert.assertSame( resource, utils.getResource( reference, null, false ) );
+ }
+
+ @Test
+ public void getResource()
+ {
+ final SubscriptionFhirEndpoint subscriptionFhirEndpoint = new SubscriptionFhirEndpoint();
+ final UUID fhirServerId = UUID.randomUUID();
+ final FhirServer fhirServer = new FhirServer();
+ fhirServer.setId( fhirServerId );
+ fhirServer.setFhirVersion( FhirVersion.R4 );
+ fhirServer.setFhirEndpoint( subscriptionFhirEndpoint );
+ final FhirContext fhirContext = FhirContext.forR4();
+ final UUID fhirServerResourceId = UUID.randomUUID();
+ final FhirServerResource fhirServerResource = new FhirServerResource();
+ fhirServerResource.setId( fhirServerResourceId );
+ fhirServerResource.setFhirServer( fhirServer );
+ final ResourceSystem resourceSystem = new ResourceSystem( FhirResourceType.ORGANIZATION, "http://test.com", "OT_", null, null );
+ Mockito.doReturn( scriptExecution ).when( scriptExecutionContext ).getScriptExecution();
+ Mockito.doReturn( variables ).when( scriptExecution ).getVariables();
+ Mockito.doReturn( context ).when( variables ).get( Mockito.eq( "context" ) );
+ Mockito.doReturn( request ).when( context ).getFhirRequest();
+ Mockito.doReturn( fhirServerResourceId ).when( request ).getFhirServerResourceId();
+ Mockito.doReturn( FhirVersion.R4 ).when( request ).getVersion();
+ Mockito.doReturn( Optional.of( resourceSystem ) ).when( request ).getOptionalResourceSystem( FhirResourceType.ORGANIZATION );
+ Mockito.doReturn( Optional.of( fhirServerResource ) ).when( fhirServerResourceRepository ).findOneByIdCached( Mockito.eq( fhirServerResourceId ) );
+ Mockito.doReturn( Optional.of( fhirContext ) ).when( fhirResourceRepository ).findFhirContext( Mockito.eq( FhirVersion.R4 ) );
+
+ final Patient resource = new Patient();
+ final IIdType id = new IdType( "Patient", "123" );
+ resource.setId( id );
+ final Reference reference = new Reference( id );
+
+ Mockito.doReturn( Optional.of( resource ) ).when( fhirResourceRepository )
+ .find( Mockito.eq( fhirServerId ), Mockito.eq( FhirVersion.R4 ),
+ Mockito.same( subscriptionFhirEndpoint ), Mockito.eq( "Patient" ), Mockito.eq( "123" ) );
+
+ final Resource result = (Resource) utils.getResource( reference, null, false );
+ Assert.assertTrue( result instanceof Patient );
+ Assert.assertEquals( id, result.getIdElement() );
+ Assert.assertNotSame( resource, result );
+
+ Mockito.verify( fhirResourceRepository ).find( Mockito.eq( fhirServerId ), Mockito.eq( FhirVersion.R4 ),
+ Mockito.same( subscriptionFhirEndpoint ), Mockito.eq( "Patient" ), Mockito.eq( "123" ) );
+ }
+
+ @Test
+ public void getResourceRefreshed()
+ {
+ final SubscriptionFhirEndpoint subscriptionFhirEndpoint = new SubscriptionFhirEndpoint();
+ final UUID fhirServerId = UUID.randomUUID();
+ final FhirServer fhirServer = new FhirServer();
+ fhirServer.setId( fhirServerId );
+ fhirServer.setFhirVersion( FhirVersion.R4 );
+ fhirServer.setFhirEndpoint( subscriptionFhirEndpoint );
+ final FhirContext fhirContext = FhirContext.forR4();
+ final UUID fhirServerResourceId = UUID.randomUUID();
+ final FhirServerResource fhirServerResource = new FhirServerResource();
+ fhirServerResource.setId( fhirServerResourceId );
+ fhirServerResource.setFhirServer( fhirServer );
+ final ResourceSystem resourceSystem = new ResourceSystem( FhirResourceType.ORGANIZATION, "http://test.com", "OT_", null, null );
+ Mockito.doReturn( scriptExecution ).when( scriptExecutionContext ).getScriptExecution();
+ Mockito.doReturn( variables ).when( scriptExecution ).getVariables();
+ Mockito.doReturn( context ).when( variables ).get( Mockito.eq( "context" ) );
+ Mockito.doReturn( request ).when( context ).getFhirRequest();
+ Mockito.doReturn( fhirServerResourceId ).when( request ).getFhirServerResourceId();
+ Mockito.doReturn( FhirVersion.R4 ).when( request ).getVersion();
+ Mockito.doReturn( Optional.of( resourceSystem ) ).when( request ).getOptionalResourceSystem( FhirResourceType.ORGANIZATION );
+ Mockito.doReturn( Optional.of( fhirServerResource ) ).when( fhirServerResourceRepository ).findOneByIdCached( Mockito.eq( fhirServerResourceId ) );
+ Mockito.doReturn( Optional.of( fhirContext ) ).when( fhirResourceRepository ).findFhirContext( Mockito.eq( FhirVersion.R4 ) );
+
+ final Patient resource = new Patient();
+ final IIdType id = new IdType( "Patient", "123" );
+ resource.setId( id );
+ final Reference reference = new Reference( id );
+
+ Mockito.doReturn( Optional.of( resource ) ).when( fhirResourceRepository )
+ .findRefreshed( Mockito.eq( fhirServerId ), Mockito.eq( FhirVersion.R4 ),
+ Mockito.same( subscriptionFhirEndpoint ), Mockito.eq( "Patient" ), Mockito.eq( "123" ) );
+
+ final Resource result = (Resource) utils.getResource( reference, null, true );
+ Assert.assertTrue( result instanceof Patient );
+ Assert.assertEquals( id, result.getIdElement() );
+ Assert.assertNotSame( resource, result );
+
+ Mockito.verify( fhirResourceRepository ).findRefreshed( Mockito.eq( fhirServerId ), Mockito.eq( FhirVersion.R4 ),
+ Mockito.same( subscriptionFhirEndpoint ), Mockito.eq( "Patient" ), Mockito.eq( "123" ) );
+ }
+
+ @Test
+ public void initReference()
+ {
+ final SubscriptionFhirEndpoint subscriptionFhirEndpoint = new SubscriptionFhirEndpoint();
+ final UUID fhirServerId = UUID.randomUUID();
+ final FhirServer fhirServer = new FhirServer();
+ fhirServer.setId( fhirServerId );
+ fhirServer.setFhirVersion( FhirVersion.R4 );
+ fhirServer.setFhirEndpoint( subscriptionFhirEndpoint );
+ final FhirContext fhirContext = FhirContext.forR4();
+ final UUID fhirServerResourceId = UUID.randomUUID();
+ final FhirServerResource fhirServerResource = new FhirServerResource();
+ fhirServerResource.setId( fhirServerResourceId );
+ fhirServerResource.setFhirServer( fhirServer );
+ final ResourceSystem resourceSystem = new ResourceSystem( FhirResourceType.ORGANIZATION, "http://test.com", "OT_", null, null );
+ Mockito.doReturn( scriptExecution ).when( scriptExecutionContext ).getScriptExecution();
+ Mockito.doReturn( variables ).when( scriptExecution ).getVariables();
+ Mockito.doReturn( context ).when( variables ).get( Mockito.eq( "context" ) );
+ Mockito.doReturn( request ).when( context ).getFhirRequest();
+ Mockito.doReturn( fhirServerResourceId ).when( request ).getFhirServerResourceId();
+ Mockito.doReturn( FhirVersion.R4 ).when( request ).getVersion();
+ Mockito.doReturn( Optional.of( resourceSystem ) ).when( request ).getOptionalResourceSystem( FhirResourceType.ORGANIZATION );
+ Mockito.doReturn( Optional.of( fhirServerResource ) ).when( fhirServerResourceRepository ).findOneByIdCached( Mockito.eq( fhirServerResourceId ) );
+ Mockito.doReturn( Optional.of( fhirContext ) ).when( fhirResourceRepository ).findFhirContext( Mockito.eq( FhirVersion.R4 ) );
+
+ final Patient resource = new Patient();
+ final IIdType id = new IdType( "Patient", "123" );
+ resource.setId( id );
+ final Reference reference = new Reference( id );
+
+ Mockito.doReturn( Optional.of( resource ) ).when( fhirResourceRepository )
+ .find( Mockito.eq( fhirServerId ), Mockito.eq( FhirVersion.R4 ),
+ Mockito.same( subscriptionFhirEndpoint ), Mockito.eq( "Patient" ), Mockito.eq( "123" ) );
+
+ utils.initReference( reference, "PATIENT" );
+ Assert.assertTrue( reference.getResource() instanceof Patient );
+ Assert.assertEquals( id, reference.getResource().getIdElement() );
+ Assert.assertNotSame( resource, reference.getResource() );
+
+ Mockito.verify( fhirResourceRepository ).find( Mockito.eq( fhirServerId ), Mockito.eq( FhirVersion.R4 ),
+ Mockito.same( subscriptionFhirEndpoint ), Mockito.eq( "Patient" ), Mockito.eq( "123" ) );
+ }
+}
\ No newline at end of file
diff --git a/fhir-r4/src/test/java/org/dhis2/fhir/adapter/fhir/transform/fhir/impl/util/r4/R4AddressFhirToDhisTransformerUtilsTest.java b/fhir-r4/src/test/java/org/dhis2/fhir/adapter/fhir/transform/fhir/impl/util/r4/R4AddressFhirToDhisTransformerUtilsTest.java
new file mode 100644
index 00000000..6b3cfda6
--- /dev/null
+++ b/fhir-r4/src/test/java/org/dhis2/fhir/adapter/fhir/transform/fhir/impl/util/r4/R4AddressFhirToDhisTransformerUtilsTest.java
@@ -0,0 +1,180 @@
+package org.dhis2.fhir.adapter.fhir.transform.fhir.impl.util.r4;
+
+/*
+ * Copyright (c) 2004-2019, University of Oslo
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * Neither the name of the HISP project nor the names of its contributors may
+ * be used to endorse or promote products derived from this software without
+ * specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
+ * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+import org.dhis2.fhir.adapter.fhir.script.ScriptExecutionContext;
+import org.hl7.fhir.r4.model.Address;
+import org.hl7.fhir.r4.model.Period;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.mockito.InjectMocks;
+import org.mockito.Mock;
+import org.mockito.junit.MockitoJUnit;
+import org.mockito.junit.MockitoRule;
+
+import java.time.ZonedDateTime;
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.List;
+
+/**
+ * Unit tests for {@link R4AddressFhirToDhisTransformerUtils}.
+ *
+ * @author volsch
+ */
+public class R4AddressFhirToDhisTransformerUtilsTest
+{
+ @Mock
+ private ScriptExecutionContext scriptExecutionContext;
+
+ @InjectMocks
+ private R4DateTimeFhirToDhisTransformerUtils dateTimeFhirToDhisTransformerUtils;
+
+ private R4AddressFhirToDhisTransformerUtils utils;
+
+ @Rule
+ public MockitoRule rule = MockitoJUnit.rule();
+
+ @Before
+ public void before()
+ {
+ utils = new R4AddressFhirToDhisTransformerUtils( scriptExecutionContext, dateTimeFhirToDhisTransformerUtils );
+ }
+
+ @Test
+ public void primaryExpired()
+ {
+ final List addresses = new ArrayList<>();
+ addresses.add( new Address().setType( Address.AddressType.PHYSICAL ).setUse( Address.AddressUse.HOME )
+ .setPeriod( new Period().setEnd( Date.from( ZonedDateTime.now().minusDays( 1 ).toInstant() ) ) ) );
+ addresses.add( new Address().setType( Address.AddressType.POSTAL ).setUse( Address.AddressUse.OLD ) );
+ Assert.assertSame( addresses.get( 1 ), utils.getPrimaryAddress( addresses ) );
+ }
+
+ @Test
+ public void primaryTemp()
+ {
+ final List addresses = new ArrayList<>();
+ addresses.add( new Address().setType( Address.AddressType.PHYSICAL ).setUse( Address.AddressUse.TEMP ) );
+ addresses.add( new Address().setType( Address.AddressType.PHYSICAL ).setUse( Address.AddressUse.OLD ) );
+ Assert.assertSame( addresses.get( 0 ), utils.getPrimaryAddress( addresses ) );
+ }
+
+ @Test
+ public void primaryWork()
+ {
+ final List addresses = new ArrayList<>();
+ addresses.add( new Address().setType( Address.AddressType.PHYSICAL ).setUse( Address.AddressUse.HOME ) );
+ addresses.add( new Address().setType( Address.AddressType.PHYSICAL ).setUse( Address.AddressUse.WORK ) );
+ Assert.assertSame( addresses.get( 0 ), utils.getPrimaryAddress( addresses ) );
+ }
+
+ @Test
+ public void primaryNull()
+ {
+ final List addresses = new ArrayList<>();
+ addresses.add( new Address().setType( Address.AddressType.PHYSICAL ).setUse( Address.AddressUse.NULL ) );
+ addresses.add( new Address().setType( Address.AddressType.PHYSICAL ).setUse( Address.AddressUse.WORK ) );
+ Assert.assertSame( addresses.get( 0 ), utils.getPrimaryAddress( addresses ) );
+ }
+
+ @Test
+ public void primaryUnspecified()
+ {
+ final List addresses = new ArrayList<>();
+ addresses.add( new Address().setType( Address.AddressType.PHYSICAL ) );
+ addresses.add( new Address().setType( Address.AddressType.PHYSICAL ).setUse( Address.AddressUse.WORK ) );
+ Assert.assertSame( addresses.get( 0 ), utils.getPrimaryAddress( addresses ) );
+ }
+
+ @Test
+ public void primaryPhysical()
+ {
+ final List addresses = new ArrayList<>();
+ addresses.add( new Address().setType( Address.AddressType.PHYSICAL ).setUse( Address.AddressUse.HOME ) );
+ addresses.add( new Address().setType( Address.AddressType.POSTAL ).setUse( Address.AddressUse.HOME ) );
+ Assert.assertSame( addresses.get( 0 ), utils.getPrimaryAddress( addresses ) );
+ }
+
+ @Test
+ public void primaryBoth()
+ {
+ final List addresses = new ArrayList<>();
+ addresses.add( new Address().setType( Address.AddressType.POSTAL ).setUse( Address.AddressUse.HOME ) );
+ addresses.add( new Address().setType( Address.AddressType.BOTH ).setUse( Address.AddressUse.HOME ) );
+ Assert.assertSame( addresses.get( 1 ), utils.getPrimaryAddress( addresses ) );
+ }
+
+ @Test
+ public void primaryEmpty()
+ {
+ final List addresses = new ArrayList<>();
+ Assert.assertNotNull( utils.getPrimaryAddress( addresses ) );
+ }
+
+ @Test
+ public void getSingleLine()
+ {
+ final Address address = new Address().addLine( "Lower Road 20" ).addLine( "Apartment 19" );
+ Assert.assertEquals( "Lower Road 20 Apartment 19", utils.getSingleLine( address ) );
+ }
+
+ @Test
+ public void getSingleLineOneLine()
+ {
+ final Address address = new Address().addLine( "Lower Road 20" );
+ Assert.assertEquals( "Lower Road 20", utils.getSingleLine( address ) );
+ }
+
+ @Test
+ public void getConstructedText()
+ {
+ final Address address = new Address().addLine( "Lower Road 20" ).addLine( "Apartment 19" )
+ .setCity( "Freetown" ).setPostalCode( "2009" ).setState( "Other State" ).setCountry( "Sierra Leone" );
+ Assert.assertEquals( "Lower Road 20 / Apartment 19 / 2009 Freetown / Other State", utils.getConstructedText( address ) );
+ }
+
+ @Test
+ public void getConstructedTextWithoutPostcode()
+ {
+ final Address address = new Address().addLine( "Lower Road 20" ).addLine( "Apartment 19" )
+ .setCity( "Freetown" ).setState( "Other State" ).setCountry( "Sierra Leone" );
+ Assert.assertEquals( "Lower Road 20 / Apartment 19 / Freetown / Other State", utils.getConstructedText( address ) );
+ }
+
+ @Test
+ public void getText()
+ {
+ final Address address = new Address().addLine( "Lower Road 20" ).addLine( "Apartment 19" )
+ .setCity( "Freetown" ).setState( "Other State" ).setCountry( "Sierra Leone" ).setText( "Summary Address" );
+ Assert.assertEquals( "Summary Address", utils.getText( address ) );
+ }
+}
\ No newline at end of file
diff --git a/fhir-r4/src/test/java/org/dhis2/fhir/adapter/fhir/transform/fhir/impl/util/r4/R4CodeFhirToDhisTransformerUtilsTest.java b/fhir-r4/src/test/java/org/dhis2/fhir/adapter/fhir/transform/fhir/impl/util/r4/R4CodeFhirToDhisTransformerUtilsTest.java
new file mode 100644
index 00000000..21ec7c12
--- /dev/null
+++ b/fhir-r4/src/test/java/org/dhis2/fhir/adapter/fhir/transform/fhir/impl/util/r4/R4CodeFhirToDhisTransformerUtilsTest.java
@@ -0,0 +1,385 @@
+package org.dhis2.fhir.adapter.fhir.transform.fhir.impl.util.r4;
+
+/*
+ * Copyright (c) 2004-2019, University of Oslo
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * Neither the name of the HISP project nor the names of its contributors may
+ * be used to endorse or promote products derived from this software without
+ * specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
+ * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+import org.dhis2.fhir.adapter.fhir.metadata.model.Code;
+import org.dhis2.fhir.adapter.fhir.metadata.model.FhirResourceType;
+import org.dhis2.fhir.adapter.fhir.metadata.model.System;
+import org.dhis2.fhir.adapter.fhir.metadata.model.SystemCode;
+import org.dhis2.fhir.adapter.fhir.metadata.repository.CodeRepository;
+import org.dhis2.fhir.adapter.fhir.metadata.repository.SystemCodeRepository;
+import org.dhis2.fhir.adapter.fhir.model.SystemCodeValue;
+import org.dhis2.fhir.adapter.fhir.script.ScriptExecution;
+import org.dhis2.fhir.adapter.fhir.script.ScriptExecutionContext;
+import org.dhis2.fhir.adapter.fhir.transform.fhir.FhirToDhisTransformerContext;
+import org.dhis2.fhir.adapter.fhir.transform.fhir.model.FhirRequest;
+import org.dhis2.fhir.adapter.fhir.transform.fhir.model.ResourceSystem;
+import org.hl7.fhir.r4.model.CodeableConcept;
+import org.hl7.fhir.r4.model.Coding;
+import org.hl7.fhir.r4.model.Observation;
+import org.junit.Assert;
+import org.junit.Rule;
+import org.junit.Test;
+import org.mockito.InjectMocks;
+import org.mockito.Mock;
+import org.mockito.Mockito;
+import org.mockito.junit.MockitoJUnit;
+import org.mockito.junit.MockitoRule;
+
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+import java.util.Optional;
+
+/**
+ * Unit tests for {@link R4CodeFhirToDhisTransformerUtils}.
+ *
+ * @author volsch
+ */
+public class R4CodeFhirToDhisTransformerUtilsTest
+{
+ @Mock
+ private ScriptExecutionContext scriptExecutionContext;
+
+ @Mock
+ private CodeRepository codeRepository;
+
+ @Mock
+ private SystemCodeRepository systemCodeRepository;
+
+ @Mock
+ private FhirToDhisTransformerContext context;
+
+ @Mock
+ private FhirRequest request;
+
+ @Mock
+ private ScriptExecution scriptExecution;
+
+ @Mock
+ private Map variables;
+
+ @InjectMocks
+ private R4CodeFhirToDhisTransformerUtils utils;
+
+ @Rule
+ public MockitoRule rule = MockitoJUnit.rule();
+
+ @Test
+ public void getSystemCodeValuesNull()
+ {
+ Assert.assertTrue( utils.getSystemCodeValues( null ).isEmpty() );
+ }
+
+ @Test
+ public void getCodeSystemNull()
+ {
+ Assert.assertNull( utils.getCode( null, "http://test.com/1" ) );
+ }
+
+ @Test
+ public void getCodeNotFound()
+ {
+ final CodeableConcept codeableConcept = new CodeableConcept();
+ codeableConcept.addCoding( new Coding().setSystem( "http://test.com/1" ).setCode( "C1" ) )
+ .addCoding( new Coding().setSystem( "http://test.com/2" ).setCode( "C2" ) );
+ Assert.assertNull( utils.getCode( codeableConcept, "http://test.com/3" ) );
+ }
+
+ @Test
+ public void getCode()
+ {
+ final CodeableConcept codeableConcept = new CodeableConcept();
+ codeableConcept.addCoding( new Coding().setSystem( "http://test.com/1" ).setCode( "C1" ) )
+ .addCoding( new Coding().setSystem( "http://test.com/2" ).setCode( "C2" ) );
+ Assert.assertEquals( "C2", utils.getCode( codeableConcept, "http://test.com/2" ) );
+ }
+
+ @Test
+ public void getSystemCodeValues()
+ {
+ final CodeableConcept codeableConcept = new CodeableConcept();
+ codeableConcept.addCoding( new Coding().setSystem( "http://test.com/1" ).setCode( "C1" ) )
+ .addCoding( new Coding().setSystem( "http://test.com/2" ).setCode( "C2" ) );
+ final List systemCodeValues = utils.getSystemCodeValues( codeableConcept );
+ Assert.assertEquals( new SystemCodeValue( "http://test.com/1", "C1" ), systemCodeValues.get( 0 ) );
+ Assert.assertEquals( new SystemCodeValue( "http://test.com/2", "C2" ), systemCodeValues.get( 1 ) );
+ }
+
+ @Test
+ public void containsMappingCodeNull()
+ {
+ Assert.assertFalse( utils.containsMappingCode( null, new Object[0] ) );
+ }
+
+ @Test
+ public void containsMappingCodeEmptyArray()
+ {
+ Assert.assertFalse( utils.containsMappingCode( new CodeableConcept().addCoding(), new Object[0] ) );
+ }
+
+ @Test
+ public void containsMappingCodeFound()
+ {
+ final System system1 = new System();
+ system1.setSystemUri( "http://test.com/3" );
+ final SystemCode systemCode1 = new SystemCode();
+ systemCode1.setSystemCode( "C3" );
+ systemCode1.setSystem( system1 );
+ systemCode1.setCode( new Code() );
+
+ final System system2 = new System();
+ system2.setSystemUri( "http://test.com/2" );
+ final SystemCode systemCode2 = new SystemCode();
+ systemCode2.setSystemCode( "C2" );
+ systemCode2.setSystem( system2 );
+ systemCode2.setCode( new Code() );
+
+ Mockito.doReturn( Arrays.asList( systemCode1, systemCode2 ) ).when( systemCodeRepository )
+ .findAllByCodes( Mockito.eq( Arrays.asList( "X_20", "X_21" ) ) );
+ Assert.assertTrue( utils.containsMappingCode( new CodeableConcept().addCoding( new Coding().setSystem( "http://test.com/1" ).setCode( "C1" ) )
+ .addCoding( new Coding().setSystem( "http://test.com/2" ).setCode( "C2" ) ), new Object[]{ "X_20", "X_21" } ) );
+ }
+
+ @Test
+ public void containsMappingCodeNotFound()
+ {
+ final System system1 = new System();
+ system1.setSystemUri( "http://test.com/3" );
+ final SystemCode systemCode1 = new SystemCode();
+ systemCode1.setSystemCode( "C3" );
+ systemCode1.setSystem( system1 );
+ systemCode1.setCode( new Code() );
+
+ final System system2 = new System();
+ system2.setSystemUri( "http://test.com/4" );
+ final SystemCode systemCode2 = new SystemCode();
+ systemCode2.setSystemCode( "C4" );
+ systemCode2.setSystem( system2 );
+ systemCode2.setCode( new Code() );
+
+ Mockito.doReturn( Arrays.asList( systemCode1, systemCode2 ) ).when( systemCodeRepository )
+ .findAllByCodes( Mockito.eq( Arrays.asList( "X_20", "X_21" ) ) );
+ Assert.assertFalse( utils.containsMappingCode( new CodeableConcept().addCoding( new Coding().setSystem( "http://test.com/1" ).setCode( "C1" ) )
+ .addCoding( new Coding().setSystem( "http://test.com/2" ).setCode( "C2" ) ), new Object[]{ "X_20", "X_21" } ) );
+ }
+
+ @Test
+ public void containsCodeNull()
+ {
+ Assert.assertFalse( utils.containsCode( null, "http://test.com/2", "C2" ) );
+ }
+
+ @Test
+ public void containsCode()
+ {
+ final CodeableConcept codeableConcept = new CodeableConcept();
+ codeableConcept.addCoding( new Coding().setSystem( "http://test.com/1" ).setCode( "C1" ) )
+ .addCoding( new Coding().setSystem( "http://test.com/2" ).setCode( "C2" ) );
+ Assert.assertTrue( utils.containsCode( codeableConcept, "http://test.com/2", "C2" ) );
+ }
+
+ @Test
+ public void containsCodeNot()
+ {
+ final CodeableConcept codeableConcept = new CodeableConcept();
+ codeableConcept.addCoding( new Coding().setSystem( "http://test.com/1" ).setCode( "C1" ) )
+ .addCoding( new Coding().setSystem( "http://test.com/2" ).setCode( "C2" ) );
+ Assert.assertFalse( utils.containsCode( codeableConcept, "http://test.com/3", "C3" ) );
+ }
+
+ @Test
+ public void containsAnyCode()
+ {
+ final CodeableConcept codeableConcept = new CodeableConcept();
+ codeableConcept.addCoding( new Coding().setSystem( "http://test.com/1" ).setCode( "C1" ) )
+ .addCoding( new Coding().setSystem( "http://test.com/2" ).setCode( "C2" ) );
+ final List systemCodeValues = Arrays.asList(
+ new SystemCodeValue( "http://test.com/3", "C3" ),
+ new SystemCodeValue( "http://test.com/2", "C2" )
+ );
+ Assert.assertTrue( utils.containsAnyCode( codeableConcept, systemCodeValues ) );
+ }
+
+ @Test
+ public void containsAnyCodeNot()
+ {
+ final CodeableConcept codeableConcept = new CodeableConcept();
+ codeableConcept.addCoding( new Coding().setSystem( "http://test.com/1" ).setCode( "C1" ) )
+ .addCoding( new Coding().setSystem( "http://test.com/2" ).setCode( "C2" ) );
+ final List systemCodeValues = Arrays.asList(
+ new SystemCodeValue( "http://test.com/3", "C3" ),
+ new SystemCodeValue( "http://test.com/4", "C4" )
+ );
+ Assert.assertFalse( utils.containsAnyCode( codeableConcept, systemCodeValues ) );
+ }
+
+ @Test
+ public void containsAnyCodeConceptNull()
+ {
+ final List systemCodeValues = Arrays.asList(
+ new SystemCodeValue( "http://test.com/3", "C3" ),
+ new SystemCodeValue( "http://test.com/4", "C4" )
+ );
+ Assert.assertFalse( utils.containsAnyCode( null, systemCodeValues ) );
+ }
+
+ @Test
+ public void containsAnyCodeValuesNull()
+ {
+ final CodeableConcept codeableConcept = new CodeableConcept();
+ codeableConcept.addCoding( new Coding().setSystem( "http://test.com/1" ).setCode( "C1" ) )
+ .addCoding( new Coding().setSystem( "http://test.com/2" ).setCode( "C2" ) );
+ Assert.assertFalse( utils.containsAnyCode( codeableConcept, null ) );
+ }
+
+ @Test
+ public void getSystemCodeValuesByMappingCodesNull()
+ {
+ Assert.assertTrue( utils.getSystemCodeValuesByMappingCodes( null ).isEmpty() );
+ }
+
+ @Test
+ public void getSystemCodeValuesByMappingCodesEmpty()
+ {
+ Assert.assertTrue( utils.getSystemCodeValuesByMappingCodes( new Object[0] ).isEmpty() );
+ }
+
+ @Test
+ public void getSystemCodeValuesByMappingCodes()
+ {
+ final System system1 = new System();
+ system1.setSystemUri( "http://test.com/3" );
+ final Code code1 = new Code();
+ code1.setCode( "X_20" );
+ final SystemCode systemCode1 = new SystemCode();
+ systemCode1.setSystemCode( "C3" );
+ systemCode1.setSystem( system1 );
+ systemCode1.setCode( code1 );
+
+ final System system2 = new System();
+ system2.setSystemUri( "http://test.com/4" );
+ final Code code2 = new Code();
+ code2.setCode( "X_21" );
+ final SystemCode systemCode2 = new SystemCode();
+ systemCode2.setSystemCode( "C4" );
+ systemCode2.setSystem( system2 );
+ systemCode2.setCode( code2 );
+
+ final System system3 = new System();
+ system2.setSystemUri( "http://test.com/5" );
+ final Code code3 = new Code();
+ code3.setCode( "X_21" );
+ final SystemCode systemCode3 = new SystemCode();
+ systemCode3.setSystemCode( "C5" );
+ systemCode3.setSystem( system2 );
+ systemCode3.setCode( code3 );
+
+ Mockito.doReturn( Arrays.asList( systemCode1, systemCode2, systemCode3 ) ).when( systemCodeRepository )
+ .findAllByCodes( Mockito.eq( Arrays.asList( "X_20", "X_21" ) ) );
+ final Map> result =
+ utils.getSystemCodeValuesByMappingCodes( new Object[]{ "X_20", "X_21" } );
+
+ Assert.assertEquals( 2, result.size() );
+ Assert.assertNotNull( result.get( "X_20" ) );
+ Assert.assertEquals( 1, result.get( "X_20" ).size() );
+ Assert.assertEquals( systemCode1.getCalculatedSystemCodeValue(), result.get( "X_20" ).get( 0 ) );
+ Assert.assertNotNull( result.get( "X_21" ) );
+ Assert.assertEquals( 2, result.get( "X_21" ).size() );
+ Assert.assertEquals( systemCode2.getCalculatedSystemCodeValue(), result.get( "X_21" ).get( 0 ) );
+ Assert.assertEquals( systemCode3.getCalculatedSystemCodeValue(), result.get( "X_21" ).get( 1 ) );
+ }
+
+ @Test
+ public void getResourceCodes()
+ {
+ final CodeableConcept codeableConcept = new CodeableConcept();
+ codeableConcept.addCoding( new Coding().setSystem( "http://test.com/1" ).setCode( "C1" ) )
+ .addCoding( new Coding().setSystem( "http://test.com/2" ).setCode( "C2" ) );
+ final Observation observation = new Observation();
+ observation.setCode( codeableConcept );
+ final List systemCodeValues = utils.getResourceCodes( observation );
+ Assert.assertNotNull( systemCodeValues );
+ Assert.assertEquals( 2, systemCodeValues.size() );
+ Assert.assertEquals( new SystemCodeValue( "http://test.com/1", "C1" ), systemCodeValues.get( 0 ) );
+ Assert.assertEquals( new SystemCodeValue( "http://test.com/2", "C2" ), systemCodeValues.get( 1 ) );
+ }
+
+ @Test
+ public void getMappedCodeNotFound()
+ {
+ final ResourceSystem resourceSystem = new ResourceSystem( FhirResourceType.ORGANIZATION, "http://test.com/2", null, null, null );
+
+ Mockito.doReturn( scriptExecution ).when( scriptExecutionContext ).getScriptExecution();
+ Mockito.doReturn( variables ).when( scriptExecution ).getVariables();
+ Mockito.doReturn( context ).when( variables ).get( Mockito.eq( "context" ) );
+ Mockito.doReturn( request ).when( context ).getFhirRequest();
+ Mockito.doReturn( Optional.of( resourceSystem ) ).when( request ).getOptionalResourceSystem( FhirResourceType.ORGANIZATION );
+
+ Assert.assertNull( utils.getMappedCode( "TEST_1", "ORGANIZATION" ) );
+ }
+
+ @Test
+ public void getMappedCodeFound()
+ {
+ final ResourceSystem resourceSystem = new ResourceSystem( FhirResourceType.ORGANIZATION, "http://test.com/2", null, null, null );
+
+ Mockito.doReturn( scriptExecution ).when( scriptExecutionContext ).getScriptExecution();
+ Mockito.doReturn( variables ).when( scriptExecution ).getVariables();
+ Mockito.doReturn( context ).when( variables ).get( Mockito.eq( "context" ) );
+ Mockito.doReturn( request ).when( context ).getFhirRequest();
+ Mockito.doReturn( Optional.of( resourceSystem ) ).when( request ).getOptionalResourceSystem( FhirResourceType.ORGANIZATION );
+
+ final Code code = new Code();
+ code.setCode( "ABC_1" );
+ Mockito.doReturn( Collections.singletonList( code ) ).when( codeRepository ).findAllBySystemCodes( Mockito.eq( Collections.singleton( "http://test.com/2|TEST_1" ) ) );
+
+ Assert.assertEquals( "ABC_1", utils.getMappedCode( "TEST_1", "ORGANIZATION" ) );
+ }
+
+ @Test
+ public void getMappedCodeFoundMapped()
+ {
+ final ResourceSystem resourceSystem = new ResourceSystem( FhirResourceType.ORGANIZATION, "http://test.com/2", null, null, null );
+
+ Mockito.doReturn( scriptExecution ).when( scriptExecutionContext ).getScriptExecution();
+ Mockito.doReturn( variables ).when( scriptExecution ).getVariables();
+ Mockito.doReturn( context ).when( variables ).get( Mockito.eq( "context" ) );
+ Mockito.doReturn( request ).when( context ).getFhirRequest();
+ Mockito.doReturn( Optional.of( resourceSystem ) ).when( request ).getOptionalResourceSystem( FhirResourceType.ORGANIZATION );
+
+ final Code code = new Code();
+ code.setCode( "ABC_1" );
+ code.setMappedCode( "CBA_1" );
+ Mockito.doReturn( Collections.singletonList( code ) ).when( codeRepository ).findAllBySystemCodes( Mockito.eq( Collections.singleton( "http://test.com/2|TEST_1" ) ) );
+
+ Assert.assertEquals( "CBA_1", utils.getMappedCode( "TEST_1", "ORGANIZATION" ) );
+ }
+}
\ No newline at end of file
diff --git a/fhir-r4/src/test/java/org/dhis2/fhir/adapter/fhir/transform/fhir/impl/util/r4/R4ContactPointFhirToDhisTransformerUtilsTest.java b/fhir-r4/src/test/java/org/dhis2/fhir/adapter/fhir/transform/fhir/impl/util/r4/R4ContactPointFhirToDhisTransformerUtilsTest.java
new file mode 100644
index 00000000..d6af8609
--- /dev/null
+++ b/fhir-r4/src/test/java/org/dhis2/fhir/adapter/fhir/transform/fhir/impl/util/r4/R4ContactPointFhirToDhisTransformerUtilsTest.java
@@ -0,0 +1,153 @@
+package org.dhis2.fhir.adapter.fhir.transform.fhir.impl.util.r4;
+
+/*
+ * Copyright (c) 2004-2019, University of Oslo
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * Neither the name of the HISP project nor the names of its contributors may
+ * be used to endorse or promote products derived from this software without
+ * specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
+ * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+import org.dhis2.fhir.adapter.fhir.script.ScriptExecutionContext;
+import org.hl7.fhir.r4.model.ContactPoint;
+import org.hl7.fhir.r4.model.Period;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.mockito.InjectMocks;
+import org.mockito.Mock;
+import org.mockito.junit.MockitoJUnit;
+import org.mockito.junit.MockitoRule;
+
+import java.time.ZonedDateTime;
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.List;
+
+/**
+ * Unit tests for {@link R4ContactPointFhirToDhisTransformerUtils}.
+ *
+ * @author volsch
+ */
+public class R4ContactPointFhirToDhisTransformerUtilsTest
+{
+ @Mock
+ private ScriptExecutionContext scriptExecutionContext;
+
+ @InjectMocks
+ private R4DateTimeFhirToDhisTransformerUtils dateTimeFhirToDhisTransformerUtils;
+
+ private R4ContactPointFhirToDhisTransformerUtils utils;
+
+ @Rule
+ public MockitoRule rule = MockitoJUnit.rule();
+
+ @Before
+ public void before()
+ {
+ utils = new R4ContactPointFhirToDhisTransformerUtils( scriptExecutionContext, dateTimeFhirToDhisTransformerUtils );
+ }
+
+ @Test
+ public void primaryExpired()
+ {
+ final List contactPoints = new ArrayList<>();
+ contactPoints.add( new ContactPoint().setRank( 1 ).setUse( ContactPoint.ContactPointUse.HOME ).setSystem( ContactPoint.ContactPointSystem.PHONE ).setValue( "4711" )
+ .setPeriod( new Period().setEnd( Date.from( ZonedDateTime.now().minusDays( 1 ).toInstant() ) ) ) );
+ contactPoints.add( new ContactPoint().setRank( 999 ).setUse( ContactPoint.ContactPointUse.OLD ).setSystem( ContactPoint.ContactPointSystem.PHONE ).setValue( "4712" ) );
+ Assert.assertEquals( contactPoints.get( 1 ).getValue(), utils.getPhoneContactPointValue( contactPoints ) );
+ }
+
+ @Test
+ public void primaryEmail()
+ {
+ final List contactPoints = new ArrayList<>();
+ contactPoints.add( new ContactPoint().setRank( 999 ).setUse( ContactPoint.ContactPointUse.OLD ).setSystem( ContactPoint.ContactPointSystem.PHONE ).setValue( "4712" ) );
+ contactPoints.add( new ContactPoint().setRank( 1 ).setUse( ContactPoint.ContactPointUse.HOME ).setSystem( ContactPoint.ContactPointSystem.EMAIL ).setValue( "4711" ) );
+ Assert.assertEquals( contactPoints.get( 0 ).getValue(), utils.getPhoneContactPointValue( contactPoints ) );
+ }
+
+ @Test
+ public void primaryRank()
+ {
+ final List contactPoints = new ArrayList<>();
+ contactPoints.add( new ContactPoint().setRank( 1 ).setUse( ContactPoint.ContactPointUse.HOME ).setSystem( ContactPoint.ContactPointSystem.PHONE ).setValue( "4712" ) );
+ contactPoints.add( new ContactPoint().setRank( 2 ).setUse( ContactPoint.ContactPointUse.HOME ).setSystem( ContactPoint.ContactPointSystem.PHONE ).setValue( "4711" ) );
+ Assert.assertEquals( contactPoints.get( 0 ).getValue(), utils.getPhoneContactPointValue( contactPoints ) );
+ }
+
+ @Test
+ public void primaryTemp()
+ {
+ final List contactPoints = new ArrayList<>();
+ contactPoints.add( new ContactPoint().setRank( 1 ).setUse( ContactPoint.ContactPointUse.OLD ).setSystem( ContactPoint.ContactPointSystem.PHONE ).setValue( "4711" ) );
+ contactPoints.add( new ContactPoint().setRank( 1 ).setUse( ContactPoint.ContactPointUse.TEMP ).setSystem( ContactPoint.ContactPointSystem.PHONE ).setValue( "4712" ) );
+ Assert.assertEquals( contactPoints.get( 1 ).getValue(), utils.getPhoneContactPointValue( contactPoints ) );
+ }
+
+ @Test
+ public void primaryWork()
+ {
+ final List contactPoints = new ArrayList<>();
+ contactPoints.add( new ContactPoint().setRank( 1 ).setUse( ContactPoint.ContactPointUse.TEMP ).setSystem( ContactPoint.ContactPointSystem.PHONE ).setValue( "4711" ) );
+ contactPoints.add( new ContactPoint().setRank( 1 ).setUse( ContactPoint.ContactPointUse.WORK ).setSystem( ContactPoint.ContactPointSystem.PHONE ).setValue( "4712" ) );
+ Assert.assertEquals( contactPoints.get( 1 ).getValue(), utils.getPhoneContactPointValue( contactPoints ) );
+ }
+
+ @Test
+ public void primaryMobile()
+ {
+ final List contactPoints = new ArrayList<>();
+ contactPoints.add( new ContactPoint().setRank( 1 ).setUse( ContactPoint.ContactPointUse.TEMP ).setSystem( ContactPoint.ContactPointSystem.PHONE ).setValue( "4711" ) );
+ contactPoints.add( new ContactPoint().setRank( 1 ).setUse( ContactPoint.ContactPointUse.MOBILE ).setSystem( ContactPoint.ContactPointSystem.PHONE ).setValue( "4712" ) );
+ Assert.assertEquals( contactPoints.get( 1 ).getValue(), utils.getPhoneContactPointValue( contactPoints ) );
+ }
+
+ @Test
+ public void primaryHome()
+ {
+ final List contactPoints = new ArrayList<>();
+ contactPoints.add( new ContactPoint().setRank( 1 ).setUse( ContactPoint.ContactPointUse.TEMP ).setSystem( ContactPoint.ContactPointSystem.PHONE ).setValue( "4711" ) );
+ contactPoints.add( new ContactPoint().setRank( 1 ).setUse( ContactPoint.ContactPointUse.HOME ).setSystem( ContactPoint.ContactPointSystem.PHONE ).setValue( "4712" ) );
+ Assert.assertEquals( contactPoints.get( 1 ).getValue(), utils.getPhoneContactPointValue( contactPoints ) );
+ }
+
+ @Test
+ public void primaryNull()
+ {
+ final List contactPoints = new ArrayList<>();
+ contactPoints.add( new ContactPoint().setRank( 1 ).setUse( ContactPoint.ContactPointUse.NULL ).setSystem( ContactPoint.ContactPointSystem.PHONE ).setValue( "4711" ) );
+ contactPoints.add( new ContactPoint().setRank( 1 ).setUse( ContactPoint.ContactPointUse.TEMP ).setSystem( ContactPoint.ContactPointSystem.PHONE ).setValue( "4712" ) );
+ Assert.assertEquals( contactPoints.get( 0 ).getValue(), utils.getPhoneContactPointValue( contactPoints ) );
+ }
+
+ @Test
+ public void primaryOther()
+ {
+ final List contactPoints = new ArrayList<>();
+ contactPoints.add( new ContactPoint().setRank( 1 ).setSystem( ContactPoint.ContactPointSystem.PHONE ).setValue( "4711" ) );
+ contactPoints.add( new ContactPoint().setRank( 1 ).setUse( ContactPoint.ContactPointUse.TEMP ).setSystem( ContactPoint.ContactPointSystem.PHONE ).setValue( "4712" ) );
+ Assert.assertEquals( contactPoints.get( 0 ).getValue(), utils.getPhoneContactPointValue( contactPoints ) );
+ }
+}
\ No newline at end of file
diff --git a/fhir-r4/src/test/java/org/dhis2/fhir/adapter/fhir/transform/fhir/impl/util/r4/R4DateTimeFhirToDhisTransformerUtilsTest.java b/fhir-r4/src/test/java/org/dhis2/fhir/adapter/fhir/transform/fhir/impl/util/r4/R4DateTimeFhirToDhisTransformerUtilsTest.java
new file mode 100644
index 00000000..05897031
--- /dev/null
+++ b/fhir-r4/src/test/java/org/dhis2/fhir/adapter/fhir/transform/fhir/impl/util/r4/R4DateTimeFhirToDhisTransformerUtilsTest.java
@@ -0,0 +1,256 @@
+package org.dhis2.fhir.adapter.fhir.transform.fhir.impl.util.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.model.api.TemporalPrecisionEnum;
+import org.dhis2.fhir.adapter.fhir.script.ScriptExecutionContext;
+import org.dhis2.fhir.adapter.model.DateUnit;
+import org.hl7.fhir.r4.model.DateTimeType;
+import org.hl7.fhir.r4.model.Period;
+import org.junit.Assert;
+import org.junit.Rule;
+import org.junit.Test;
+import org.mockito.InjectMocks;
+import org.mockito.Mock;
+import org.mockito.junit.MockitoJUnit;
+import org.mockito.junit.MockitoRule;
+
+import java.time.LocalDate;
+import java.time.ZonedDateTime;
+import java.util.Calendar;
+import java.util.Date;
+import java.util.GregorianCalendar;
+
+/**
+ * Unit tests for {@link R4DateTimeFhirToDhisTransformerUtils}.
+ *
+ * @author volsch
+ */
+public class R4DateTimeFhirToDhisTransformerUtilsTest
+{
+ @Mock
+ private ScriptExecutionContext scriptExecutionContext;
+
+ @InjectMocks
+ private R4DateTimeFhirToDhisTransformerUtils utils;
+
+ @Rule
+ public MockitoRule rule = MockitoJUnit.rule();
+
+ @Test
+ public void hasDayPrecisionDay()
+ {
+ Assert.assertTrue( utils.hasDayPrecision( new DateTimeType( new Date(), TemporalPrecisionEnum.DAY ) ) );
+ }
+
+ @Test
+ public void hasDayPrecisionSecond()
+ {
+ Assert.assertTrue( utils.hasDayPrecision( new DateTimeType( new Date(), TemporalPrecisionEnum.SECOND ) ) );
+ }
+
+ @Test
+ public void hasDayPrecisionNull()
+ {
+ Assert.assertTrue( utils.hasDayPrecision( null ) );
+ }
+
+ @Test
+ public void hasDayPrecisionMonth()
+ {
+ Assert.assertFalse( utils.hasDayPrecision( new DateTimeType( new Date(), TemporalPrecisionEnum.MONTH ) ) );
+ }
+
+ @Test
+ public void isValidNowNull()
+ {
+ Assert.assertTrue( utils.isValidNow( null ) );
+ }
+
+ @Test
+ public void isValidNowStart()
+ {
+ Assert.assertTrue( utils.isValidNow( new Period().setStart( Date.from( ZonedDateTime.now().plusDays( 2 ).toInstant() ) ) ) );
+ }
+
+ @Test
+ public void isValidNowEndFuture()
+ {
+ Assert.assertTrue( utils.isValidNow( new Period().setEnd( Date.from( ZonedDateTime.now().plusDays( 2 ).toInstant() ) ) ) );
+ }
+
+ @Test
+ public void isValidNowEndPast()
+ {
+ Assert.assertFalse( utils.isValidNow( new Period().setEnd( Date.from( ZonedDateTime.now().minusDays( 2 ).toInstant() ) ) ) );
+ }
+
+ @Test
+ public void getPreciseDateNull()
+ {
+ Assert.assertNull( utils.getPreciseDate( null ) );
+ }
+
+ @Test
+ public void getPreciseDateInternNull()
+ {
+ Assert.assertNull( utils.getPreciseDate( new DateTimeType( (Date) null ) ) );
+ }
+
+ @Test
+ public void getPreciseDateMonth()
+ {
+ Assert.assertNull( utils.getPreciseDate( new DateTimeType( new Date(), TemporalPrecisionEnum.MONTH ) ) );
+ }
+
+ @Test
+ public void getPreciseDateMinute()
+ {
+ Assert.assertNotNull( utils.getPreciseDate( new DateTimeType( new Date(), TemporalPrecisionEnum.SECOND ) ) );
+ }
+
+
+ @Test
+ public void getPrecisePastDateNull()
+ {
+ Assert.assertNull( utils.getPrecisePastDate( null ) );
+ }
+
+ @Test
+ public void getPrecisePastDateInternNull()
+ {
+ Assert.assertNull( utils.getPrecisePastDate( new DateTimeType( (Date) null ) ) );
+ }
+
+ @Test
+ public void getPrecisePastDateMonth()
+ {
+ Assert.assertNull( utils.getPrecisePastDate( new DateTimeType( new Date( System.currentTimeMillis() - 1000 ), TemporalPrecisionEnum.MONTH ) ) );
+ }
+
+ @Test
+ public void getPrecisePastDateMinute()
+ {
+ Assert.assertNotNull( utils.getPrecisePastDate( new DateTimeType( new Date( System.currentTimeMillis() - 1000 ), TemporalPrecisionEnum.SECOND ) ) );
+ }
+
+ @Test
+ public void getPrecisePastDateFuture()
+ {
+ Assert.assertNull( utils.getPrecisePastDate( new DateTimeType( new Date( System.currentTimeMillis() + 20_000 ), TemporalPrecisionEnum.SECOND ) ) );
+ }
+
+ @Test
+ public void getAgeRelative()
+ {
+ final GregorianCalendar cal1 = new GregorianCalendar( 2017, Calendar.JUNE, 8, 15, 16, 17 );
+ final GregorianCalendar cal2 = new GregorianCalendar( 2017, Calendar.JUNE, 6, 14, 16, 17 );
+ Assert.assertEquals( (Integer) 2, utils.getAge( new DateTimeType( cal1.getTime() ), cal2.getTime(), DateUnit.DAYS ) );
+ }
+
+ @Test
+ public void getAgeToday()
+ {
+ Assert.assertEquals( (Integer) 2, utils.getAge( LocalDate.now().minusDays( 2 ), DateUnit.DAYS ) );
+ }
+
+ @Test
+ public void getAgeNull()
+ {
+ Assert.assertNull( utils.getAge( new Date(), null, DateUnit.DAYS ) );
+ }
+
+ @Test
+ public void getYoungerThanRelative()
+ {
+ final GregorianCalendar cal1 = new GregorianCalendar( 2017, Calendar.JUNE, 8, 15, 16, 17 );
+ final GregorianCalendar cal2 = new GregorianCalendar( 2017, Calendar.JUNE, 6, 14, 16, 17 );
+ Assert.assertTrue( utils.isYoungerThan( new DateTimeType( cal1.getTime() ), cal2.getTime(), 3, DateUnit.DAYS ) );
+ }
+
+ @Test
+ public void getNotYoungerThanRelative()
+ {
+ final GregorianCalendar cal1 = new GregorianCalendar( 2017, Calendar.JUNE, 8, 15, 16, 17 );
+ final GregorianCalendar cal2 = new GregorianCalendar( 2017, Calendar.JUNE, 6, 14, 16, 17 );
+ Assert.assertFalse( utils.isYoungerThan( new DateTimeType( cal1.getTime() ), cal2.getTime(), 2, DateUnit.DAYS ) );
+ }
+
+ @Test
+ public void getYoungerThanToday()
+ {
+ Assert.assertTrue( utils.isYoungerThan( LocalDate.now().minusDays( 2 ), 3, DateUnit.DAYS ) );
+ }
+
+ @Test
+ public void getNotYoungerThanToday()
+ {
+ Assert.assertFalse( utils.isYoungerThan( LocalDate.now().minusDays( 2 ), 2, DateUnit.DAYS ) );
+ }
+
+ @Test
+ public void getAgeYoungerThanNull()
+ {
+ Assert.assertFalse( utils.isYoungerThan( new Date(), null, 3, DateUnit.DAYS ) );
+ }
+
+ @Test
+ public void getOlderThanRelative()
+ {
+ final GregorianCalendar cal1 = new GregorianCalendar( 2017, Calendar.JUNE, 8, 15, 16, 17 );
+ final GregorianCalendar cal2 = new GregorianCalendar( 2017, Calendar.JUNE, 6, 14, 16, 17 );
+ Assert.assertTrue( utils.isOlderThan( new DateTimeType( cal1.getTime() ), cal2.getTime(), 1, DateUnit.DAYS ) );
+ }
+
+ @Test
+ public void getNotOlderThanRelative()
+ {
+ final GregorianCalendar cal1 = new GregorianCalendar( 2017, Calendar.JUNE, 8, 15, 16, 17 );
+ final GregorianCalendar cal2 = new GregorianCalendar( 2017, Calendar.JUNE, 6, 14, 16, 17 );
+ Assert.assertFalse( utils.isOlderThan( new DateTimeType( cal1.getTime() ), cal2.getTime(), 2, DateUnit.DAYS ) );
+ }
+
+ @Test
+ public void getOlderThanToday()
+ {
+ Assert.assertTrue( utils.isOlderThan( LocalDate.now().minusDays( 2 ), 1, DateUnit.DAYS ) );
+ }
+
+ @Test
+ public void getNotOlderThanToday()
+ {
+ Assert.assertFalse( utils.isOlderThan( LocalDate.now().minusDays( 2 ), 2, DateUnit.DAYS ) );
+ }
+
+ @Test
+ public void getAgeOlderThanNull()
+ {
+ Assert.assertFalse( utils.isOlderThan( new Date(), null, 3, DateUnit.DAYS ) );
+ }
+}
\ No newline at end of file
diff --git a/fhir-r4/src/test/java/org/dhis2/fhir/adapter/fhir/transform/fhir/impl/util/r4/R4GeoFhirToDhisTransformerUtilsTest.java b/fhir-r4/src/test/java/org/dhis2/fhir/adapter/fhir/transform/fhir/impl/util/r4/R4GeoFhirToDhisTransformerUtilsTest.java
new file mode 100644
index 00000000..63d974a0
--- /dev/null
+++ b/fhir-r4/src/test/java/org/dhis2/fhir/adapter/fhir/transform/fhir/impl/util/r4/R4GeoFhirToDhisTransformerUtilsTest.java
@@ -0,0 +1,85 @@
+package org.dhis2.fhir.adapter.fhir.transform.fhir.impl.util.r4;
+
+/*
+ * Copyright (c) 2004-2019, University of Oslo
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * Neither the name of the HISP project nor the names of its contributors may
+ * be used to endorse or promote products derived from this software without
+ * specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
+ * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+import org.dhis2.fhir.adapter.fhir.script.ScriptExecutionContext;
+import org.dhis2.fhir.adapter.geo.Location;
+import org.hl7.fhir.r4.model.Address;
+import org.hl7.fhir.r4.model.DecimalType;
+import org.hl7.fhir.r4.model.Extension;
+import org.junit.Assert;
+import org.junit.Rule;
+import org.junit.Test;
+import org.mockito.InjectMocks;
+import org.mockito.Mock;
+import org.mockito.junit.MockitoJUnit;
+import org.mockito.junit.MockitoRule;
+
+/**
+ * Unit tests for {@link R4GeoFhirToDhisTransformerUtils}.
+ *
+ * @author volsch
+ */
+public class R4GeoFhirToDhisTransformerUtilsTest
+{
+ @Mock
+ private ScriptExecutionContext scriptExecutionContext;
+
+ @InjectMocks
+ private R4GeoFhirToDhisTransformerUtils utils;
+
+ @Rule
+ public MockitoRule rule = MockitoJUnit.rule();
+
+ @Test
+ public void getLocation()
+ {
+ final Address address = new Address();
+ address.addExtension()
+ .setUrl( "http://hl7.org/fhir/StructureDefinition/geolocation" )
+ .addExtension( new Extension()
+ .setUrl( "latitude" )
+ .setValue( new DecimalType( 8.4665341 ) ) )
+ .addExtension( new Extension()
+ .setUrl( "longitude" )
+ .setValue( new DecimalType( -13.262743 ) ) );
+ final Location location = utils.getLocation( address );
+ Assert.assertNotNull( location );
+ Assert.assertEquals( 8.4665341, location.getLatitude(), 0 );
+ Assert.assertEquals( -13.262743, location.getLongitude(), 0 );
+ }
+
+ @Test
+ public void getNoLocation()
+ {
+ final Address address = new Address();
+ final Location location = utils.getLocation( address );
+ Assert.assertNull( location );
+ }
+}
\ No newline at end of file
diff --git a/fhir-r4/src/test/java/org/dhis2/fhir/adapter/fhir/transform/fhir/impl/util/r4/R4HumanNameFhirToDhisTransformerUtilsTest.java b/fhir-r4/src/test/java/org/dhis2/fhir/adapter/fhir/transform/fhir/impl/util/r4/R4HumanNameFhirToDhisTransformerUtilsTest.java
new file mode 100644
index 00000000..4d978ba0
--- /dev/null
+++ b/fhir-r4/src/test/java/org/dhis2/fhir/adapter/fhir/transform/fhir/impl/util/r4/R4HumanNameFhirToDhisTransformerUtilsTest.java
@@ -0,0 +1,151 @@
+package org.dhis2.fhir.adapter.fhir.transform.fhir.impl.util.r4;
+
+/*
+ * Copyright (c) 2004-2019, University of Oslo
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * Neither the name of the HISP project nor the names of its contributors may
+ * be used to endorse or promote products derived from this software without
+ * specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
+ * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+import org.dhis2.fhir.adapter.fhir.script.ScriptExecutionContext;
+import org.hl7.fhir.r4.model.HumanName;
+import org.hl7.fhir.r4.model.Period;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.mockito.InjectMocks;
+import org.mockito.Mock;
+import org.mockito.junit.MockitoJUnit;
+import org.mockito.junit.MockitoRule;
+
+import java.time.ZonedDateTime;
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.List;
+
+/**
+ * Unit tests for {@link R4HumanNameFhirToDhisTransformerUtils}.
+ *
+ * @author volsch
+ */
+public class R4HumanNameFhirToDhisTransformerUtilsTest
+{
+ @Mock
+ private ScriptExecutionContext scriptExecutionContext;
+
+ @InjectMocks
+ private R4DateTimeFhirToDhisTransformerUtils dateTimeFhirToDhisTransformerUtils;
+
+ private R4HumanNameFhirToDhisTransformerUtils utils;
+
+ @Rule
+ public MockitoRule rule = MockitoJUnit.rule();
+
+ @Before
+ public void before()
+ {
+ utils = new R4HumanNameFhirToDhisTransformerUtils( scriptExecutionContext, dateTimeFhirToDhisTransformerUtils );
+ }
+
+ @Test
+ public void primaryExpired()
+ {
+ final List humanNames = new ArrayList<>();
+ humanNames.add( new HumanName().setUse( HumanName.NameUse.OFFICIAL )
+ .setPeriod( new Period().setEnd( Date.from( ZonedDateTime.now().minusDays( 1 ).toInstant() ) ) ) );
+ humanNames.add( new HumanName().setUse( HumanName.NameUse.OLD ) );
+ Assert.assertEquals( humanNames.get( 1 ), utils.getPrimaryName( humanNames ) );
+ }
+
+ @Test
+ public void primaryMaiden()
+ {
+ final List humanNames = new ArrayList<>();
+ humanNames.add( new HumanName().setUse( HumanName.NameUse.MAIDEN ) );
+ humanNames.add( new HumanName().setUse( HumanName.NameUse.OLD ) );
+ Assert.assertEquals( humanNames.get( 0 ), utils.getPrimaryName( humanNames ) );
+ }
+
+ @Test
+ public void primaryTemp()
+ {
+ final List humanNames = new ArrayList<>();
+ humanNames.add( new HumanName().setUse( HumanName.NameUse.TEMP ) );
+ humanNames.add( new HumanName().setUse( HumanName.NameUse.MAIDEN ) );
+ Assert.assertEquals( humanNames.get( 0 ), utils.getPrimaryName( humanNames ) );
+ }
+
+ @Test
+ public void primaryNickname()
+ {
+ final List humanNames = new ArrayList<>();
+ humanNames.add( new HumanName().setUse( HumanName.NameUse.NICKNAME ) );
+ humanNames.add( new HumanName().setUse( HumanName.NameUse.TEMP ) );
+ Assert.assertEquals( humanNames.get( 0 ), utils.getPrimaryName( humanNames ) );
+ }
+
+ @Test
+ public void primaryAnonymous()
+ {
+ final List humanNames = new ArrayList<>();
+ humanNames.add( new HumanName().setUse( HumanName.NameUse.ANONYMOUS ) );
+ humanNames.add( new HumanName().setUse( HumanName.NameUse.NICKNAME ) );
+ Assert.assertEquals( humanNames.get( 0 ), utils.getPrimaryName( humanNames ) );
+ }
+
+ @Test
+ public void primaryUsual()
+ {
+ final List humanNames = new ArrayList<>();
+ humanNames.add( new HumanName().setUse( HumanName.NameUse.USUAL ) );
+ humanNames.add( new HumanName().setUse( HumanName.NameUse.ANONYMOUS ) );
+ Assert.assertEquals( humanNames.get( 0 ), utils.getPrimaryName( humanNames ) );
+ }
+
+ @Test
+ public void primaryNull()
+ {
+ final List humanNames = new ArrayList<>();
+ humanNames.add( new HumanName().setUse( HumanName.NameUse.NULL ) );
+ humanNames.add( new HumanName().setUse( HumanName.NameUse.ANONYMOUS ) );
+ Assert.assertEquals( humanNames.get( 0 ), utils.getPrimaryName( humanNames ) );
+ }
+
+ @Test
+ public void primaryOther()
+ {
+ final List humanNames = new ArrayList<>();
+ humanNames.add( new HumanName() );
+ humanNames.add( new HumanName().setUse( HumanName.NameUse.ANONYMOUS ) );
+ Assert.assertEquals( humanNames.get( 0 ), utils.getPrimaryName( humanNames ) );
+ }
+
+ @Test
+ public void primaryNothing()
+ {
+ final List humanNames = new ArrayList<>();
+ Assert.assertNotNull( utils.getPrimaryName( humanNames ) );
+ }
+}
\ No newline at end of file
diff --git a/fhir-r4/src/test/java/org/dhis2/fhir/adapter/fhir/transform/fhir/impl/util/r4/R4IdentifierFhirToDhisTransformerUtilsTest.java b/fhir-r4/src/test/java/org/dhis2/fhir/adapter/fhir/transform/fhir/impl/util/r4/R4IdentifierFhirToDhisTransformerUtilsTest.java
new file mode 100644
index 00000000..99d7ee8e
--- /dev/null
+++ b/fhir-r4/src/test/java/org/dhis2/fhir/adapter/fhir/transform/fhir/impl/util/r4/R4IdentifierFhirToDhisTransformerUtilsTest.java
@@ -0,0 +1,163 @@
+package org.dhis2.fhir.adapter.fhir.transform.fhir.impl.util.r4;
+
+/*
+ * Copyright (c) 2004-2019, University of Oslo
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * Neither the name of the HISP project nor the names of its contributors may
+ * be used to endorse or promote products derived from this software without
+ * specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
+ * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+import org.dhis2.fhir.adapter.fhir.metadata.model.FhirResourceType;
+import org.dhis2.fhir.adapter.fhir.script.ScriptExecution;
+import org.dhis2.fhir.adapter.fhir.script.ScriptExecutionContext;
+import org.dhis2.fhir.adapter.fhir.transform.fhir.FhirToDhisTransformerContext;
+import org.dhis2.fhir.adapter.fhir.transform.fhir.impl.util.ReferenceFhirToDhisTransformerUtils;
+import org.dhis2.fhir.adapter.fhir.transform.fhir.model.FhirRequest;
+import org.dhis2.fhir.adapter.fhir.transform.fhir.model.ResourceSystem;
+import org.dhis2.fhir.adapter.fhir.transform.util.FhirIdentifierUtils;
+import org.hl7.fhir.r4.model.IdType;
+import org.hl7.fhir.r4.model.Patient;
+import org.hl7.fhir.r4.model.Reference;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.mockito.Mock;
+import org.mockito.Mockito;
+import org.mockito.junit.MockitoJUnit;
+import org.mockito.junit.MockitoRule;
+
+import java.util.Map;
+import java.util.Optional;
+
+/**
+ * Unit tests for {@link R4IdentifierFhirToDhisTransformerUtils}.
+ *
+ * @author volsch
+ */
+public class R4IdentifierFhirToDhisTransformerUtilsTest
+{
+ @Mock
+ private ScriptExecutionContext scriptExecutionContext;
+
+ @Mock
+ private ReferenceFhirToDhisTransformerUtils referenceFhirToDhisTransformerUtils;
+
+ @Mock
+ private FhirToDhisTransformerContext context;
+
+ @Mock
+ private FhirRequest request;
+
+ @Mock
+ private ScriptExecution scriptExecution;
+
+ @Mock
+ private Map variables;
+
+ private final FhirIdentifierUtils fhirIdentifierUtils = new FhirIdentifierUtils();
+
+ private R4IdentifierFhirToDhisTransformerUtils utils;
+
+ @Rule
+ public MockitoRule rule = MockitoJUnit.rule();
+
+ @Before
+ public void before()
+ {
+ utils = new R4IdentifierFhirToDhisTransformerUtils( scriptExecutionContext, fhirIdentifierUtils, referenceFhirToDhisTransformerUtils );
+ }
+
+ @Test
+ public void getReferenceIdentifierWithSystemNull()
+ {
+ Assert.assertNull( utils.getReferenceIdentifier( null, "PATIENT", "http://test.com" ) );
+ }
+
+ @Test
+ public void getReferenceIdentifierWithSystem()
+ {
+ final Patient patient = new Patient();
+ patient.addIdentifier().setSystem( "http://test.com" ).setValue( "ABC_123" );
+ final Reference reference = new Reference( new IdType( "Patient", "5671" ) );
+ Mockito.doAnswer( invocation -> {
+ ((Reference) invocation.getArgument( 0 )).setResource( patient );
+ return null;
+ } ).when( referenceFhirToDhisTransformerUtils ).initReference( Mockito.same( reference ), Mockito.eq( "PATIENT" ) );
+ Assert.assertEquals( "ABC_123", utils.getReferenceIdentifier( reference, "PATIENT", "http://test.com" ) );
+ Mockito.verify( referenceFhirToDhisTransformerUtils ).initReference( Mockito.same( reference ), Mockito.eq( "PATIENT" ) );
+ }
+
+ @Test
+ public void getReferenceIdentifierWithDifferentSystem()
+ {
+ final Patient patient = new Patient();
+ patient.addIdentifier().setSystem( "http://test.com" ).setValue( "ABC_123" );
+ final Reference reference = new Reference( new IdType( "Patient", "5671" ) );
+ Mockito.doAnswer( invocation -> {
+ ((Reference) invocation.getArgument( 0 )).setResource( patient );
+ return null;
+ } ).when( referenceFhirToDhisTransformerUtils ).initReference( Mockito.same( reference ), Mockito.eq( "PATIENT" ) );
+ Assert.assertNull( utils.getReferenceIdentifier( reference, "PATIENT", "http://test2.com" ) );
+ Mockito.verify( referenceFhirToDhisTransformerUtils ).initReference( Mockito.same( reference ), Mockito.eq( "PATIENT" ) );
+ }
+
+ @Test
+ public void getReferenceIdentifierWithDefaultSystem()
+ {
+ final Patient patient = new Patient();
+ patient.addIdentifier().setSystem( "http://test.com" ).setValue( "ABC_123" );
+
+ final ResourceSystem resourceSystem = new ResourceSystem( FhirResourceType.PATIENT, "http://test.com", null, null, null );
+ Mockito.doReturn( scriptExecution ).when( scriptExecutionContext ).getScriptExecution();
+ Mockito.doReturn( variables ).when( scriptExecution ).getVariables();
+ Mockito.doReturn( context ).when( variables ).get( Mockito.eq( "context" ) );
+ Mockito.doReturn( request ).when( context ).getFhirRequest();
+ Mockito.doReturn( Optional.of( resourceSystem ) ).when( request ).getOptionalResourceSystem( FhirResourceType.PATIENT );
+
+ final Reference reference = new Reference( new IdType( "Patient", "5671" ) );
+ Mockito.doAnswer( invocation -> {
+ ((Reference) invocation.getArgument( 0 )).setResource( patient );
+ return null;
+ } ).when( referenceFhirToDhisTransformerUtils ).initReference( Mockito.same( reference ), Mockito.eq( "PATIENT" ) );
+ Assert.assertEquals( "ABC_123", utils.getReferenceIdentifier( reference, "PATIENT" ) );
+ Mockito.verify( referenceFhirToDhisTransformerUtils ).initReference( Mockito.same( reference ), Mockito.eq( "PATIENT" ) );
+ }
+
+ @Test
+ public void getResourceIdentifierWithDefaultSystem()
+ {
+ final Patient patient = new Patient();
+ patient.addIdentifier().setSystem( "http://test.com" ).setValue( "ABC_123" );
+
+ final ResourceSystem resourceSystem = new ResourceSystem( FhirResourceType.PATIENT, "http://test.com", null, null, null );
+ Mockito.doReturn( scriptExecution ).when( scriptExecutionContext ).getScriptExecution();
+ Mockito.doReturn( variables ).when( scriptExecution ).getVariables();
+ Mockito.doReturn( context ).when( variables ).get( Mockito.eq( "context" ) );
+ Mockito.doReturn( request ).when( context ).getFhirRequest();
+ Mockito.doReturn( Optional.of( resourceSystem ) ).when( request ).getOptionalResourceSystem( FhirResourceType.PATIENT );
+
+ Assert.assertEquals( "ABC_123", utils.getResourceIdentifier( patient, "PATIENT" ) );
+ }
+}
\ No newline at end of file
diff --git a/fhir-r4/src/test/java/org/dhis2/fhir/adapter/fhir/transform/fhir/impl/util/r4/R4OrganizationFhirToDhisTransformerUtilsTest.java b/fhir-r4/src/test/java/org/dhis2/fhir/adapter/fhir/transform/fhir/impl/util/r4/R4OrganizationFhirToDhisTransformerUtilsTest.java
new file mode 100644
index 00000000..8ec5cf60
--- /dev/null
+++ b/fhir-r4/src/test/java/org/dhis2/fhir/adapter/fhir/transform/fhir/impl/util/r4/R4OrganizationFhirToDhisTransformerUtilsTest.java
@@ -0,0 +1,206 @@
+package org.dhis2.fhir.adapter.fhir.transform.fhir.impl.util.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.context.FhirContext;
+import org.dhis2.fhir.adapter.dhis.model.Reference;
+import org.dhis2.fhir.adapter.dhis.model.ReferenceType;
+import org.dhis2.fhir.adapter.dhis.orgunit.OrganizationUnit;
+import org.dhis2.fhir.adapter.dhis.orgunit.OrganizationUnitService;
+import org.dhis2.fhir.adapter.fhir.metadata.model.FhirResourceType;
+import org.dhis2.fhir.adapter.fhir.metadata.model.FhirServer;
+import org.dhis2.fhir.adapter.fhir.metadata.model.FhirServerResource;
+import org.dhis2.fhir.adapter.fhir.metadata.model.SubscriptionFhirEndpoint;
+import org.dhis2.fhir.adapter.fhir.metadata.repository.FhirServerResourceRepository;
+import org.dhis2.fhir.adapter.fhir.model.FhirVersion;
+import org.dhis2.fhir.adapter.fhir.repository.FhirResourceRepository;
+import org.dhis2.fhir.adapter.fhir.repository.HierarchicallyFhirResourceRepository;
+import org.dhis2.fhir.adapter.fhir.script.ScriptExecution;
+import org.dhis2.fhir.adapter.fhir.script.ScriptExecutionContext;
+import org.dhis2.fhir.adapter.fhir.transform.fhir.FhirToDhisTransformerContext;
+import org.dhis2.fhir.adapter.fhir.transform.fhir.model.FhirRequest;
+import org.dhis2.fhir.adapter.fhir.transform.fhir.model.ResourceSystem;
+import org.hl7.fhir.instance.model.api.IBaseReference;
+import org.hl7.fhir.instance.model.api.IBaseResource;
+import org.hl7.fhir.r4.model.Bundle;
+import org.hl7.fhir.r4.model.IdType;
+import org.hl7.fhir.r4.model.Organization;
+import org.junit.Assert;
+import org.junit.Rule;
+import org.junit.Test;
+import org.mockito.InjectMocks;
+import org.mockito.Mock;
+import org.mockito.Mockito;
+import org.mockito.junit.MockitoJUnit;
+import org.mockito.junit.MockitoRule;
+
+import java.util.List;
+import java.util.Map;
+import java.util.Optional;
+import java.util.UUID;
+import java.util.function.Function;
+
+/**
+ * Unit tests for {@link R4OrganizationFhirToDhisTransformerUtils}.
+ *
+ * @author volsch
+ */
+public class R4OrganizationFhirToDhisTransformerUtilsTest
+{
+ @Mock
+ private ScriptExecutionContext scriptExecutionContext;
+
+ @Mock
+ private OrganizationUnitService organizationUnitService;
+
+ @Mock
+ private FhirServerResourceRepository fhirServerResourceRepository;
+
+ @Mock
+ private FhirResourceRepository fhirResourceRepository;
+
+ @Mock
+ private HierarchicallyFhirResourceRepository hierarchicallyFhirResourceRepository;
+
+ @Mock
+ private FhirToDhisTransformerContext context;
+
+ @Mock
+ private FhirRequest request;
+
+ @Mock
+ private ScriptExecution scriptExecution;
+
+ @Mock
+ private Map variables;
+
+ @InjectMocks
+ private R4OrganizationFhirToDhisTransformerUtils utils;
+
+ @Rule
+ public MockitoRule rule = MockitoJUnit.rule();
+
+ @Test
+ public void exists()
+ {
+ Mockito.doReturn( Optional.of( new OrganizationUnit() ) ).when( organizationUnitService ).findOneByReference( Mockito.eq( new Reference( "ABC_123", ReferenceType.CODE ) ) );
+ Assert.assertTrue( utils.exists( "ABC_123" ) );
+ Mockito.verify( organizationUnitService ).findOneByReference( Mockito.eq( new Reference( "ABC_123", ReferenceType.CODE ) ) );
+ }
+
+ @Test
+ public void existsNot()
+ {
+ Mockito.doReturn( Optional.empty() ).when( organizationUnitService ).findOneByReference( Mockito.eq( new Reference( "ABC_123", ReferenceType.CODE ) ) );
+ Assert.assertFalse( utils.exists( "ABC_123" ) );
+ Mockito.verify( organizationUnitService ).findOneByReference( Mockito.eq( new Reference( "ABC_123", ReferenceType.CODE ) ) );
+ }
+
+ @Test
+ public void existsWithPrefix()
+ {
+ final ResourceSystem resourceSystem = new ResourceSystem( FhirResourceType.ORGANIZATION, "http://test.com", "OT_", null, null );
+ Mockito.doReturn( scriptExecution ).when( scriptExecutionContext ).getScriptExecution();
+ Mockito.doReturn( variables ).when( scriptExecution ).getVariables();
+ Mockito.doReturn( context ).when( variables ).get( Mockito.eq( "context" ) );
+ Mockito.doReturn( request ).when( context ).getFhirRequest();
+ Mockito.doReturn( Optional.of( resourceSystem ) ).when( request ).getOptionalResourceSystem( FhirResourceType.ORGANIZATION );
+
+
+ Mockito.doReturn( Optional.of( new OrganizationUnit() ) ).when( organizationUnitService ).findOneByReference( Mockito.eq( new Reference( "OT_ABC_123", ReferenceType.CODE ) ) );
+ Assert.assertEquals( "OT_ABC_123", utils.existsWithPrefix( "ABC_123" ) );
+ Mockito.verify( organizationUnitService ).findOneByReference( Mockito.eq( new Reference( "OT_ABC_123", ReferenceType.CODE ) ) );
+ }
+
+ @Test
+ public void existsNotWithPrefix()
+ {
+ final ResourceSystem resourceSystem = new ResourceSystem( FhirResourceType.ORGANIZATION, "http://test.com", "OT_", null, null );
+ Mockito.doReturn( scriptExecution ).when( scriptExecutionContext ).getScriptExecution();
+ Mockito.doReturn( variables ).when( scriptExecution ).getVariables();
+ Mockito.doReturn( context ).when( variables ).get( Mockito.eq( "context" ) );
+ Mockito.doReturn( request ).when( context ).getFhirRequest();
+ Mockito.doReturn( Optional.of( resourceSystem ) ).when( request ).getOptionalResourceSystem( FhirResourceType.ORGANIZATION );
+
+ Mockito.doReturn( Optional.empty() ).when( organizationUnitService ).findOneByReference( Mockito.eq( new Reference( "OT_ABC_123", ReferenceType.CODE ) ) );
+ Assert.assertNull( utils.existsWithPrefix( "ABC_123" ) );
+ Mockito.verify( organizationUnitService ).findOneByReference( Mockito.eq( new Reference( "OT_ABC_123", ReferenceType.CODE ) ) );
+ }
+
+ @Test
+ public void findHierarchy()
+ {
+ final SubscriptionFhirEndpoint subscriptionFhirEndpoint = new SubscriptionFhirEndpoint();
+ final FhirServer fhirServer = new FhirServer();
+ fhirServer.setFhirEndpoint( subscriptionFhirEndpoint );
+ final FhirContext fhirContext = FhirContext.forR4();
+ final UUID fhirServerResourceId = UUID.randomUUID();
+ final FhirServerResource fhirServerResource = new FhirServerResource();
+ fhirServerResource.setFhirServer( fhirServer );
+ final ResourceSystem resourceSystem = new ResourceSystem( FhirResourceType.ORGANIZATION, "http://test.com", "OT_", null, null );
+ Mockito.doReturn( scriptExecution ).when( scriptExecutionContext ).getScriptExecution();
+ Mockito.doReturn( variables ).when( scriptExecution ).getVariables();
+ Mockito.doReturn( context ).when( variables ).get( Mockito.eq( "context" ) );
+ Mockito.doReturn( request ).when( context ).getFhirRequest();
+ Mockito.doReturn( fhirServerResourceId ).when( request ).getFhirServerResourceId();
+ Mockito.doReturn( FhirVersion.R4 ).when( request ).getVersion();
+ Mockito.doReturn( Optional.of( resourceSystem ) ).when( request ).getOptionalResourceSystem( FhirResourceType.ORGANIZATION );
+ Mockito.doReturn( Optional.of( fhirServerResource ) ).when( fhirServerResourceRepository ).findOneByIdCached( Mockito.eq( fhirServerResourceId ) );
+ Mockito.doReturn( Optional.of( fhirContext ) ).when( fhirResourceRepository ).findFhirContext( Mockito.eq( FhirVersion.R4 ) );
+
+ final Organization org1 = (Organization) new Organization().setId( new IdType( "Organization", "1" ) );
+ final Organization org2 = (Organization) new Organization().setId( new IdType( "2" ) );
+ final Organization org3 = (Organization) new Organization().setId( new IdType( ("3") ) );
+ final Organization org4 = (Organization) new Organization().setId( new IdType( "Organization", "4" ) );
+
+ Mockito.doAnswer( invocation -> {
+ final Function parentReferenceFunction = invocation.getArgument( 6 );
+ Assert.assertEquals( org3.getPartOf(), parentReferenceFunction.apply( org3 ) );
+ Assert.assertEquals( org4.getPartOf(), parentReferenceFunction.apply( org4 ) );
+ return new Bundle()
+ .addEntry( new Bundle.BundleEntryComponent().setResource( org3 ) )
+ .addEntry( new Bundle.BundleEntryComponent().setResource( org4 ) );
+ } )
+ .when( hierarchicallyFhirResourceRepository )
+ .findWithParents( Mockito.eq( fhirServerResourceId ), Mockito.eq( FhirVersion.R4 ), Mockito.same( subscriptionFhirEndpoint ),
+ Mockito.eq( "Organization" ), Mockito.eq( "3" ), Mockito.eq( "organizationPartOf" ), Mockito.any() );
+
+ final org.hl7.fhir.r4.model.Reference org2Ref = new org.hl7.fhir.r4.model.Reference( org2.getIdElement() );
+ org2Ref.setResource( org2 );
+ org1.setPartOf( org2Ref );
+ org2.setPartOf( new org.hl7.fhir.r4.model.Reference( org3.getIdElement() ) );
+ org3.setPartOf( new org.hl7.fhir.r4.model.Reference( org4.getIdElement() ) );
+
+ final org.hl7.fhir.r4.model.Reference org1Ref = new org.hl7.fhir.r4.model.Reference( org1.getIdElement() );
+ org1Ref.setResource( org1 );
+
+ final List extends IBaseResource> hierarchy = utils.findHierarchy( org1Ref );
+
+ }
+}
\ No newline at end of file
diff --git a/fhir-r4/src/test/java/org/dhis2/fhir/adapter/fhir/transform/fhir/impl/util/r4/R4PatientFhirToDhisTransformerUtilsTest.java b/fhir-r4/src/test/java/org/dhis2/fhir/adapter/fhir/transform/fhir/impl/util/r4/R4PatientFhirToDhisTransformerUtilsTest.java
new file mode 100644
index 00000000..0eedbce9
--- /dev/null
+++ b/fhir-r4/src/test/java/org/dhis2/fhir/adapter/fhir/transform/fhir/impl/util/r4/R4PatientFhirToDhisTransformerUtilsTest.java
@@ -0,0 +1,72 @@
+package org.dhis2.fhir.adapter.fhir.transform.fhir.impl.util.r4;
+
+/*
+ * Copyright (c) 2004-2019, University of Oslo
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * Neither the name of the HISP project nor the names of its contributors may
+ * be used to endorse or promote products derived from this software without
+ * specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
+ * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+import org.dhis2.fhir.adapter.fhir.script.ScriptExecutionContext;
+import org.hl7.fhir.r4.model.Address;
+import org.hl7.fhir.r4.model.Patient;
+import org.junit.Assert;
+import org.junit.Rule;
+import org.junit.Test;
+import org.mockito.InjectMocks;
+import org.mockito.Mock;
+import org.mockito.junit.MockitoJUnit;
+import org.mockito.junit.MockitoRule;
+
+/**
+ * Unit tests for {@link R4PatientFhirToDhisTransformerUtils}.
+ *
+ * @author volsch
+ */
+public class R4PatientFhirToDhisTransformerUtilsTest
+{
+ @Mock
+ private ScriptExecutionContext scriptExecutionContext;
+
+ @InjectMocks
+ private R4PatientFhirToDhisTransformerUtils utils;
+
+ @Rule
+ public MockitoRule rule = MockitoJUnit.rule();
+
+ @Test
+ public void nullPatient()
+ {
+ Assert.assertNull( utils.getBirthPlaceAddress( null ) );
+ }
+
+ @Test
+ public void validAddress()
+ {
+ final Address address = new Address().setText( "Hospital" );
+ final Patient patient = new Patient();
+ patient.addExtension().setUrl( "http://hl7.org/fhir/StructureDefinition/birthPlace" ).setValue( address );
+ Assert.assertSame( address, utils.getBirthPlaceAddress( patient ) );
+ }
+}
\ No newline at end of file
diff --git a/fhir-r4/src/test/resources/logback-test.xml b/fhir-r4/src/test/resources/logback-test.xml
new file mode 100644
index 00000000..b9b017dd
--- /dev/null
+++ b/fhir-r4/src/test/resources/logback-test.xml
@@ -0,0 +1,33 @@
+
+
+
+
+
+
+
diff --git a/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/metadata/model/FhirResourceType.java b/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/metadata/model/FhirResourceType.java
index 36a833bb..935fdd6d 100644
--- a/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/metadata/model/FhirResourceType.java
+++ b/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/metadata/model/FhirResourceType.java
@@ -63,7 +63,9 @@ public enum FhirResourceType
OBSERVATION( "Observation", 20, "Observation" ),
ORGANIZATION( "Organization", 1, "Organization" ),
PATIENT( "Patient", 10, "Patient" ),
- RELATED_PERSON( "RelatedPerson", 11, "RelatedPerson" );
+ RELATED_PERSON( "RelatedPerson", 11, "RelatedPerson" ),
+ PRACTITIONER( "Practitioner", 9, "Practitioner" ),
+ ;
private static final Map resourcesBySimpleClassName = Arrays.stream( values() ).flatMap( v -> v.getSimpleClassNames().stream().map( scn -> new SimpleEntry<>( scn, v ) ) )
.collect( Collectors.toMap( SimpleEntry::getKey, SimpleEntry::getValue ) );
diff --git a/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/model/FhirVersion.java b/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/model/FhirVersion.java
index 0b40b17e..04404563 100644
--- a/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/model/FhirVersion.java
+++ b/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/model/FhirVersion.java
@@ -1,7 +1,7 @@
package org.dhis2.fhir.adapter.fhir.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
@@ -44,12 +44,15 @@
*/
public enum FhirVersion
{
- DSTU3( FhirVersionEnum.DSTU3 );
+ DSTU3( FhirVersionEnum.DSTU3 ),
+ R4( FhirVersionEnum.R4 );
public static final Set ALL = Collections.unmodifiableSet( new HashSet<>( Arrays.asList( FhirVersion.values() ) ) );
public static final Set DSTU3_ONLY = Collections.singleton( DSTU3 );
+ public static final Set R4_ONLY = Collections.singleton( R4 );
+
@Nullable
public static FhirVersion get( @Nullable FhirVersionEnum fhirVersionEnum )
{
diff --git a/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/repository/impl/AbstractFhirResourceRepositorySupport.java b/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/repository/impl/AbstractFhirResourceRepositorySupport.java
new file mode 100644
index 00000000..d7c1db85
--- /dev/null
+++ b/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/repository/impl/AbstractFhirResourceRepositorySupport.java
@@ -0,0 +1,78 @@
+package org.dhis2.fhir.adapter.fhir.repository.impl;
+
+/*
+ * Copyright (c) 2004-2019, University of Oslo
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * Neither the name of the HISP project nor the names of its contributors may
+ * be used to endorse or promote products derived from this software without
+ * specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
+ * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+import org.dhis2.fhir.adapter.fhir.metadata.model.FhirServerResource;
+import org.dhis2.fhir.adapter.fhir.model.FhirVersionRestricted;
+import org.hl7.fhir.instance.model.api.IAnyResource;
+import org.hl7.fhir.instance.model.api.IBaseBundle;
+import org.hl7.fhir.instance.model.api.IBaseResource;
+
+import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
+import java.util.List;
+import java.util.UUID;
+
+/**
+ * Abstract FHIR version dependent base class for FHIR resource repository utils.
+ *
+ * @author volsch
+ */
+public abstract class AbstractFhirResourceRepositorySupport implements FhirVersionRestricted
+{
+ @Nonnull
+ protected abstract AbstractFhirRepositoryResourceUtils createFhirRepositoryResourceUtils( @Nonnull UUID fhirServerId );
+
+ @Nonnull
+ protected abstract IAnyResource createFhirSubscription( @Nonnull FhirServerResource fhirServerResource );
+
+ @Nullable
+ protected abstract IBaseResource getFirstResource( @Nonnull IBaseBundle bundle );
+
+ @Nonnull
+ protected abstract Class extends IBaseBundle> getBundleClass();
+
+ @Nonnull
+ protected abstract IBaseBundle createBundle( @Nonnull List extends IBaseResource> resources );
+
+ @Nonnull
+ protected String createWebHookUrl( @Nonnull FhirServerResource fhirServerResource )
+ {
+ final StringBuilder url = new StringBuilder( fhirServerResource.getFhirServer().getAdapterEndpoint().getBaseUrl() );
+ if ( url.charAt( url.length() - 1 ) != '/' )
+ {
+ url.append( '/' );
+ }
+ url.append( "remote-fhir-rest-hook/" );
+ url.append( fhirServerResource.getFhirServer().getId() );
+ url.append( '/' );
+ url.append( fhirServerResource.getId() );
+ return url.toString();
+ }
+}
diff --git a/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/repository/impl/AbstractFhirResourceRepositoryImpl.java b/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/repository/impl/FhirResourceRepositoryImpl.java
similarity index 90%
rename from fhir/src/main/java/org/dhis2/fhir/adapter/fhir/repository/impl/AbstractFhirResourceRepositoryImpl.java
rename to fhir/src/main/java/org/dhis2/fhir/adapter/fhir/repository/impl/FhirResourceRepositoryImpl.java
index 077e295c..954f7f76 100644
--- a/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/repository/impl/AbstractFhirResourceRepositoryImpl.java
+++ b/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/repository/impl/FhirResourceRepositoryImpl.java
@@ -59,7 +59,6 @@
import org.dhis2.fhir.adapter.fhir.server.StoredFhirResourceService;
import org.dhis2.fhir.adapter.rest.RestBadRequestException;
import org.dhis2.fhir.adapter.util.NameUtils;
-import org.hl7.fhir.instance.model.api.IAnyResource;
import org.hl7.fhir.instance.model.api.IBaseBundle;
import org.hl7.fhir.instance.model.api.IBaseResource;
import org.slf4j.Logger;
@@ -69,6 +68,7 @@
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.CachePut;
import org.springframework.cache.annotation.Cacheable;
+import org.springframework.stereotype.Component;
import org.springframework.transaction.event.TransactionPhase;
import org.springframework.transaction.event.TransactionalEventListener;
@@ -88,8 +88,9 @@
*
* @author volsch
*/
+@Component
@CacheConfig( cacheNames = "fhirResources", cacheManager = "fhirCacheManager" )
-public abstract class AbstractFhirResourceRepositoryImpl implements FhirResourceRepository
+public class FhirResourceRepositoryImpl implements FhirResourceRepository
{
private final Logger logger = LoggerFactory.getLogger( getClass() );
@@ -101,22 +102,19 @@ public abstract class AbstractFhirResourceRepositoryImpl implements FhirResource
private final Map fhirContexts;
- public AbstractFhirResourceRepositoryImpl( @Nonnull ScriptExecutor scriptExecutor, @Nonnull StoredFhirResourceService storedItemService, @Nonnull FhirServerResourceRepository fhirServerResourceRepository,
- @Nonnull ObjectProvider> fhirContexts )
+ private final Map supports = new HashMap<>();
+
+ public FhirResourceRepositoryImpl( @Nonnull ScriptExecutor scriptExecutor, @Nonnull StoredFhirResourceService storedItemService, @Nonnull FhirServerResourceRepository fhirServerResourceRepository,
+ @Nonnull ObjectProvider> fhirContexts, @Nonnull ObjectProvider> supports )
{
this.scriptExecutor = scriptExecutor;
this.storedItemService = storedItemService;
this.fhirServerResourceRepository = fhirServerResourceRepository;
this.fhirContexts = fhirContexts.getIfAvailable( Collections::emptyList ).stream().filter( fc -> (FhirVersion.get( fc.getVersion().getVersion() ) != null) )
.collect( Collectors.toMap( fc -> FhirVersion.get( fc.getVersion().getVersion() ), fc -> fc ) );
+ supports.getIfAvailable( Collections::emptyList ).forEach( s -> s.getFhirVersions().forEach( v -> FhirResourceRepositoryImpl.this.supports.put( v, s ) ) );
}
- @Nonnull
- protected abstract AbstractFhirRepositoryResourceUtils createFhirRepositoryResourceUtils( @Nonnull UUID fhirServerId );
-
- @Nonnull
- protected abstract IAnyResource createFhirSubscription( @Nonnull FhirServerResource fhirServerResource );
-
@Nonnull
@Override
public Optional findFhirContext( @Nonnull FhirVersion fhirVersion )
@@ -214,9 +212,10 @@ public IBaseResource transform( @Nonnull UUID fhirServerId, @Nonnull FhirVersion
final Optional fhirServerResource = fhirServerResourceRepository.findFirstCached( fhirServerId, fhirResourceType );
if ( fhirServerResource.isPresent() && (fhirServerResource.get().getImpTransformScript() != null) )
{
+ final AbstractFhirResourceRepositorySupport support = supports.get( fhirVersion );
final Map variables = new HashMap<>();
variables.put( ScriptVariable.RESOURCE.getVariableName(), resource );
- variables.put( ScriptVariable.UTILS.getVariableName(), createFhirRepositoryResourceUtils( fhirServerId ) );
+ variables.put( ScriptVariable.UTILS.getVariableName(), support.createFhirRepositoryResourceUtils( fhirServerId ) );
try
{
@@ -329,13 +328,15 @@ else if ( (methodOutcome.getId() != null) && methodOutcome.getId().hasVersionIdP
@TransactionalEventListener( phase = TransactionPhase.BEFORE_COMMIT, classes = AutoCreatedFhirServerResourceEvent.class )
public void autoCreatedSubscriptionResource( @Nonnull AutoCreatedFhirServerResourceEvent event )
{
- final FhirContext fhirContext = fhirContexts.get( event.getFhirServerResource().getFhirServer().getFhirVersion() );
+ final FhirVersion fhirVersion = event.getFhirServerResource().getFhirServer().getFhirVersion();
+ final FhirContext fhirContext = fhirContexts.get( fhirVersion );
final IGenericClient client = FhirClientUtils.createClient( fhirContext, event.getFhirServerResource().getFhirServer().getFhirEndpoint() );
+ final AbstractFhirResourceRepositorySupport support = supports.get( fhirVersion );
final MethodOutcome methodOutcome;
try
{
- methodOutcome = client.create().resource( createFhirSubscription( event.getFhirServerResource() ) ).execute();
+ methodOutcome = client.create().resource( support.createFhirSubscription( event.getFhirServerResource() ) ).execute();
}
catch ( BaseServerResponseException e )
{
@@ -347,38 +348,18 @@ public void autoCreatedSubscriptionResource( @Nonnull AutoCreatedFhirServerResou
event.getFhirServerResource().setFhirSubscriptionId( id );
}
- @Nonnull
- protected String createWebHookUrl( @Nonnull FhirServerResource fhirServerResource )
- {
- final StringBuilder url = new StringBuilder( fhirServerResource.getFhirServer().getAdapterEndpoint().getBaseUrl() );
- if ( url.charAt( url.length() - 1 ) != '/' )
- {
- url.append( '/' );
- }
- url.append( "remote-fhir-rest-hook/" );
- url.append( fhirServerResource.getFhirServer().getId() );
- url.append( '/' );
- url.append( fhirServerResource.getId() );
- return url.toString();
- }
-
- @Nullable
- protected abstract IBaseResource getFirstResource( @Nonnull IBaseBundle bundle );
-
- @Nonnull
- protected abstract Class extends IBaseBundle> getBundleClass();
-
@Nonnull
protected Optional findByToken( @Nonnull UUID fhirServerId, @Nonnull FhirVersion fhirVersion, @Nonnull SubscriptionFhirEndpoint fhirEndpoint, @Nonnull String resourceType, @Nonnull String field, @Nonnull SystemCodeValue identifier )
{
final FhirContext fhirContext = fhirContexts.get( fhirVersion );
final IGenericClient client = FhirClientUtils.createClient( fhirContext, fhirEndpoint );
+ final AbstractFhirResourceRepositorySupport support = supports.get( fhirVersion );
logger.debug( "Reading {}?{}={} from FHIR endpoints {}.", resourceType, identifier, field, fhirEndpoint.getBaseUrl() );
- final IBaseBundle bundle = client.search().forResource( resourceType ).returnBundle( getBundleClass() )
+ final IBaseBundle bundle = client.search().forResource( resourceType ).returnBundle( support.getBundleClass() )
.where( new TokenClientParam( field ).exactly().systemAndIdentifier( identifier.getSystem(), identifier.getCode() ) )
.cacheControl( new CacheControlDirective().setNoCache( true ) ).execute();
- final IBaseResource resource = getFirstResource( bundle );
+ final IBaseResource resource = support.getFirstResource( bundle );
logger.debug( "Read {}?{}={} from FHIR endpoints {} (found={}).", resourceType, identifier, field, fhirEndpoint.getBaseUrl(), (resource != null) );
return Optional.ofNullable( transform( fhirServerId, fhirVersion, resource ) );
}
diff --git a/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/repository/impl/AbstractHierarchicallyFhirResourceRepositoryImpl.java b/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/repository/impl/HierarchicallyFhirResourceRepositoryImpl.java
similarity index 84%
rename from fhir/src/main/java/org/dhis2/fhir/adapter/fhir/repository/impl/AbstractHierarchicallyFhirResourceRepositoryImpl.java
rename to fhir/src/main/java/org/dhis2/fhir/adapter/fhir/repository/impl/HierarchicallyFhirResourceRepositoryImpl.java
index b08bc739..77d2157e 100644
--- a/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/repository/impl/AbstractHierarchicallyFhirResourceRepositoryImpl.java
+++ b/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/repository/impl/HierarchicallyFhirResourceRepositoryImpl.java
@@ -36,15 +36,19 @@
import org.hl7.fhir.instance.model.api.IBaseBundle;
import org.hl7.fhir.instance.model.api.IBaseReference;
import org.hl7.fhir.instance.model.api.IBaseResource;
+import org.springframework.beans.factory.ObjectProvider;
import org.springframework.cache.annotation.CacheConfig;
import org.springframework.cache.annotation.Cacheable;
+import org.springframework.stereotype.Component;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import java.util.ArrayList;
import java.util.Collections;
+import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
+import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.UUID;
@@ -55,14 +59,18 @@
*
* @author volsch
*/
+@Component
@CacheConfig( cacheNames = "hierarchicallyFhirResources", cacheManager = "fhirCacheManager" )
-public abstract class AbstractHierarchicallyFhirResourceRepositoryImpl implements HierarchicallyFhirResourceRepository
+public class HierarchicallyFhirResourceRepositoryImpl implements HierarchicallyFhirResourceRepository
{
private final FhirResourceRepository fhirResourceRepository;
- public AbstractHierarchicallyFhirResourceRepositoryImpl( @Nonnull FhirResourceRepository fhirResourceRepository )
+ private final Map supports = new HashMap<>();
+
+ public HierarchicallyFhirResourceRepositoryImpl( @Nonnull FhirResourceRepository fhirResourceRepository, @Nonnull ObjectProvider> supports )
{
this.fhirResourceRepository = fhirResourceRepository;
+ supports.getIfAvailable( Collections::emptyList ).forEach( s -> s.getFhirVersions().forEach( v -> HierarchicallyFhirResourceRepositoryImpl.this.supports.put( v, s ) ) );
}
@Nonnull
@@ -72,15 +80,16 @@ public IBaseBundle findWithParents( @Nonnull UUID fhirServerId, @Nonnull FhirVer
@Nonnull String resourceType, @Nullable String resourceId, @Nonnull String hierarchyType,
@Nonnull Function parentReferenceFunction )
{
+ final AbstractFhirResourceRepositorySupport support = supports.get( fhirVersion );
if ( resourceId == null )
{
- return createBundle( Collections.emptyList() );
+ return support.createBundle( Collections.emptyList() );
}
IBaseResource child = fhirResourceRepository.find( fhirServerId, fhirVersion, fhirEndpoint, resourceType, resourceId ).orElse( null );
if ( child == null )
{
- return createBundle( Collections.emptyList() );
+ return support.createBundle( Collections.emptyList() );
}
final Set processedResources = new HashSet<>();
@@ -119,9 +128,6 @@ public IBaseBundle findWithParents( @Nonnull UUID fhirServerId, @Nonnull FhirVer
}
}
- return createBundle( result );
+ return support.createBundle( result );
}
-
- @Nonnull
- protected abstract IBaseBundle createBundle( @Nonnull List extends IBaseResource> resources );
}
diff --git a/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/util/AbstractFhirResourceDhisToFhirTransformerUtils.java b/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/util/AbstractFhirResourceDhisToFhirTransformerUtils.java
index bc91061a..7eedc0dd 100644
--- a/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/util/AbstractFhirResourceDhisToFhirTransformerUtils.java
+++ b/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/util/AbstractFhirResourceDhisToFhirTransformerUtils.java
@@ -37,6 +37,7 @@
import org.dhis2.fhir.adapter.scriptable.Scriptable;
import org.dhis2.fhir.adapter.util.EnumValueUtils;
import org.hl7.fhir.instance.model.api.IBase;
+import org.hl7.fhir.instance.model.api.IBaseElement;
import org.hl7.fhir.instance.model.api.IBaseReference;
import org.hl7.fhir.instance.model.api.IBaseResource;
import org.hl7.fhir.instance.model.api.ICompositeType;
@@ -109,6 +110,9 @@ public IBaseResource createResource( @Nonnull Object fhirResourceType )
return createResource( resourceType.getResourceTypeName() );
}
+ @Nonnull
+ public abstract IBaseElement createType( @Nonnull String fhirType );
+
@Nonnull
public IBaseResource createResource( @Nonnull FhirResourceType fhirResourceType )
{
diff --git a/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/fhir/impl/util/AbstractCodeFhirToDhisTransformerUtils.java b/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/fhir/impl/util/AbstractCodeFhirToDhisTransformerUtils.java
index ff7b4057..c9fae084 100644
--- a/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/fhir/impl/util/AbstractCodeFhirToDhisTransformerUtils.java
+++ b/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/fhir/impl/util/AbstractCodeFhirToDhisTransformerUtils.java
@@ -1,7 +1,7 @@
package org.dhis2.fhir.adapter.fhir.transform.fhir.impl.util;
/*
- * 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
@@ -136,19 +136,33 @@ public final String getScriptAttrName()
returnDescription = "Returns if the code is included." )
public abstract boolean containsCode( @Nullable ICompositeType codeableConcept, @Nullable String system, @Nonnull String code );
+ @SuppressWarnings( "unchecked" )
@ScriptMethod( description = "Returns if the specified codeable concept contains any combination of the specified list of system code values (type SystemCodeValue).",
args = {
@ScriptMethodArg( value = "codeableConcept", description = "The codeable concept that should be checked." ),
@ScriptMethodArg( value = "systemCodeValues", description = "List of system code values that should be used for the check (type SystemCodeValue)." ),
},
returnDescription = "Returns if any system code value is included." )
- public boolean containsAnyCode( @Nullable ICompositeType codeableConcept, @Nullable Collection systemCodeValues )
+ public boolean containsAnyCode( @Nullable Object codeableConcept, @Nullable Collection systemCodeValues )
{
if ( (codeableConcept == null) || (systemCodeValues == null) )
{
return false;
}
- return systemCodeValues.stream().anyMatch( scv -> containsCode( codeableConcept, scv.getSystem(), scv.getCode() ) );
+ final Collection extends ICompositeType> codeableConcepts;
+ if ( codeableConcept instanceof ICompositeType )
+ {
+ codeableConcepts = Collections.singletonList( (ICompositeType) codeableConcept );
+ }
+ else if ( codeableConcept instanceof Collection )
+ {
+ codeableConcepts = (Collection extends ICompositeType>) codeableConcept;
+ }
+ else
+ {
+ throw new IllegalArgumentException( "Expected composite type: " + codeableConcept.getClass() );
+ }
+ return systemCodeValues.stream().anyMatch( scv -> codeableConcepts.stream().anyMatch( cc -> containsCode( cc, scv.getSystem(), scv.getCode() ) ) );
}
@ScriptMethod( description = "Returns a map where the key is each specified mapping code and the value is a list of system code values (type SystemCodeValue). " +
diff --git a/fhir/src/main/resources/db/migration/production/V1.1.0.8_0_0__FHIR_Version_R4.sql b/fhir/src/main/resources/db/migration/production/V1.1.0.8_0_0__FHIR_Version_R4.sql
new file mode 100644
index 00000000..3f8b06d6
--- /dev/null
+++ b/fhir/src/main/resources/db/migration/production/V1.1.0.8_0_0__FHIR_Version_R4.sql
@@ -0,0 +1,35 @@
+/*
+ * 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_version_enum VALUES('R4');
+INSERT INTO fhir_resource_type_enum VALUES('PRACTITIONER');
+
+INSERT INTO fhir_script_source_version(script_source_id, fhir_version)
+SELECT script_source_id, 'R4' FROM fhir_script_source_version WHERE fhir_version = 'DSTU3';
diff --git a/fhir/src/main/resources/db/migration/programs/child_programme/V1.1.0.8_100_0__Child__Programme_FHIR_Version_R4.sql b/fhir/src/main/resources/db/migration/programs/child_programme/V1.1.0.8_100_0__Child__Programme_FHIR_Version_R4.sql
new file mode 100644
index 00000000..4261f6c0
--- /dev/null
+++ b/fhir/src/main/resources/db/migration/programs/child_programme/V1.1.0.8_100_0__Child__Programme_FHIR_Version_R4.sql
@@ -0,0 +1,56 @@
+/*
+ * 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
+
+DELETE FROM fhir_script_source_version WHERE script_source_id='78b4af9b-c247-4290-abee-44983938b265' AND fhir_version='R4';
+INSERT INTO fhir_script_source (id, version, script_id, source_text, source_type)
+VALUES ('43e49f59-67d6-4919-a808-6a35bc55e18c', 0, '33952dc7-bbf9-474d-8eaa-ab866a926da3',
+'var result;
+var doseGiven = input.getIntegerOptionValue(args[''dataElement''], 1, args[''optionValuePattern'']);
+if (doseGiven == null)
+{
+ output.setNotGiven(true);
+ result = !output.getIdElement().isEmpty();
+}
+else
+{
+ output.setVaccineCode(codeUtils.getRuleCodeableConcept());
+ if (output.getVaccineCode().isEmpty())
+ {
+ output.getVaccineCode().setText(dataElementUtils.getDataElementName(args[''dataElement'']));
+ }
+ output.setPrimarySource(input.isProvidedElsewhere(args[''dataElement'']) == false);
+ output.setNotGiven(false);
+ output.setProtocolApplied(null);
+ output.addProtocolApplied().setDoseNumber(fhirResourceUtils.createType(''positiveInt'').setValue(doseGiven));
+ result = true;
+}
+result', 'JAVASCRIPT');
+INSERT INTO fhir_script_source_version (script_source_id, fhir_version)
+VALUES ('43e49f59-67d6-4919-a808-6a35bc55e18c', 'R4');
diff --git a/pom.xml b/pom.xml
index 3f1a11aa..bb652a08 100644
--- a/pom.xml
+++ b/pom.xml
@@ -48,6 +48,7 @@
dhis
fhir
fhir-dstu3
+ fhir-r4
app
@@ -231,6 +232,16 @@
hapi-fhir-validation-resources-dstu3
${hapi-fhir.version}
+
+ ca.uhn.hapi.fhir
+ hapi-fhir-structures-r4
+ ${hapi-fhir.version}
+
+
+ ca.uhn.hapi.fhir
+ hapi-fhir-validation-resources-r4
+ ${hapi-fhir.version}
+
org.apache.commons