Skip to content

Commit

Permalink
Added more documentation for REST interfaces.
Browse files Browse the repository at this point in the history
  • Loading branch information
volsch committed Nov 13, 2018
1 parent dfcf16a commit 2722571
Show file tree
Hide file tree
Showing 94 changed files with 3,890 additions and 345 deletions.
7 changes: 6 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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")

Expand Down Expand Up @@ -117,3 +117,8 @@ For the initial setup of the Adapter a simple user interface is provided. To acc

With the default configuration the initial setup user interface can be accessed in any web browser by using http://localhost:8081/setup. The web browser will ask for a username and password of a DHIS2 user that has privilege F_SYSTEM_SETTING. After
successful authentication a setup form will be displayed with further instructions and examples. The initial setup can be made once only and the initial setup form will not be accessible anymore.

### API for Administration and Mapping
The adapter provides REST interfaces for administration and mapping. The documentation is currently generated automatically when building the adapter. Unit test execution must not be skipped in this case when building the adapter. The documentation can be
found at docs/api-guide.html. If the Adapter has been started by command line without changing the port, then the guide is available at http://localhost:8081/docs/api-guide.html.

Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,7 @@ protected void configure( @Nonnull HttpSecurity http ) throws Exception
.antMatchers( HttpMethod.GET, "/favicon.ico" ).permitAll()
.antMatchers( HttpMethod.GET, "/actuator/health" ).permitAll()
.antMatchers( HttpMethod.GET, "/actuator/info" ).permitAll()
.antMatchers( HttpMethod.GET, "/docs/**" ).permitAll()
.antMatchers( HttpMethod.OPTIONS, "/api/**" ).permitAll()
.antMatchers( "/actuator/**" ).hasRole( AdapterAuthorities.ADMINISTRATION_AUTHORITY )
.anyRequest().authenticated()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,6 @@
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.TreeSet;
import java.util.stream.Collectors;

/**
Expand Down Expand Up @@ -164,7 +163,7 @@ private void createRemoteSubscription( @Nonnull RemoteSubscriptionSetup setup, @
if ( StringUtils.isNotBlank( setup.getFhirSetup().getHeaderName() ) &&
StringUtils.isNotBlank( setup.getFhirSetup().getHeaderValue() ) )
{
fhirEndpoint.setHeaders( new TreeSet<>( Collections.singleton( new RequestHeader(
fhirEndpoint.setHeaders( new ArrayList<>( Collections.singleton( new RequestHeader(
StringUtils.trim( setup.getFhirSetup().getHeaderName() ),
StringUtils.trim( setup.getFhirSetup().getHeaderValue() ),
AUTHORIZATION_HEADER_NAME.equalsIgnoreCase( StringUtils.trim( setup.getFhirSetup().getHeaderName() ) ) ) ) ) );
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,20 +32,21 @@
import com.fasterxml.jackson.databind.type.TypeFactory;
import com.fasterxml.jackson.databind.util.Converter;

import javax.annotation.Nullable;
import java.util.ArrayList;
import java.util.List;

/**
* 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.
* Converts persistent bags to a list. This is required when class name is serialized
* and used list class cannot be instantiated otherwise.
*
* @author volsch
*/
public class PersistentBagConverter implements Converter<List<?>, List<?>>
public class PersistentBagConverter implements Converter<List<?>, ArrayList<?>>
{
@Override
public List<?> convert( List<?> value )
@Nullable
public ArrayList<?> convert( @Nullable List<?> value )
{
if ( value == null )
{
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
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 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;

/**
* Validation that a value is a supported value.
*
* @author volsch
*/
@Constraint( validatedBy = EnumValueValidator.class )
@Target( { ElementType.FIELD } )
@Retention( RetentionPolicy.RUNTIME )
public @interface EnumValue
{
String message() default "Supported enum values {value}, unsupported {unsupported}";

Class<? extends Enum<?>> value();

String[] unsupported() default {};

String[] supported() default {};

Class<?>[] groups() default {};

Class<? extends Payload>[] payload() default {};
}
Original file line number Diff line number Diff line change
@@ -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<EnumValue, Object>
{
private Set<Enum<?>> 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<Enum<?>> values;
if ( value instanceof Collection )
{
values = (Collection<Enum<?>>) 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<Enum<?>> getSupported( @Nonnull Class<? extends Enum<?>> enumClass, @Nonnull String[] unsupportedValues, @Nonnull String[] supportedValues )
{
final Set<Enum<?>> unsupported = new HashSet<>();
for ( final String value : unsupportedValues )
{
unsupported.add( Enum.valueOf( (Class<? extends Enum>) enumClass, value ) );
}
final SortedSet<Enum<?>> supported = new TreeSet<>( Comparator.comparing( Enum::name ) );
if ( supportedValues.length > 0 )
{
for ( final String value : supportedValues )
{
supported.add( Enum.valueOf( (Class<? extends Enum>) enumClass, value ) );
}
}
else
{
supported.addAll( Arrays.asList( enumClass.getEnumConstants() ) );
}
supported.removeAll( unsupported );
return supported;
}
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package org.dhis2.fhir.adapter.setup;
package org.dhis2.fhir.adapter.validator;

/*
* Copyright (c) 2004-2018, University of Oslo
Expand Down Expand Up @@ -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 {};

Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package org.dhis2.fhir.adapter.setup;
package org.dhis2.fhir.adapter.validator;

/*
* Copyright (c) 2004-2018, University of Oslo
Expand Down Expand Up @@ -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;
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package org.dhis2.fhir.adapter.setup;
package org.dhis2.fhir.adapter.validator;

/*
* Copyright (c) 2004-2018, University of Oslo
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package org.dhis2.fhir.adapter.setup;
package org.dhis2.fhir.adapter.validator;

/*
* Copyright (c) 2004-2018, University of Oslo
Expand Down Expand Up @@ -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;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,9 +29,15 @@
*/

import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonProperty;
import org.apache.commons.lang3.StringUtils;
import org.dhis2.fhir.adapter.validator.EnumValue;

import javax.annotation.Nonnull;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Size;
import java.io.Serializable;
import java.util.Objects;

Expand All @@ -46,8 +52,14 @@ public class Reference implements Serializable
{
private static final long serialVersionUID = 6049184293580457755L;

public static final int MAX_VALUE_LENGTH = 200;

@NotBlank
@Size( max = MAX_VALUE_LENGTH )
private final String value;

@NotNull
@EnumValue( ReferenceType.class )
private final ReferenceType type;

@Nonnull
Expand Down Expand Up @@ -82,6 +94,12 @@ public ReferenceType getType()
return type;
}

@JsonIgnore
public boolean isValid()
{
return StringUtils.isNotBlank( getValue() ) && (StringUtils.length( getValue() ) <= MAX_VALUE_LENGTH);
}

@Override
public boolean equals( Object o )
{
Expand Down
5 changes: 5 additions & 0 deletions fhir/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,11 @@
<configuration>
<backend>html</backend>
<doctype>book</doctype>
<logHandler>
<failIf>
<severity>WARN</severity>
</failIf>
</logHandler>
</configuration>
</execution>
</executions>
Expand Down
Loading

0 comments on commit 2722571

Please sign in to comment.