diff --git a/README.md b/README.md index 41d6779f..178da6c3 100644 --- a/README.md +++ b/README.md @@ -3,7 +3,7 @@ This repository contains the source code of the DHIS2 FHIR Adapter. The current scope of the Adapter is to import data into DHIS2 Tracker by using FHIR Subscriptions. Event if the adapter may support more FHIR Resource types at the moment, the initial official support is for FHIR Patient resources that are transformed to DHIS2 Tracked Entity instances. -The import works on the basis of a domain specific business rule engine that decides about transformations of medical data to questionnaire-like structures (DHIS2 Tracker Programs and their Program Stages). +The import works on the basis of a domain specific business rule engine that decides about transformations of patient related medical data to questionnaire-like structures (DHIS2 Tracker Programs and their Program Stages). ![DHIS2 FHIR Adapter High Level Architecture](docs/images/DHIS2_FHIR_Adapter_High_Level_Architecture.png "DHIS2 FHIR Adapter High Level Architecture") diff --git a/app/src/main/java/org/dhis2/fhir/adapter/setup/RemoteSubscriptionAdapterSetup.java b/app/src/main/java/org/dhis2/fhir/adapter/setup/RemoteSubscriptionAdapterSetup.java index a8bc4ba1..5b849671 100644 --- a/app/src/main/java/org/dhis2/fhir/adapter/setup/RemoteSubscriptionAdapterSetup.java +++ b/app/src/main/java/org/dhis2/fhir/adapter/setup/RemoteSubscriptionAdapterSetup.java @@ -29,6 +29,7 @@ */ import org.dhis2.fhir.adapter.fhir.metadata.model.SubscriptionAdapterEndpoint; +import org.dhis2.fhir.adapter.validator.HttpUrl; import javax.validation.constraints.NotBlank; import javax.validation.constraints.Size; diff --git a/app/src/main/java/org/dhis2/fhir/adapter/setup/RemoteSubscriptionFhirSetup.java b/app/src/main/java/org/dhis2/fhir/adapter/setup/RemoteSubscriptionFhirSetup.java index 1a4faa74..a10dcc6a 100644 --- a/app/src/main/java/org/dhis2/fhir/adapter/setup/RemoteSubscriptionFhirSetup.java +++ b/app/src/main/java/org/dhis2/fhir/adapter/setup/RemoteSubscriptionFhirSetup.java @@ -31,6 +31,7 @@ import org.dhis2.fhir.adapter.fhir.metadata.model.RequestHeader; import org.dhis2.fhir.adapter.fhir.metadata.model.SubscriptionFhirEndpoint; import org.dhis2.fhir.adapter.fhir.metadata.model.SubscriptionType; +import org.dhis2.fhir.adapter.validator.HttpUrl; import javax.validation.constraints.Min; import javax.validation.constraints.NotBlank; diff --git a/app/src/main/java/org/dhis2/fhir/adapter/setup/SystemUriSetup.java b/app/src/main/java/org/dhis2/fhir/adapter/setup/SystemUriSetup.java index ece929d7..a5807c34 100644 --- a/app/src/main/java/org/dhis2/fhir/adapter/setup/SystemUriSetup.java +++ b/app/src/main/java/org/dhis2/fhir/adapter/setup/SystemUriSetup.java @@ -30,6 +30,7 @@ import org.dhis2.fhir.adapter.fhir.metadata.model.RemoteSubscriptionSystem; import org.dhis2.fhir.adapter.fhir.metadata.model.System; +import org.dhis2.fhir.adapter.validator.Uri; import javax.validation.constraints.NotBlank; import javax.validation.constraints.Pattern; diff --git a/common/src/main/java/org/dhis2/fhir/adapter/jackson/PersistentBagConverter.java b/common/src/main/java/org/dhis2/fhir/adapter/validator/EnumValue.java similarity index 61% rename from common/src/main/java/org/dhis2/fhir/adapter/jackson/PersistentBagConverter.java rename to common/src/main/java/org/dhis2/fhir/adapter/validator/EnumValue.java index 2c737098..87f0ccb2 100644 --- a/common/src/main/java/org/dhis2/fhir/adapter/jackson/PersistentBagConverter.java +++ b/common/src/main/java/org/dhis2/fhir/adapter/validator/EnumValue.java @@ -1,4 +1,4 @@ -package org.dhis2.fhir.adapter.jackson; +package org.dhis2.fhir.adapter.validator; /* * Copyright (c) 2004-2018, University of Oslo @@ -28,41 +28,32 @@ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -import com.fasterxml.jackson.databind.JavaType; -import com.fasterxml.jackson.databind.type.TypeFactory; -import com.fasterxml.jackson.databind.util.Converter; - -import java.util.ArrayList; -import java.util.List; +import javax.validation.Constraint; +import javax.validation.Payload; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; /** - * Converts lists (especially persistent bag) to a list. - * This is required when class name is serialized and used sorted set class - * cannot be instantiated otherwise. + * Validation that a value is a supported value. * * @author volsch */ -public class PersistentBagConverter implements Converter, List> +@Constraint( validatedBy = EnumValueValidator.class ) +@Target( { ElementType.FIELD } ) +@Retention( RetentionPolicy.RUNTIME ) +public @interface EnumValue { - @Override - public List convert( List value ) - { - if ( value == null ) - { - return null; - } - return new ArrayList<>( value ); - } + String message() default "Supported enum values {value}, unsupported {unsupported}"; + + Class> value(); + + String[] unsupported() default {}; + + String[] supported() default {}; - @Override - public JavaType getInputType( TypeFactory typeFactory ) - { - return typeFactory.constructCollectionType( List.class, Object.class ); - } + Class[] groups() default {}; - @Override - public JavaType getOutputType( TypeFactory typeFactory ) - { - return typeFactory.constructCollectionType( ArrayList.class, Object.class ); - } -} + Class[] payload() default {}; +} \ No newline at end of file diff --git a/common/src/main/java/org/dhis2/fhir/adapter/validator/EnumValueValidator.java b/common/src/main/java/org/dhis2/fhir/adapter/validator/EnumValueValidator.java new file mode 100644 index 00000000..058bc9b5 --- /dev/null +++ b/common/src/main/java/org/dhis2/fhir/adapter/validator/EnumValueValidator.java @@ -0,0 +1,118 @@ +package org.dhis2.fhir.adapter.validator; + +/* + * Copyright (c) 2004-2018, University of Oslo + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * Neither the name of the HISP project nor the names of its contributors may + * be used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +import org.apache.commons.lang3.StringUtils; + +import javax.annotation.Nonnull; +import javax.validation.ConstraintValidator; +import javax.validation.ConstraintValidatorContext; +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.Comparator; +import java.util.HashSet; +import java.util.Set; +import java.util.SortedSet; +import java.util.TreeSet; + +/** + * Validates that value is enumeration value. This is primarily used for documentation + * purpose (Spring REST Docs). + * + * @author volsch + */ +public class EnumValueValidator implements ConstraintValidator +{ + private Set> supported; + + @SuppressWarnings( "unchecked" ) + @Override + public void initialize( EnumValue enumValue ) + { + supported = new HashSet<>( getSupported( enumValue.value(), enumValue.unsupported(), enumValue.supported() ) ); + } + + @SuppressWarnings( "unchecked" ) + @Override + public boolean isValid( Object value, ConstraintValidatorContext context ) + { + if ( value == null ) + { + return true; + } + + final Collection> values; + if ( value instanceof Collection ) + { + values = (Collection>) value; + } + else + { + values = Collections.singleton( (Enum) value ); + } + + for ( final Enum v : values ) + { + if ( (v != null) && !supported.contains( v ) ) + { + { + context.buildConstraintViolationWithTemplate( "Supported values " + StringUtils.join( supported, ", " ) ); + return false; + } + } + } + return true; + } + + @SuppressWarnings( "unchecked" ) + @Nonnull + public static SortedSet> getSupported( @Nonnull Class> enumClass, @Nonnull String[] unsupportedValues, @Nonnull String[] supportedValues ) + { + final Set> unsupported = new HashSet<>(); + for ( final String value : unsupportedValues ) + { + unsupported.add( Enum.valueOf( (Class) enumClass, value ) ); + } + final SortedSet> supported = new TreeSet<>( Comparator.comparing( Enum::name ) ); + if ( supportedValues.length > 0 ) + { + for ( final String value : supportedValues ) + { + supported.add( Enum.valueOf( (Class) enumClass, value ) ); + } + } + else + { + supported.addAll( Arrays.asList( enumClass.getEnumConstants() ) ); + } + supported.removeAll( unsupported ); + return supported; + } +} diff --git a/app/src/main/java/org/dhis2/fhir/adapter/setup/HttpUrl.java b/common/src/main/java/org/dhis2/fhir/adapter/validator/HttpUrl.java similarity index 95% rename from app/src/main/java/org/dhis2/fhir/adapter/setup/HttpUrl.java rename to common/src/main/java/org/dhis2/fhir/adapter/validator/HttpUrl.java index dd62507b..d23037fe 100644 --- a/app/src/main/java/org/dhis2/fhir/adapter/setup/HttpUrl.java +++ b/common/src/main/java/org/dhis2/fhir/adapter/validator/HttpUrl.java @@ -1,4 +1,4 @@ -package org.dhis2.fhir.adapter.setup; +package org.dhis2.fhir.adapter.validator; /* * Copyright (c) 2004-2018, University of Oslo @@ -45,7 +45,7 @@ @Retention( RetentionPolicy.RUNTIME ) public @interface HttpUrl { - String message() default "Not a valid HTTP/HTTPS URL."; + String message() default "Not a valid HTTP/HTTPS URL"; Class[] groups() default {}; diff --git a/app/src/main/java/org/dhis2/fhir/adapter/setup/HttpUrlValidator.java b/common/src/main/java/org/dhis2/fhir/adapter/validator/HttpUrlValidator.java similarity index 96% rename from app/src/main/java/org/dhis2/fhir/adapter/setup/HttpUrlValidator.java rename to common/src/main/java/org/dhis2/fhir/adapter/validator/HttpUrlValidator.java index 96bf4f80..e98035f0 100644 --- a/app/src/main/java/org/dhis2/fhir/adapter/setup/HttpUrlValidator.java +++ b/common/src/main/java/org/dhis2/fhir/adapter/validator/HttpUrlValidator.java @@ -1,4 +1,4 @@ -package org.dhis2.fhir.adapter.setup; +package org.dhis2.fhir.adapter.validator; /* * Copyright (c) 2004-2018, University of Oslo @@ -55,13 +55,13 @@ public boolean isValid( String value, ConstraintValidatorContext context ) } catch ( MalformedURLException e ) { - context.buildConstraintViolationWithTemplate( "Not a valid URL." ); + context.buildConstraintViolationWithTemplate( "Not a valid URL" ); return false; } if ( !"http".equals( protocol ) && !"https".equals( protocol ) ) { - context.buildConstraintViolationWithTemplate( "URL must use protocol HTTP or HTTPS." ); + context.buildConstraintViolationWithTemplate( "URL must use protocol HTTP or HTTPS" ); return false; } diff --git a/app/src/main/java/org/dhis2/fhir/adapter/setup/Uri.java b/common/src/main/java/org/dhis2/fhir/adapter/validator/Uri.java similarity index 98% rename from app/src/main/java/org/dhis2/fhir/adapter/setup/Uri.java rename to common/src/main/java/org/dhis2/fhir/adapter/validator/Uri.java index 8a344413..46c2f7a5 100644 --- a/app/src/main/java/org/dhis2/fhir/adapter/setup/Uri.java +++ b/common/src/main/java/org/dhis2/fhir/adapter/validator/Uri.java @@ -1,4 +1,4 @@ -package org.dhis2.fhir.adapter.setup; +package org.dhis2.fhir.adapter.validator; /* * Copyright (c) 2004-2018, University of Oslo diff --git a/app/src/main/java/org/dhis2/fhir/adapter/setup/UriValidator.java b/common/src/main/java/org/dhis2/fhir/adapter/validator/UriValidator.java similarity index 97% rename from app/src/main/java/org/dhis2/fhir/adapter/setup/UriValidator.java rename to common/src/main/java/org/dhis2/fhir/adapter/validator/UriValidator.java index d9fc87a8..a7cad9db 100644 --- a/app/src/main/java/org/dhis2/fhir/adapter/setup/UriValidator.java +++ b/common/src/main/java/org/dhis2/fhir/adapter/validator/UriValidator.java @@ -1,4 +1,4 @@ -package org.dhis2.fhir.adapter.setup; +package org.dhis2.fhir.adapter.validator; /* * Copyright (c) 2004-2018, University of Oslo @@ -54,7 +54,7 @@ public boolean isValid( String value, ConstraintValidatorContext context ) } catch ( URISyntaxException e ) { - context.buildConstraintViolationWithTemplate( "Not a valid URI." ); + context.buildConstraintViolationWithTemplate( "Not a valid URI" ); return false; } diff --git a/fhir/src/asciidoc/api-guide.adoc b/fhir/src/main/asciidoc/api-guide.adoc similarity index 100% rename from fhir/src/asciidoc/api-guide.adoc rename to fhir/src/main/asciidoc/api-guide.adoc diff --git a/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/metadata/model/AbstractRule.java b/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/metadata/model/AbstractRule.java index a45a2787..4f14bd1a 100644 --- a/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/metadata/model/AbstractRule.java +++ b/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/metadata/model/AbstractRule.java @@ -84,7 +84,7 @@ public abstract class AbstractRule extends VersionedBaseMetadata implements Seri private String name; private String description; - private boolean enabled; + private boolean enabled = true; private int evaluationOrder; private DhisResourceType dhisResourceType; private FhirResourceType fhirResourceType; diff --git a/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/metadata/model/Code.java b/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/metadata/model/Code.java index 9740ece5..2144b79f 100644 --- a/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/metadata/model/Code.java +++ b/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/metadata/model/Code.java @@ -29,6 +29,7 @@ */ import com.fasterxml.jackson.annotation.JsonIgnore; +import org.springframework.data.rest.core.annotation.RestResource; import javax.persistence.Basic; import javax.persistence.Column; @@ -98,7 +99,7 @@ public void setName( String name ) } @Basic - @Column( name = "code", nullable = false, length = 50 ) + @Column( name = "code", nullable = false, length = 50, unique = true ) public String getCode() { return code; @@ -148,6 +149,7 @@ public void setCodeCategory( CodeCategory codeCategory ) @OneToMany( mappedBy = "code" ) @OrderBy( "id" ) @JsonIgnore + @RestResource( exported = false ) public List getSystemCodes() { return systemCodes; diff --git a/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/metadata/model/CodeCategory.java b/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/metadata/model/CodeCategory.java index 33e11a63..e0bd3bd1 100644 --- a/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/metadata/model/CodeCategory.java +++ b/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/metadata/model/CodeCategory.java @@ -74,7 +74,7 @@ public void setName( String name ) } @Basic - @Column( name = "code", nullable = false, length = 50 ) + @Column( name = "code", nullable = false, length = 50, unique = true ) public String getCode() { return code; diff --git a/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/metadata/model/CodeSet.java b/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/metadata/model/CodeSet.java index 55101eb8..fb3de041 100644 --- a/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/metadata/model/CodeSet.java +++ b/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/metadata/model/CodeSet.java @@ -42,6 +42,12 @@ import java.util.Objects; import java.util.SortedSet; +/** + * Contains a collection of {@linkplain Code code}. E.g. there may be multiple codes + * for vaccines that cause an immunization for measles. + * + * @author volsch + */ @Entity @Table( name = "fhir_code_set" ) public class CodeSet extends VersionedBaseMetadata implements Serializable @@ -67,7 +73,7 @@ public void setName( String name ) } @Basic - @Column( name = "code", nullable = false, length = 50 ) + @Column( name = "code", nullable = false, length = 50, unique = true ) public String getCode() { return code; diff --git a/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/metadata/model/CodeSetValue.java b/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/metadata/model/CodeSetValue.java index 0cdf0213..61fafb04 100644 --- a/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/metadata/model/CodeSetValue.java +++ b/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/metadata/model/CodeSetValue.java @@ -40,6 +40,12 @@ import java.io.Serializable; import java.util.Objects; +/** + * Contains the value of the {@linkplain CodeSet code set} which can additionally be + * enabled and disabled. + * + * @author volsch + */ @Entity @Table( name = "fhir_code_set_value" ) public class CodeSetValue implements Serializable, Comparable, FhirAdapterMetadata @@ -48,7 +54,7 @@ public class CodeSetValue implements Serializable, Comparable, Fhi private CodeSetValueId id; - private boolean enabled; + private boolean enabled = true; @EmbeddedId public CodeSetValueId getId() diff --git a/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/metadata/model/CodeSetValueId.java b/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/metadata/model/CodeSetValueId.java index 560e3f7c..d923c087 100644 --- a/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/metadata/model/CodeSetValueId.java +++ b/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/metadata/model/CodeSetValueId.java @@ -36,6 +36,12 @@ import java.io.Serializable; import java.util.Objects; +/** + * Contains the ID of a value of the {@linkplain CodeSet code set} which can additionally + * be enabled and disabled. This is just a single assigned code. + * + * @author volsch + */ @Embeddable public class CodeSetValueId implements Serializable { diff --git a/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/metadata/model/Constant.java b/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/metadata/model/Constant.java index f5e95c15..71e077af 100644 --- a/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/metadata/model/Constant.java +++ b/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/metadata/model/Constant.java @@ -28,12 +28,17 @@ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ +import org.dhis2.fhir.adapter.validator.EnumValue; + import javax.persistence.Basic; import javax.persistence.Column; import javax.persistence.Entity; import javax.persistence.EnumType; import javax.persistence.Enumerated; import javax.persistence.Table; +import javax.validation.constraints.NotBlank; +import javax.validation.constraints.NotNull; +import javax.validation.constraints.Size; import java.io.Serializable; /** @@ -54,11 +59,25 @@ public class Constant extends VersionedBaseMetadata implements Serializable public static final int MAX_VALUE_LENGTH = 250; + @NotBlank + @Size( max = MAX_NAME_LENGTH ) private String name; + private String description; + + @NotNull + @EnumValue( ConstantCategory.class ) private ConstantCategory category; + + @NotBlank + @Size( max = MAX_CODE_LENGTH ) private String code; + + @NotNull + @EnumValue( DataType.class ) private DataType dataType; + + @Size( max = MAX_VALUE_LENGTH ) private String value; @Basic @@ -99,7 +118,7 @@ public void setCategory( ConstantCategory category ) } @Basic - @Column( name = "code", nullable = false, length = 50 ) + @Column( name = "code", nullable = false, length = 50, unique = true ) public String getCode() { return code; diff --git a/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/metadata/model/ExecutableScript.java b/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/metadata/model/ExecutableScript.java index 665a391e..13ab684d 100644 --- a/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/metadata/model/ExecutableScript.java +++ b/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/metadata/model/ExecutableScript.java @@ -28,24 +28,23 @@ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -import com.fasterxml.jackson.annotation.JsonIgnore; -import com.fasterxml.jackson.databind.annotation.JsonSerialize; -import org.dhis2.fhir.adapter.jackson.PersistentBagConverter; -import org.springframework.data.rest.core.annotation.RestResource; +import com.fasterxml.jackson.annotation.JsonFilter; +import org.dhis2.fhir.adapter.jackson.ToManyPropertyFilter; import javax.persistence.Basic; import javax.persistence.CascadeType; import javax.persistence.Column; import javax.persistence.Entity; -import javax.persistence.FetchType; import javax.persistence.JoinColumn; import javax.persistence.ManyToOne; import javax.persistence.OneToMany; import javax.persistence.OrderBy; import javax.persistence.Table; +import javax.validation.constraints.NotBlank; +import javax.validation.constraints.NotNull; +import javax.validation.constraints.Size; import java.io.Serializable; import java.util.List; -import java.util.UUID; /** * Contains the definition of an executable {@linkplain Script script} where values of all mandatory arguments must @@ -55,6 +54,7 @@ */ @Entity @Table( name = "fhir_executable_script" ) +@JsonFilter( ToManyPropertyFilter.FILTER_NAME ) public class ExecutableScript extends VersionedBaseMetadata implements Serializable { private static final long serialVersionUID = -2006842064596779970L; @@ -63,16 +63,23 @@ public class ExecutableScript extends VersionedBaseMetadata implements Serializa public static final int MAX_CODE_LENGTH = 100; - private UUID id; + @NotNull private Script script; + + @NotBlank + @Size( max = MAX_NAME_LENGTH ) private String name; + + @NotBlank + @Size( max = MAX_CODE_LENGTH ) private String code; + private String description; + private List overrideArguments; @ManyToOne @JoinColumn( name = "script_id", referencedColumnName = "id", nullable = false ) - @JsonIgnore public Script getScript() { return script; @@ -96,7 +103,7 @@ public void setName( String name ) } @Basic - @Column( name = "code", nullable = false ) + @Column( name = "code", nullable = false, unique = true ) public String getCode() { return code; @@ -119,10 +126,8 @@ public void setDescription( String description ) this.description = description; } - @OneToMany( mappedBy = "script", cascade = CascadeType.ALL, orphanRemoval = true, fetch = FetchType.EAGER ) + @OneToMany( mappedBy = "script", cascade = CascadeType.ALL, orphanRemoval = true ) @OrderBy( "id" ) - @RestResource - @JsonSerialize( converter = PersistentBagConverter.class ) public List getOverrideArguments() { return overrideArguments; diff --git a/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/metadata/model/ExecutableScriptArg.java b/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/metadata/model/ExecutableScriptArg.java index bcce9142..f7e2aa46 100644 --- a/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/metadata/model/ExecutableScriptArg.java +++ b/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/metadata/model/ExecutableScriptArg.java @@ -31,6 +31,7 @@ import com.fasterxml.jackson.annotation.JsonIgnore; import org.dhis2.fhir.adapter.fhir.metadata.repository.FhirAdapterMetadata; import org.hibernate.annotations.GenericGenerator; +import org.springframework.data.rest.core.annotation.RestResource; import javax.persistence.Basic; import javax.persistence.Column; @@ -40,6 +41,7 @@ import javax.persistence.JoinColumn; import javax.persistence.ManyToOne; import javax.persistence.Table; +import javax.validation.constraints.NotNull; import java.io.Serializable; import java.util.UUID; @@ -56,10 +58,16 @@ public class ExecutableScriptArg implements Serializable, FhirAdapterMetadata executableScriptArgs; + private final Script script; + private final List scriptArgs; + private final ScriptSource scriptSource; - public ExecutableScriptInfo( @JsonProperty( "executableScript" ) @NonNull ExecutableScript executableScript, @JsonProperty( "script" ) @Nonnull Script script, @JsonProperty( "scriptSource" ) @Nonnull ScriptSource scriptSource ) + public ExecutableScriptInfo( @JsonProperty( "executableScript" ) @NonNull ExecutableScript executableScript, @JsonProperty( "executableScriptArgs" ) @Nonnull List executableScriptArgs, + @JsonProperty( "script" ) @Nonnull Script script, @JsonProperty( "scriptArgs" ) @Nonnull List scriptArgs, + @JsonProperty( "scriptSource" ) @Nonnull ScriptSource scriptSource ) { this.executableScript = executableScript; + // do not use persistence specific list implementations when serializing to JSON + this.executableScriptArgs = new ArrayList<>( executableScriptArgs ); this.script = script; + // do not use persistence specific list implementations when serializing to JSON + this.scriptArgs = new ArrayList<>( scriptArgs ); this.scriptSource = scriptSource; } @@ -68,6 +80,18 @@ public Script getScript() return script; } + @Nonnull + public List getExecutableScriptArgs() + { + return executableScriptArgs; + } + + @Nonnull + public List getScriptArgs() + { + return scriptArgs; + } + @Nonnull public ScriptSource getScriptSource() { diff --git a/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/metadata/model/MappedTrackerProgram.java b/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/metadata/model/MappedTrackerProgram.java index a6712f36..92b6aabc 100644 --- a/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/metadata/model/MappedTrackerProgram.java +++ b/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/metadata/model/MappedTrackerProgram.java @@ -54,7 +54,7 @@ public class MappedTrackerProgram extends VersionedBaseMetadata implements Seria private String name; private String description; private Reference programReference; - private boolean enabled; + private boolean enabled = true; private TrackedEntityRule trackedEntityRule; private boolean creationEnabled; private ExecutableScript creationApplicableScript; diff --git a/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/metadata/model/MappedTrackerProgramStage.java b/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/metadata/model/MappedTrackerProgramStage.java index b8c9b081..a5714a6f 100644 --- a/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/metadata/model/MappedTrackerProgramStage.java +++ b/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/metadata/model/MappedTrackerProgramStage.java @@ -52,7 +52,7 @@ public class MappedTrackerProgramStage extends VersionedBaseMetadata implements private String name; private String description; private Reference programStageReference; - private boolean enabled; + private boolean enabled = true; private MappedTrackerProgram program; private boolean creationEnabled; private ExecutableScript creationApplicableScript; diff --git a/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/metadata/model/RemoteSubscription.java b/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/metadata/model/RemoteSubscription.java index f3cb755d..3c91d11a 100644 --- a/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/metadata/model/RemoteSubscription.java +++ b/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/metadata/model/RemoteSubscription.java @@ -32,6 +32,7 @@ import com.fasterxml.jackson.annotation.JsonProperty; import org.dhis2.fhir.adapter.fhir.model.FhirVersion; import org.dhis2.fhir.adapter.jackson.ToManyPropertyFilter; +import org.dhis2.fhir.adapter.validator.EnumValue; import javax.persistence.Basic; import javax.persistence.CascadeType; @@ -44,6 +45,11 @@ import javax.persistence.OrderBy; import javax.persistence.Table; import javax.persistence.Transient; +import javax.validation.Valid; +import javax.validation.constraints.Min; +import javax.validation.constraints.NotBlank; +import javax.validation.constraints.NotNull; +import javax.validation.constraints.Size; import java.io.Serializable; import java.util.List; import java.util.Set; @@ -64,18 +70,44 @@ public class RemoteSubscription extends VersionedBaseMetadata implements Seriali public static final int MAX_CODE_LENGTH = 20; + @NotBlank + @Size( max = MAX_NAME_LENGTH ) private String name; + + @NotBlank + @Size( max = MAX_CODE_LENGTH ) private String code; - private boolean enabled; + + private boolean enabled = true; + private boolean locked; + private String description; + + @NotNull + @EnumValue( FhirVersion.class ) private FhirVersion fhirVersion; + + @Min( 0 ) private int toleranceMillis; + + @NotNull + @Valid private SubscriptionDhisEndpoint dhisEndpoint; + + @NotNull + @Valid private SubscriptionFhirEndpoint fhirEndpoint; + + @NotNull + @Valid private SubscriptionAdapterEndpoint adapterEndpoint; + private List resources; + private List systems; + + @EnumValue( value = FhirResourceType.class, supported = { "PATIENT" } ) private Set autoCreatedSubscriptionResources; @Basic @@ -91,7 +123,7 @@ public void setName( String name ) } @Basic - @Column( name = "code", nullable = false, length = 20 ) + @Column( name = "code", nullable = false, length = 20, unique = true ) public String getCode() { return code; diff --git a/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/metadata/model/RemoteSubscriptionResource.java b/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/metadata/model/RemoteSubscriptionResource.java index c0dc6dfd..22b6696c 100644 --- a/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/metadata/model/RemoteSubscriptionResource.java +++ b/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/metadata/model/RemoteSubscriptionResource.java @@ -29,6 +29,8 @@ */ import com.fasterxml.jackson.annotation.JsonIgnore; +import com.fasterxml.jackson.annotation.JsonInclude; +import org.dhis2.fhir.adapter.validator.EnumValue; import org.springframework.data.rest.core.annotation.RestResource; import javax.persistence.Basic; @@ -41,6 +43,8 @@ import javax.persistence.ManyToOne; import javax.persistence.OneToOne; import javax.persistence.Table; +import javax.validation.constraints.NotNull; +import javax.validation.constraints.Size; import java.io.Serializable; /** @@ -54,11 +58,22 @@ public class RemoteSubscriptionResource extends VersionedBaseMetadata implements { private static final long serialVersionUID = -6797001318266984453L; + public static final int MAX_CRITERIA_PARAMETERS_LENGTH = 200; + + @NotNull + @EnumValue( FhirResourceType.class ) private FhirResourceType fhirResourceType; + + @Size( max = MAX_CRITERIA_PARAMETERS_LENGTH ) private String fhirCriteriaParameters; + private String description; + + @NotNull private RemoteSubscription remoteSubscription; + private RemoteSubscriptionResourceUpdate resourceUpdate; + private String fhirSubscriptionId; @Basic @@ -75,7 +90,7 @@ public void setFhirResourceType( FhirResourceType fhirResourceType ) } @Basic - @Column( name = "fhir_criteria_parameters", length = 200 ) + @Column( name = "fhir_criteria_parameters", length = MAX_CRITERIA_PARAMETERS_LENGTH ) public String getFhirCriteriaParameters() { return fhirCriteriaParameters; @@ -125,6 +140,7 @@ public void setResourceUpdate( RemoteSubscriptionResourceUpdate resourceUpdate ) @Basic @Column( name = "fhir_subscription_id", length = 100 ) + @JsonInclude( JsonInclude.Include.NON_NULL ) public String getFhirSubscriptionId() { return fhirSubscriptionId; diff --git a/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/metadata/model/RemoteSubscriptionSystem.java b/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/metadata/model/RemoteSubscriptionSystem.java index 9832f7bc..2307db8f 100644 --- a/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/metadata/model/RemoteSubscriptionSystem.java +++ b/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/metadata/model/RemoteSubscriptionSystem.java @@ -28,6 +28,8 @@ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ +import org.dhis2.fhir.adapter.validator.EnumValue; + import javax.persistence.Basic; import javax.persistence.Column; import javax.persistence.Entity; @@ -36,8 +38,15 @@ import javax.persistence.JoinColumn; import javax.persistence.ManyToOne; import javax.persistence.Table; +import javax.validation.constraints.NotNull; +import javax.validation.constraints.Size; import java.io.Serializable; +/** + * Contains the subscription related system URI assignments. + * + * @author volsch + */ @Entity @Table( name = "fhir_remote_subscription_system" ) public class RemoteSubscriptionSystem extends VersionedBaseMetadata implements Serializable @@ -46,9 +55,17 @@ public class RemoteSubscriptionSystem extends VersionedBaseMetadata implements S public static final int MAX_CODE_PREFIX_LENGTH = 20; + @NotNull private RemoteSubscription remoteSubscription; + + @NotNull + @EnumValue( FhirResourceType.class ) private FhirResourceType fhirResourceType; + + @NotNull private System system; + + @Size( max = MAX_CODE_PREFIX_LENGTH ) private String codePrefix; @Basic diff --git a/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/metadata/model/RequestHeader.java b/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/metadata/model/RequestHeader.java index 6ff5788a..78a4194e 100644 --- a/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/metadata/model/RequestHeader.java +++ b/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/metadata/model/RequestHeader.java @@ -37,6 +37,8 @@ import javax.annotation.Nonnull; import javax.persistence.Embeddable; +import javax.validation.constraints.NotBlank; +import javax.validation.constraints.Size; import java.io.Serializable; import java.util.Objects; @@ -50,10 +52,13 @@ public class RequestHeader implements Serializable, Comparable, C public static final int MAX_VALUE_LENGTH = 200; + @NotBlank + @Size( max = MAX_NAME_LENGTH ) private String name; @JsonProperty @SecuredProperty + @Size( max = MAX_VALUE_LENGTH ) private String value; private boolean secure; diff --git a/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/metadata/model/Script.java b/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/metadata/model/Script.java index bfa30b33..ba254945 100644 --- a/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/metadata/model/Script.java +++ b/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/metadata/model/Script.java @@ -28,10 +28,12 @@ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ +import com.fasterxml.jackson.annotation.JsonFilter; import com.fasterxml.jackson.annotation.JsonIgnore; import com.fasterxml.jackson.databind.annotation.JsonSerialize; import org.dhis2.fhir.adapter.fhir.metadata.model.jackson.ScriptVariablePersistentSortedSetConverter; -import org.dhis2.fhir.adapter.jackson.PersistentBagConverter; +import org.dhis2.fhir.adapter.jackson.ToManyPropertyFilter; +import org.dhis2.fhir.adapter.validator.EnumValue; import javax.persistence.Basic; import javax.persistence.CascadeType; @@ -46,6 +48,9 @@ import javax.persistence.OneToMany; import javax.persistence.OrderBy; import javax.persistence.Table; +import javax.validation.constraints.NotBlank; +import javax.validation.constraints.NotNull; +import javax.validation.constraints.Size; import java.io.Serializable; import java.util.List; import java.util.SortedSet; @@ -59,6 +64,7 @@ */ @Entity @Table( name = "fhir_script" ) +@JsonFilter( ToManyPropertyFilter.FILTER_NAME ) public class Script extends VersionedBaseMetadata implements Serializable { private static final long serialVersionUID = 2166269559735726192L; @@ -67,15 +73,34 @@ public class Script extends VersionedBaseMetadata implements Serializable public static final int MAX_CODE_LENGTH = 50; + @NotBlank + @Size( max = MAX_NAME_LENGTH ) private String name; + private String description; + + @NotBlank + @Size( max = MAX_CODE_LENGTH ) private String code; + + @NotNull + @EnumValue( ScriptType.class ) private ScriptType scriptType; + + @NotNull + @EnumValue( DataType.class ) private DataType returnType; + + @EnumValue( TransformDataType.class ) private TransformDataType inputType; + + @EnumValue( TransformDataType.class ) private TransformDataType outputType; + private List arguments; + private SortedSet variables; + private List sources; @Basic @@ -103,7 +128,7 @@ public void setDescription( String description ) } @Basic - @Column( name = "code", nullable = false, length = 50 ) + @Column( name = "code", nullable = false, length = 50, unique = true ) public String getCode() { return code; @@ -168,7 +193,6 @@ public void setOutputType( TransformDataType outputType ) @OneToMany( mappedBy = "script" ) @OrderBy( "id" ) - @JsonSerialize( converter = PersistentBagConverter.class ) public List getArguments() { return arguments; diff --git a/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/metadata/model/ScriptArg.java b/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/metadata/model/ScriptArg.java index 9bdde6f9..77593824 100644 --- a/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/metadata/model/ScriptArg.java +++ b/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/metadata/model/ScriptArg.java @@ -28,7 +28,9 @@ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -import com.fasterxml.jackson.annotation.JsonIgnore; +import com.fasterxml.jackson.annotation.JsonFilter; +import org.dhis2.fhir.adapter.jackson.ToOnePropertyFilter; +import org.dhis2.fhir.adapter.validator.EnumValue; import javax.annotation.Nullable; import javax.persistence.Basic; @@ -39,6 +41,9 @@ import javax.persistence.JoinColumn; import javax.persistence.ManyToOne; import javax.persistence.Table; +import javax.validation.constraints.NotBlank; +import javax.validation.constraints.NotNull; +import javax.validation.constraints.Size; import java.io.Serializable; import java.util.regex.Pattern; @@ -50,6 +55,7 @@ */ @Entity @Table( name = "fhir_script_argument" ) +@JsonFilter( ToOnePropertyFilter.FILTER_NAME ) public class ScriptArg extends VersionedBaseMetadata implements Serializable { private static final long serialVersionUID = -5052962742547037363L; @@ -62,12 +68,23 @@ public class ScriptArg extends VersionedBaseMetadata implements Serializable protected static final String ARRAY_SEPARATOR_REGEXP = Pattern.quote( ARRAY_SEPARATOR ); + @NotBlank + @Size( max = MAX_NAME_LENGTH ) private String name; + + @NotNull + @EnumValue( DataType.class ) private DataType dataType; + private boolean mandatory; + private boolean array; + private String defaultValue; + private String description; + + @NotNull private Script script; @Basic @@ -145,7 +162,6 @@ public void setDescription( String description ) @ManyToOne( optional = false ) @JoinColumn( name = "script_id", referencedColumnName = "id", nullable = false ) - @JsonIgnore public Script getScript() { return script; diff --git a/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/metadata/model/ScriptSource.java b/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/metadata/model/ScriptSource.java index 1a9efca1..daf4e88d 100644 --- a/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/metadata/model/ScriptSource.java +++ b/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/metadata/model/ScriptSource.java @@ -28,10 +28,10 @@ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -import com.fasterxml.jackson.annotation.JsonIgnore; import com.fasterxml.jackson.databind.annotation.JsonSerialize; import org.dhis2.fhir.adapter.fhir.metadata.model.jackson.FhirVersionPersistentSortedSetConverter; import org.dhis2.fhir.adapter.fhir.model.FhirVersion; +import org.dhis2.fhir.adapter.validator.EnumValue; import javax.persistence.Basic; import javax.persistence.CollectionTable; @@ -44,6 +44,9 @@ import javax.persistence.ManyToOne; import javax.persistence.OrderBy; import javax.persistence.Table; +import javax.validation.constraints.NotBlank; +import javax.validation.constraints.NotNull; +import javax.validation.constraints.Size; import java.io.Serializable; import java.util.SortedSet; @@ -58,9 +61,18 @@ public class ScriptSource extends VersionedBaseMetadata implements Serializable { private static final long serialVersionUID = 6002604151209645784L; + @NotBlank private String sourceText; + + @NotNull + @EnumValue( ScriptSourceType.class ) private ScriptSourceType sourceType; + + @NotNull private Script script; + + @NotNull + @Size( min = 1 ) private SortedSet fhirVersions; @Basic @@ -90,7 +102,6 @@ public void setSourceType( ScriptSourceType language ) @ManyToOne @JoinColumn( name = "script_id", referencedColumnName = "id", nullable = false ) - @JsonIgnore public Script getScript() { return script; diff --git a/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/metadata/model/SubscriptionAdapterEndpoint.java b/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/metadata/model/SubscriptionAdapterEndpoint.java index 9593615e..903ee844 100644 --- a/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/metadata/model/SubscriptionAdapterEndpoint.java +++ b/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/metadata/model/SubscriptionAdapterEndpoint.java @@ -28,11 +28,16 @@ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ +import org.hibernate.validator.constraints.URL; + import javax.persistence.Basic; import javax.persistence.Column; import javax.persistence.Embeddable; import javax.persistence.EnumType; import javax.persistence.Enumerated; +import javax.validation.constraints.NotBlank; +import javax.validation.constraints.NotNull; +import javax.validation.constraints.Size; import java.io.Serializable; /** @@ -49,8 +54,16 @@ public class SubscriptionAdapterEndpoint implements Serializable public static final int MAX_AUTHORIZATION_HEADER_LENGTH = 200; + @NotBlank + @URL + @Size( max = MAX_BASE_URL_LENGTH ) private String baseUrl; + + @NotBlank + @Size( max = MAX_AUTHORIZATION_HEADER_LENGTH ) private String authorizationHeader; + + @NotNull private SubscriptionType subscriptionType = SubscriptionType.REST_HOOK; @Basic diff --git a/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/metadata/model/SubscriptionDhisEndpoint.java b/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/metadata/model/SubscriptionDhisEndpoint.java index ef02f060..394dcedf 100644 --- a/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/metadata/model/SubscriptionDhisEndpoint.java +++ b/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/metadata/model/SubscriptionDhisEndpoint.java @@ -38,6 +38,9 @@ import javax.persistence.Embeddable; import javax.persistence.EnumType; import javax.persistence.Enumerated; +import javax.validation.constraints.NotBlank; +import javax.validation.constraints.NotNull; +import javax.validation.constraints.Size; import java.io.Serializable; /** @@ -55,8 +58,15 @@ public class SubscriptionDhisEndpoint implements Serializable public static final int MAX_PASSWORD_LENGTH = 200; + @NotNull private AuthenticationMethod authenticationMethod; + + @NotBlank + @Size( max = MAX_USERNAME_LENGTH ) private String username; + + @NotBlank + @Size( max = MAX_PASSWORD_LENGTH ) private String password; @Enumerated( EnumType.STRING ) diff --git a/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/metadata/model/SubscriptionFhirEndpoint.java b/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/metadata/model/SubscriptionFhirEndpoint.java index 95405982..a4f0f243 100644 --- a/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/metadata/model/SubscriptionFhirEndpoint.java +++ b/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/metadata/model/SubscriptionFhirEndpoint.java @@ -30,6 +30,7 @@ import com.fasterxml.jackson.databind.annotation.JsonSerialize; import org.dhis2.fhir.adapter.jackson.PersistentSortedSetConverter; +import org.hibernate.validator.constraints.URL; import javax.persistence.Basic; import javax.persistence.CollectionTable; @@ -39,6 +40,9 @@ import javax.persistence.FetchType; import javax.persistence.JoinColumn; import javax.persistence.OrderBy; +import javax.validation.Valid; +import javax.validation.constraints.NotBlank; +import javax.validation.constraints.Size; import java.io.Serializable; import java.util.SortedSet; @@ -54,9 +58,16 @@ public class SubscriptionFhirEndpoint implements Serializable public static final int MAX_BASE_URL_LENGTH = 200; + @NotBlank + @URL + @Size( max = MAX_BASE_URL_LENGTH ) private String baseUrl; + private boolean logging; + private boolean verboseLogging; + + @Valid private SortedSet headers; @Basic diff --git a/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/metadata/model/System.java b/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/metadata/model/System.java index 5eb35152..e519c627 100644 --- a/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/metadata/model/System.java +++ b/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/metadata/model/System.java @@ -32,6 +32,8 @@ import javax.persistence.Column; import javax.persistence.Entity; import javax.persistence.Table; +import javax.validation.constraints.NotBlank; +import javax.validation.constraints.Size; import java.io.Serializable; @Entity @@ -46,11 +48,22 @@ public class System extends VersionedBaseMetadata implements Serializable public static final int MAX_SYSTEM_URI_LENGTH = 120; + @NotBlank + @Size( max = MAX_NAME_LENGTH ) private String name; + + @NotBlank + @Size( max = MAX_CODE_LENGTH ) private String code; + + @NotBlank + @Size( max = MAX_SYSTEM_URI_LENGTH ) private String systemUri; - private boolean enabled; + + private boolean enabled = true; + private String description; + private boolean descriptionProtected; @Basic @@ -66,7 +79,7 @@ public void setName( String name ) } @Basic - @Column( name = "code", nullable = false, length = 50 ) + @Column( name = "code", nullable = false, length = 50, unique = true ) public String getCode() { return code; diff --git a/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/metadata/model/SystemCode.java b/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/metadata/model/SystemCode.java index ce6c9807..513de938 100644 --- a/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/metadata/model/SystemCode.java +++ b/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/metadata/model/SystemCode.java @@ -38,8 +38,12 @@ import javax.persistence.JoinColumn; import javax.persistence.ManyToOne; import javax.persistence.PrePersist; +import javax.persistence.PreUpdate; import javax.persistence.Table; import javax.persistence.Transient; +import javax.validation.constraints.NotBlank; +import javax.validation.constraints.NotNull; +import javax.validation.constraints.Size; import java.io.Serializable; /** @@ -60,9 +64,16 @@ public class SystemCode extends VersionedBaseMetadata implements Serializable public static final int MAX_SYSTEM_CODE_LENGTH = 120; + @NotNull private System system; + + @NotBlank + @Size( max = MAX_SYSTEM_CODE_LENGTH ) private String systemCode; + + @NotNull private Code code; + private String systemCodeValue; @Basic @@ -122,6 +133,7 @@ public SystemCodeValue getCalculatedSystemCodeValue() } @PrePersist + @PreUpdate protected void prePersist() { setSystemCodeValue( getSystem().getSystemUri() + SystemCodeValue.SEPARATOR + getSystemCode() ); diff --git a/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/metadata/repository/FhirResourceMappingRepository.java b/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/metadata/repository/FhirResourceMappingRepository.java index 6e6914c3..c3eed3d2 100644 --- a/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/metadata/repository/FhirResourceMappingRepository.java +++ b/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/metadata/repository/FhirResourceMappingRepository.java @@ -36,7 +36,6 @@ import org.springframework.cache.annotation.Cacheable; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.data.repository.query.Param; -import org.springframework.data.rest.core.annotation.RepositoryRestResource; import org.springframework.data.rest.core.annotation.RestResource; import org.springframework.security.access.prepost.PreAuthorize; @@ -51,7 +50,6 @@ * @author volsch */ @CacheConfig( cacheManager = "metadataCacheManager", cacheNames = "resourceMapping" ) -@RepositoryRestResource @PreAuthorize( "hasRole('DATA_MAPPING')" ) public interface FhirResourceMappingRepository extends JpaRepository { diff --git a/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/metadata/repository/ScriptSourceRepository.java b/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/metadata/repository/ScriptSourceRepository.java index 3267a6ac..01c46024 100644 --- a/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/metadata/repository/ScriptSourceRepository.java +++ b/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/metadata/repository/ScriptSourceRepository.java @@ -46,7 +46,7 @@ * @author volsch */ @CacheConfig( cacheManager = "metadataCacheManager", cacheNames = "scriptSource" ) -@RepositoryRestResource +@RepositoryRestResource( path = "scriptSources" ) @PreAuthorize( "hasRole('DATA_MAPPING')" ) public interface ScriptSourceRepository extends JpaRepository { diff --git a/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/metadata/repository/impl/CustomExecutableScriptRepositoryImpl.java b/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/metadata/repository/impl/CustomExecutableScriptRepositoryImpl.java index 84a4edb7..517eed62 100644 --- a/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/metadata/repository/impl/CustomExecutableScriptRepositoryImpl.java +++ b/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/metadata/repository/impl/CustomExecutableScriptRepositoryImpl.java @@ -90,6 +90,6 @@ public Optional findInfo( @Nullable ExecutableScript execu } Hibernate.initialize( scriptSource.getFhirVersions() ); - return Optional.of( new ExecutableScriptInfo( es, es.getScript(), scriptSource ) ); + return Optional.of( new ExecutableScriptInfo( es, es.getOverrideArguments(), es.getScript(), es.getScript().getArguments(), scriptSource ) ); } } diff --git a/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/metadata/repository/validator/BeforeCreateSaveExecutableScriptValidator.java b/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/metadata/repository/validator/BeforeCreateSaveExecutableScriptValidator.java index 8bf9f9cf..2dfd43dc 100644 --- a/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/metadata/repository/validator/BeforeCreateSaveExecutableScriptValidator.java +++ b/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/metadata/repository/validator/BeforeCreateSaveExecutableScriptValidator.java @@ -38,6 +38,7 @@ import javax.annotation.Nonnull; import java.util.HashSet; +import java.util.Objects; import java.util.Set; import java.util.UUID; import java.util.stream.Collectors; @@ -89,26 +90,35 @@ public void validate( Object target, @Nonnull Errors errors ) errors.rejectValue( "code", "ExecutableScript.code.length", new Object[]{ ExecutableScript.MAX_CODE_LENGTH }, "Code must not be longer than {0} characters." ); } - if ( executableScript.getOverrideArguments() != null ) + if ( executableScript.getScript() != null ) { - final Set argIds = new HashSet<>(); - int index = 0; - for ( final ExecutableScriptArg arg : executableScript.getOverrideArguments() ) + if ( executableScript.getOverrideArguments() != null ) { - errors.pushNestedPath( "overrideArguments[" + index + "]" ); - if ( !argIds.add( arg.getArgument().getId() ) ) + final Set argIds = new HashSet<>(); + int index = 0; + for ( final ExecutableScriptArg arg : executableScript.getOverrideArguments() ) { - errors.rejectValue( null, "ExecutableScript.overrideArguments.duplicate", - new Object[]{ arg.getArgument().getId() }, "Duplicate override argument for argument {0}." ); + errors.pushNestedPath( "overrideArguments[" + index + "]" ); + if ( arg.getScript() == null ) + { + arg.setScript( executableScript ); + } + if ( !Objects.equals( arg.getScript().getId(), executableScript.getId() ) ) + { + errors.rejectValue( "script", "ExecutableScript.overrideArguments.script", + "Executable script argument does not reference executable script to which it belongs to." ); + } + if ( !argIds.add( arg.getArgument().getId() ) ) + { + errors.rejectValue( null, "ExecutableScript.overrideArguments.duplicate", + new Object[]{ arg.getArgument().getId() }, "Duplicate override argument for argument {0}." ); + } + argValidator.validate( arg, errors ); + errors.popNestedPath(); + index++; } - argValidator.validate( arg, errors ); - errors.popNestedPath(); - index++; } - } - if ( executableScript.getScript() != null ) - { final Set mandatoryArgNames = executableScript.getScript().getArguments() .stream().filter( a -> a.isMandatory() && (a.getDefaultValue() == null) ) .map( ScriptArg::getName ).collect( Collectors.toSet() ); diff --git a/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/metadata/repository/validator/BeforeCreateSaveRemoteSubscriptionResourceValidator.java b/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/metadata/repository/validator/BeforeCreateSaveRemoteSubscriptionResourceValidator.java index cdba9785..f39af3e3 100644 --- a/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/metadata/repository/validator/BeforeCreateSaveRemoteSubscriptionResourceValidator.java +++ b/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/metadata/repository/validator/BeforeCreateSaveRemoteSubscriptionResourceValidator.java @@ -28,6 +28,7 @@ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ +import org.apache.commons.lang3.StringUtils; import org.dhis2.fhir.adapter.fhir.metadata.model.RemoteSubscriptionResource; import org.springframework.stereotype.Component; import org.springframework.validation.Errors; @@ -58,5 +59,10 @@ public void validate( Object target, @Nonnull Errors errors ) { errors.rejectValue( "fhirResourceType", "RemoteSubscriptionResource.fhirResourceType.null", "FHIR resource type is mandatory." ); } + if ( StringUtils.length( remoteSubscriptionResource.getFhirCriteriaParameters() ) > RemoteSubscriptionResource.MAX_CRITERIA_PARAMETERS_LENGTH ) + { + errors.rejectValue( "fhirCriteriaParameters", "RemoteSubscriptionResource.fhirCriteriaParameters.length", new Object[]{ RemoteSubscriptionResource.MAX_CRITERIA_PARAMETERS_LENGTH }, + "FHIR criteria parameters must not be longer than {0} characters." ); + } } } diff --git a/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/script/impl/ScriptExecutorImpl.java b/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/script/impl/ScriptExecutorImpl.java index 2df28c1c..275288b6 100644 --- a/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/script/impl/ScriptExecutorImpl.java +++ b/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/script/impl/ScriptExecutorImpl.java @@ -161,8 +161,8 @@ protected Object convertSimpleReturnValue( @Nullable Object value ) private Map createArgs( @Nonnull ExecutableScriptInfo executableScriptInfo, @Nonnull Map arguments ) { - final Collection scriptArgs = executableScriptInfo.getScript().getArguments(); - final Collection executableScriptArgs = executableScriptInfo.getExecutableScript().getOverrideArguments(); + final Collection scriptArgs = executableScriptInfo.getScriptArgs(); + final Collection executableScriptArgs = executableScriptInfo.getExecutableScriptArgs(); final Map args = new HashMap<>(); // use default values of the script first diff --git a/fhir/src/test/java/org/dhis2/fhir/adapter/fhir/AbstractJpaRepositoryRestDocsTest.java b/fhir/src/test/java/org/dhis2/fhir/adapter/fhir/AbstractJpaRepositoryRestDocsTest.java index b1e960fa..2474c72b 100644 --- a/fhir/src/test/java/org/dhis2/fhir/adapter/fhir/AbstractJpaRepositoryRestDocsTest.java +++ b/fhir/src/test/java/org/dhis2/fhir/adapter/fhir/AbstractJpaRepositoryRestDocsTest.java @@ -32,24 +32,25 @@ import org.junit.Rule; import org.springframework.restdocs.JUnitRestDocumentation; import org.springframework.restdocs.mockmvc.RestDocumentationResultHandler; -import org.springframework.test.annotation.Rollback; import org.springframework.test.web.servlet.MockMvc; import org.springframework.test.web.servlet.setup.MockMvcBuilders; -import org.springframework.transaction.annotation.Transactional; import static org.springframework.restdocs.mockmvc.MockMvcRestDocumentation.document; import static org.springframework.restdocs.mockmvc.MockMvcRestDocumentation.documentationConfiguration; import static org.springframework.restdocs.operation.preprocess.Preprocessors.*; +import static org.springframework.security.test.web.servlet.setup.SecurityMockMvcConfigurers.springSecurity; /** * Abstract test class that generate REST documentation for JPA repositories. * * @author volsch */ -@Transactional -@Rollback -public abstract class AbstractJpaRepositoryRestDocsTest extends AbstractJpaRepositoryTest +//@Transactional +//@Rollback +public abstract class AbstractJpaRepositoryRestDocsTest extends AbstractMockMvcTest { + public static final String API_BASE_URI = "http://localhost:8081/api"; + @Rule public final JUnitRestDocumentation restDocumentation = new JUnitRestDocumentation(); @@ -65,6 +66,7 @@ public void beforeAbstractJpaRepositoryRestDocsTest() preprocessResponse( removeHeaders( "X-Content-Type-Options", "X-XSS-Protection", "X-Frame-Options" ), prettyPrint() ) ); docMockMvc = MockMvcBuilders.webAppContextSetup( context ) + .apply( springSecurity() ) .apply( documentationConfiguration( restDocumentation ).uris().withPort( 8081 ) ) .alwaysDo( documentationHandler ) .build(); diff --git a/fhir/src/test/java/org/dhis2/fhir/adapter/fhir/AbstractJpaRepositoryTest.java b/fhir/src/test/java/org/dhis2/fhir/adapter/fhir/AbstractMockMvcTest.java similarity index 65% rename from fhir/src/test/java/org/dhis2/fhir/adapter/fhir/AbstractJpaRepositoryTest.java rename to fhir/src/test/java/org/dhis2/fhir/adapter/fhir/AbstractMockMvcTest.java index d2094c72..ec080b17 100644 --- a/fhir/src/test/java/org/dhis2/fhir/adapter/fhir/AbstractJpaRepositoryTest.java +++ b/fhir/src/test/java/org/dhis2/fhir/adapter/fhir/AbstractMockMvcTest.java @@ -28,51 +28,49 @@ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -import org.dhis2.fhir.adapter.fhir.data.DataBasePackage; -import org.dhis2.fhir.adapter.fhir.metadata.MetadataBasePackage; import org.dhis2.fhir.adapter.jackson.JacksonConfig; import org.junit.Before; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.autoconfigure.EnableAutoConfiguration; -import org.springframework.boot.autoconfigure.data.web.SpringDataWebAutoConfiguration; -import org.springframework.boot.autoconfigure.domain.EntityScan; -import org.springframework.boot.autoconfigure.jackson.JacksonAutoConfiguration; -import org.springframework.boot.autoconfigure.web.servlet.WebMvcAutoConfiguration; -import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest; import org.springframework.boot.test.context.SpringBootTest; -import org.springframework.context.annotation.ComponentScan; -import org.springframework.data.rest.webmvc.config.RepositoryRestMvcConfiguration; -import org.springframework.test.context.ContextConfiguration; +import org.springframework.restdocs.constraints.ConstraintDescriptionResolver; import org.springframework.test.context.TestPropertySource; import org.springframework.test.context.junit4.SpringRunner; import org.springframework.test.web.servlet.MockMvc; import org.springframework.test.web.servlet.setup.MockMvcBuilders; import org.springframework.web.context.WebApplicationContext; +import static org.springframework.security.test.web.servlet.setup.SecurityMockMvcConfigurers.springSecurity; + /** * Abstract base class for JPA Repository dependent tests. * * @author volsch */ @RunWith( SpringRunner.class ) -@EnableAutoConfiguration -@ContextConfiguration( classes = { JacksonAutoConfiguration.class, WebMvcAutoConfiguration.class, SpringDataWebAutoConfiguration.class, JacksonConfig.class, TestConfig.class, TestWebSecurityConfig.class, RepositoryRestMvcConfiguration.class } ) -@ComponentScan( basePackageClasses = { DataBasePackage.class, MetadataBasePackage.class } ) -@EntityScan( basePackageClasses = { DataBasePackage.class, MetadataBasePackage.class } ) -@SpringBootTest( webEnvironment = SpringBootTest.WebEnvironment.MOCK ) +@SpringBootTest( webEnvironment = SpringBootTest.WebEnvironment.MOCK, classes = { JacksonConfig.class, MockMvcTestConfig.class, MockMvcTestWebSecurityConfig.class } ) @TestPropertySource( "classpath:test.properties" ) -@DataJpaTest -public abstract class AbstractJpaRepositoryTest +public abstract class AbstractMockMvcTest { + public static final String AUTHORIZATION_HEADER_NAME = "Authorization"; + + public static final String CODE_MAPPING_AUTHORIZATION_HEADER_VALUE = "Basic MmgybWFxdTgyN2Q6cGFzc3dvcmQ="; + + public static final String DATA_MAPPING_AUTHORIZATION_HEADER_VALUE = "Basic MmgybWFxdTgyN2U6cGFzc3dvcmQ="; + + public static final String ADMINISTRATION_AUTHORIZATION_HEADER_VALUE = "Basic MmgybWFxdTgyN2Y6cGFzc3dvcmQ="; + + @Autowired + protected ConstraintDescriptionResolver constraintDescriptionResolver; + @Autowired protected WebApplicationContext context; protected MockMvc mockMvc; @Before - public void beforeAbstractJpaRepositoryTest() + public void beforeAbstractTest() { - mockMvc = MockMvcBuilders.webAppContextSetup( context ).build(); + mockMvc = MockMvcBuilders.webAppContextSetup( context ).apply( springSecurity() ).build(); } } diff --git a/fhir/src/test/java/org/dhis2/fhir/adapter/fhir/ConstrainedFields.java b/fhir/src/test/java/org/dhis2/fhir/adapter/fhir/ConstrainedFields.java index 52ac6f1c..003d9b1a 100644 --- a/fhir/src/test/java/org/dhis2/fhir/adapter/fhir/ConstrainedFields.java +++ b/fhir/src/test/java/org/dhis2/fhir/adapter/fhir/ConstrainedFields.java @@ -28,6 +28,7 @@ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ +import org.springframework.restdocs.constraints.ConstraintDescriptionResolver; import org.springframework.restdocs.constraints.ConstraintDescriptions; import org.springframework.restdocs.payload.FieldDescriptor; import org.springframework.util.StringUtils; @@ -46,9 +47,9 @@ public class ConstrainedFields { private final ConstraintDescriptions constraintDescriptions; - public ConstrainedFields( @Nonnull Class input ) + public ConstrainedFields( @Nonnull Class input, @Nonnull ConstraintDescriptionResolver descriptionResolver ) { - this.constraintDescriptions = new ConstraintDescriptions( input ); + constraintDescriptions = new ConstraintDescriptions( input, descriptionResolver ); } public FieldDescriptor withPath( @Nonnull String path ) diff --git a/fhir/src/test/java/org/dhis2/fhir/adapter/fhir/TestConfig.java b/fhir/src/test/java/org/dhis2/fhir/adapter/fhir/MockMvcTestConfig.java similarity index 60% rename from fhir/src/test/java/org/dhis2/fhir/adapter/fhir/TestConfig.java rename to fhir/src/test/java/org/dhis2/fhir/adapter/fhir/MockMvcTestConfig.java index 4ff2ab2a..f8efbcca 100644 --- a/fhir/src/test/java/org/dhis2/fhir/adapter/fhir/TestConfig.java +++ b/fhir/src/test/java/org/dhis2/fhir/adapter/fhir/MockMvcTestConfig.java @@ -28,19 +28,32 @@ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ +import org.apache.commons.lang3.StringUtils; import org.dhis2.fhir.adapter.converter.ZonedDateTimeToDateConverter; +import org.dhis2.fhir.adapter.fhir.data.DataBasePackage; +import org.dhis2.fhir.adapter.fhir.metadata.MetadataBasePackage; import org.dhis2.fhir.adapter.script.ScriptCompiler; import org.dhis2.fhir.adapter.script.impl.ScriptCompilerImpl; -import org.springframework.boot.test.autoconfigure.restdocs.RestDocsAutoConfiguration; +import org.dhis2.fhir.adapter.validator.EnumValue; +import org.dhis2.fhir.adapter.validator.EnumValueValidator; +import org.springframework.boot.autoconfigure.EnableAutoConfiguration; +import org.springframework.boot.autoconfigure.data.redis.RedisAutoConfiguration; +import org.springframework.boot.autoconfigure.data.redis.RedisRepositoriesAutoConfiguration; +import org.springframework.boot.autoconfigure.jms.artemis.ArtemisAutoConfiguration; +import org.springframework.cloud.netflix.hystrix.HystrixAutoConfiguration; import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Primary; import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer; import org.springframework.data.rest.core.config.RepositoryRestConfiguration; import org.springframework.data.rest.core.mapping.RepositoryDetectionStrategy; import org.springframework.data.rest.webmvc.config.RepositoryRestConfigurer; import org.springframework.data.rest.webmvc.config.RepositoryRestConfigurerAdapter; import org.springframework.format.FormatterRegistry; -import org.springframework.test.context.ContextConfiguration; +import org.springframework.restdocs.constraints.Constraint; +import org.springframework.restdocs.constraints.ConstraintDescriptionResolver; +import org.springframework.restdocs.constraints.ResourceBundleConstraintDescriptionResolver; import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; import javax.annotation.Nonnull; @@ -53,8 +66,9 @@ * @author volsch */ @Configuration -@ContextConfiguration( classes = { RestDocsAutoConfiguration.class } ) -public class TestConfig +@EnableAutoConfiguration( exclude = { RedisAutoConfiguration.class, RedisRepositoriesAutoConfiguration.class, ArtemisAutoConfiguration.class, HystrixAutoConfiguration.class } ) +@ComponentScan( basePackageClasses = { DataBasePackage.class, MetadataBasePackage.class } ) +public class MockMvcTestConfig { @Nonnull @Bean @@ -98,4 +112,29 @@ public void configureRepositoryRestConfiguration( RepositoryRestConfiguration co } }; } + + @Nonnull + @Primary + @Bean + protected ConstraintDescriptionResolver constraintDescriptionResolver() + { + return new ResourceBundleConstraintDescriptionResolver() + { + @Override + public String resolveDescription( Constraint constraint ) + { + if ( EnumValue.class.getName().equals( constraint.getName() ) ) + { + @SuppressWarnings( "unchecked" ) final Class> value = (Class>) constraint.getConfiguration().get( "value" ); + final String[] unsupported = (String[]) constraint.getConfiguration().get( "unsupported" ); + final String[] supported = (String[]) constraint.getConfiguration().get( "supported" ); + if ( value != null ) + { + return "Supported values are " + StringUtils.join( EnumValueValidator.getSupported( value, unsupported, supported ), ", " ); + } + } + return super.resolveDescription( constraint ); + } + }; + } } diff --git a/fhir/src/test/java/org/dhis2/fhir/adapter/fhir/TestWebSecurityConfig.java b/fhir/src/test/java/org/dhis2/fhir/adapter/fhir/MockMvcTestWebSecurityConfig.java similarity index 68% rename from fhir/src/test/java/org/dhis2/fhir/adapter/fhir/TestWebSecurityConfig.java rename to fhir/src/test/java/org/dhis2/fhir/adapter/fhir/MockMvcTestWebSecurityConfig.java index 0b4b55b8..28c074c3 100644 --- a/fhir/src/test/java/org/dhis2/fhir/adapter/fhir/TestWebSecurityConfig.java +++ b/fhir/src/test/java/org/dhis2/fhir/adapter/fhir/MockMvcTestWebSecurityConfig.java @@ -30,12 +30,15 @@ import org.springframework.context.annotation.Configuration; import org.springframework.http.HttpMethod; +import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder; import org.springframework.security.config.annotation.web.builders.HttpSecurity; import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; import org.springframework.security.config.http.SessionCreationPolicy; +import org.springframework.security.crypto.password.PasswordEncoder; import javax.annotation.Nonnull; +import java.util.Objects; /** * Security configuration of tests. @@ -44,7 +47,7 @@ */ @Configuration @EnableWebSecurity -public class TestWebSecurityConfig extends WebSecurityConfigurerAdapter +public class MockMvcTestWebSecurityConfig extends WebSecurityConfigurerAdapter { protected static final String DHIS_BASIC_REALM = "DHIS2"; @@ -60,4 +63,34 @@ protected void configure( @Nonnull HttpSecurity http ) throws Exception .and() .httpBasic().realmName( DHIS_BASIC_REALM ); } + + @Override + protected void configure( @Nonnull AuthenticationManagerBuilder auth ) throws Exception + { + auth.inMemoryAuthentication().passwordEncoder( new PasswordEncoder() + { + @Override + public String encode( CharSequence rawPassword ) + { + return String.valueOf( rawPassword ); + } + + @Override + public boolean matches( CharSequence rawPassword, String encodedPassword ) + { + return Objects.equals( String.valueOf( rawPassword ), encodedPassword ); + } + } ) + .withUser( "2h2maqu827d" ) + .password( "password" ) + .roles( "CODE_MAPPING" ) + .and() + .withUser( "2h2maqu827e" ) + .password( "password" ) + .roles( "DATA_MAPPING" ) + .and() + .withUser( "2h2maqu827f" ) + .password( "password" ) + .roles( "AUTHORIZATION" ); + } } diff --git a/fhir/src/test/java/org/dhis2/fhir/adapter/fhir/metadata/repository/CodeCategoryRepositoryRestDocsTest.java b/fhir/src/test/java/org/dhis2/fhir/adapter/fhir/metadata/repository/CodeCategoryRepositoryRestDocsTest.java index 5bf604fb..58e930a8 100644 --- a/fhir/src/test/java/org/dhis2/fhir/adapter/fhir/metadata/repository/CodeCategoryRepositoryRestDocsTest.java +++ b/fhir/src/test/java/org/dhis2/fhir/adapter/fhir/metadata/repository/CodeCategoryRepositoryRestDocsTest.java @@ -33,18 +33,22 @@ import org.dhis2.fhir.adapter.fhir.ConstrainedFields; import org.dhis2.fhir.adapter.fhir.metadata.model.CodeCategory; import org.junit.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.data.domain.Example; import org.springframework.http.MediaType; import org.springframework.restdocs.payload.JsonFieldType; -import org.springframework.security.test.context.support.WithMockUser; +import javax.annotation.Nonnull; import java.util.Objects; import static org.hamcrest.Matchers.is; +import static org.springframework.restdocs.hypermedia.HypermediaDocumentation.linkWithRel; +import static org.springframework.restdocs.hypermedia.HypermediaDocumentation.links; +import static org.springframework.restdocs.mockmvc.RestDocumentationRequestBuilders.get; import static org.springframework.restdocs.mockmvc.RestDocumentationRequestBuilders.post; -import static org.springframework.restdocs.payload.PayloadDocumentation.requestFields; +import static org.springframework.restdocs.payload.PayloadDocumentation.*; import static org.springframework.restdocs.snippet.Attributes.attributes; import static org.springframework.restdocs.snippet.Attributes.key; -import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*; /** @@ -54,13 +58,15 @@ */ public class CodeCategoryRepositoryRestDocsTest extends AbstractJpaRepositoryRestDocsTest { + @Autowired( required = false ) + private CodeCategoryRepository codeCategoryRepository; + @Test - @WithMockUser( username = "2h2maqu827d", password = "code_test", roles = "CODE_MAPPING" ) public void createCodeCategory() throws Exception { - final ConstrainedFields fields = new ConstrainedFields( CodeCategory.class ); - final String location = docMockMvc.perform( post( "/api/codeCategories" ).contentType( MediaType.APPLICATION_JSON ) - .content( IOUtils.resourceToByteArray( "/org/dhis2/fhir/adapter/fhir/metadata/repository/createCodeCategory.json" ) ) ) + final ConstrainedFields fields = new ConstrainedFields( CodeCategory.class, constraintDescriptionResolver ); + final String location = docMockMvc.perform( post( "/api/codeCategories" ).header( AUTHORIZATION_HEADER_NAME, CODE_MAPPING_AUTHORIZATION_HEADER_VALUE ) + .contentType( MediaType.APPLICATION_JSON ).content( IOUtils.resourceToByteArray( "/org/dhis2/fhir/adapter/fhir/metadata/repository/createCodeCategory.json" ) ) ) .andExpect( status().isCreated() ) .andExpect( header().exists( "Location" ) ) .andDo( documentationHandler.document( requestFields( @@ -71,11 +77,41 @@ public void createCodeCategory() throws Exception ) ) ).andReturn().getResponse().getHeader( "Location" ); mockMvc - .perform( get( Objects.requireNonNull( location ) ) ) + .perform( get( Objects.requireNonNull( location ) ).header( AUTHORIZATION_HEADER_NAME, CODE_MAPPING_AUTHORIZATION_HEADER_VALUE ) ) .andExpect( status().isOk() ) .andExpect( jsonPath( "lastUpdatedBy", is( "2h2maqu827d" ) ) ) .andExpect( jsonPath( "name", is( "Test Code Category" ) ) ) .andExpect( jsonPath( "code", is( "TEST_CODE_CATEGORY" ) ) ) + .andExpect( jsonPath( "description", is( "This is a test code category." ) ) ) .andExpect( jsonPath( "_links.self.href", is( location ) ) ); } + + @Test + public void readCodeCategory() throws Exception + { + final ConstrainedFields fields = new ConstrainedFields( CodeCategory.class, constraintDescriptionResolver ); + final String codeCategoryId = loadCodeCategory( "ORGANIZATION_UNIT" ).getId().toString(); + docMockMvc.perform( get( "/api/codeCategories/{codeCategoryId}", codeCategoryId ).header( AUTHORIZATION_HEADER_NAME, CODE_MAPPING_AUTHORIZATION_HEADER_VALUE ) ) + .andExpect( status().isOk() ) + .andDo( documentationHandler.document( links( + linkWithRel( "self" ).description( "Link to this resource itself." ), + linkWithRel( "codeCategory" ).description( "Link to this resource itself." ) ), responseFields( + attributes( key( "title" ).value( "Fields for code category reading" ) ), + fields.withPath( "createdAt" ).description( "The timestamp when the resource has been created." ).type( JsonFieldType.STRING ), + fields.withPath( "lastUpdatedBy" ).description( "The ID of the user that has updated the user the last time or null if the data has been imported to the database directly." ).type( JsonFieldType.STRING ).optional(), + fields.withPath( "lastUpdatedAt" ).description( "The timestamp when the resource has been updated the last time." ).type( JsonFieldType.STRING ), + fields.withPath( "name" ).description( "The unique name of the code category." ).type( JsonFieldType.STRING ), + fields.withPath( "code" ).description( "The unique code of the code category." ).type( JsonFieldType.STRING ), + fields.withPath( "description" ).description( "The detailed description that describes for which purpose the code category is used." ).type( JsonFieldType.STRING ).optional(), + subsectionWithPath( "_links" ).description( "Links to other resources" ) + ) ) ); + } + + @Nonnull + protected CodeCategory loadCodeCategory( @Nonnull String code ) + { + final CodeCategory example = new CodeCategory(); + example.setCode( code ); + return codeCategoryRepository.findOne( Example.of( example ) ).orElseThrow( () -> new AssertionError( "Code category does not exist: " + code ) ); + } } \ No newline at end of file diff --git a/fhir/src/test/java/org/dhis2/fhir/adapter/fhir/metadata/repository/CodeRepositoryRestDocsTest.java b/fhir/src/test/java/org/dhis2/fhir/adapter/fhir/metadata/repository/CodeRepositoryRestDocsTest.java new file mode 100644 index 00000000..18a5c987 --- /dev/null +++ b/fhir/src/test/java/org/dhis2/fhir/adapter/fhir/metadata/repository/CodeRepositoryRestDocsTest.java @@ -0,0 +1,139 @@ +package org.dhis2.fhir.adapter.fhir.metadata.repository; + +/* + * Copyright (c) 2004-2018, University of Oslo + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * Neither the name of the HISP project nor the names of its contributors may + * be used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +import org.apache.commons.io.IOUtils; +import org.dhis2.fhir.adapter.fhir.AbstractJpaRepositoryRestDocsTest; +import org.dhis2.fhir.adapter.fhir.ConstrainedFields; +import org.dhis2.fhir.adapter.fhir.metadata.model.Code; +import org.dhis2.fhir.adapter.fhir.metadata.model.CodeCategory; +import org.junit.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.data.domain.Example; +import org.springframework.http.MediaType; +import org.springframework.restdocs.payload.JsonFieldType; + +import javax.annotation.Nonnull; +import java.nio.charset.StandardCharsets; +import java.util.Objects; + +import static org.hamcrest.Matchers.is; +import static org.springframework.restdocs.hypermedia.HypermediaDocumentation.linkWithRel; +import static org.springframework.restdocs.hypermedia.HypermediaDocumentation.links; +import static org.springframework.restdocs.mockmvc.RestDocumentationRequestBuilders.get; +import static org.springframework.restdocs.mockmvc.RestDocumentationRequestBuilders.post; +import static org.springframework.restdocs.payload.PayloadDocumentation.*; +import static org.springframework.restdocs.snippet.Attributes.attributes; +import static org.springframework.restdocs.snippet.Attributes.key; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*; + +/** + * Tests for {@link CodeRepository}. + * + * @author volsch + */ +public class CodeRepositoryRestDocsTest extends AbstractJpaRepositoryRestDocsTest +{ + @Autowired( required = false ) + private CodeCategoryRepository codeCategoryRepository; + + @Autowired( required = false ) + private CodeRepository codeRepository; + + @Test + public void createCode() throws Exception + { + final ConstrainedFields fields = new ConstrainedFields( Code.class, constraintDescriptionResolver ); + final String codeCategoryId = loadCodeCategory( "ORGANIZATION_UNIT" ).getId().toString(); + final String request = IOUtils.resourceToString( "/org/dhis2/fhir/adapter/fhir/metadata/repository/createCode.json", StandardCharsets.UTF_8 ) + .replace( "$codeCategory", API_BASE_URI + "/codeCategories/" + codeCategoryId ); + final String location = docMockMvc.perform( post( "/api/codes" ).header( AUTHORIZATION_HEADER_NAME, CODE_MAPPING_AUTHORIZATION_HEADER_VALUE ) + .contentType( MediaType.APPLICATION_JSON ).content( request ) ) + .andExpect( status().isCreated() ) + .andExpect( header().exists( "Location" ) ) + .andDo( documentationHandler.document( requestFields( + attributes( key( "title" ).value( "Fields for code creation" ) ), + fields.withPath( "name" ).description( "The unique name of the code." ).type( JsonFieldType.STRING ), + fields.withPath( "code" ).description( "The unique code of the code." ).type( JsonFieldType.STRING ), + fields.withPath( "mappedCode" ).description( "The optional mapped code (e.g. organization unit code as it exists on DHIS2). If this is not specified the code itself is used." ).type( JsonFieldType.STRING ).optional(), + fields.withPath( "description" ).description( "The detailed description that describes for which purpose the code is used." ).type( JsonFieldType.STRING ).optional(), + fields.withPath( "codeCategory" ).description( "The reference to the code category to which this code belongs to." ).type( JsonFieldType.STRING ) + ) ) ).andReturn().getResponse().getHeader( "Location" ); + + mockMvc + .perform( get( Objects.requireNonNull( location ) ).header( AUTHORIZATION_HEADER_NAME, CODE_MAPPING_AUTHORIZATION_HEADER_VALUE ) ) + .andExpect( status().isOk() ) + .andExpect( jsonPath( "lastUpdatedBy", is( "2h2maqu827d" ) ) ) + .andExpect( jsonPath( "name", is( "Test Code" ) ) ) + .andExpect( jsonPath( "code", is( "TEST_CODE" ) ) ) + .andExpect( jsonPath( "mappedCode", is( "MAPPED_TEST_CODE" ) ) ) + .andExpect( jsonPath( "description", is( "This is a test code." ) ) ) + .andExpect( jsonPath( "_links.self.href", is( location ) ) ) + .andExpect( jsonPath( "_links.codeCategory.href", is( location + "/codeCategory" ) ) ); + } + + @Test + public void readCode() throws Exception + { + final ConstrainedFields fields = new ConstrainedFields( Code.class, constraintDescriptionResolver ); + final String codeId = loadCode( "OU_FT_CH" ).getId().toString(); + docMockMvc.perform( get( "/api/codes/{codeId}", codeId ).header( AUTHORIZATION_HEADER_NAME, CODE_MAPPING_AUTHORIZATION_HEADER_VALUE ) ) + .andExpect( status().isOk() ) + .andDo( documentationHandler.document( links( + linkWithRel( "self" ).description( "Link to this resource itself." ), + linkWithRel( "code" ).description( "Link to this resource itself." ), + linkWithRel( "codeCategory" ).description( "Link to this resource itself." ) ), responseFields( + attributes( key( "title" ).value( "Fields for code category reading" ) ), + fields.withPath( "createdAt" ).description( "The timestamp when the resource has been created." ).type( JsonFieldType.STRING ), + fields.withPath( "lastUpdatedBy" ).description( "The ID of the user that has updated the user the last time or null if the data has been imported to the database directly." ).type( JsonFieldType.STRING ).optional(), + fields.withPath( "lastUpdatedAt" ).description( "The timestamp when the resource has been updated the last time." ).type( JsonFieldType.STRING ), + fields.withPath( "name" ).description( "The unique name of the code." ).type( JsonFieldType.STRING ), + fields.withPath( "code" ).description( "The unique code of the code." ).type( JsonFieldType.STRING ), + fields.withPath( "mappedCode" ).description( "The optional mapped code (e.g. organization unit code as it exists on DHIS2). If this is not specified the code itself is used." ).type( JsonFieldType.STRING ).optional(), + fields.withPath( "description" ).description( "The detailed description that describes for which purpose the code is used." ).type( JsonFieldType.STRING ).optional(), + subsectionWithPath( "_links" ).description( "Links to other resources" ) + ) ) ); + } + + @Nonnull + protected Code loadCode( @Nonnull String code ) + { + final Code example = new Code(); + example.setCode( code ); + return codeRepository.findOne( Example.of( example ) ).orElseThrow( () -> new AssertionError( "Code does not exist: " + code ) ); + } + + @Nonnull + protected CodeCategory loadCodeCategory( @Nonnull String code ) + { + final CodeCategory example = new CodeCategory(); + example.setCode( code ); + return codeCategoryRepository.findOne( Example.of( example ) ).orElseThrow( () -> new AssertionError( "Code category does not exist: " + code ) ); + } +} \ No newline at end of file diff --git a/fhir/src/test/java/org/dhis2/fhir/adapter/fhir/metadata/repository/ConstantRepositoryRestDocsTest.java b/fhir/src/test/java/org/dhis2/fhir/adapter/fhir/metadata/repository/ConstantRepositoryRestDocsTest.java new file mode 100644 index 00000000..f7e465a2 --- /dev/null +++ b/fhir/src/test/java/org/dhis2/fhir/adapter/fhir/metadata/repository/ConstantRepositoryRestDocsTest.java @@ -0,0 +1,128 @@ +package org.dhis2.fhir.adapter.fhir.metadata.repository; + +/* + * Copyright (c) 2004-2018, University of Oslo + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * Neither the name of the HISP project nor the names of its contributors may + * be used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +import org.apache.commons.io.IOUtils; +import org.dhis2.fhir.adapter.fhir.AbstractJpaRepositoryRestDocsTest; +import org.dhis2.fhir.adapter.fhir.ConstrainedFields; +import org.dhis2.fhir.adapter.fhir.metadata.model.Constant; +import org.junit.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.data.domain.Example; +import org.springframework.http.MediaType; +import org.springframework.restdocs.payload.JsonFieldType; + +import javax.annotation.Nonnull; +import java.nio.charset.StandardCharsets; +import java.util.Objects; + +import static org.hamcrest.Matchers.is; +import static org.springframework.restdocs.hypermedia.HypermediaDocumentation.linkWithRel; +import static org.springframework.restdocs.hypermedia.HypermediaDocumentation.links; +import static org.springframework.restdocs.mockmvc.RestDocumentationRequestBuilders.get; +import static org.springframework.restdocs.mockmvc.RestDocumentationRequestBuilders.post; +import static org.springframework.restdocs.payload.PayloadDocumentation.*; +import static org.springframework.restdocs.snippet.Attributes.attributes; +import static org.springframework.restdocs.snippet.Attributes.key; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*; + +/** + * Tests for {@link ConstantRepository}. + * + * @author volsch + */ +public class ConstantRepositoryRestDocsTest extends AbstractJpaRepositoryRestDocsTest +{ + @Autowired( required = false ) + private ConstantRepository constantRepository; + + @Test + public void createConstant() throws Exception + { + final ConstrainedFields fields = new ConstrainedFields( Constant.class, constraintDescriptionResolver ); + final String request = IOUtils.resourceToString( "/org/dhis2/fhir/adapter/fhir/metadata/repository/createConstant.json", StandardCharsets.UTF_8 ); + final String location = docMockMvc.perform( post( "/api/constants" ).header( AUTHORIZATION_HEADER_NAME, CODE_MAPPING_AUTHORIZATION_HEADER_VALUE ) + .contentType( MediaType.APPLICATION_JSON ).content( request ) ) + .andExpect( status().isCreated() ) + .andExpect( header().exists( "Location" ) ) + .andDo( documentationHandler.document( requestFields( + attributes( key( "title" ).value( "Fields for constant creation" ) ), + fields.withPath( "name" ).description( "The unique name of the constant." ).type( JsonFieldType.STRING ), + fields.withPath( "code" ).description( "The unique code of the constant." ).type( JsonFieldType.STRING ), + fields.withPath( "description" ).description( "The detailed description that describes for which purpose the constant is used." ).type( JsonFieldType.STRING ).optional(), + fields.withPath( "category" ).description( "The constant category to which the constant belongs to." ).type( JsonFieldType.STRING ), + fields.withPath( "dataType" ).description( "The data type of the constant value." ).type( JsonFieldType.STRING ), + fields.withPath( "value" ).description( "The value of the constant (must have the specified data type)." ).type( JsonFieldType.STRING ).optional() + ) ) ).andReturn().getResponse().getHeader( "Location" ); + + mockMvc + .perform( get( Objects.requireNonNull( location ) ).header( AUTHORIZATION_HEADER_NAME, CODE_MAPPING_AUTHORIZATION_HEADER_VALUE ) ) + .andExpect( status().isOk() ) + .andExpect( jsonPath( "lastUpdatedBy", is( "2h2maqu827d" ) ) ) + .andExpect( jsonPath( "name", is( "Gender Female" ) ) ) + .andExpect( jsonPath( "code", is( "GENDER_FEMALE" ) ) ) + .andExpect( jsonPath( "description", is( "Constant for Gender option value as it is used by DHIS2." ) ) ) + .andExpect( jsonPath( "category", is( "GENDER" ) ) ) + .andExpect( jsonPath( "dataType", is( "STRING" ) ) ) + .andExpect( jsonPath( "value", is( "Female" ) ) ) + .andExpect( jsonPath( "_links.self.href", is( location ) ) ); + } + + @Test + public void readConstant() throws Exception + { + final ConstrainedFields fields = new ConstrainedFields( Constant.class, constraintDescriptionResolver ); + final String constantId = loadConstant( "GENDER_MALE" ).getId().toString(); + docMockMvc.perform( get( "/api/constants/{constantId}", constantId ).header( AUTHORIZATION_HEADER_NAME, CODE_MAPPING_AUTHORIZATION_HEADER_VALUE ) ) + .andExpect( status().isOk() ) + .andDo( documentationHandler.document( links( + linkWithRel( "self" ).description( "Link to this resource itself." ), + linkWithRel( "constant" ).description( "Link to this resource itself." ) ), responseFields( + attributes( key( "title" ).value( "Fields for constant reading" ) ), + fields.withPath( "createdAt" ).description( "The timestamp when the resource has been created." ).type( JsonFieldType.STRING ), + fields.withPath( "lastUpdatedBy" ).description( "The ID of the user that has updated the user the last time or null if the data has been imported to the database directly." ).type( JsonFieldType.STRING ).optional(), + fields.withPath( "lastUpdatedAt" ).description( "The timestamp when the resource has been updated the last time." ).type( JsonFieldType.STRING ), + fields.withPath( "name" ).description( "The unique name of the constant." ).type( JsonFieldType.STRING ), + fields.withPath( "code" ).description( "The unique code of the constant." ).type( JsonFieldType.STRING ), + fields.withPath( "description" ).description( "The detailed description that describes for which purpose the constant is used." ).type( JsonFieldType.STRING ).optional(), + fields.withPath( "category" ).description( "The constant category to which the constant belongs to." ).type( JsonFieldType.STRING ), + fields.withPath( "dataType" ).description( "The data type of the constant value." ).type( JsonFieldType.STRING ), + fields.withPath( "value" ).description( "The data type of the constant value (must have the specified data type)." ).type( JsonFieldType.STRING ), + subsectionWithPath( "_links" ).description( "Links to other resources" ) + ) ) ); + } + + @Nonnull + protected Constant loadConstant( @Nonnull String code ) + { + final Constant example = new Constant(); + example.setCode( code ); + return constantRepository.findOne( Example.of( example ) ).orElseThrow( () -> new AssertionError( "Constant does not exist: " + code ) ); + } +} \ No newline at end of file diff --git a/fhir/src/test/java/org/dhis2/fhir/adapter/fhir/metadata/repository/ExecutableScriptRepositoryRestDocsTest.java b/fhir/src/test/java/org/dhis2/fhir/adapter/fhir/metadata/repository/ExecutableScriptRepositoryRestDocsTest.java new file mode 100644 index 00000000..1898a7dc --- /dev/null +++ b/fhir/src/test/java/org/dhis2/fhir/adapter/fhir/metadata/repository/ExecutableScriptRepositoryRestDocsTest.java @@ -0,0 +1,168 @@ +package org.dhis2.fhir.adapter.fhir.metadata.repository; + +/* + * Copyright (c) 2004-2018, University of Oslo + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * Neither the name of the HISP project nor the names of its contributors may + * be used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +import org.apache.commons.io.IOUtils; +import org.dhis2.fhir.adapter.fhir.AbstractJpaRepositoryRestDocsTest; +import org.dhis2.fhir.adapter.fhir.ConstrainedFields; +import org.dhis2.fhir.adapter.fhir.metadata.model.ExecutableScript; +import org.dhis2.fhir.adapter.fhir.metadata.model.Script; +import org.dhis2.fhir.adapter.fhir.metadata.model.ScriptArg; +import org.junit.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.data.domain.Example; +import org.springframework.data.domain.ExampleMatcher; +import org.springframework.http.MediaType; +import org.springframework.restdocs.payload.JsonFieldType; + +import javax.annotation.Nonnull; +import java.nio.charset.StandardCharsets; +import java.util.Objects; + +import static org.hamcrest.Matchers.is; +import static org.springframework.restdocs.hypermedia.HypermediaDocumentation.linkWithRel; +import static org.springframework.restdocs.hypermedia.HypermediaDocumentation.links; +import static org.springframework.restdocs.mockmvc.RestDocumentationRequestBuilders.get; +import static org.springframework.restdocs.mockmvc.RestDocumentationRequestBuilders.post; +import static org.springframework.restdocs.payload.PayloadDocumentation.*; +import static org.springframework.restdocs.snippet.Attributes.attributes; +import static org.springframework.restdocs.snippet.Attributes.key; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*; + +/** + * Tests for {@link ExecutableScriptRepository}. + * + * @author volsch + */ +public class ExecutableScriptRepositoryRestDocsTest extends AbstractJpaRepositoryRestDocsTest +{ + @Autowired( required = false ) + private ScriptRepository scriptRepository; + + @Autowired( required = false ) + private ScriptArgRepository scriptArgRepository; + + @Autowired( required = false ) + private ExecutableScriptRepository executableScriptRepository; + + @Test + public void createExecutableScript() throws Exception + { + final Script script = loadScript( "TRANSFORM_FHIR_OB_BODY_WEIGHT" ); + final String scriptId = script.getId().toString(); + final String dataElementScriptArgId = loadScriptArg( script, "dataElement" ).getId().toString(); + final String roundScriptArgId = loadScriptArg( script, "round" ).getId().toString(); + + final ConstrainedFields fields = new ConstrainedFields( ExecutableScript.class, constraintDescriptionResolver ); + final String request = IOUtils.resourceToString( "/org/dhis2/fhir/adapter/fhir/metadata/repository/createExecutableScript.json", StandardCharsets.UTF_8 ) + .replace( "$scriptId", API_BASE_URI + "/script/" + scriptId ) + .replace( "$dataElementArgId", API_BASE_URI + "/scriptArgs/" + dataElementScriptArgId ) + .replace( "$roundArgId", API_BASE_URI + "/scriptArgs/" + roundScriptArgId ); + final String location = docMockMvc.perform( post( "/api/executableScripts" ).header( AUTHORIZATION_HEADER_NAME, DATA_MAPPING_AUTHORIZATION_HEADER_VALUE ) + .contentType( MediaType.APPLICATION_JSON ).content( request ) ) + .andExpect( status().isCreated() ) + .andExpect( header().exists( "Location" ) ) + .andDo( documentationHandler.document( requestFields( + attributes( key( "title" ).value( "Fields for executable script creation" ) ), + fields.withPath( "script" ).description( "The reference to the script resource that is executed by this resource definition." ).type( JsonFieldType.STRING ), + fields.withPath( "name" ).description( "The name of the executable script." ).type( JsonFieldType.STRING ), + fields.withPath( "code" ).description( "The code of the executable script." ).type( JsonFieldType.STRING ), + fields.withPath( "description" ).description( "The detailed description of the purpose of the executable script." ).type( JsonFieldType.STRING ).optional(), + fields.withPath( "overrideArguments" ).description( "The overridden arguments of the script resource. If the script resource defined mandatory arguments with null values, these must be specified for the executable script. " + + "Otherwise the script cannot be executed. If no argument value should be overridden, this field need not to be specified." ).type( JsonFieldType.ARRAY ).optional(), + fields.withPath( "overrideArguments[].argument" ).description( "Reference to the script argument resource for which the value should be overridden." ).type( JsonFieldType.STRING ), + fields.withPath( "overrideArguments[].overrideValue" ).description( "The value that should be used for the argument when executing the script. The value must match the data type of the argument." ).type( JsonFieldType.STRING ), + fields.withPath( "overrideArguments[].enabled" ).description( "Specifies if the override argument is enabled. If the override argument is not enabled, the value of the script argument itself is used." ).type( JsonFieldType.BOOLEAN ) + ) ) ).andReturn().getResponse().getHeader( "Location" ); + + mockMvc + .perform( get( Objects.requireNonNull( location ) ).header( AUTHORIZATION_HEADER_NAME, DATA_MAPPING_AUTHORIZATION_HEADER_VALUE ) ) + .andExpect( status().isOk() ) + .andExpect( jsonPath( "lastUpdatedBy", is( "2h2maqu827e" ) ) ) + .andExpect( jsonPath( "name", is( "CP: Birth Weight" ) ) ) + .andExpect( jsonPath( "code", is( "CP_BIRTH_WEIGHT" ) ) ) + .andExpect( jsonPath( "overrideArguments.length()", is( 2 ) ) ) + .andExpect( jsonPath( "_links.self.href", is( location ) ) ); + } + + @Test + public void readExecutableScript() throws Exception + { + final ConstrainedFields fields = new ConstrainedFields( ExecutableScript.class, constraintDescriptionResolver ); + final String executableScriptId = loadExecutableScript( "CP_OPV_DOSE" ).getId().toString(); + docMockMvc.perform( get( "/api/executableScripts/{executableScriptId}", executableScriptId ).header( AUTHORIZATION_HEADER_NAME, DATA_MAPPING_AUTHORIZATION_HEADER_VALUE ) ) + .andExpect( status().isOk() ) + .andDo( documentationHandler.document( links( + linkWithRel( "self" ).description( "Link to this resource itself." ), + linkWithRel( "executableScript" ).description( "Link to this resource itself." ), + linkWithRel( "script" ).description( "Link to the script to which the resource belongs to." ) ), responseFields( + attributes( key( "title" ).value( "Fields for executable script reading" ) ), + fields.withPath( "createdAt" ).description( "The timestamp when the resource has been created." ).type( JsonFieldType.STRING ), + fields.withPath( "lastUpdatedBy" ).description( "The ID of the user that has updated the user the last time or null if the data has been imported to the database directly." ).type( JsonFieldType.STRING ).optional(), + fields.withPath( "lastUpdatedAt" ).description( "The timestamp when the resource has been updated the last time." ).type( JsonFieldType.STRING ), + fields.withPath( "name" ).description( "The name of the executable script." ).type( JsonFieldType.STRING ), + fields.withPath( "code" ).description( "The code of the executable script." ).type( JsonFieldType.STRING ), + fields.withPath( "description" ).description( "The detailed description of the purpose of the executable script." ).type( JsonFieldType.STRING ).optional(), + fields.withPath( "overrideArguments" ).description( "The overridden arguments of the script resource. If the script resource defined mandatory arguments with null values, these must be specified for the executable script. " + + "Otherwise the script cannot be executed. If no argument value should be overridden, this field need not to be specified." ).type( JsonFieldType.ARRAY ).optional(), + fields.withPath( "overrideArguments[].overrideValue" ).description( "The value that should be used for the argument when executing the script. The value must match the data type of the argument." ).type( JsonFieldType.STRING ), + fields.withPath( "overrideArguments[].enabled" ).description( "Specifies if the override argument is enabled. If the override argument is not enabled, the value of the script argument itself is used." ).type( JsonFieldType.BOOLEAN ), + subsectionWithPath( "_links" ).description( "Links to other resources" ), + subsectionWithPath( "overrideArguments[]._links" ).description( "Links to other resources" ) + ) ) ); + } + + @Nonnull + protected Script loadScript( @Nonnull String code ) + { + final Script example = new Script(); + example.setCode( code ); + return scriptRepository.findOne( Example.of( example ) ).orElseThrow( () -> new AssertionError( "Script does not exist: " + code ) ); + } + + @Nonnull + protected ScriptArg loadScriptArg( @Nonnull Script script, @Nonnull String name ) + { + final Script exampleScript = new Script(); + exampleScript.setId( script.getId() ); + final ScriptArg example = new ScriptArg(); + example.setScript( exampleScript ); + example.setName( name ); + return scriptArgRepository.findOne( Example.of( example, ExampleMatcher.matching().withIgnorePaths( "arrayValue", "mandatory" ) ) ) + .orElseThrow( () -> new AssertionError( "Script argument does not exist: " + name ) ); + } + + @Nonnull + protected ExecutableScript loadExecutableScript( @Nonnull String code ) + { + final ExecutableScript executableScript = new ExecutableScript(); + executableScript.setCode( code ); + return executableScriptRepository.findOne( Example.of( executableScript ) ).orElseThrow( () -> new AssertionError( "Executable script does not exist: " + code ) ); + } +} \ No newline at end of file diff --git a/fhir/src/test/java/org/dhis2/fhir/adapter/fhir/metadata/repository/RemoteSubscriptionRepositoryRestDocsTest.java b/fhir/src/test/java/org/dhis2/fhir/adapter/fhir/metadata/repository/RemoteSubscriptionRepositoryRestDocsTest.java new file mode 100644 index 00000000..21802adf --- /dev/null +++ b/fhir/src/test/java/org/dhis2/fhir/adapter/fhir/metadata/repository/RemoteSubscriptionRepositoryRestDocsTest.java @@ -0,0 +1,197 @@ +package org.dhis2.fhir.adapter.fhir.metadata.repository; + +/* + * Copyright (c) 2004-2018, University of Oslo + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * Neither the name of the HISP project nor the names of its contributors may + * be used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +import org.apache.commons.io.IOUtils; +import org.dhis2.fhir.adapter.fhir.AbstractJpaRepositoryRestDocsTest; +import org.dhis2.fhir.adapter.fhir.ConstrainedFields; +import org.dhis2.fhir.adapter.fhir.metadata.model.RemoteSubscription; +import org.junit.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.data.domain.Example; +import org.springframework.data.domain.ExampleMatcher; +import org.springframework.http.MediaType; +import org.springframework.restdocs.payload.JsonFieldType; + +import javax.annotation.Nonnull; +import java.nio.charset.StandardCharsets; +import java.util.Objects; + +import static org.hamcrest.Matchers.is; +import static org.springframework.restdocs.hypermedia.HypermediaDocumentation.linkWithRel; +import static org.springframework.restdocs.hypermedia.HypermediaDocumentation.links; +import static org.springframework.restdocs.mockmvc.RestDocumentationRequestBuilders.get; +import static org.springframework.restdocs.mockmvc.RestDocumentationRequestBuilders.post; +import static org.springframework.restdocs.payload.PayloadDocumentation.*; +import static org.springframework.restdocs.snippet.Attributes.attributes; +import static org.springframework.restdocs.snippet.Attributes.key; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*; + +/** + * Tests for {@link RemoteSubscriptionRepository}. + * + * @author volsch + */ +public class RemoteSubscriptionRepositoryRestDocsTest extends AbstractJpaRepositoryRestDocsTest +{ + @Autowired( required = false ) + private RemoteSubscriptionRepository remoteSubscriptionRepository; + + @Test + public void createRemoteSubscription() throws Exception + { + final ConstrainedFields fields = new ConstrainedFields( RemoteSubscription.class, constraintDescriptionResolver ); + final String request = IOUtils.resourceToString( "/org/dhis2/fhir/adapter/fhir/metadata/repository/createRemoteSubscription.json", StandardCharsets.UTF_8 ); + final String location = docMockMvc.perform( post( "/api/remoteSubscriptions" ).header( AUTHORIZATION_HEADER_NAME, ADMINISTRATION_AUTHORIZATION_HEADER_VALUE ) + .contentType( MediaType.APPLICATION_JSON ).content( request ) ) + .andExpect( status().isCreated() ) + .andExpect( header().exists( "Location" ) ) + .andDo( documentationHandler.document( requestFields( + attributes( key( "title" ).value( "Fields for remote subscription creation" ) ), + fields.withPath( "name" ).description( "The name of the remote subscription." ).type( JsonFieldType.STRING ), + fields.withPath( "code" ).description( "The code of the remote subscription." ).type( JsonFieldType.STRING ), + fields.withPath( "description" ).description( "The detailed description of the purpose of the remote subscription." ).type( JsonFieldType.STRING ).optional(), + fields.withPath( "enabled" ).description( "Specifies if this remote subscription has been enabled. " + + "If the remote subscription has not been enabled, no subscription notifications are processed from the corresponding FHIR service." ).type( JsonFieldType.BOOLEAN ), + fields.withPath( "locked" ).description( "Specifies if this remote subscription has been locked. " + + "If the remote subscription has been locked (i.e. by automatic processes), no subscription notifications are processed from the corresponding FHIR service." ).type( JsonFieldType.BOOLEAN ), + fields.withPath( "fhirVersion" ).description( "The FHIR version that should be used when communicating with the remote FHIR service." ).type( JsonFieldType.STRING ), + fields.withPath( "toleranceMillis" ).description( "The number of milli-seconds to subtract from the last updated timestamp when searching for created and updated resources." ).type( JsonFieldType.NUMBER ), + fields.withPath( "autoCreatedSubscriptionResources" ).description( "Subscription resources for which the subscriptions should be created automatically when creating the subscription resource. This value will not be returned and can only " + + "be used when creating and updating the entity." ).type( JsonFieldType.ARRAY ).optional(), + fields.withPath( "adapterEndpoint" ).description( "Specifies remote subscription settings that are relevant for the adapter." ).type( JsonFieldType.OBJECT ), + fields.withPath( "adapterEndpoint.baseUrl" ).description( "The base URL of the adapter that is used to register the subscription on the FHIR service. " + + "If the FHIR service runs on a different server, the URL must not contain localhost. If this URL is not specified it is calculated automatically." ).type( JsonFieldType.STRING ).optional(), + fields.withPath( "adapterEndpoint.subscriptionType" ).description( "The subscription type that is used to register the subscription on FHIR service. " + + "A normal REST hook subscription is sufficient (without any payload). If this causes issues on the FHIR service also a subscription with payload can be used." ) + .type( JsonFieldType.STRING ), + fields.withPath( "adapterEndpoint.authorizationHeader" ).description( "The authorization header value that is expected by the adapter when it receives a subscription notification from the FHIR service. " + + "This should include a bearer token." ).type( JsonFieldType.STRING ), + fields.withPath( "dhisEndpoint" ).description( "Specifies remote subscription settings that are relevant for the connection to DHIS2." ).type( JsonFieldType.OBJECT ), + fields.withPath( "dhisEndpoint.authenticationMethod" ).description( "The authentication method that should be used when connecting to DHIS2." ).type( JsonFieldType.STRING ), + fields.withPath( "dhisEndpoint.username" ).description( "The username that is used to connect to DHIS2 when handling data of this remote subscription." ).type( JsonFieldType.STRING ), + fields.withPath( "dhisEndpoint.password" ).description( "The password that is used to connect to DHIS2 when handling data of this remote subscription. " + + "This value will not be returned and will be set using the original value when performing an update without this value." ).type( JsonFieldType.STRING ).optional(), + fields.withPath( "fhirEndpoint" ).description( "Specifies remote subscription settings that are relevant for the connection to FHIR." ).type( JsonFieldType.OBJECT ), + fields.withPath( "fhirEndpoint.baseUrl" ).description( "The base URL of the FHIR endpoints on the FHIR service." ).type( JsonFieldType.STRING ), + fields.withPath( "fhirEndpoint.headers" ).description( "The headers that are sent to the remote FHIR service when connecting to the FHIR endpoints." ).type( JsonFieldType.ARRAY ), + fields.withPath( "fhirEndpoint.headers[].name" ).description( "The name of the header for which the value will be sent (e.g. Authorization)." ).type( JsonFieldType.STRING ), + fields.withPath( "fhirEndpoint.headers[].value" ).description( "The value of the header for which the value will be sent (e.g. a bearer token). If the value of the header is marked as secure, " + + "the value will not be returned and will be set using the original value when performing an update without this value." ).type( JsonFieldType.STRING ).optional(), + fields.withPath( "fhirEndpoint.headers[].secure" ).description( "Specifies if the value of the header is secure and should not be returned " + + "(e.g. when it contains authentication information)." ).type( JsonFieldType.BOOLEAN ) + ) ) ).andReturn().getResponse().getHeader( "Location" ); + + mockMvc + .perform( get( Objects.requireNonNull( location ) ).header( AUTHORIZATION_HEADER_NAME, ADMINISTRATION_AUTHORIZATION_HEADER_VALUE ) ) + .andExpect( status().isOk() ) + .andExpect( jsonPath( "lastUpdatedBy", is( "2h2maqu827f" ) ) ) + .andExpect( jsonPath( "name", is( "Main Subscription" ) ) ) + .andExpect( jsonPath( "code", is( "MAIN_SUBSCRIPTION" ) ) ) + .andExpect( jsonPath( "description", is( "Main FHIR service on which the adapter has subscriptions." ) ) ) + .andExpect( jsonPath( "enabled", is( true ) ) ) + .andExpect( jsonPath( "locked", is( false ) ) ) + .andExpect( jsonPath( "autoCreatedSubscriptionResources" ).doesNotExist() ) + .andExpect( jsonPath( "adapterEndpoint.baseUrl", is( "http://localhist:8081" ) ) ) + .andExpect( jsonPath( "adapterEndpoint.subscriptionType", is( "REST_HOOK" ) ) ) + .andExpect( jsonPath( "adapterEndpoint.authorizationHeader", is( "Bearer 98a7558102b7bdc4da5c8f74ca63958c498b4bd9231bd3b0cc" ) ) ) + .andExpect( jsonPath( "dhisEndpoint.authenticationMethod", is( "BASIC" ) ) ) + .andExpect( jsonPath( "dhisEndpoint.username", is( "admin" ) ) ) + .andExpect( jsonPath( "dhisEndpoint.password" ).doesNotExist() ) + .andExpect( jsonPath( "fhirEndpoint.baseUrl", is( "http://localhost:8082/hapi-fhir-jpaserver-example/baseDstu3" ) ) ) + .andExpect( jsonPath( "fhirEndpoint.headers[0].name", is( "Authorization" ) ) ) + .andExpect( jsonPath( "fhirEndpoint.headers[0].value" ).doesNotExist() ) + .andExpect( jsonPath( "fhirEndpoint.headers[0].secure", is( true ) ) ) + .andExpect( jsonPath( "_links.self.href", is( location ) ) ); + } + + @Test + public void readRemoteSubscription() throws Exception + { + final ConstrainedFields fields = new ConstrainedFields( RemoteSubscription.class, constraintDescriptionResolver ); + final String remoteSubscriptionId = loadRemoteSubscription( "DEFAULT_SUBSCRIPTION" ).getId().toString(); + docMockMvc.perform( get( "/api/remoteSubscriptions/{remoteSubscriptionId}", remoteSubscriptionId ).header( AUTHORIZATION_HEADER_NAME, ADMINISTRATION_AUTHORIZATION_HEADER_VALUE ) ) + .andExpect( status().isOk() ) + .andDo( documentationHandler.document( links( + linkWithRel( "self" ).description( "Link to this resource itself." ), + linkWithRel( "remoteSubscription" ).description( "Link to this resource itself." ), + linkWithRel( "systems" ).description( "Link to the system URI resources that belong to this remote subscription." ), + linkWithRel( "resources" ).description( "Link to the subscribed FHIR resources that belong to this remote subscription." ) ), responseFields( + attributes( key( "title" ).value( "Fields for remote subscription reading" ) ), + fields.withPath( "createdAt" ).description( "The timestamp when the resource has been created." ).type( JsonFieldType.STRING ), + fields.withPath( "lastUpdatedBy" ).description( "The ID of the user that has updated the user the last time or null if the data has been imported to the database directly." ).type( JsonFieldType.STRING ).optional(), + fields.withPath( "lastUpdatedAt" ).description( "The timestamp when the resource has been updated the last time." ).type( JsonFieldType.STRING ), + fields.withPath( "name" ).description( "The name of the remote subscription." ).type( JsonFieldType.STRING ), + fields.withPath( "code" ).description( "The code of the remote subscription." ).type( JsonFieldType.STRING ), + fields.withPath( "description" ).description( "The detailed description of the purpose of the remote subscription." ).type( JsonFieldType.STRING ).optional(), + fields.withPath( "enabled" ).description( "Specifies if this remote subscription has been enabled. " + + "If the remote subscription has not been enabled, no subscription notifications are processed from the corresponding FHIR service." ).type( JsonFieldType.BOOLEAN ), + fields.withPath( "locked" ).description( "Specifies if this remote subscription has been locked. " + + "If the remote subscription has been locked (i.e. by automatic processes), no subscription notifications are processed from the corresponding FHIR service." ).type( JsonFieldType.BOOLEAN ), + fields.withPath( "fhirVersion" ).description( "The FHIR version that should be used when communicating with the remote FHIR service." ).type( JsonFieldType.STRING ), + fields.withPath( "toleranceMillis" ).description( "The number of milli-seconds to subtract from the last updated timestamp when searching for created and updated resources." ).type( JsonFieldType.NUMBER ), + fields.withPath( "autoCreatedSubscriptionResources" ).description( "Subscription resources for which the subscriptions should be created automatically when creating the subscription resource. This value will not be returned and can only " + + "be used when creating and updating the entity." ).type( JsonFieldType.ARRAY ).optional(), + fields.withPath( "adapterEndpoint" ).description( "Specifies remote subscription settings that are relevant for the adapter." ).type( JsonFieldType.OBJECT ), + fields.withPath( "adapterEndpoint.baseUrl" ).description( "The base URL of the adapter that is used to register the subscription on the FHIR service. " + + "If the FHIR service runs on a different server, the URL must not contain localhost. If this URL is not specified it is calculated automatically." ).type( JsonFieldType.STRING ).optional(), + fields.withPath( "adapterEndpoint.subscriptionType" ).description( "The subscription type that is used to register the subscription on FHIR service. " + + "A normal REST hook subscription is sufficient (without any payload). If this causes issues on the FHIR service also a subscription with payload can be used." ) + .type( JsonFieldType.STRING ), + fields.withPath( "adapterEndpoint.authorizationHeader" ).description( "The authorization header value that is expected by the adapter when it receives a subscription notification from the FHIR service. " + + "This should include a bearer token." ).type( JsonFieldType.STRING ), + fields.withPath( "dhisEndpoint" ).description( "Specifies remote subscription settings that are relevant for the connection to DHIS2." ).type( JsonFieldType.OBJECT ), + fields.withPath( "dhisEndpoint.authenticationMethod" ).description( "The authentication method that should be used when connecting to DHIS2." ).type( JsonFieldType.STRING ), + fields.withPath( "dhisEndpoint.username" ).description( "The username that is used to connect to DHIS2 when handling data of this remote subscription." ).type( JsonFieldType.STRING ), + fields.withPath( "dhisEndpoint.password" ).description( "The password that is used to connect to DHIS2 when handling data of this remote subscription. " + + "This value will not be returned and will be set using the original value when performing an update without this value." ).type( JsonFieldType.STRING ).optional(), + fields.withPath( "fhirEndpoint" ).description( "Specifies remote subscription settings that are relevant for the connection to FHIR." ).type( JsonFieldType.OBJECT ), + fields.withPath( "fhirEndpoint.baseUrl" ).description( "The base URL of the FHIR endpoints on the FHIR service." ).type( JsonFieldType.STRING ), + fields.withPath( "fhirEndpoint.logging" ).description( "Specifies if basic logging should be enabled when communicating with the FHIR endpoints of this FHIR service." ).type( JsonFieldType.BOOLEAN ).optional(), + fields.withPath( "fhirEndpoint.verboseLogging" ).description( "Specifies if verbose logging (includes complete pazload) should be enabled when communicating with the FHIR endpoints of this FHIR service. " + + "Enabling verbose logging may log confidential patient data. This could violate data protection laws and regulations." ).type( JsonFieldType.BOOLEAN ).optional(), + fields.withPath( "fhirEndpoint.headers" ).description( "The headers that are sent to the remote FHIR service when connecting to the FHIR endpoints." ).type( JsonFieldType.ARRAY ), + fields.withPath( "fhirEndpoint.headers[].name" ).description( "The name of the header for which the value will be sent (e.g. Authorization)." ).type( JsonFieldType.STRING ), + fields.withPath( "fhirEndpoint.headers[].value" ).description( "The value of the header for which the value will be sent (e.g. a bearer token). If the value of the header is marked as secure, " + + "the value will not be returned and will be set using the original value when performing an update without this value." ).type( JsonFieldType.STRING ).optional(), + fields.withPath( "fhirEndpoint.headers[].secure" ).description( "Specifies if the value of the header is secure and should not be returned " + + "(e.g. when it contains authentication information)." ).type( JsonFieldType.BOOLEAN ), + subsectionWithPath( "_links" ).description( "Links to other resources" ) + ) ) ); + } + + @Nonnull + protected RemoteSubscription loadRemoteSubscription( @Nonnull String code ) + { + final RemoteSubscription remoteSubscription = new RemoteSubscription(); + remoteSubscription.setCode( code ); + return remoteSubscriptionRepository.findOne( Example.of( remoteSubscription, + ExampleMatcher.matching().withIgnorePaths( "toleranceMillis", "logging", "verboseLogging", "enabled", "locked" ) ) ).orElseThrow( () -> new AssertionError( "Remote subscription does not exist: " + code ) ); + } +} \ No newline at end of file diff --git a/fhir/src/test/java/org/dhis2/fhir/adapter/fhir/metadata/repository/RemoteSubscriptionResourceRepositoryRestDocsTest.java b/fhir/src/test/java/org/dhis2/fhir/adapter/fhir/metadata/repository/RemoteSubscriptionResourceRepositoryRestDocsTest.java new file mode 100644 index 00000000..cb24092b --- /dev/null +++ b/fhir/src/test/java/org/dhis2/fhir/adapter/fhir/metadata/repository/RemoteSubscriptionResourceRepositoryRestDocsTest.java @@ -0,0 +1,141 @@ +package org.dhis2.fhir.adapter.fhir.metadata.repository; + +/* + * Copyright (c) 2004-2018, University of Oslo + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * Neither the name of the HISP project nor the names of its contributors may + * be used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +import org.apache.commons.io.IOUtils; +import org.dhis2.fhir.adapter.fhir.AbstractJpaRepositoryRestDocsTest; +import org.dhis2.fhir.adapter.fhir.ConstrainedFields; +import org.dhis2.fhir.adapter.fhir.metadata.model.RemoteSubscription; +import org.dhis2.fhir.adapter.fhir.metadata.model.RemoteSubscriptionResource; +import org.junit.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.data.domain.Example; +import org.springframework.data.domain.ExampleMatcher; +import org.springframework.http.MediaType; +import org.springframework.restdocs.payload.JsonFieldType; + +import javax.annotation.Nonnull; +import java.nio.charset.StandardCharsets; +import java.util.Objects; +import java.util.UUID; + +import static org.hamcrest.Matchers.is; +import static org.springframework.restdocs.hypermedia.HypermediaDocumentation.linkWithRel; +import static org.springframework.restdocs.hypermedia.HypermediaDocumentation.links; +import static org.springframework.restdocs.mockmvc.RestDocumentationRequestBuilders.get; +import static org.springframework.restdocs.mockmvc.RestDocumentationRequestBuilders.post; +import static org.springframework.restdocs.payload.PayloadDocumentation.*; +import static org.springframework.restdocs.snippet.Attributes.attributes; +import static org.springframework.restdocs.snippet.Attributes.key; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*; + +/** + * Tests for {@link RemoteSubscriptionResourceRepository}. + * + * @author volsch + */ +public class RemoteSubscriptionResourceRepositoryRestDocsTest extends AbstractJpaRepositoryRestDocsTest +{ + @Autowired( required = false ) + private SystemRepository systemRepository; + + @Autowired( required = false ) + private RemoteSubscriptionRepository remoteSubscriptionRepository; + + @Autowired( required = false ) + private RemoteSubscriptionResourceRepository remoteSubscriptionResourceRepository; + + @Test + public void createRemoteSubscriptionResource() throws Exception + { + final String remoteSubscriptionId = loadRemoteSubscription( "DEFAULT_SUBSCRIPTION" ).getId().toString(); + final ConstrainedFields fields = new ConstrainedFields( RemoteSubscriptionResource.class, constraintDescriptionResolver ); + final String request = IOUtils.resourceToString( "/org/dhis2/fhir/adapter/fhir/metadata/repository/createRemoteSubscriptionResource.json", StandardCharsets.UTF_8 ) + .replace( "$remoteSubscriptionId", API_BASE_URI + "/remoteSubscriptions/" + remoteSubscriptionId ); + final String location = docMockMvc.perform( post( "/api/remoteSubscriptionResources" ).header( AUTHORIZATION_HEADER_NAME, ADMINISTRATION_AUTHORIZATION_HEADER_VALUE ) + .contentType( MediaType.APPLICATION_JSON ).content( request ) ) + .andExpect( status().isCreated() ) + .andExpect( header().exists( "Location" ) ) + .andDo( documentationHandler.document( requestFields( + attributes( key( "title" ).value( "Fields for script source creation" ) ), + fields.withPath( "remoteSubscription" ).description( "The reference to the remote subscription to which this resource belongs to." ).type( JsonFieldType.STRING ), + fields.withPath( "fhirResourceType" ).description( "The type of the subscribed FHIR resource." ).type( JsonFieldType.STRING ), + fields.withPath( "description" ).description( "The detailed description of the purpose of the subscribed FHIR resource." ).type( JsonFieldType.STRING ).optional(), + fields.withPath( "fhirCriteriaParameters" ).description( "The prefix that should be added to the codes when mapping them to DHIS2." ).type( JsonFieldType.STRING ).optional() + ) ) ).andReturn().getResponse().getHeader( "Location" ); + + mockMvc + .perform( get( Objects.requireNonNull( location ) ).header( AUTHORIZATION_HEADER_NAME, ADMINISTRATION_AUTHORIZATION_HEADER_VALUE ) ) + .andExpect( status().isOk() ) + .andExpect( jsonPath( "lastUpdatedBy", is( "2h2maqu827f" ) ) ) + .andExpect( jsonPath( "fhirResourceType", is( "IMMUNIZATION" ) ) ) + .andExpect( jsonPath( "description", is( "Subscription for all immunizations." ) ) ) + .andExpect( jsonPath( "fhirCriteriaParameters" ).doesNotExist() ) + .andExpect( jsonPath( "fhirSubscriptionId" ).doesNotExist() ) + .andExpect( jsonPath( "_links.self.href", is( location ) ) ); + } + + @Test + public void readRemoteSubscriptionResource() throws Exception + { + final ConstrainedFields fields = new ConstrainedFields( RemoteSubscriptionResource.class, constraintDescriptionResolver ); + final String remoteSubscriptionResourceId = loadRemoteSubscriptionResource( "667bfa41-867c-4796-86b6-eb9f9ed4dc94" ).getId().toString(); + docMockMvc.perform( get( "/api/remoteSubscriptionResources/{remoteSubscriptionResourceId}", remoteSubscriptionResourceId ).header( AUTHORIZATION_HEADER_NAME, ADMINISTRATION_AUTHORIZATION_HEADER_VALUE ) ) + .andExpect( status().isOk() ) + .andDo( documentationHandler.document( links( + linkWithRel( "self" ).description( "Link to this resource itself." ), + linkWithRel( "remoteSubscriptionResource" ).description( "Link to this resource itself." ), + linkWithRel( "remoteSubscription" ).description( "The reference to the remote subscription to which this resource belongs to." ) ), responseFields( + attributes( key( "title" ).value( "Fields for remote subscription resource reading" ) ), + fields.withPath( "createdAt" ).description( "The timestamp when the resource has been created." ).type( JsonFieldType.STRING ), + fields.withPath( "lastUpdatedBy" ).description( "The ID of the user that has updated the user the last time or null if the data has been imported to the database directly." ).type( JsonFieldType.STRING ).optional(), + fields.withPath( "lastUpdatedAt" ).description( "The timestamp when the resource has been updated the last time." ).type( JsonFieldType.STRING ), + fields.withPath( "fhirResourceType" ).description( "The type of the subscribed FHIR resource." ).type( JsonFieldType.STRING ), + fields.withPath( "description" ).description( "The detailed description of the purpose of the subscribed FHIR resource." ).type( JsonFieldType.STRING ).optional(), + fields.withPath( "fhirCriteriaParameters" ).description( "The prefix that should be added to the codes when mapping them to DHIS2." ).type( JsonFieldType.STRING ).optional(), + fields.withPath( "hirSubscriptionId" ).description( "The ID of the automatically created FHIR subscription on the FHIR service." ).type( JsonFieldType.STRING ).optional(), + subsectionWithPath( "_links" ).description( "Links to other resources" ) + ) ) ); + } + + @Nonnull + protected RemoteSubscription loadRemoteSubscription( @Nonnull String code ) + { + final RemoteSubscription remoteSubscription = new RemoteSubscription(); + remoteSubscription.setCode( code ); + return remoteSubscriptionRepository.findOne( Example.of( remoteSubscription, + ExampleMatcher.matching().withIgnorePaths( "toleranceMillis", "logging", "verboseLogging", "enabled", "locked" ) ) ).orElseThrow( () -> new AssertionError( "Remote subscription does not exist: " + code ) ); + } + + @Nonnull + protected RemoteSubscriptionResource loadRemoteSubscriptionResource( @Nonnull String id ) + { + return remoteSubscriptionResourceRepository.findById( UUID.fromString( id ) ).orElseThrow( () -> new AssertionError( "Remote subscription resource does not exist: " + id ) ); + } +} \ No newline at end of file diff --git a/fhir/src/test/java/org/dhis2/fhir/adapter/fhir/metadata/repository/RemoteSubscriptionSystemRepositoryRestDocsTest.java b/fhir/src/test/java/org/dhis2/fhir/adapter/fhir/metadata/repository/RemoteSubscriptionSystemRepositoryRestDocsTest.java new file mode 100644 index 00000000..1e8db39f --- /dev/null +++ b/fhir/src/test/java/org/dhis2/fhir/adapter/fhir/metadata/repository/RemoteSubscriptionSystemRepositoryRestDocsTest.java @@ -0,0 +1,149 @@ +package org.dhis2.fhir.adapter.fhir.metadata.repository; + +/* + * Copyright (c) 2004-2018, University of Oslo + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * Neither the name of the HISP project nor the names of its contributors may + * be used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +import org.apache.commons.io.IOUtils; +import org.dhis2.fhir.adapter.fhir.AbstractJpaRepositoryRestDocsTest; +import org.dhis2.fhir.adapter.fhir.ConstrainedFields; +import org.dhis2.fhir.adapter.fhir.metadata.model.RemoteSubscription; +import org.dhis2.fhir.adapter.fhir.metadata.model.RemoteSubscriptionSystem; +import org.dhis2.fhir.adapter.fhir.metadata.model.System; +import org.junit.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.data.domain.Example; +import org.springframework.data.domain.ExampleMatcher; +import org.springframework.http.MediaType; +import org.springframework.restdocs.payload.JsonFieldType; + +import javax.annotation.Nonnull; +import java.nio.charset.StandardCharsets; +import java.util.Objects; +import java.util.UUID; + +import static org.hamcrest.Matchers.is; +import static org.springframework.restdocs.hypermedia.HypermediaDocumentation.linkWithRel; +import static org.springframework.restdocs.hypermedia.HypermediaDocumentation.links; +import static org.springframework.restdocs.mockmvc.RestDocumentationRequestBuilders.get; +import static org.springframework.restdocs.mockmvc.RestDocumentationRequestBuilders.post; +import static org.springframework.restdocs.payload.PayloadDocumentation.*; +import static org.springframework.restdocs.snippet.Attributes.attributes; +import static org.springframework.restdocs.snippet.Attributes.key; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*; + +/** + * Tests for {@link RemoteSubscriptionSystem}. + * + * @author volsch + */ +public class RemoteSubscriptionSystemRepositoryRestDocsTest extends AbstractJpaRepositoryRestDocsTest +{ + @Autowired( required = false ) + private SystemRepository systemRepository; + + @Autowired( required = false ) + private RemoteSubscriptionRepository remoteSubscriptionRepository; + + @Autowired( required = false ) + private RemoteSubscriptionSystemRepository remoteSubscriptionSystemRepository; + + @Test + public void createRemoteSubscriptionSystem() throws Exception + { + final String systemId = loadSystem( "SYSTEM_SL_LOCATION" ).getId().toString(); + final String remoteSubscriptionId = loadRemoteSubscription( "DEFAULT_SUBSCRIPTION" ).getId().toString(); + final ConstrainedFields fields = new ConstrainedFields( RemoteSubscriptionSystem.class, constraintDescriptionResolver ); + final String request = IOUtils.resourceToString( "/org/dhis2/fhir/adapter/fhir/metadata/repository/createRemoteSubscriptionSystem.json", StandardCharsets.UTF_8 ) + .replace( "$systemId", API_BASE_URI + "/systems/" + systemId ).replace( "$remoteSubscriptionId", API_BASE_URI + "/remoteSubscriptions/" + remoteSubscriptionId ); + final String location = docMockMvc.perform( post( "/api/remoteSubscriptionSystems" ).header( AUTHORIZATION_HEADER_NAME, ADMINISTRATION_AUTHORIZATION_HEADER_VALUE ) + .contentType( MediaType.APPLICATION_JSON ).content( request ) ) + .andExpect( status().isCreated() ) + .andExpect( header().exists( "Location" ) ) + .andDo( documentationHandler.document( requestFields( + attributes( key( "title" ).value( "Fields for script source creation" ) ), + fields.withPath( "remoteSubscription" ).description( "The reference to the remote subscription to which this resource belongs to." ).type( JsonFieldType.STRING ), + fields.withPath( "system" ).description( "The reference to the system URI that should be mapped to the remote subscription." ).type( JsonFieldType.STRING ), + fields.withPath( "fhirResourceType" ).description( "The FHIR resource type to which the system URI should be mapped." ).type( JsonFieldType.STRING ), + fields.withPath( "codePrefix" ).description( "The prefix that should be added to the codes when mapping them to DHIS2." ).type( JsonFieldType.STRING ).optional() + ) ) ).andReturn().getResponse().getHeader( "Location" ); + + mockMvc + .perform( get( Objects.requireNonNull( location ) ).header( AUTHORIZATION_HEADER_NAME, ADMINISTRATION_AUTHORIZATION_HEADER_VALUE ) ) + .andExpect( status().isOk() ) + .andExpect( jsonPath( "lastUpdatedBy", is( "2h2maqu827f" ) ) ) + .andExpect( jsonPath( "fhirResourceType", is( "LOCATION" ) ) ) + .andExpect( jsonPath( "codePrefix", is( "LOC_" ) ) ) + .andExpect( jsonPath( "_links.self.href", is( location ) ) ); + } + + @Test + public void readRemoteSubscriptionSystem() throws Exception + { + final ConstrainedFields fields = new ConstrainedFields( RemoteSubscriptionSystem.class, constraintDescriptionResolver ); + final String remoteSubscriptionSystemId = loadRemoteSubscriptionSystem( "ea9804a3-9e82-4d0d-9cd2-e417b32b1c0c" ).getId().toString(); + docMockMvc.perform( get( "/api/remoteSubscriptionSystems/{remoteSubscriptionSystemId}", remoteSubscriptionSystemId ).header( AUTHORIZATION_HEADER_NAME, ADMINISTRATION_AUTHORIZATION_HEADER_VALUE ) ) + .andExpect( status().isOk() ) + .andDo( documentationHandler.document( links( + linkWithRel( "self" ).description( "Link to this resource itself." ), + linkWithRel( "remoteSubscriptionSystem" ).description( "Link to this resource itself." ), + linkWithRel( "remoteSubscription" ).description( "The reference to the remote subscription to which this resource belongs to." ), + linkWithRel( "system" ).description( "The reference to the system URI that should be mapped to the remote subscription." ) ), responseFields( + attributes( key( "title" ).value( "Fields for remote subscription system reading" ) ), + fields.withPath( "createdAt" ).description( "The timestamp when the resource has been created." ).type( JsonFieldType.STRING ), + fields.withPath( "lastUpdatedBy" ).description( "The ID of the user that has updated the user the last time or null if the data has been imported to the database directly." ).type( JsonFieldType.STRING ).optional(), + fields.withPath( "lastUpdatedAt" ).description( "The timestamp when the resource has been updated the last time." ).type( JsonFieldType.STRING ), + fields.withPath( "fhirResourceType" ).description( "The FHIR resource type to which the system URI should be mapped." ).type( JsonFieldType.STRING ), + fields.withPath( "codePrefix" ).description( "The prefix that should be added to the codes when mapping them to DHIS2." ).type( JsonFieldType.STRING ).optional(), + subsectionWithPath( "_links" ).description( "Links to other resources" ) + ) ) ); + } + + @Nonnull + protected System loadSystem( @Nonnull String code ) + { + final System system = new System(); + system.setCode( code ); + return systemRepository.findOne( Example.of( system, ExampleMatcher.matching().withIgnorePaths( "enabled" ) ) ) + .orElseThrow( () -> new AssertionError( "System does not exist: " + code ) ); + } + + @Nonnull + protected RemoteSubscription loadRemoteSubscription( @Nonnull String code ) + { + final RemoteSubscription remoteSubscription = new RemoteSubscription(); + remoteSubscription.setCode( code ); + return remoteSubscriptionRepository.findOne( Example.of( remoteSubscription, + ExampleMatcher.matching().withIgnorePaths( "toleranceMillis", "logging", "verboseLogging", "enabled", "locked" ) ) ).orElseThrow( () -> new AssertionError( "Remote subscription does not exist: " + code ) ); + } + + @Nonnull + protected RemoteSubscriptionSystem loadRemoteSubscriptionSystem( @Nonnull String id ) + { + return remoteSubscriptionSystemRepository.findById( UUID.fromString( id ) ).orElseThrow( () -> new AssertionError( "Remote subscription system does not exist: " + id ) ); + } +} \ No newline at end of file diff --git a/fhir/src/test/java/org/dhis2/fhir/adapter/fhir/metadata/repository/ScriptArgRepositoryRestDocsTest.java b/fhir/src/test/java/org/dhis2/fhir/adapter/fhir/metadata/repository/ScriptArgRepositoryRestDocsTest.java new file mode 100644 index 00000000..18d8abeb --- /dev/null +++ b/fhir/src/test/java/org/dhis2/fhir/adapter/fhir/metadata/repository/ScriptArgRepositoryRestDocsTest.java @@ -0,0 +1,144 @@ +package org.dhis2.fhir.adapter.fhir.metadata.repository; + +/* + * Copyright (c) 2004-2018, University of Oslo + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * Neither the name of the HISP project nor the names of its contributors may + * be used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +import org.apache.commons.io.IOUtils; +import org.dhis2.fhir.adapter.fhir.AbstractJpaRepositoryRestDocsTest; +import org.dhis2.fhir.adapter.fhir.ConstrainedFields; +import org.dhis2.fhir.adapter.fhir.metadata.model.Script; +import org.dhis2.fhir.adapter.fhir.metadata.model.ScriptArg; +import org.junit.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.data.domain.Example; +import org.springframework.http.MediaType; +import org.springframework.restdocs.payload.JsonFieldType; + +import javax.annotation.Nonnull; +import java.nio.charset.StandardCharsets; +import java.util.Objects; +import java.util.UUID; + +import static org.hamcrest.Matchers.is; +import static org.springframework.restdocs.hypermedia.HypermediaDocumentation.linkWithRel; +import static org.springframework.restdocs.hypermedia.HypermediaDocumentation.links; +import static org.springframework.restdocs.mockmvc.RestDocumentationRequestBuilders.get; +import static org.springframework.restdocs.mockmvc.RestDocumentationRequestBuilders.post; +import static org.springframework.restdocs.payload.PayloadDocumentation.*; +import static org.springframework.restdocs.snippet.Attributes.attributes; +import static org.springframework.restdocs.snippet.Attributes.key; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*; + +/** + * Tests for {@link ScriptArg}. + * + * @author volsch + */ +public class ScriptArgRepositoryRestDocsTest extends AbstractJpaRepositoryRestDocsTest +{ + @Autowired( required = false ) + private ScriptRepository scriptRepository; + + @Autowired( required = false ) + private ScriptArgRepository scriptArgRepository; + + @Test + public void createScriptArg() throws Exception + { + final String scriptId = loadScript( "TRANSFORM_FHIR_OB_BODY_WEIGHT" ).getId().toString(); + final ConstrainedFields fields = new ConstrainedFields( ScriptArg.class, constraintDescriptionResolver ); + final String request = IOUtils.resourceToString( "/org/dhis2/fhir/adapter/fhir/metadata/repository/createScriptArg.json", StandardCharsets.UTF_8 ) + .replace( "$scriptId", API_BASE_URI + "/script/" + scriptId ); + final String location = docMockMvc.perform( post( "/api/scriptArgs" ).header( AUTHORIZATION_HEADER_NAME, DATA_MAPPING_AUTHORIZATION_HEADER_VALUE ) + .contentType( MediaType.APPLICATION_JSON ).content( request ) ) + .andExpect( status().isCreated() ) + .andExpect( header().exists( "Location" ) ) + .andDo( documentationHandler.document( requestFields( + attributes( key( "title" ).value( "Fields for script argument creation" ) ), + fields.withPath( "script" ).description( "The reference to the script resource to which this argument belongs to." ).type( JsonFieldType.STRING ), + fields.withPath( "name" ).description( "The name of the script argument. This is also used inside the script source to access the argument value." ).type( JsonFieldType.STRING ), + fields.withPath( "description" ).description( "The detailed description of the purpose of the argument." ).type( JsonFieldType.STRING ).optional(), + fields.withPath( "dataType" ).description( "The data type of the argument value." ).type( JsonFieldType.STRING ), + fields.withPath( "array" ).description( "Specifies if the argument contains an array of values. The values must be separated by a pipe character." ).type( JsonFieldType.BOOLEAN ), + fields.withPath( "mandatory" ).description( "Specifies if the argument is mandatory and cannot be null when the script is executed." ).type( JsonFieldType.BOOLEAN ), + fields.withPath( "defaultValue" ).description( "The default value of the argument. This may be overridden by the executable script. The value must be convertible to the specified data type. " + + "If the argument is an array, the array values must be separated by pipe characters." ).type( JsonFieldType.STRING ).optional() + ) ) ).andReturn().getResponse().getHeader( "Location" ); + + mockMvc + .perform( get( Objects.requireNonNull( location ) ).header( AUTHORIZATION_HEADER_NAME, DATA_MAPPING_AUTHORIZATION_HEADER_VALUE ) ) + .andExpect( status().isOk() ) + .andExpect( jsonPath( "lastUpdatedBy", is( "2h2maqu827e" ) ) ) + .andExpect( jsonPath( "name", is( "weightUnit" ) ) ) + .andExpect( jsonPath( "description", is( "The resulting weight unit in which the value will be set on the data element." ) ) ) + .andExpect( jsonPath( "dataType", is( "WEIGHT_UNIT" ) ) ) + .andExpect( jsonPath( "array", is( false ) ) ) + .andExpect( jsonPath( "mandatory", is( true ) ) ) + .andExpect( jsonPath( "defaultValue", is( "KILO_GRAM" ) ) ) + .andExpect( jsonPath( "_links.self.href", is( location ) ) ); + } + + @Test + public void readScriptArg() throws Exception + { + final ConstrainedFields fields = new ConstrainedFields( ScriptArg.class, constraintDescriptionResolver ); + final String scriptArgId = loadScriptArg( "d8cd0e7d-7780-45d1-8094-b448b480e6b8" ).getId().toString(); + docMockMvc.perform( get( "/api/scriptArgs/{scriptArgId}", scriptArgId ).header( AUTHORIZATION_HEADER_NAME, DATA_MAPPING_AUTHORIZATION_HEADER_VALUE ) ) + .andExpect( status().isOk() ) + .andDo( documentationHandler.document( links( + linkWithRel( "self" ).description( "Link to this resource itself." ), + linkWithRel( "scriptArg" ).description( "Link to this resource itself." ), + linkWithRel( "script" ).description( "Link to the script to which the resource belongs to." ) ), responseFields( + attributes( key( "title" ).value( "Fields for script argument reading" ) ), + fields.withPath( "createdAt" ).description( "The timestamp when the resource has been created." ).type( JsonFieldType.STRING ), + fields.withPath( "lastUpdatedBy" ).description( "The ID of the user that has updated the user the last time or null if the data has been imported to the database directly." ).type( JsonFieldType.STRING ).optional(), + fields.withPath( "lastUpdatedAt" ).description( "The timestamp when the resource has been updated the last time." ).type( JsonFieldType.STRING ), + fields.withPath( "name" ).description( "The name of the script argument. This is also used inside the script source to access the argument value." ).type( JsonFieldType.STRING ), + fields.withPath( "description" ).description( "The detailed description of the purpose of the argument." ).type( JsonFieldType.STRING ).optional(), + fields.withPath( "dataType" ).description( "The data type of the argument value." ).type( JsonFieldType.STRING ), + fields.withPath( "array" ).description( "Specifies if the argument contains an array of values. The values must be separated by a pipe character." ).type( JsonFieldType.BOOLEAN ), + fields.withPath( "mandatory" ).description( "Specifies if the argument is mandatory and cannot be null when the script is executed." ).type( JsonFieldType.BOOLEAN ), + fields.withPath( "defaultValue" ).description( "Specifies if the argument is mandatory and cannot be null when the script is executed." ).type( JsonFieldType.STRING ).optional(), + subsectionWithPath( "_links" ).description( "Links to other resources" ) + ) ) ); + } + + @Nonnull + protected Script loadScript( @Nonnull String code ) + { + final Script example = new Script(); + example.setCode( code ); + return scriptRepository.findOne( Example.of( example ) ).orElseThrow( () -> new AssertionError( "Script does not exist: " + code ) ); + } + + @Nonnull + protected ScriptArg loadScriptArg( @Nonnull String scriptId ) + { + return scriptArgRepository.findById( UUID.fromString( scriptId ) ).orElseThrow( () -> new AssertionError( "Script source does not exist: " + scriptId ) ); + } +} \ No newline at end of file diff --git a/fhir/src/test/java/org/dhis2/fhir/adapter/fhir/metadata/repository/ScriptRepositoryRestDocsTest.java b/fhir/src/test/java/org/dhis2/fhir/adapter/fhir/metadata/repository/ScriptRepositoryRestDocsTest.java new file mode 100644 index 00000000..55dc91ea --- /dev/null +++ b/fhir/src/test/java/org/dhis2/fhir/adapter/fhir/metadata/repository/ScriptRepositoryRestDocsTest.java @@ -0,0 +1,137 @@ +package org.dhis2.fhir.adapter.fhir.metadata.repository; + +/* + * Copyright (c) 2004-2018, University of Oslo + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * Neither the name of the HISP project nor the names of its contributors may + * be used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +import org.apache.commons.io.IOUtils; +import org.dhis2.fhir.adapter.fhir.AbstractJpaRepositoryRestDocsTest; +import org.dhis2.fhir.adapter.fhir.ConstrainedFields; +import org.dhis2.fhir.adapter.fhir.metadata.model.Script; +import org.hamcrest.Matchers; +import org.junit.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.data.domain.Example; +import org.springframework.http.MediaType; +import org.springframework.restdocs.payload.JsonFieldType; + +import javax.annotation.Nonnull; +import java.nio.charset.StandardCharsets; +import java.util.Objects; + +import static org.hamcrest.Matchers.is; +import static org.springframework.restdocs.hypermedia.HypermediaDocumentation.linkWithRel; +import static org.springframework.restdocs.hypermedia.HypermediaDocumentation.links; +import static org.springframework.restdocs.mockmvc.RestDocumentationRequestBuilders.get; +import static org.springframework.restdocs.mockmvc.RestDocumentationRequestBuilders.post; +import static org.springframework.restdocs.payload.PayloadDocumentation.*; +import static org.springframework.restdocs.snippet.Attributes.attributes; +import static org.springframework.restdocs.snippet.Attributes.key; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*; + +/** + * Tests for {@link ScriptRepository}. + * + * @author volsch + */ +public class ScriptRepositoryRestDocsTest extends AbstractJpaRepositoryRestDocsTest +{ + @Autowired( required = false ) + private ScriptRepository scriptRepository; + + @Test + public void createScript() throws Exception + { + final ConstrainedFields fields = new ConstrainedFields( Script.class, constraintDescriptionResolver ); + final String request = IOUtils.resourceToString( "/org/dhis2/fhir/adapter/fhir/metadata/repository/createScript.json", StandardCharsets.UTF_8 ); + final String location = docMockMvc.perform( post( "/api/scripts" ).header( AUTHORIZATION_HEADER_NAME, DATA_MAPPING_AUTHORIZATION_HEADER_VALUE ) + .contentType( MediaType.APPLICATION_JSON ).content( request ) ) + .andExpect( status().isCreated() ) + .andExpect( header().exists( "Location" ) ) + .andDo( documentationHandler.document( requestFields( + attributes( key( "title" ).value( "Fields for script creation" ) ), + fields.withPath( "name" ).description( "The unique name of the script." ).type( JsonFieldType.STRING ), + fields.withPath( "code" ).description( "The unique code of the script." ).type( JsonFieldType.STRING ), + fields.withPath( "description" ).description( "The detailed description that describes for which purpose the script is used." ).type( JsonFieldType.STRING ).optional(), + fields.withPath( "scriptType" ).description( "The the of the script that describes its purpose." ).type( JsonFieldType.STRING ), + fields.withPath( "returnType" ).description( "The data type of the value that is returned by the script." ).type( JsonFieldType.STRING ), + fields.withPath( "inputType" ).description( "The required data type of the transformation input." ).type( JsonFieldType.STRING ).optional(), + fields.withPath( "outputType" ).description( "The required data type of the transformation output." ).type( JsonFieldType.STRING ).optional(), + fields.withPath( "variables" ).description( "The variables that are required for the script execution." ).type( JsonFieldType.ARRAY ).optional() + ) ) ).andReturn().getResponse().getHeader( "Location" ); + + mockMvc + .perform( get( Objects.requireNonNull( location ) ).header( AUTHORIZATION_HEADER_NAME, DATA_MAPPING_AUTHORIZATION_HEADER_VALUE ) ) + .andExpect( status().isOk() ) + .andExpect( jsonPath( "lastUpdatedBy", is( "2h2maqu827e" ) ) ) + .andExpect( jsonPath( "name", is( "Transforms FHIR Immunization to Y/N data element" ) ) ) + .andExpect( jsonPath( "code", is( "TRANSFORM_FHIR_IMMUNIZATION_YN" ) ) ) + .andExpect( jsonPath( "description", is( "Transforms FHIR Immunization to Y/N data element." ) ) ) + .andExpect( jsonPath( "scriptType", is( "TRANSFORM_TO_DHIS" ) ) ) + .andExpect( jsonPath( "returnType", is( "BOOLEAN" ) ) ) + .andExpect( jsonPath( "inputType", is( "FHIR_IMMUNIZATION" ) ) ) + .andExpect( jsonPath( "outputType", is( "DHIS_EVENT" ) ) ) + .andExpect( jsonPath( "variables", Matchers.contains( "CONTEXT", "INPUT", "OUTPUT" ) ) ) + .andExpect( jsonPath( "_links.self.href", is( location ) ) ); + } + + @Test + public void readScript() throws Exception + { + final ConstrainedFields fields = new ConstrainedFields( Script.class, constraintDescriptionResolver ); + final String scriptId = loadScript( "TRANSFORM_FHIR_IMMUNIZATION_OS" ).getId().toString(); + docMockMvc.perform( get( "/api/scripts/{scriptId}", scriptId ).header( AUTHORIZATION_HEADER_NAME, DATA_MAPPING_AUTHORIZATION_HEADER_VALUE ) ) + .andExpect( status().isOk() ) + .andDo( documentationHandler.document( links( + linkWithRel( "self" ).description( "Link to this resource itself." ), + linkWithRel( "script" ).description( "Link to this resource itself." ), + linkWithRel( "arguments" ).description( "Link to the arguments that are provided as variable args (contains a map with all argument values)." ), + linkWithRel( "sources" ).description( "Link to the source codes of the scripts (multiple scripts for different FHIR versions)." ) ), responseFields( + attributes( key( "title" ).value( "Fields for script reading" ) ), + fields.withPath( "createdAt" ).description( "The timestamp when the resource has been created." ).type( JsonFieldType.STRING ), + fields.withPath( "lastUpdatedBy" ).description( "The ID of the user that has updated the user the last time or null if the data has been imported to the database directly." ).type( JsonFieldType.STRING ).optional(), + fields.withPath( "lastUpdatedAt" ).description( "The timestamp when the resource has been updated the last time." ).type( JsonFieldType.STRING ), + fields.withPath( "name" ).description( "The unique name of the script." ).type( JsonFieldType.STRING ), + fields.withPath( "code" ).description( "The unique code of the script." ).type( JsonFieldType.STRING ), + fields.withPath( "description" ).description( "The detailed description that describes for which purpose the script is used." ).type( JsonFieldType.STRING ).optional(), + fields.withPath( "scriptType" ).description( "The the of the script that describes its purpose." ).type( JsonFieldType.STRING ), + fields.withPath( "returnType" ).description( "The data type of the value that is returned by the script." ).type( JsonFieldType.STRING ), + fields.withPath( "inputType" ).description( "The required data type of the transformation input." ).type( JsonFieldType.STRING ).optional(), + fields.withPath( "outputType" ).description( "The required data type of the transformation output." ).type( JsonFieldType.STRING ).optional(), + fields.withPath( "variables" ).description( "The variables that are required for the script execution." ).type( JsonFieldType.ARRAY ).optional(), + subsectionWithPath( "_links" ).description( "Links to other resources" ) + ) ) ); + } + + @Nonnull + protected Script loadScript( @Nonnull String code ) + { + final Script example = new Script(); + example.setCode( code ); + return scriptRepository.findOne( Example.of( example ) ).orElseThrow( () -> new AssertionError( "Script does not exist: " + code ) ); + } +} \ No newline at end of file diff --git a/fhir/src/test/java/org/dhis2/fhir/adapter/fhir/metadata/repository/ScriptSourceRepositoryRestDocsTest.java b/fhir/src/test/java/org/dhis2/fhir/adapter/fhir/metadata/repository/ScriptSourceRepositoryRestDocsTest.java new file mode 100644 index 00000000..f45697ff --- /dev/null +++ b/fhir/src/test/java/org/dhis2/fhir/adapter/fhir/metadata/repository/ScriptSourceRepositoryRestDocsTest.java @@ -0,0 +1,134 @@ +package org.dhis2.fhir.adapter.fhir.metadata.repository; + +/* + * Copyright (c) 2004-2018, University of Oslo + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * Neither the name of the HISP project nor the names of its contributors may + * be used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +import org.apache.commons.io.IOUtils; +import org.dhis2.fhir.adapter.fhir.AbstractJpaRepositoryRestDocsTest; +import org.dhis2.fhir.adapter.fhir.ConstrainedFields; +import org.dhis2.fhir.adapter.fhir.metadata.model.Script; +import org.dhis2.fhir.adapter.fhir.metadata.model.ScriptSource; +import org.hamcrest.Matchers; +import org.junit.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.data.domain.Example; +import org.springframework.http.MediaType; +import org.springframework.restdocs.payload.JsonFieldType; + +import javax.annotation.Nonnull; +import java.nio.charset.StandardCharsets; +import java.util.Objects; +import java.util.UUID; + +import static org.hamcrest.Matchers.is; +import static org.springframework.restdocs.hypermedia.HypermediaDocumentation.linkWithRel; +import static org.springframework.restdocs.hypermedia.HypermediaDocumentation.links; +import static org.springframework.restdocs.mockmvc.RestDocumentationRequestBuilders.get; +import static org.springframework.restdocs.mockmvc.RestDocumentationRequestBuilders.post; +import static org.springframework.restdocs.payload.PayloadDocumentation.*; +import static org.springframework.restdocs.snippet.Attributes.attributes; +import static org.springframework.restdocs.snippet.Attributes.key; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*; + +/** + * Tests for {@link ScriptSourceRepository}. + * + * @author volsch + */ +public class ScriptSourceRepositoryRestDocsTest extends AbstractJpaRepositoryRestDocsTest +{ + @Autowired( required = false ) + private ScriptRepository scriptRepository; + + @Autowired( required = false ) + private ScriptSourceRepository scriptSourceRepository; + + @Test + public void createScriptSource() throws Exception + { + final String scriptId = loadScript( "TRANSFORM_FHIR_OB_BODY_WEIGHT" ).getId().toString(); + final ConstrainedFields fields = new ConstrainedFields( ScriptSource.class, constraintDescriptionResolver ); + final String request = IOUtils.resourceToString( "/org/dhis2/fhir/adapter/fhir/metadata/repository/createScriptSource.json", StandardCharsets.UTF_8 ).replace( "$scriptId", API_BASE_URI + "/script/" + scriptId ); + final String location = docMockMvc.perform( post( "/api/scriptSources" ).header( AUTHORIZATION_HEADER_NAME, DATA_MAPPING_AUTHORIZATION_HEADER_VALUE ) + .contentType( MediaType.APPLICATION_JSON ).content( request ) ) + .andExpect( status().isCreated() ) + .andExpect( header().exists( "Location" ) ) + .andDo( documentationHandler.document( requestFields( + attributes( key( "title" ).value( "Fields for script source creation" ) ), + fields.withPath( "script" ).description( "The reference to the script to which this source belongs to." ).type( JsonFieldType.STRING ), + fields.withPath( "sourceType" ).description( "The type of the source code (the programming language)." ).type( JsonFieldType.STRING ), + fields.withPath( "sourceText" ).description( "The code of the script in the configured programming language (source type)." ).type( JsonFieldType.STRING ).optional(), + fields.withPath( "fhirVersions" ).description( "The FHIR versions that are supported by this script source." ).type( JsonFieldType.ARRAY ) + ) ) ).andReturn().getResponse().getHeader( "Location" ); + + mockMvc + .perform( get( Objects.requireNonNull( location ) ).header( AUTHORIZATION_HEADER_NAME, DATA_MAPPING_AUTHORIZATION_HEADER_VALUE ) ) + .andExpect( status().isOk() ) + .andExpect( jsonPath( "lastUpdatedBy", is( "2h2maqu827e" ) ) ) + .andExpect( jsonPath( "sourceText", is( "output.setValue(args['dataElement'], vitalSignUtils.getWeight(input.value, args['weightUnit'], args['round']), null, args['override'], context.getFhirRequest().getLastUpdated())" ) ) ) + .andExpect( jsonPath( "sourceType", is( "JAVASCRIPT" ) ) ) + .andExpect( jsonPath( "fhirVersions", Matchers.contains( "DSTU3" ) ) ) + .andExpect( jsonPath( "_links.self.href", is( location ) ) ); + } + + @Test + public void readScriptSource() throws Exception + { + final ConstrainedFields fields = new ConstrainedFields( ScriptSource.class, constraintDescriptionResolver ); + final String scriptSourceId = loadScriptSource( "081c4642-bb83-44ab-b90f-aa206ad347aa" ).getId().toString(); + docMockMvc.perform( get( "/api/scriptSources/{scriptSourceId}", scriptSourceId ).header( AUTHORIZATION_HEADER_NAME, DATA_MAPPING_AUTHORIZATION_HEADER_VALUE ) ) + .andExpect( status().isOk() ) + .andDo( documentationHandler.document( links( + linkWithRel( "self" ).description( "Link to this resource itself." ), + linkWithRel( "scriptSource" ).description( "Link to this resource itself." ), + linkWithRel( "script" ).description( "Link to the script to which the resource belongs to." ) ), responseFields( + attributes( key( "title" ).value( "Fields for script source reading" ) ), + fields.withPath( "createdAt" ).description( "The timestamp when the resource has been created." ).type( JsonFieldType.STRING ), + fields.withPath( "lastUpdatedBy" ).description( "The ID of the user that has updated the user the last time or null if the data has been imported to the database directly." ).type( JsonFieldType.STRING ).optional(), + fields.withPath( "lastUpdatedAt" ).description( "The timestamp when the resource has been updated the last time." ).type( JsonFieldType.STRING ), + fields.withPath( "sourceType" ).description( "The type of the source code (the programming language)." ).type( JsonFieldType.STRING ), + fields.withPath( "sourceText" ).description( "The code of the script in the configured programming language (source type)." ).type( JsonFieldType.STRING ), + fields.withPath( "fhirVersions" ).description( "The FHIR versions that are supported by this script source." ).type( JsonFieldType.ARRAY ), + subsectionWithPath( "_links" ).description( "Links to other resources" ) + ) ) ); + } + + @Nonnull + protected Script loadScript( @Nonnull String code ) + { + final Script example = new Script(); + example.setCode( code ); + return scriptRepository.findOne( Example.of( example ) ).orElseThrow( () -> new AssertionError( "Script does not exist: " + code ) ); + } + + @Nonnull + protected ScriptSource loadScriptSource( @Nonnull String scriptId ) + { + return scriptSourceRepository.findById( UUID.fromString( scriptId ) ).orElseThrow( () -> new AssertionError( "Script source does not exist: " + scriptId ) ); + } +} \ No newline at end of file diff --git a/fhir/src/test/java/org/dhis2/fhir/adapter/fhir/metadata/repository/SystemCodeRepositoryRestDocsTest.java b/fhir/src/test/java/org/dhis2/fhir/adapter/fhir/metadata/repository/SystemCodeRepositoryRestDocsTest.java new file mode 100644 index 00000000..8a2554b1 --- /dev/null +++ b/fhir/src/test/java/org/dhis2/fhir/adapter/fhir/metadata/repository/SystemCodeRepositoryRestDocsTest.java @@ -0,0 +1,145 @@ +package org.dhis2.fhir.adapter.fhir.metadata.repository; + +/* + * Copyright (c) 2004-2018, University of Oslo + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * Neither the name of the HISP project nor the names of its contributors may + * be used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +import org.apache.commons.io.IOUtils; +import org.dhis2.fhir.adapter.fhir.AbstractJpaRepositoryRestDocsTest; +import org.dhis2.fhir.adapter.fhir.ConstrainedFields; +import org.dhis2.fhir.adapter.fhir.metadata.model.Code; +import org.dhis2.fhir.adapter.fhir.metadata.model.System; +import org.dhis2.fhir.adapter.fhir.metadata.model.SystemCode; +import org.junit.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.data.domain.Example; +import org.springframework.data.domain.ExampleMatcher; +import org.springframework.http.MediaType; +import org.springframework.restdocs.payload.JsonFieldType; + +import javax.annotation.Nonnull; +import java.nio.charset.StandardCharsets; +import java.util.Objects; +import java.util.UUID; + +import static org.hamcrest.Matchers.is; +import static org.springframework.restdocs.hypermedia.HypermediaDocumentation.linkWithRel; +import static org.springframework.restdocs.hypermedia.HypermediaDocumentation.links; +import static org.springframework.restdocs.mockmvc.RestDocumentationRequestBuilders.get; +import static org.springframework.restdocs.mockmvc.RestDocumentationRequestBuilders.post; +import static org.springframework.restdocs.payload.PayloadDocumentation.*; +import static org.springframework.restdocs.snippet.Attributes.attributes; +import static org.springframework.restdocs.snippet.Attributes.key; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*; + +/** + * Tests for {@link SystemCodeRepository}. + * + * @author volsch + */ +public class SystemCodeRepositoryRestDocsTest extends AbstractJpaRepositoryRestDocsTest +{ + @Autowired( required = false ) + private CodeRepository codeRepository; + + @Autowired( required = false ) + private SystemRepository systemRepository; + + @Autowired( required = false ) + private SystemCodeRepository systemCodeRepository; + + @Test + public void createSystemCode() throws Exception + { + final String systemId = loadSystem( "SYSTEM_SL_ORGANIZATION" ).getId().toString(); + final String codeId = loadCode( "OU_FT_CH" ).getId().toString(); + final ConstrainedFields fields = new ConstrainedFields( SystemCode.class, constraintDescriptionResolver ); + final String json = IOUtils.resourceToString( "/org/dhis2/fhir/adapter/fhir/metadata/repository/createSystemCode.json", StandardCharsets.UTF_8 ) + .replace( "$systemId", API_BASE_URI + "/systems/" + systemId ).replace( "$codeId", API_BASE_URI + "/codes/" + codeId ); + final String location = docMockMvc.perform( post( "/api/systemCodes" ).header( AUTHORIZATION_HEADER_NAME, CODE_MAPPING_AUTHORIZATION_HEADER_VALUE ) + .contentType( MediaType.APPLICATION_JSON ).content( json ) ) + .andExpect( status().isCreated() ) + .andExpect( header().exists( "Location" ) ) + .andDo( documentationHandler.document( requestFields( + attributes( key( "title" ).value( "Fields for system code creation" ) ), + fields.withPath( "system" ).description( "The reference to the system to which the code belongs to." ).type( JsonFieldType.STRING ), + fields.withPath( "systemCode" ).description( "The code of the system." ).type( JsonFieldType.STRING ), + fields.withPath( "code" ).description( "The reference to the internal code that is used for the system specific code." ).type( JsonFieldType.STRING ) + ) ) ).andReturn().getResponse().getHeader( "Location" ); + + mockMvc + .perform( get( Objects.requireNonNull( location ) ).header( AUTHORIZATION_HEADER_NAME, CODE_MAPPING_AUTHORIZATION_HEADER_VALUE ) ) + .andExpect( status().isOk() ) + .andExpect( jsonPath( "lastUpdatedBy", is( "2h2maqu827d" ) ) ) + .andExpect( jsonPath( "systemCode", is( "982783729" ) ) ) + .andExpect( jsonPath( "_links.self.href", is( location ) ) ); + } + + @Test + public void readSystemCode() throws Exception + { + final ConstrainedFields fields = new ConstrainedFields( SystemCode.class, constraintDescriptionResolver ); + final String systemCodeId = loadSystemCode( "c513935c-9cd2-4357-a679-60f0c79bfacb" ).getId().toString(); + docMockMvc.perform( get( "/api/systemCodes/{systemCodeId}", systemCodeId ).header( AUTHORIZATION_HEADER_NAME, CODE_MAPPING_AUTHORIZATION_HEADER_VALUE ) ) + .andExpect( status().isOk() ) + .andDo( documentationHandler.document( links( + linkWithRel( "self" ).description( "Link to this resource itself." ), + linkWithRel( "systemCode" ).description( "Link to this resource itself." ), + linkWithRel( "system" ).description( "Link to the system resource to which the code belongs to." ), + linkWithRel( "code" ).description( "Link to the internal code that is mapped to the system specific code." ) ), responseFields( + attributes( key( "title" ).value( "Fields for system code reading" ) ), + fields.withPath( "createdAt" ).description( "The timestamp when the resource has been created." ).type( JsonFieldType.STRING ), + fields.withPath( "lastUpdatedBy" ).description( "The ID of the user that has updated the user the last time or null if the data has been imported to the database directly." ).type( JsonFieldType.STRING ).optional(), + fields.withPath( "lastUpdatedAt" ).description( "The timestamp when the resource has been updated the last time." ).type( JsonFieldType.STRING ), + fields.withPath( "systemCode" ).description( "The code of the system." ).type( JsonFieldType.STRING ), + fields.withPath( "systemCodeValue" ).description( "The combination of system URI and code separated by a pipe character (generated, cannot be updated)." ).type( JsonFieldType.STRING ), + subsectionWithPath( "_links" ).description( "Links to other resources" ) + ) ) ); + } + + @Nonnull + protected System loadSystem( @Nonnull String code ) + { + final System example = new System(); + example.setCode( code ); + return systemRepository.findOne( Example.of( example, ExampleMatcher.matching().withIgnorePaths( "enabled" ) ) ).orElseThrow( () -> new AssertionError( "System does not exist: " + code ) ); + } + + @Nonnull + protected Code loadCode( @Nonnull String code ) + { + final Code example = new Code(); + example.setCode( code ); + return codeRepository.findOne( Example.of( example ) ).orElseThrow( () -> new AssertionError( "Code does not exist: " + code ) ); + } + + @Nonnull + protected SystemCode loadSystemCode( @Nonnull String id ) + { + return systemCodeRepository.findById( UUID.fromString( id ) ).orElseThrow( () -> new AssertionError( "System code does not exist: " + id ) ); + } +} \ No newline at end of file diff --git a/fhir/src/test/java/org/dhis2/fhir/adapter/fhir/metadata/repository/SystemRepositoryRestDocsTest.java b/fhir/src/test/java/org/dhis2/fhir/adapter/fhir/metadata/repository/SystemRepositoryRestDocsTest.java new file mode 100644 index 00000000..94beb273 --- /dev/null +++ b/fhir/src/test/java/org/dhis2/fhir/adapter/fhir/metadata/repository/SystemRepositoryRestDocsTest.java @@ -0,0 +1,127 @@ +package org.dhis2.fhir.adapter.fhir.metadata.repository; + +/* + * Copyright (c) 2004-2018, University of Oslo + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * Neither the name of the HISP project nor the names of its contributors may + * be used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +import org.apache.commons.io.IOUtils; +import org.dhis2.fhir.adapter.fhir.AbstractJpaRepositoryRestDocsTest; +import org.dhis2.fhir.adapter.fhir.ConstrainedFields; +import org.dhis2.fhir.adapter.fhir.metadata.model.System; +import org.junit.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.data.domain.Example; +import org.springframework.data.domain.ExampleMatcher; +import org.springframework.http.MediaType; +import org.springframework.restdocs.payload.JsonFieldType; + +import javax.annotation.Nonnull; +import java.util.Objects; + +import static org.hamcrest.Matchers.is; +import static org.springframework.restdocs.hypermedia.HypermediaDocumentation.linkWithRel; +import static org.springframework.restdocs.hypermedia.HypermediaDocumentation.links; +import static org.springframework.restdocs.mockmvc.RestDocumentationRequestBuilders.get; +import static org.springframework.restdocs.mockmvc.RestDocumentationRequestBuilders.post; +import static org.springframework.restdocs.payload.PayloadDocumentation.*; +import static org.springframework.restdocs.snippet.Attributes.attributes; +import static org.springframework.restdocs.snippet.Attributes.key; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*; + +/** + * Tests for {@link SystemRepository}. + * + * @author volsch + */ +public class SystemRepositoryRestDocsTest extends AbstractJpaRepositoryRestDocsTest +{ + @Autowired( required = false ) + private SystemRepository systemRepository; + + @Test + public void createSystem() throws Exception + { + final ConstrainedFields fields = new ConstrainedFields( System.class, constraintDescriptionResolver ); + final String location = docMockMvc.perform( post( "/api/systems" ).header( AUTHORIZATION_HEADER_NAME, CODE_MAPPING_AUTHORIZATION_HEADER_VALUE ) + .contentType( MediaType.APPLICATION_JSON ).content( IOUtils.resourceToByteArray( "/org/dhis2/fhir/adapter/fhir/metadata/repository/createSystem.json" ) ) ) + .andExpect( status().isCreated() ) + .andExpect( header().exists( "Location" ) ) + .andDo( documentationHandler.document( requestFields( + attributes( key( "title" ).value( "Fields for system creation" ) ), + fields.withPath( "name" ).description( "The unique name of the system." ).type( JsonFieldType.STRING ), + fields.withPath( "code" ).description( "The unique code of the system." ).type( JsonFieldType.STRING ), + fields.withPath( "description" ).description( "The detailed description that describes for which purpose the system is used." ).type( JsonFieldType.STRING ).optional(), + fields.withPath( "systemUri" ).description( "The system URI of the system." ).type( JsonFieldType.STRING ), + fields.withPath( "enabled" ).description( "Specifies if this system and its code are enabled." ).type( JsonFieldType.BOOLEAN ), + fields.withPath( "descriptionProtected" ).description( "Specifies if the description contains license information that must not be changed." ).type( JsonFieldType.BOOLEAN ).optional() + ) ) ).andReturn().getResponse().getHeader( "Location" ); + + mockMvc + .perform( get( Objects.requireNonNull( location ) ).header( AUTHORIZATION_HEADER_NAME, CODE_MAPPING_AUTHORIZATION_HEADER_VALUE ) ) + .andExpect( status().isOk() ) + .andExpect( jsonPath( "lastUpdatedBy", is( "2h2maqu827d" ) ) ) + .andExpect( jsonPath( "name", is( "Sierra Leone Patient" ) ) ) + .andExpect( jsonPath( "code", is( "SYSTEM_SL_PATIENT" ) ) ) + .andExpect( jsonPath( "description", is( "All Sierra Leone patients." ) ) ) + .andExpect( jsonPath( "systemUri", is( "http://example.sl/patients" ) ) ) + .andExpect( jsonPath( "enabled", is( true ) ) ) + .andExpect( jsonPath( "descriptionProtected", is( false ) ) ) + .andExpect( jsonPath( "_links.self.href", is( location ) ) ); + } + + @Test + public void readSystem() throws Exception + { + final ConstrainedFields fields = new ConstrainedFields( System.class, constraintDescriptionResolver ); + final String systemId = loadSystem( "SYSTEM_SL_ORGANIZATION" ).getId().toString(); + docMockMvc.perform( get( "/api/systems/{systemId}", systemId ).header( AUTHORIZATION_HEADER_NAME, CODE_MAPPING_AUTHORIZATION_HEADER_VALUE ) ) + .andExpect( status().isOk() ) + .andDo( documentationHandler.document( links( + linkWithRel( "self" ).description( "Link to this resource itself." ), + linkWithRel( "system" ).description( "Link to this resource itself." ) ), responseFields( + attributes( key( "title" ).value( "Fields for system reading" ) ), + fields.withPath( "createdAt" ).description( "The timestamp when the resource has been created." ).type( JsonFieldType.STRING ), + fields.withPath( "lastUpdatedBy" ).description( "The ID of the user that has updated the user the last time or null if the data has been imported to the database directly." ).type( JsonFieldType.STRING ).optional(), + fields.withPath( "lastUpdatedAt" ).description( "The timestamp when the resource has been updated the last time." ).type( JsonFieldType.STRING ), + fields.withPath( "name" ).description( "The unique name of the system." ).type( JsonFieldType.STRING ), + fields.withPath( "code" ).description( "The unique code of the system." ).type( JsonFieldType.STRING ), + fields.withPath( "description" ).description( "The detailed description that describes for which purpose the system is used." ).type( JsonFieldType.STRING ).optional(), + fields.withPath( "systemUri" ).description( "The system URI of the system." ).type( JsonFieldType.STRING ), + fields.withPath( "enabled" ).description( "Specifies if this system and its code are enabled." ).type( JsonFieldType.BOOLEAN ), + fields.withPath( "descriptionProtected" ).description( "Specifies if the description contains license information that must not be changed." ).type( JsonFieldType.BOOLEAN ).optional(), + subsectionWithPath( "_links" ).description( "Links to other resources" ) + ) ) ); + } + + @Nonnull + protected System loadSystem( @Nonnull String code ) + { + final System example = new System(); + example.setCode( code ); + return systemRepository.findOne( Example.of( example, ExampleMatcher.matching().withIgnorePaths( "enabled" ) ) ).orElseThrow( () -> new AssertionError( "System does not exist: " + code ) ); + } +} \ No newline at end of file diff --git a/fhir/src/test/resources/data.sql b/fhir/src/test/resources/data.sql index adaecbc3..b862f8e6 100644 --- a/fhir/src/test/resources/data.sql +++ b/fhir/src/test/resources/data.sql @@ -27,4 +27,70 @@ -- INSERT INTO fhir_code_category(id, version, created_at, last_updated_at, last_updated_by, name, code, description) -VALUES (RANDOM_UUID(), 0, CURRENT_TIMESTAMP(), CURRENT_TIMESTAMP(), '2h2maqu827d', 'Organization Unit', 'ORGANIZATION_UNIT', 'Includes the mapping for organization units.'); +VALUES ('8673a315dd274e4cbb8b1212808d4ca1', 0, CURRENT_TIMESTAMP(), CURRENT_TIMESTAMP(), '2h2maqu827d', 'Organization Unit', 'ORGANIZATION_UNIT', 'Includes the mapping for organization units.'); + +INSERT INTO fhir_code(id, version, created_at, last_updated_at, last_updated_by, name, code, mapped_code, description, code_category_id) +VALUES ('348d9391c77048538cdff3c5c6830485', 0, CURRENT_TIMESTAMP(), CURRENT_TIMESTAMP(), '2h2maqu827d', 'Central Hospital Freetown', 'OU_FT_CH', 'OU_6125', 'Organization unit Central Hospital in Freetown.', '8673a315dd274e4cbb8b1212808d4ca1'); + +INSERT INTO fhir_constant (id, version, created_at, last_updated_at, last_updated_by, category, name, code, data_type, value) +VALUES ('fa4a3a0eca4640e4b8323aec96bed55e', 0, CURRENT_TIMESTAMP(), CURRENT_TIMESTAMP(), '2h2maqu827d', 'GENDER', 'Gender Male', 'GENDER_MALE', 'STRING', 'Male'); + +INSERT INTO fhir_script (id, version, created_at, last_updated_at, last_updated_by, code, name, description, script_type, return_type, input_type, output_type) +VALUES ('f18acd12bc854f79935d353904eadc0b', 0, CURRENT_TIMESTAMP(), CURRENT_TIMESTAMP(), '2h2maqu827d', 'TRANSFORM_FHIR_IMMUNIZATION_OS', 'Transforms FHIR Immunization to option set data element', 'Transforms FHIR Immunization to an option set data element.', +'TRANSFORM_TO_DHIS', 'BOOLEAN', 'FHIR_IMMUNIZATION', 'DHIS_EVENT'); +INSERT INTO fhir_script_variable (script_id, variable) VALUES ('f18acd12bc854f79935d353904eadc0b', 'CONTEXT'); +INSERT INTO fhir_script_variable (script_id, variable) VALUES ('f18acd12bc854f79935d353904eadc0b', 'INPUT'); +INSERT INTO fhir_script_variable (script_id, variable) VALUES ('f18acd12bc854f79935d353904eadc0b', 'OUTPUT'); +INSERT INTO fhir_script_argument(id, version, created_at, last_updated_at, last_updated_by, script_id, name, data_type, mandatory, array_value, default_value, description) +VALUES ('44134ba8d77f4c4d90c6b434ffbe7958', 0, CURRENT_TIMESTAMP(), CURRENT_TIMESTAMP(), '2h2maqu827d', 'f18acd12bc854f79935d353904eadc0b', +'dataElement', 'DATA_ELEMENT_REF', TRUE, FALSE, NULL, 'Data element with given vaccine on which option set value must be set.'); +INSERT INTO fhir_script_argument(id, version, created_at, last_updated_at, last_updated_by, script_id, name, data_type, mandatory, array_value, default_value, description) +VALUES ('404ae6f6618749148f4b80a72764c1d8', 0, CURRENT_TIMESTAMP(), CURRENT_TIMESTAMP(), '2h2maqu827d', 'f18acd12bc854f79935d353904eadc0b', +'optionValuePattern', 'PATTERN', FALSE, FALSE, NULL, 'Regular expression pattern to extract subsequent integer option value from option code. If the pattern is not specified the whole code will be used as an integer value.'); +INSERT INTO fhir_script_source (id, version, created_at, last_updated_at, last_updated_by, script_id, source_text, source_type) +VALUES ('081c4642bb8344abb90faa206ad347aa', 0, CURRENT_TIMESTAMP(), CURRENT_TIMESTAMP(), '2h2maqu827d', 'f18acd12bc854f79935d353904eadc0b', +'output.setIntegerOptionValue(args[''dataElement''], immunizationUtils.getMaxDoseSequence(input), 1, false, args[''optionValuePattern''], (input.hasPrimarySource()?!input.getPrimarySource():null))', 'JAVASCRIPT'); +INSERT INTO fhir_script_source_version (script_source_id, fhir_version) +VALUES ('081c4642bb8344abb90faa206ad347aa', 'DSTU3'); + +INSERT INTO fhir_executable_script (id, version, created_at, last_updated_at, last_updated_by, script_id, name, code, description) +VALUES ('1a2950cf08424dd39453284fb08789d3', 0, CURRENT_TIMESTAMP(), CURRENT_TIMESTAMP(), '2h2maqu827d', 'f18acd12bc854f79935d353904eadc0b', 'CP: OPV Dose', 'CP_OPV_DOSE', 'Transforms FHIR Immunization for OPV vaccines.'); +INSERT INTO fhir_executable_script_argument(id, executable_script_id, script_argument_id, override_value, enabled) +VALUES ('4a8ba21510e946f2921fda3973836119', '1a2950cf08424dd39453284fb08789d3', '44134ba8d77f4c4d90c6b434ffbe7958', 'CODE:DE_2006104', TRUE); + +INSERT INTO fhir_script (id, version, created_at, last_updated_at, last_updated_by, code, name, description, script_type, return_type, input_type, output_type) +VALUES ('f1da6937e2fe47a4b0f38bbff7818ee1', 0, CURRENT_TIMESTAMP(), CURRENT_TIMESTAMP(), '2h2maqu827d', 'TRANSFORM_FHIR_OB_BODY_WEIGHT', 'Transforms FHIR Observation Body Weight', 'Transforms FHIR Observation Body Weight to a data element and performs weight unit conversion.', +'TRANSFORM_TO_DHIS', 'BOOLEAN', 'FHIR_OBSERVATION', 'DHIS_EVENT'); +INSERT INTO fhir_script_variable (script_id, variable) VALUES ('f1da6937e2fe47a4b0f38bbff7818ee1', 'CONTEXT'); +INSERT INTO fhir_script_variable (script_id, variable) VALUES ('f1da6937e2fe47a4b0f38bbff7818ee1', 'INPUT'); +INSERT INTO fhir_script_variable (script_id, variable) VALUES ('f1da6937e2fe47a4b0f38bbff7818ee1', 'OUTPUT'); +INSERT INTO fhir_script_argument(id, version, created_at, last_updated_at, last_updated_by, script_id, name, data_type, mandatory, array_value, default_value, description) +VALUES ('0767919959ae45309411ac5814102372', 0, CURRENT_TIMESTAMP(), CURRENT_TIMESTAMP(), '2h2maqu827d', 'f1da6937e2fe47a4b0f38bbff7818ee1', +'dataElement', 'DATA_ELEMENT_REF', TRUE, FALSE, NULL, 'Data element on which the body weight must be set.'); +INSERT INTO fhir_script_argument(id, version, created_at, last_updated_at, last_updated_by, script_id, name, data_type, mandatory, array_value, default_value, description) +VALUES ('1ef4f760de9a4c29a321a8eee5c52313', 0, CURRENT_TIMESTAMP(), CURRENT_TIMESTAMP(), '2h2maqu827d', 'f1da6937e2fe47a4b0f38bbff7818ee1', +'override', 'BOOLEAN', TRUE, FALSE, 'true', 'Specifies if an existing value should be overridden.'); +INSERT INTO fhir_script_argument(id, version, created_at, last_updated_at, last_updated_by, script_id, name, data_type, mandatory, array_value, default_value, description) +VALUES ('d8cd0e7d778045d18094b448b480e6b8', 0, CURRENT_TIMESTAMP(), CURRENT_TIMESTAMP(), '2h2maqu827d', 'f1da6937e2fe47a4b0f38bbff7818ee1', +'round', 'BOOLEAN', TRUE, FALSE, 'true', 'Specifies if the resulting value should be rounded.'); + +INSERT INTO fhir_system (id, version, created_at, last_updated_at, last_updated_by, name, code, system_uri, description_protected, enabled) +VALUES ('2dd51309331940d29a1fbe2a102df4a7', 0, CURRENT_TIMESTAMP(), CURRENT_TIMESTAMP(), '2h2maqu827d', 'Sierra Leone Location', 'SYSTEM_SL_LOCATION', 'http://example.sl/locations', FALSE, TRUE); +INSERT INTO fhir_system (id, version, created_at, last_updated_at, last_updated_by, name, code, system_uri, description_protected, enabled) +VALUES ('c4e9ac6acc8f4c73aab60fa6775c0ca3', 0, CURRENT_TIMESTAMP(), CURRENT_TIMESTAMP(), '2h2maqu827d', 'Sierra Leone Organization', 'SYSTEM_SL_ORGANIZATION', 'http://example.sl/organizations', FALSE, TRUE); + +INSERT INTO fhir_system_code(id, version, created_at, last_updated_at, last_updated_by, code_id, system_id, system_code, system_code_value) +VALUES ('c513935c9cd24357a67960f0c79bfacb', 0, CURRENT_TIMESTAMP(), CURRENT_TIMESTAMP(), '2h2maqu827d', '348d9391c77048538cdff3c5c6830485', 'c4e9ac6acc8f4c73aab60fa6775c0ca3', '982737', 'http://example.sl/organizations|982737'); + +INSERT INTO fhir_remote_subscription(id, version, created_at, last_updated_at, last_updated_by, name, code, description, fhir_version, web_hook_authorization_header, +dhis_authentication_method, dhis_username, dhis_password, remote_base_url, tolerance_millis, logging, verbose_logging, adapter_base_url, subscription_type, enabled, locked) +VALUES ('73cd99c50ca842ada53b1891fccce08f', 0, CURRENT_TIMESTAMP(), CURRENT_TIMESTAMP(), '2h2maqu827d', 'HAPI FHIR JPA Server', 'DEFAULT_SUBSCRIPTION', 'HAPI FHIR JPA Server.', 'DSTU3', +'Bearer jhsj832jDShf8ehShdu7ejhDhsilwmdsgs', 'BASIC', 'admin', 'district', 'http://localhost:8082/hapifhirjpaserverexample/baseDstu3', 60000, FALSE, FALSE, +'http://localhost:8081', 'REST_HOOK_WITH_JSON_PAYLOAD', TRUE, FALSE); +INSERT INTO fhir_remote_subscription_header (remote_subscription_id, name, value, secure) +VALUES ('73cd99c50ca842ada53b1891fccce08f', 'Authorization', 'Bearer jshru38jsHdsdfy38sh38H3d', TRUE); + +INSERT INTO fhir_remote_subscription_resource (id, version, created_at, last_updated_at, last_updated_by, remote_subscription_id, fhir_resource_type, fhir_criteria_parameters, description) +VALUES ('667bfa41867c479686b6eb9f9ed4dc94', 0, CURRENT_TIMESTAMP(), CURRENT_TIMESTAMP(), '2h2maqu827d', '73cd99c50ca842ada53b1891fccce08f', 'PATIENT', '_format=json', 'Subscription for all Patients.'); +INSERT INTO fhir_remote_subscription_system (id, version, created_at, last_updated_at, last_updated_by, remote_subscription_id, fhir_resource_type, system_id) +VALUES ('ea9804a39e824d0d9cd2e417b32b1c0c', 0, CURRENT_TIMESTAMP(), CURRENT_TIMESTAMP(), '2h2maqu827d', '73cd99c50ca842ada53b1891fccce08f', 'ORGANIZATION', 'c4e9ac6acc8f4c73aab60fa6775c0ca3'); diff --git a/fhir/src/test/resources/org/dhis2/fhir/adapter/fhir/metadata/repository/createCode.json b/fhir/src/test/resources/org/dhis2/fhir/adapter/fhir/metadata/repository/createCode.json new file mode 100644 index 00000000..41b63e51 --- /dev/null +++ b/fhir/src/test/resources/org/dhis2/fhir/adapter/fhir/metadata/repository/createCode.json @@ -0,0 +1,7 @@ +{ + "name": "Test Code", + "code": "TEST_CODE", + "mappedCode": "MAPPED_TEST_CODE", + "description": "This is a test code.", + "codeCategory": "$codeCategory" +} diff --git a/fhir/src/test/resources/org/dhis2/fhir/adapter/fhir/metadata/repository/createConstant.json b/fhir/src/test/resources/org/dhis2/fhir/adapter/fhir/metadata/repository/createConstant.json new file mode 100644 index 00000000..b5b1e18c --- /dev/null +++ b/fhir/src/test/resources/org/dhis2/fhir/adapter/fhir/metadata/repository/createConstant.json @@ -0,0 +1,8 @@ +{ + "category": "GENDER", + "name": "Gender Female", + "description": "Constant for Gender option value as it is used by DHIS2.", + "code": "GENDER_FEMALE", + "dataType": "STRING", + "value": "Female" +} diff --git a/fhir/src/test/resources/org/dhis2/fhir/adapter/fhir/metadata/repository/createExecutableScript.json b/fhir/src/test/resources/org/dhis2/fhir/adapter/fhir/metadata/repository/createExecutableScript.json new file mode 100644 index 00000000..047699dd --- /dev/null +++ b/fhir/src/test/resources/org/dhis2/fhir/adapter/fhir/metadata/repository/createExecutableScript.json @@ -0,0 +1,17 @@ +{ + "script": "$scriptId", + "name": "CP: Birth Weight", + "code": "CP_BIRTH_WEIGHT", + "overrideArguments": [ + { + "argument": "$dataElementArgId", + "overrideValue": "CODE:DE_2005736", + "enabled": true + }, + { + "argument": "$roundArgId", + "overrideValue": "false", + "enabled": true + } + ] +} diff --git a/fhir/src/test/resources/org/dhis2/fhir/adapter/fhir/metadata/repository/createRemoteSubscription.json b/fhir/src/test/resources/org/dhis2/fhir/adapter/fhir/metadata/repository/createRemoteSubscription.json new file mode 100644 index 00000000..ec30c698 --- /dev/null +++ b/fhir/src/test/resources/org/dhis2/fhir/adapter/fhir/metadata/repository/createRemoteSubscription.json @@ -0,0 +1,24 @@ +{ + "name": "Main Subscription", + "code": "MAIN_SUBSCRIPTION", + "description": "Main FHIR service on which the adapter has subscriptions.", + "enabled": true, + "locked": false, + "fhirVersion": "DSTU3", + "toleranceMillis": 2000, + "autoCreatedSubscriptionResources": [ "PATIENT" ], + "adapterEndpoint": { + "baseUrl": "http://localhist:8081", + "subscriptionType": "REST_HOOK", + "authorizationHeader": "Bearer 98a7558102b7bdc4da5c8f74ca63958c498b4bd9231bd3b0cc" + }, + "dhisEndpoint": { + "authenticationMethod": "BASIC", + "username": "admin", + "password": "district" + }, + "fhirEndpoint": { + "baseUrl": "http://localhost:8082/hapi-fhir-jpaserver-example/baseDstu3", + "headers": [ { "name": "Authorization", "value": "Bearer 1196cc744b416a2b91c8239c1a1e251c139d2a89df86427241", "secure": true } ] + } +} diff --git a/fhir/src/test/resources/org/dhis2/fhir/adapter/fhir/metadata/repository/createRemoteSubscriptionResource.json b/fhir/src/test/resources/org/dhis2/fhir/adapter/fhir/metadata/repository/createRemoteSubscriptionResource.json new file mode 100644 index 00000000..6946536f --- /dev/null +++ b/fhir/src/test/resources/org/dhis2/fhir/adapter/fhir/metadata/repository/createRemoteSubscriptionResource.json @@ -0,0 +1,6 @@ +{ + "remoteSubscription": "$remoteSubscriptionId", + "fhirResourceType": "IMMUNIZATION", + "description": "Subscription for all immunizations.", + "fhirCriteriaParameters": null +} diff --git a/fhir/src/test/resources/org/dhis2/fhir/adapter/fhir/metadata/repository/createRemoteSubscriptionSystem.json b/fhir/src/test/resources/org/dhis2/fhir/adapter/fhir/metadata/repository/createRemoteSubscriptionSystem.json new file mode 100644 index 00000000..538de138 --- /dev/null +++ b/fhir/src/test/resources/org/dhis2/fhir/adapter/fhir/metadata/repository/createRemoteSubscriptionSystem.json @@ -0,0 +1,6 @@ +{ + "remoteSubscription": "$remoteSubscriptionId", + "fhirResourceType": "LOCATION", + "system": "$systemId", + "codePrefix": "LOC_" +} diff --git a/fhir/src/test/resources/org/dhis2/fhir/adapter/fhir/metadata/repository/createScript.json b/fhir/src/test/resources/org/dhis2/fhir/adapter/fhir/metadata/repository/createScript.json new file mode 100644 index 00000000..2fe5eb91 --- /dev/null +++ b/fhir/src/test/resources/org/dhis2/fhir/adapter/fhir/metadata/repository/createScript.json @@ -0,0 +1,10 @@ +{ + "name": "Transforms FHIR Immunization to Y/N data element", + "code": "TRANSFORM_FHIR_IMMUNIZATION_YN", + "description": "Transforms FHIR Immunization to Y/N data element.", + "scriptType": "TRANSFORM_TO_DHIS", + "returnType": "BOOLEAN", + "inputType": "FHIR_IMMUNIZATION", + "outputType": "DHIS_EVENT", + "variables": [ "CONTEXT", "INPUT", "OUTPUT" ] +} diff --git a/fhir/src/test/resources/org/dhis2/fhir/adapter/fhir/metadata/repository/createScriptArg.json b/fhir/src/test/resources/org/dhis2/fhir/adapter/fhir/metadata/repository/createScriptArg.json new file mode 100644 index 00000000..b6338da8 --- /dev/null +++ b/fhir/src/test/resources/org/dhis2/fhir/adapter/fhir/metadata/repository/createScriptArg.json @@ -0,0 +1,9 @@ +{ + "script": "$scriptId", + "name": "weightUnit", + "description": "The resulting weight unit in which the value will be set on the data element.", + "dataType": "WEIGHT_UNIT", + "mandatory": true, + "array": false, + "defaultValue": "KILO_GRAM" +} diff --git a/fhir/src/test/resources/org/dhis2/fhir/adapter/fhir/metadata/repository/createScriptSource.json b/fhir/src/test/resources/org/dhis2/fhir/adapter/fhir/metadata/repository/createScriptSource.json new file mode 100644 index 00000000..2c123f6f --- /dev/null +++ b/fhir/src/test/resources/org/dhis2/fhir/adapter/fhir/metadata/repository/createScriptSource.json @@ -0,0 +1,6 @@ +{ + "script": "$scriptId", + "sourceType": "JAVASCRIPT", + "sourceText": "output.setValue(args['dataElement'], vitalSignUtils.getWeight(input.value, args['weightUnit'], args['round']), null, args['override'], context.getFhirRequest().getLastUpdated())", + "fhirVersions": [ "DSTU3" ] +} diff --git a/fhir/src/test/resources/org/dhis2/fhir/adapter/fhir/metadata/repository/createSystem.json b/fhir/src/test/resources/org/dhis2/fhir/adapter/fhir/metadata/repository/createSystem.json new file mode 100644 index 00000000..07a36a14 --- /dev/null +++ b/fhir/src/test/resources/org/dhis2/fhir/adapter/fhir/metadata/repository/createSystem.json @@ -0,0 +1,8 @@ +{ + "name": "Sierra Leone Patient", + "code": "SYSTEM_SL_PATIENT", + "description": "All Sierra Leone patients.", + "descriptionProtected": false, + "systemUri": "http://example.sl/patients", + "enabled": true +} diff --git a/fhir/src/test/resources/org/dhis2/fhir/adapter/fhir/metadata/repository/createSystemCode.json b/fhir/src/test/resources/org/dhis2/fhir/adapter/fhir/metadata/repository/createSystemCode.json new file mode 100644 index 00000000..8c123048 --- /dev/null +++ b/fhir/src/test/resources/org/dhis2/fhir/adapter/fhir/metadata/repository/createSystemCode.json @@ -0,0 +1,5 @@ +{ + "system": "$systemId", + "code": "$codeId", + "systemCode": "982783729" +} \ No newline at end of file diff --git a/fhir/src/test/resources/test.properties b/fhir/src/test/resources/test.properties index 34c38a22..bfef2a46 100644 --- a/fhir/src/test/resources/test.properties +++ b/fhir/src/test/resources/test.properties @@ -27,10 +27,12 @@ # -spring.datasource.url=jdbc:h2:mem:testdb +spring.datasource.url=jdbc:h2:mem:testdb;DB_CLOSE_DELAY=-1 spring.datasource.username=sa spring.datasource.driver-class-name=org.h2.Driver +spring.jpa.open-in-view=false +spring.jpa.show-sql=true spring.jpa.properties.hibernate.jdbc.time_zone=UTC spring.flyway.enabled=false diff --git a/pom.xml b/pom.xml index bb613d4e..3c5194a2 100644 --- a/pom.xml +++ b/pom.xml @@ -63,7 +63,7 @@ 1.8 1.8 - 3.5.0 + 3.6.0 2.0.1.RELEASE